图像处理?没有Python可不行

python基础

浏览数:824

2018-2-3

python作为简单高效又很强大的一门编程语言,对于图像的处理自然也是轻松拿下,scikit-image是python中处理图像的一个库,对大多数的图像处理算法进行了封装,用户只需调用相关的接口即可。

入门示例

首先使用pip安装skimage

pip install skimage

我们都知道,图像是由像素构成的,其中彩色图像还包括不同的通道,视频则是在图像的基础上加入时间变量而已。一张图就像是由像素点构成的表格一样,skimage正是基于此来进行图像的处理操作。具体来说,skimage使用numpy作为图像的数据类型,numpy不用多说,它可以让你在 Python 中使用向量和数学矩阵(有不了解的可以网上找找教程)。

我们首先来打开一张图片看看。

为了方便,我们使用skimage自带的一些图片,这些图片放在data模块下,使用data.导入即可。如果要打开本地文件夹下的图片,可以使用io模块下的imread方法,保存图片可以使用io.imsave。

from skimage import data, io
img = data.chelsea()
# 查看图片,使用io模块中的imshow方法
io.imshow(img)
# 保存图片
io.imsave('F:cat.jpg', img)

接下来进行一些简单的图像处理。

一张图片在skimage中表示为多维数组的形式,比如img[2:16, 1:10, 0]表示宽度上从第2个像素点到第16个像素点,高度上从1到10的像素点,红色通道。如果为灰度图像则没有通道。也可以使用img[2, 3, 0]的形式,表示第二行,第三列,红色通道。

使用.shape显示图片的信息。如果图片为彩色图片,显示为宽度像素值,高度像素值,通道。

print(img.shape)
(300, 451, 3)

我们可以只显示图片的某一个通道,其中红绿蓝三个通道分别用数字0,1,2表示。比如只显示红色通道:

R = img[:, :, 0]
io.imshow(R)

我们也可以通过只显示某部分像素区间来剪裁图像,比如:

img_c = img[80:180, 100:200, :]
io.imshow(img_c)

还可以通过控制像素把图像二值化,也就是把像素值转换为0,1的形式。

from skimage import color
img4 = data.chelsea()
# 将彩色图像转为灰度图像,用到color模块
img_gray = color.rgb2gray(img4)

# 拆包获得行列数
rows, cols = img_gray.shape

# 循环,如果像素值小于0.5为0,反之则为1。
for i in range(rows):
    for j in range(cols):
        if img_gray[i, j]<=0.5:
            img_gray[i, j] = 0
        else:
            img_gray[i, j] = 1
io.imshow(img_gray)

图像增强

作为图像处理的一个部分,图像增强用来改善图像的图像的质量,也就是让图片更好看。skimage提供了强大的函数支持。

比如通过灰度变换改变图片的对比度和亮度。常见的算法有伽马调整和和log对数调整,在此不深究具体的算法细节,会用即可。

这里使用到matplotlib包,这是python中的一个绘图工具,用来展示图像,绘制统计图等。

from skimage import exposure, img_as_float
import matplotlib.pyplot as plt

# 把图像的像素值转换为浮点数
imge5 = img_as_float(data.moon())

# 使用伽马调整
# 第二个参数控制亮度,大于1增强亮度,小于1降低。
gam1 = exposure.adjust_gamma(imge5, 2)


# 对数调整
log1 = exposure.adjust_log(imge5, 0.7)


# 用一行两列来展示图像
plt.subplot(1, 3, 1)
plt.imshow(imge5, plt.cm.gray)

plt.subplot(1, 3, 2)
plt.imshow(gam1, plt.cm.gray)

plt.subplot(1, 3, 3)
plt.imshow(log1, plt.cm.gray)

另一个很有用的图像增强算法是直方图均衡化,能有效的改善图像。直方图均衡化简单来说就是通过将直方图变为均匀分布的来改善对比度。直方图的横坐标代表某个像素,纵坐标代表该像素有多少个。

# 直方图均衡化
import matplotlib.pyplot as plt

img6 = data.moon()
# 指定绘制的大小
plt.figure("hist", figsize=(8, 8))

# 把图像的二维数组按行转为一维数组,这样才能绘制直方图
arr = img6.flatten()

plt.subplot(2,2,1)
plt.imshow(img6, plt.cm.gray)
plt.subplot(2,2,2)
# 绘制直方图
plt.hist(arr, bins=256, normed=1, edgecolor='None',facecolor='red')

# 对直方图进行均衡化
img_c = exposure.equalize_hist(img6)
arr_c = img_c.flatten()
plt.subplot(2,2,3)
plt.imshow(img_c, plt.cm.gray)
plt.subplot(2,2,4)
plt.hist(arr_c, bins=256, normed=1, edgecolor='None', facecolor='red')

plt.show()

可以明显的看出,经过直方图均衡化之后,图像质量改善了许多。

再一个图像增强中常用的算法就是各种滤波器,像平滑化滤波器,锐化滤波器等。这其中,平滑滤波器可以用来去除噪声和平滑化处理图像,具体使用到的滤波器为低通滤波和中值滤波。不过低通噪声去除噪声的同时也平滑化了边和尖锐的细节,中值滤波则不会。

除了低通还有高通,除了中值还有最大值,最小值,均值等滤波器,在此不多赘述,查官方手册即可。

