基本图像操作和处理
我简单看了一下这本书,大体上是一本实战类的书。此处我假定所有的读者都有一定的python基础,知道如何安装库,能大致看懂python语法。下面正式开始本书内容。
要用到什么库
我们本次要使用的库如下:
- PIL:Python Imaging Library,能提供通用的图像处理功能,如图像缩放、裁剪、旋转等
- Matplotlib:画图用的库,能画各种各样的图,稍后就会见到
- numpy:科学计算工具包,矩阵之类的计算非常有用,可以说科学领域只要用python,这个库八成避免不了
- scipy:科学计算工具包,但是相对于numpy来讲封装的更好,比如可以实现高斯模糊等等操作
由于本书侧重与实践,因此下面将开始对这四个库的实战讲解:
PIL
输入输出
前文有提到过PIL能提供图片处理的基本功能,那么首先让我们尝试一下输入输出:
# 导入PIL
from PIL import Image
# 导入plt,注意,只是因为我用jupyter了暂时无法直接显示PIL的图片才需要导入,否则不需要
import matplotlib.pyplot as plt
# 读取图片
img = Image.open('img.png')
# 显示图片
plt.imshow(img)
plt.show()
运行结果如下:
为了验证我们是不是真的读取了图片,现在再让我们试一下颜色转换:
# 导入PIL
from PIL import Image
# 导入plt,注意,只是因为我用jupyter了暂时无法直接显示PIL的图片才需要导入,否则不需要
import matplotlib.pyplot as plt
import matplotlib
# 读取图片
img = Image.open('img.png')
# 颜色转换
img = img.convert('L')
# 显示图片
# 由于jupyter会将灰度映射为其他颜色,因此需要指定cmap='gray'
plt.imshow(img, cmap='gray')
plt.show()
运行结果:
可以看到图片成功的转为灰色了,注释中我提到了一个小坑,但是如果是本地运行的话应该不会出现上述问题,感兴趣的话可以自行尝试一下。
基本操作
由于很多操作都只有一行,因此,此处我们将转换图像格式、创建缩略图、复制与粘贴图像区域、调整尺寸与旋转全部放到一起来描述、同样的,所有需要注意的地方都注释在了代码中:
# 导入PIL
from PIL import Image
import matplotlib.pyplot as plt
# 读取图片
img_png = Image.open('img.png')
# 转换图像格式
# 此处与书上讲的不一样,其实PIL没有那么智能,png默认编码是RGBA,保存为JPEG格式需要先转化一下才可以保存
img_png = img_png.convert('RGB')
img_png.save('img.jpg')
img_jpg = Image.open('img.jpg')
plt.imshow(img_jpg)
plt.show()
# 缩略图
img_thumbnail = img_jpg
img_thumbnail.thumbnail((128,128))
plt.imshow(img_thumbnail)
plt.show()
# 复制粘贴
# 规定裁剪区域
box = (10,10,50,50)
# 从原图裁剪一块
region = img_jpg.crop(box)
# 粘贴回去
past_box = (50,50,90,90)
img_jpg.paste(region, past_box)
plt.imshow(img_jpg)
plt.show()
# 旋转
img_rotate = img_jpg.rotate(45)
plt.imshow(img_rotate)
plt.show()
# 调整尺寸
img_resize = img_rotate.resize((50, 50))
plt.imshow(img_resize)
plt.show()
运行结果如下:
看大小不是很直观,但是我们看一下坐标就可以很容易的发现,原始图像大小是200多,经过了resize等之后大小变为了100左右,50左右。
Matplotlib
由于我的使用的是远程环境,有一部分操作要借助Matplotlib显示,因此前面其实我们已经初步用过了Matplotlib,下面开始较为详细的介绍
绘制图像、点、线
下面将直接以代码的形式展示要如何画图:
import matplotlib.pyplot as plt
from PIL import Image
import numpy as np
# 读取图像到数组中
im = np.array(Image.open('img.jpg'))
# 绘制图像
plt.imshow(im)
# 一些点,注意,x和y在一起才构成完整的几个点
x = [100,100,400,400]
y = [200,500,200,500]
# 使用红色星状标记绘制点
plt.plot(x,y,'r*')
# 绘制连接前两个点的线
plt.plot(x[:2],y[:2])
# 添加标题,显示绘制的图像
plt.title('Plotting: "img.jpg"')
# 控制是否显示坐标轴
plt.axis('off')
plt.show()
最终画出来的图如下:
之所以有大量空白是因为我们设置的点的坐标超过了图片的大小,否则应该会全部在图片中。另外,我们关闭了坐标轴的显示来让图片更清晰。
图像轮廓与直方图
首先来看代码与运行结果:
import matplotlib.pyplot as plt
from PIL import Image
import numpy as np
# 读取图像到数组中,如果不是为了显示直方图可以不用转为np
im = np.array(Image.open('img.jpg').convert('L'))
# 绘制图像
# 在图像窗口中绘制轮廓线
plt.contour(im,origin='image')
# 设置x与y的刻度相等
plt.axis('equal')
# 控制是否显示坐标轴
plt.axis('off')
plt.show()
# 画直方图
plt.hist(im.flatten(),128)
plt.show()
NumPy
我们在上一小节之中其实已经用过numpy了,就在生成直方图这里。除此之外,numpy还可以存储图片,进行矩阵运算等等操作
存储图片
我们先来看一段代码:
from PIL import Image
import numpy as np
img = np.array(Image.open('img.jpg').convert('L'), 'f')
print(img.shape,img.dtype)
img = np.array(Image.open('img.jpg'))
print(img.shape,img.dtype)
这是其输出:
可以看到,图像被编码成了矩阵的形式,灰度图是单通道的,彩色是三通道的。在创建数组时使用额外的参数“f”可以将数据类型转换为浮点型。
此外,numpy的使用与python的数组非常相似,由于这一点无法在图像中很好的展示,因此下面将简单介绍一下而没有运行实例:
灰度变换
示例代码:
import matplotlib.pyplot as plt
from PIL import Image
import numpy as np
image1 = np.array(Image.open('img.jpg').convert('L')) # 原图
image2 = 255 - image1 # 求反
image3 = (100.0/255) * image1 + 100 # 将图像像素值变换到 100...200 区间
image4 = 255.0 * (image1/255.0)**2 # 对图像像素值求平方后得到的图像
# 创建2x2的图像布局,并在每个子图中显示图片
plt.subplot(2, 2, 1)
plt.imshow(image1, cmap='gray')
plt.title('Image 1')
plt.subplot(2, 2, 2)
plt.imshow(image2, cmap='gray')
plt.title('Image 2')
plt.subplot(2, 2, 3)
plt.imshow(image3, cmap='gray')
plt.title('Image 3')
plt.subplot(2, 2, 4)
plt.imshow(image4, cmap='gray')
plt.title('Image 4')
# 调整子图之间的间距
plt.tight_layout()
# 显示图像
plt.show()
运行结果:
SciPy
scipy提供了很多已经封装好的操作,但是在这里我们只测试一下图像滤波,形态学。
滤波
import matplotlib.pyplot as plt
from PIL import Image
import numpy as np
from scipy import ndimage
# 读取原图像
image1 = np.array(Image.open('img.jpg').convert('L'))
# 高斯,模糊图像
image2 = ndimage.gaussian_filter(image1,5)
# 单通道模糊图像
image3 = np.array(Image.open('img.jpg'))
image3[:,:,1] = ndimage.gaussian_filter(image3[:,:,1],10)
# 求梯度图
image4 = ndimage.gaussian_gradient_magnitude(image1, sigma=3)
# 创建2x2的图像布局,并在每个子图中显示图片
plt.subplot(2, 2, 1)
plt.imshow(image1, cmap='gray')
plt.title('Image 1')
plt.subplot(2, 2, 2)
plt.imshow(image2, cmap='gray')
plt.title('Image 2')
plt.subplot(2, 2, 3)
plt.imshow(image3, cmap='gray')
plt.title('Image 3')
plt.subplot(2, 2, 4)
plt.imshow(image4, cmap='gray')
plt.title('Image 4')
# 调整子图之间的间距
plt.tight_layout()
# 显示图像
plt.show()
重要的代码已经都在代码中注释了,现在我们来看一下运行结果。
首先是image1,这是原图的灰度图,用于与其他几张图片对比。
然后是image2,对其进行了高斯模糊,可以很清楚的看到模糊了很多。
然后是image3,这是对原图在红色通道上进行了高斯模糊,可以看到图标周围有一圈红晕,这就是单通道模糊的结果。
最后是image4,对灰度图进行了梯度处理。
形态学
import numpy as np
from PIL import Image
from scipy.ndimage import binary_dilation, binary_erosion
from matplotlib import pyplot as plt
# 读取图像
image_path = "img.jpg"
image = Image.open(image_path)
# 将图像转换为灰度图像
gray_image = image.convert("L")
# 将灰度图像转换为NumPy数组
threshold = 175
image_array = np.array(gray_image)
image_array[image_array < threshold] = 0
# 定义膨胀和侵蚀的结构元素
structure_element = np.ones((3, 3), dtype=bool) # 这里使用3x3的正方形结构元素
# 进行形态学膨胀操作
dilated_array = binary_dilation(image_array, structure=structure_element)
# 进行形态学侵蚀操作
eroded_array = binary_erosion(image_array, structure=structure_element)
# 将NumPy数组转换回图像
dilated_image = Image.fromarray(dilated_array.astype(np.uint8))
eroded_image = Image.fromarray(eroded_array.astype(np.uint8) )
# 显示图像
plt.subplot(1, 2, 1)
plt.gray()
plt.title('dilated_image')
plt.imshow(dilated_image)
plt.subplot(1, 2, 2)
plt.title('eroded_image')
plt.imshow(eroded_image)
plt.show()
我们首先是读取了图片,然后阈值处理,再然后用scipy对其进行了形态学操作,最后使用了plt显示图片。可以看到scipy的形态学处理效果还是非常好的。
小结
这一章其实没有讲太多东西,所有的东西基本上前面都做过了,而且我感觉我还是比较习惯用opencv来做这些操作,尤其是scipy和PIL,这两个在这一章的所有内容都可以用opencv来做。