图像处理
2023-12-03
使用 imagemagick 来做图片处理:
for file in *.jpg *.png; do
exif=$(identify -format "%[EXIF:*]" "$file")
if [ -n "$exif" ]; then
echo "Handling $file"
echo $exif
mogrify -strip "$file"
else
# echo "Ignore $file"
:
fi
done
Exif 相关信息在我之前的文章《Linux 工具箱: exiftool》中有提到。
Python PIL 图像处理
2021-09-30
效果:

图像处理 去噪
2021-07-10
最近在学习图像处理,然后看到一个概念:连通域,并且了解到了一些相关的算法:two pass,seed filling。
学习的时候是自己写方法实现,今天了解到了 CV 中有一个方法可以简单的计算连通域,然后写了一个简单的函数作为 Demo。
Python OpenCV 霍夫变换 图像处理
2021-06-04
原图(内容成 15° 娇):

原理
里面设计很多知识点,我一个都不知道,涉及图像处理,数学计算,信号变换等多个方面。
但我查到的资料似乎和下面两个概念相关。
如果看到这篇文章的人了解其中原理能够跟我讲讲 (ninedoors#126),万分感激!
傅立叶变换, Fourier Transform, FT
傅里叶变换是一种线性积分变换,用于信号在时域(或空域)和频域之间的变换,在物理学和工程学中有许多应用。
我的理解是用多个三角函数来表示一个变化曲线。
霍夫变换, Hough Transform
霍夫变换是一种特征提取,被广泛应用在图像分析、电脑视觉以及数位影像处理。霍夫变换是用来辨别找出物件中的特征,例如:线条。他的算法流程大致如下,给定一个物件、要辨别的形状的种类,算法会在参数空间中执行投票来决定物体的形状,而这是由累加空间(accumulator space)里的局部最大值来决定。
维基百科说的很清楚了,用来分析图片特征,识别出其中的形状。
步骤
- 原图(灰度处理 + 尺寸裁剪),减少无关数据,减少计算量
- 傅里叶变换,得到频域图像
- 利用霍夫变换做直线检测
- 计算倾斜角度
- 旋转校正
代码
import logging
import math
import cv2
import numpy as np
from numpy.lib.function_base import average
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s [%(name)s:%(funcName)s#%(lineno)s] %(message)s')
LOG = logging.getLogger(__name__)
# 1、灰度化读取文件
filepath = '/tmp/rotated.png'
img = cv2.imread(filepath, 0)
if img is None:
LOG.error('image not exists!')
exit()
# 2、图像延扩
h, w = img.shape[:2]
new_h = cv2.getOptimalDFTSize(h) # 傅里叶最优尺寸
new_w = cv2.getOptimalDFTSize(w)
right = new_w - w
bottom = new_h - h
# 边界扩充 cv2.copyMakeBorder(src, top, bottom, left, right, borderType, dst=None)
# BORDER_CONSTANT 常量,增加的变量通通为 value
# BORDER_REFLICATE 用边界的颜色填充
# BORDER_REFLECT 镜像
# BORDER_REFLECT_101 倒映
# BORDER_WRAP 没有规律
nimg = cv2.copyMakeBorder(img, 0, bottom, 0, right, borderType=cv2.BORDER_CONSTANT, value=0)
cv2.imshow('new image', nimg)
# 3、傅里叶变换,获到频域图像
f = np.fft.fft2(nimg)
fshift = np.fft.fftshift(f)
magnitude = np.log(np.abs(fshift))
LOG.info(magnitude)
# 二值化
magnitude_uint = magnitude.astype(np.uint8)
ret, thresh = cv2.threshold(magnitude_uint, 11, 255, cv2.THRESH_BINARY)
LOG.info(ret)
cv2.imshow('thresh', thresh)
LOG.info(thresh.dtype)
# 4、霍夫直线变换
lines = cv2.HoughLinesP(thresh, 2, np.pi/180, 30, minLineLength=40, maxLineGap=100)
LOG.info('line number: %d', len(lines))
# 创建一个新图像,标注直线
lineimg = np.ones(nimg.shape, dtype=np.uint8)
lineimg = lineimg * 255
for index, line in enumerate(lines):
LOG.info('draw line#%d: %s', index, line)
x1, y1, x2, y2 = line[0]
cv2.line(lineimg, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.imshow('line image', lineimg)
# 5、计算倾斜角度
piThresh = np.pi / 180
pi2 = np.pi / 2
angles = []
for line in lines:
LOG.info('line#%d: %s <===============', index, line)
x1, y1, x2, y2 = line[0]
if x2 - x1 == 0:
LOG.debug('skip 1')
continue
theta = (y2 - y1) / (x2 - x1)
LOG.debug('theta: %r', theta)
if abs(theta) < piThresh or abs(theta - pi2) < piThresh:
LOG.debug('skip 2: %r', theta)
continue
angle = math.atan(theta)
LOG.info('angle 1: %r', angle)
angle = angle * (180 / np.pi)
LOG.info('angle 2: %r', angle)
angle = (angle - 90)/(w/h)
LOG.info('angle 3: %r', angle)
angles.append(angle)
if not angles:
LOG.info('图片挺正的了,别折腾!')
else:
# from numpy.lib.function_base import average
# angle = average(angles)
LOG.info(' 方差: %r', np.array(angles).var())
LOG.info('标准差: %r', np.array(angles).std())
# angle = np.mean(angles)
import statistics
# LOG.debug(statistics.multimode(angles))
# angle = statistics.mode(angles)
# statistics.StatisticsError: geometric mean requires a non-empty dataset containing positive numbers
# statistics.StatisticsError: harmonic mean does not support negative values
angle = statistics.median(angles)
if 180 > angle > 90:
angle = 180 - angle
elif -180 < angle < -90:
angle = 180 + angle
LOG.info('==> %r, %r', angles, angle)
# 6、旋转
center = (w//2, h//2)
M = cv2.getRotationMatrix2D(center, angle, 1.0)
LOG.debug('=========== RotationMatrix2D ===========')
for i in M:
LOG.debug(i)
LOG.debug('^======================================^')
rotated = cv2.warpAffine(img, M, (w, h), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)
cv2.imshow('rotated', rotated)
cv2.waitKey(0)
cv2.destroyAllWindows()
参考资料与拓展阅读
开发者 图像处理 多媒体 Exif LinuxTools
2021-05-28
# 查看 Exif 信息:
exiftool media/images/django.jpg
exiftool -X media/images/django.jpg # XML 格式
exiftool -csv media/images/django.jpg # CSV 格式
exiftool media/images/
exiftool -r media/images/ # 递归遍历子目录
# 清除文件 Exif 信息:
exiftool -all= -overwrite_original media/images/django.jpg
exiftool -all= -overwrite_original media/images/
exiftool -all= -overwrite_original -ext png media/images/
# 清除指定 Exif 信息
exiftool -gps:all= *.jpg
Python PIL 图像处理
2021-05-17
抱歉,又水一篇
开发者 ImageMagick 图像处理 多媒体 LinuxTools
2021-04-24

关于 convert 的文章,之前已经写过两篇:
安装 ImageMagick
sudo apt install imagemagick
convert -list type
convert -list font # 支持的字体
获取图片信息
identify markjour.png
identify -verbose markjour.png
identify -format "Size: %w x %h\n" markjour.png
# Exif 信息
identify -format '%[Exif:*]' ~/Pictures/Photos/2019-09-14-18-48-22.jpg
# sudo apt install exif
exif ~/Pictures/Photos/2019-09-14-18-48-22.jpg
清除所有 Exif 信息
convert -strip input.jpg out.jpg
convert +profile "*" input.jpg out.jpg
图片切割
<宽> x <高> + <X轴坐标> + <Y轴坐标>
- 如果没有指定坐标,那就是切割图片。
- 宽高可以是百分比(不能混用,只要一个百分号那就都是比例)。
convert -crop 300x400+10+10 src.jpg dest.jpg
# 指定中心位址为基准点:
convert -gravity center -crop 300x400+0+0 src.jpg dest.jpg
convert -crop 25%x100% src.jpg dest.jpg
图片合并
之前(convert 图片转换的一次示例)合并图片用的就是这个命令。
# 横向
convert +append markjour-l.jpg markjour-c.jpg markjour-r.jpg markjour.jpg
# 纵向
convert -append markjour-t.jpg markjour-c.jpg markjour-b.jpg markjour.jpg
图片旋转
convert -rotate 90 input.jpg output.jpg # 顺时针
convert -rotate -90 input.jpg output.jpg # 逆时针
# 左右反转,镜像效果
convert -flop input.jpg output.jpg
# 上下反转,这个和旋转 270 效果还是不一样的
convert -flip input.jpg output.jpg
图片缩放
# 限宽我很常用,控制页面图片尺寸
convert -resize 108x markjour.png markjour-108.png
convert -sample 50% markjour.png markjour-new.png
convert -sample 200% markjour.png markjour-big.png
PS: 放大时 resize 会自动采样插值,而 sample 不会
图片压缩
convert input.jpg -quality 50 output.jpg
颜色
# 灰度,就是常见的黑白照片效果
convert -colorspace gray input.jpg output.jpg
# 分离 RGB 三个通道,输出三张图片,不知道为什么都是灰色
convert -separate input.png output.png
convert -threshold 40% input.png output.png # 也是一种常见效果,不知道叫什么
convert -negate input.png output.png # 反色
# 黑白(非灰度)sRGB -> Gray 2c
convert -monochrome input.png output.png
# 重新转成 sRGB 模式(但是颜色还是留在黑白两色)
convert -define png:color-type=2 input.png output.png
convert -colorspace sRGB -type truecolor input.jpg output.jpg
# 效果很奇特,可以试试:
convert -remap pattern:gray60 input.png output.png
# 替换
convert -fuzz 15% -fill white -opaque "rgb(143,141,250)" -opaque "rgb(216,217,62)" input.png output.png
滤镜
convert -blur 70 input.png output.png
# 后面的数字对模糊程度有着决定性作用
convert -blur 70x15 input.png output.png
convert -charcoal 2 input.png output.png # 铅笔画风格
convert -noise 3 input.png output.png
convert -paint 4 input.png output.png # 油画风格
convert -spread 50 input.png output.png # 毛玻璃
convert -swirl 60 input.png output.png # 扭曲
convert -paint 4 -raise 5x5 input.png output.png
# 调整透明度
# 先确保图片有 Alpha 通道
convert -define png:format=png32 input.png output.png
convert -channel alpha -fx "0.5" output.png output2.png
边框
# 加边框
convert -mattecolor "#000" -frame 60x60 input.png output.png
convert -mattecolor "#fff" -frame 60x60 input.png output.png
# 相同效果
convert -bordercolor "#fff" -border 60x60 input.png output.png
# 再配合上 raise:
convert -bordercolor "#fff" -border 10x10 input.png output.png
convert -raise 5x5 output.png output2.png
# 去掉边框:
convert -trim -fuzz 10% input.png output.png
水印
convert -fill "#1770CC" \
-font Ubuntu-Mono -pointsize 50 -draw 'text 130,50 "©"' \
-font 楷体_GB2312 -pointsize 40 -draw 'text 50,50 "码厩"' \
-gravity southeast \
input.png output.png
# 改用 RGBA 模式
convert -fill "rgba(23,112,204,0.25)" \
-font Ubuntu-Mono -pointsize 50 -draw 'text 130,50 "©"' \
-font 楷体_GB2312 -pointsize 40 -draw 'text 50,50 "码厩"' \
-gravity southeast \
input.png output.png
# 这个不错,京东那里学来的:
convert -size 100x100 -fill "#1770CC" -gravity center \
-font Ubuntu -pointsize 30 -draw 'rotate -45 text 0,0 "markjour"' \
xc:none miff:- | composite -tile -dissolve 25 - input.png output.png
# 图片水印
convert -size 150x50 -fill "#1770CC" -gravity center \
-font Ubuntu -pointsize 30 -draw 'text 0,0 "markjour"' \
xc:none /tmp/mark.png
convert input.png -gravity southeast -compose over /tmp/mark.png -composite output.png
其他
# 查看图片
# GNOME 桌面好像都是 eog
eog markjour.png
# 或者使用 ImageMagick 自带图片查看工具:
display markjour.png
# 查看颜色信息
convert xc:"#fff" -colorspace sRGB -format "%[pixel:u.p{0,0}]\n" txt:
convert xc:"#fff" -colorspace sRGB -format "%[pixel:u.p{0,0}]\n" info:
convert xc:"#fff" -colorspace sRGB -format "rgb(%[fx:int(255*r)],%[fx:int(255*g)],%[fx:int(255*b)])\n" info:
# 获取指定像素点的颜色(RGB)
convert "input.png[1x1+100+100]" -format "rgb(%[fx:int(255*r)],%[fx:int(255*g)],%[fx:int(255*b)])\n" info:
# 创建一张新图片
convert -size 1000x1000 xc:none /tmp/image.png
convert -size 1000x1000 xc:transparent /tmp/image.png
convert -size 1000x1000 xc:white /tmp/image.png
webp
sudo apt install -y webp
cwebp
转成 WEBP 格式
dwebp
转成别的格式
cwebp -o xxx.png xxx.webp
dwebp -o xxx.webp xxx.png
参考资料与拓展阅读
Linux 开发工具 图像处理 ImageMagick 乱码问题
2019-06-22
为什么 convert 和 montage 命令中使用的汉字都没能正确渲染在图片中去呢?
或许,可以将本文中的中文替换成 non-ascii 字符,或者 Unicode 字符。
或许,也可将 ImageMagick 替换成其他不支持中文的库。
参考了实在是太多文档,都没来得及记下来,只是从 Shell 的历史纪录中翻出来这些命令,做一个记录。
结论
字体问题,必须选择包含中文字形的字体,使用完整的字体名称,比如:Noto-Sans-Mono-CJK-SC-Regular
。
开发工具 Linux 图像处理 ImageMagick
2019-05-05
ImageMagick 是 Linux 下的一个常用的图像处理工具,核心命令是 convert, identify 等。