滤波器相关的算法放在filter模块下,记得导入。

from skimage import filters
import skimage.morphology as sm
image6 = data.camera()
# 中值滤波
# 第二个参数代表滤波器的形状,disk代表平面圆形,当然还有什么正方形,矩形啥的
edges = filters.median(image6, sm.disk(5))

plt.subplot(1, 2, 1)
plt.imshow(image6, plt.cm.gray)

plt.subplot(1, 2, 2)
plt.imshow(edges, plt.cm.gray)

与平滑滤波器正好相反,锐化滤波器可以用来提取边缘,凸显某些标志,突出细节等。其中的算法包括各种算子,roberts算子,prewitt梯度算子等等,还有微分滤波器等。

比如使用sobel描述图像中物体的边缘。

img = color.rgb2gray(data.chelsea())
# 使用sobel算子
edges = filters.sobel(img)

plt.figure("img", figsize=(8,8))
plt.subplot(1,2,1)
plt.imshow(img, plt.cm.gray)
plt.subplot(1,2,2)
plt.title("sobel")
plt.imshow(edges, plt.cm.gray)
plt.show()

图像分割

图像分割主要就是进行特征提取,从而使别图像中的物体,比如给你一张满是苹果的照片,让你统计照片中一共有多少个苹果。数出来的可不算。这时候就要用到图像分割中的一些算法了。

介绍下阈值分割。阈值是什么意思,比如你有一堆苹果,为了区分出哪些是好苹果哪些是不好的,规定尺寸大于75的就是好的,这里的75就是阈值。简单来说,阈值分割就是利用图像中要提取的目标区域与其背景在灰度特性上的差异,把图像看作具有不同灰度级的两类区域(目标区域和背景区域)的组合,选取一个比较合理的阈值,产生二值图像。

看个例子。

img = color.rgb2gray(data.chelsea())
# 基于otsu阀值分割方法
thresh = filters.threshold_otsu(img)
dst = (img<=thresh)*1.0

plt.figure("img", figsize=(8,8))
plt.subplot(1,2,1)
plt.imshow(img, plt.cm.gray)
plt.subplot(1,2,2)
plt.title("otsu")
plt.imshow(dst, plt.cm.gray)
plt.show()

再说下形态学变换,形态学变换包括膨胀处理,腐蚀处理,开闭运算,白黑帽等。膨胀处理的意思是说检测图像中像素值为1的点,然后将它周围某个区域的像素都变为1。膨胀处理可以扩充边缘和填充空洞。

看个例子。

img = data.checkerboard()
# 设置结构元素为边长5的正方形
dst = sm.dilation(img, sm.square(5))
plt.figure('dilation', figsize=(8,8))
plt.subplot(1,2,1)
plt.imshow(img, plt.cm.gray)
plt.subplot(1,2,2)
plt.imshow(dst, plt.cm.gray)

腐蚀处理正好相反,检测图像中像素值为0的点,然后将它周围某个区域的像素都变为0。

img = data.checkerboard()
# 设置结构元素为边长5的正方形
dst = sm.erosion(img, sm.square(5))
plt.figure('dilation', figsize=(8,8))
plt.subplot(1,2,1)
plt.imshow(img, plt.cm.gray)
plt.subplot(1,2,2)
plt.imshow(dst, plt.cm.gray)

图像识别

在机器学习和深度学习的推动下,图像识别获得了很大的发展,识别率节节攀升。如今的图像识别都在用深度学习算法来进行,所以这部分就不细讲了。

skimage中有一些很有用的算法用来检测轮廓。比如使用霍夫圆来检测圆形,椭圆变换来检测椭圆等等。

import numpy as np
import matplotlib.pyplot as plt
from skimage import data, color,draw,transform,feature,util

image = util.img_as_ubyte(data.coins()[0:95, 70:370]) #裁剪原图片
edges =feature.canny(image, sigma=3, low_threshold=10, high_threshold=50) #检测canny边缘

fig, (ax0,ax1) = plt.subplots(1,2, figsize=(8, 5))

ax0.imshow(edges, cmap=plt.cm.gray)  #显示canny边缘
ax0.set_title('original iamge')

hough_radii = np.arange(15, 30, 2)  #半径范围
hough_res =transform.hough_circle(edges, hough_radii)  #圆变换 

centers = []  #保存中心点坐标
accums = []   #累积值
radii = []    #半径

for radius, h in zip(hough_radii, hough_res):
    #每一个半径值,取出其中两个圆
    num_peaks = 2
    peaks =feature.peak_local_max(h, num_peaks=num_peaks) #取出峰值
    centers.extend(peaks)
    accums.extend(h[peaks[:, 0], peaks[:, 1]])
    radii.extend([radius] * num_peaks)

#画出最接近的5个圆
image = color.gray2rgb(image)
for idx in np.argsort(accums)[::-1][:5]:
    center_x, center_y = centers[idx]
    radius = radii[idx]
    cx, cy =draw.circle_perimeter(center_y, center_x, radius)
    image[cy, cx] = (255,0,0)

ax1.imshow(image)
ax1.set_title('detected image')

这篇文章大致介绍了使用skimage库来进行图像处理的一些过程,各种算法的具体使用还是查看官方手册最为妥当。

本人才疏学浅,上文中难免有些错误,还请各位品评指正。

原文地址:https://segmentfault.com/a/1190000012156514