python图像处理opencv_使用Python+OpenCV进行图像处理(二)| 视觉入门
【前言】图像预处理对于整个图像处理任务来讲特别重要。如果我们没有进行恰当的预处理,无论我们有多么好的数据也很难得到理想的结果。
本篇是视觉入门系列教程的第二篇。整个视觉入门系列内容如下:
基本的图像处理与滤波技术。
从特征检测到人脸检测。
图像分割与分水岭(Watershed)算法(TBU)
在边缘和轮廓检测中,噪声对检测的精度有很大的影响。因此,去除噪声和控制像素值的大小可以帮助模型聚焦于整体特征,获得更高的精度。对应的图像处理技术包括:模糊化(Blurring)、阈值化(thresholding)和形态转换(morphological transformation)。本篇我们将详细介绍这几个常见的图像预处理技术。(本文假设读者已经熟悉卷积的概念。)
模糊化(Blurring)
模糊化的目标是实现降噪。我们必须格外注意的是:如果我们把边缘检测算法应用到高分辨率的图像上,我们就会得到很多我们不感兴趣的检测结果;
相反,如果我们把图像模糊太多,我们就会丢失数据。因此,我们需要找到一个适当的模糊量,从而不失去理想的边缘。
有多种技术用于实现模糊效果,在这里我们讨论OpenCV中常用的四种技术:平均模糊(Averaging blurring)、高斯模糊(Gaussian blurring)、中值模糊(median blurring)和双边滤波(bilateral filtering)。这四种技术应用一个共同的基本原理,即使用滤波器(内核)对图像进行卷积运算。不同的是,在四种模糊方法中使用的滤波器的值是不同的。
平均模糊(Average blurring)是取给定内核(kernel)区域下所有像素值的平均值替换中心的值。例如,假设给定一个大小为5X5的内核(kernel),我们计算卷积结果的平均值,并将结果放在给定区域的中心。示例如下:
如果我们增加内核的大小,像素值将更加归一化。因此图像也会变得越来越模糊。让我们用下面的代码对比处理结果。(为了便于比较,将把原始图像加到结果中,进行对比显示。)
# Import the image and convert to RGB
img = cv2.imread('text.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# Plot the image with different kernel sizes
kernels = [5, 11, 17]
fig, axs = plt.subplots(nrows = 1, ncols = 3, figsize = (20, 20))
for ind, s in enumerate(kernels):
img_blurred = cv2.blur(img, ksize = (s, s))
ax = axs[ind]
ax.imshow(img_blurred)
ax.axis('off')
plt.show()
中值模糊(Medium blurring)和平均模糊(Average blurring)是一样的,只是它使用的是中值而不是平均值。正由于这个特性,当我们需要处理图像中突然出现的噪音时(如“椒盐噪音”),使用中值模糊(medium blurring)的效果要比平均模糊(average blurring)效果好。
高斯模糊(Gaussian blurring)是使用“值”具有高斯分布的核函数。由于这些值是由高斯函数生成的,因此它的参数需要一个sigma值。如上图,内核的值在靠近中心的地方变高,在靠近角的地方变小。将该方法应用于具有正态分布的噪声,如白噪声,效果较好。
双边滤波(Bilateral Filtering)是高斯模糊的一个高级版本。模糊化不仅可以溶解噪声,而且还会平滑边缘。而双边滤波器能在去除噪声的同时保持边缘锐化。这是由于它不仅使用高斯分布值,还同时考虑了距离和像素值的差异。因此,需要指定sigmaSpace和sigmaColor这两个参数。
# Blur the image
img_0 = cv2.blur(img, ksize = (7, 7))
img_1 = cv2.GaussianBlur(img, ksize = (7, 7), sigmaX = 0)
img_2 = cv2.medianBlur(img, 7)
img_3 = cv2.bilateralFilter(img, 7, sigmaSpace = 75, sigmaColor =75)
# Plot the images
images = [img_0, img_1, img_2, img_3]
fig, axs = plt.subplots(nrows = 1, ncols = 4, figsize = (20, 20))
for ind, p in enumerate(images):
ax = axs[ind]
ax.imshow(p)
ax.axis('off')
plt.show()
阈值化(Thresholding)
图像的阈值化就是利用图像像素点分布规律,设定阈值进行像素点分割,进而得到图像的二值图像。我们需要设置阈值和最大值,然后据此相应地进行像素值转换。常用的阈值化包含有五种不同的类型:二进制阈值化、反二进制阈值化、阈值化到零、反阈值化到零,和阈值截断。
img = cv2.imread('gradation.png')
# Thresholding
_, thresh_0 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
_, thresh_1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
_, thresh_2 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)
_, thresh_3 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)
_, thresh_4 = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC)
# Plot the images
images = [img, thresh_0, thresh_1, thresh_2, thresh_3, thresh_4]
fig, axs = plt.subplots(nrows = 2, ncols = 3, figsize = (13, 13))
for ind, p in enumerate(images):
ax = axs[ind//3, ind%3]
ax.imshow(p)
plt.show()
ia_10008
如上图所示,每种类型的阈值都可以用数学公式表示,I(x, y)是像素点的强度(也称为点(x, y)的像素值)。上图中的图像示例,可以更直观的理解不同阈值化类型之间的区别。
只取一个阈值并将其应用于图像的所有部分并不能满足我们的全部需求。如果我们有一张在多个不同区域亮度差异较多的图片这种情况,将一个值应用于整个图像一般不利于我们的图像处理任务。其对应更好的方法是对图像的每个部分使用不同的阈值。对应这种情况还有另外一种阈值化技术称为自适应阈值化(Adaptive threshilding)。通过对图像邻域内阈值的计算,可以得到不同光照条件下的较好结果。
# Convert the image to grayscale
img = cv2.imread('text.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Adaptive Thresholding
_, thresh_binary = cv2.threshold(img, thresh = 127, maxval = 255, type = cv2.THRESH_BINARY)
adap_mean_2 = cv2.adaptiveThreshold(img, 255,
cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY, 7, 2)
adap_mean_2_inv = cv2.adaptiveThreshold(img, 255,
cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY_INV, 7, 2)
adap_mean_8 = cv2.adaptiveThreshold(img, 255,
cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY, 7, 8)
adap_gaussian_8 = cv2.adaptiveThreshold(img, 255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 7, 8)
我们需要将颜色模式转换为灰度来进行自适应阈值化。自适应阈值的参数有maxValue(在上面的示例中设置为255)、adaptiveMethod、thresholdType、blocksize和C。这里使用的自适应方法有两种:adaptive_threshold_mean_c和adaptive_threshold_gaussian_c。让我们通过下方代码对比自适应阈值化的不同结果。
# Plot the images
images = [img, thresh_binary, adap_mean_2, adap_mean_2_inv,
adap_mean_8, adap_gaussian_8]
fig, axs = plt.subplots(nrows = 2, ncols = 3, figsize = (15, 15))
for ind, p in enumerate(images):
ax = axs[ind%2, ind//2]
ax.imshow(p, cmap = 'gray')
ax.axis('off')
plt.show()
如上图所示,左边为原始图像与二进制阈值化结果图。对比二进制阈值化结果图与右上方两张结果图(由adaptive_threshold_mean_c方法生成)可得,后者生成了更为详细的结果。我们还可以看出,当C值更大时,图像将变得更显式。C代表从均值或加权均值中减去值的大小。通过观察上图右子图上下两幅图像,我们还可以对比查看相同C值下adaptive_threshold _mean_c和adaptive_threshold _gaussian_c两种方法生成的不同效果图。
梯度(Gradient)
在数学中,梯度用于几何地表示多变量函数图形的斜率。由于它是一个向量值函数,代表着方向和大小两种属性。在这里,我们也可以将同样的概念引入到图像的像素值中。图像梯度表示像素强度或颜色模式的方向变化,因此可以通过梯度来定位边缘。
# Apply gradient filtering
sobel_x = cv2.Sobel(img, cv2.CV_64F, dx = 1, dy = 0, ksize = 5)
sobel_y = cv2.Sobel(img, cv2.CV_64F, dx = 0, dy = 1, ksize = 5)
blended = cv2.addWeighted(src1=sobel_x, alpha=0.5, src2=sobel_y,
beta=0.5, gamma=0)
laplacian = cv2.Laplacian(img, cv2.CV_64F)
Sobel运算同时使用高斯平滑和微分。我们通过cv2.Sobel()函数使用它,可以定义两个不同的方向:垂直方向(sobel_x)和水平方向(sobel_y)。dx和dy表示导数。当dx = 1时,通过计算像素值沿水平方向的导数,从而进行图像滤波。
通过函数cv2.addWeighted()对sobel_x和sobel_y的两种过滤器加权求和,可以实现两个方向上的梯度求解及图像滤波。上述代码中两种过滤器设定了相同的权重。
拉普拉斯运算使用的是x和y的二阶导数,数学表达式如下。
让我们通过下方代码更直观的看看这些处理后图像是什么样的。
# Plot the images
images = [sobel_x, sobel_y, blended, laplacian]
plt.figure(figsize = (20, 20))
for i in range(4):
plt.subplot(1, 4, i+1)
plt.imshow(images[i], cmap = 'gray')
plt.axis('off')
plt.show()
如上图所示,第一幅和第二幅图像均含有一个方向图样。在第一张图中,我们可以清楚地看到垂直方向上的边缘。在第二幅图中,我们可以看到水平线。第三幅和第四幅图像,两个方向的边缘都凸显出来了。
形态转换(Morpgological transformations)
通过滤波操作来转换图像的形态的技术称为形态变换(morphological transformation)。首先,让我们了解下腐蚀(erosion)和扩张(dilation)。
腐蚀(Erosion) 是一种缩小图形形态的技术,通常被应用在灰度图上。过滤器的形状可以是矩形、椭圆和交叉形状。通过过滤器删除给定区域下的全部0值。
代码实现如下:
img = cv2.imread('simpson.jpg')
# Create erosion kernels
kernel_0 = np.ones((9, 9), np.uint8)
kernel_1 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9, 9))
kernel_2 = cv2.getStructuringElement(cv2.MORPH_CROSS, (9, 9))
kernels = [kernel_0, kernel_1, kernel_2]
# Plot the images
plt.figure(figsize = (20, 20))
for i in range(3):
img_copy = img.copy()
img_copy = cv2.erode(img_copy, kernels[i], iterations = 3)
plt.subplot(1, 3, i+1)
plt.imshow(img_copy)
plt.axis('off')
plt.show()
上图形象的展示出不同滤波器下的不同缩放结果。我们可以看到三张分别使用基础(方形)滤波器、椭圆形滤波器和交叉滤波器处理过的结果图。可以看出其分别以“圆形”、“线性”和“对角线”的方式进行收缩。
扩张(Dilation)与侵蚀是相反的。它是一种对图形形态进行放大的操作。其作用也与侵蚀相反。实现代码如下。
# Apply dilation
kernel = np.ones((9, 9), np.uint8)
img_dilate = cv2.dilate(img, kernel, iterations = 3)
plt.figure(figsize = (20, 10))
plt.subplot(1, 2, 1); plt.imshow(img, cmap="gray")
plt.subplot(1, 2, 2); plt.imshow(img_dilate, cmap="gray")
plt.show()
开闭运算是侵蚀和扩张的混合形式。开运算是指先进行侵蚀,然后对侵蚀结果进行扩张操作。相对应的,闭运算是指先进行扩张,再进行侵蚀。
正如上图所示,闭运算一般用于检测图形的整体轮廓,开运算用于检测图形的子模式(subpatterns)。可以使用函数cv2.morphologyEx()来实现这些操作。参数op用于指定使用哪种运算类型(开/闭)。完整代码如下所示。
# Apply the operations
kernel = np.ones((9, 9), np.uint8)
img_open = cv2.morphologyEx(img, op= cv2.MORPH_OPEN, kernel)
img_close = cv2.morphologyEx(img, op= cv2.MORPH_CLOSE, kernel)
img_grad = cv2.morphologyEx(img, op= cv2.MORPH_GRADIENT, kernel)
img_tophat = cv2.morphologyEx(img, op= cv2.MORPH_TOPHAT, kernel)
img_blackhat = cv2.morphologyEx(img, op= cv2.MORPH_BLACKHAT, kernel)
# Plot the images
images = [img, img_open, img_close, img_grad,
img_tophat, img_blackhat]
fig, axs = plt.subplots(nrows = 2, ncols = 3, figsize = (15, 15))
for ind, p in enumerate(images):
ax = axs[ind//3, ind%3]
ax.imshow(p, cmap = 'gray')
ax.axis('off')
plt.show()
注意,原图中的手在分别使用开闭操作进行处理时会产生不同的结果。梯度滤波(MORPH_CGRADIENT)运算是计算扩张结果图与腐蚀结果图之差。顶帽(Top-hat)运算(MORPH_TOPHAT)是计算开运算结果图与原始图像之差,黑帽(Black Hot)运算(MORPH_BLACKHAT)是计算闭运算结果图与原始图像之差。形态学运算详细介绍参看(https://homepages.inf.ed.ac.uk/rbf/HIPR2/morops.htm)。
总结与展望
本篇介绍了OpenCV中几项比较常用的运算。下篇将介绍轮廓检测和人脸检测等检测技术。欢迎批评指正。
你也许还想看:
欢迎扫码关注:
觉得赞你就点在看,多谢大佬
python图像处理opencv_使用Python+OpenCV进行图像处理(二)| 视觉入门相关推荐
- 基于python的opencv_基于Python和OpenCV的人脸检测
面部识别一直是一个热门话题,也从来没有像现在这样容易理解.在这篇文章中,我们首先介绍如何使用Python检测人脸. 机器学习.人工智能和人脸识别是当前的重要课题.所以我想,使用Python来检测照片中 ...
- [Python从零到壹] 三十五.图像处理基础篇之OpenCV绘制各类几何图形
欢迎大家来到"Python从零到壹",在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界.所有文章都将结合案例.代码和作者的经验讲 ...
- [Python图像处理] 一.图像处理基础知识及OpenCV入门函数
该系列文章是讲解Python OpenCV图像处理知识,前期主要讲解图像入门.OpenCV基础用法,中期讲解图像处理的各种算法,包括图像锐化算子.图像增强技术.图像分割等,后期结合深度学习研究图像识别 ...
- 【OpenCV图像处理入门学习教程六】基于Python的网络爬虫与OpenCV扩展库中的人脸识别算法比较
OpenCV图像处理入门学习教程系列,上一篇第五篇:基于背景差分法的视频目标运动侦测 一.网络爬虫简介(Python3) 网络爬虫,大家应该不陌生了.接下来援引一些Jack-Cui在专栏<Pyt ...
- [Python从零到壹] 三十三.图像处理基础篇之什么是图像处理和OpenCV配置
欢迎大家来到"Python从零到壹",在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界.所有文章都将结合案例.代码和作者的经验讲 ...
- Python+OpenCV图像处理(五)——图像阈值和二值化
系列文章 Python+OpenCV图像处理(一)--OpenCV框架与图像插值算法 Python+OpenCV图像处理(二)--几何变换 Python+OpenCV图像处理(三)--彩色空间互换 P ...
- 使用Python+opencv进行图像处理(一) | 视觉入门
计算机视觉是人工智能最热门的应用领域之一.人工智能技术推动了汽车自动驾驶.机器人以及各种照片处理类软件的巨大发展.目标检测技术也在稳步推进.生成对抗网络(GANs)同样也是人们最近比较关注的一个问题. ...
- 基于Opencv的数字图像处理技巧(python)
图像处理(image processing),用计算机对图像进行分析,以达到所需结果的技术.又称影像处理.图像处理一般指数字图像处理.数字图像是指用工业相机.摄像机.扫描仪等设备经过拍摄得到的一个大的 ...
- Python+OpenCV实时图像处理
目录 1.导入库文件 2.设计GUI 3.调用摄像头 4.实时图像处理 4.1.阈值二值化 4.2.边缘检测 4.3.轮廓检测 4.4.高斯滤波 4.5.色彩转换 4.6.调节对比度 5.退出系统 初 ...
最新文章
- 第二十三周微职位elk日志系统
- [Spring MVC] - @ModelAttribute使用
- 读后感《我回阿里的29个月》
- linux环境下Ncurses实现贪吃蛇游戏
- java学习(63):普通内部类
- Java_Arrays.fill() 初始化二维数组一个指定值
- 在韩家炜老师的实验室和家里作客 — 旅美散记之二
- python catia 接口_使用Python在CATIA中创建新产品
- 连接树莓派后斐讯k2路由cpu占用100%
- ad转换中断方式C语言程序,STC12C2052AD AD转换C程序 +PWM输出功能实现
- java POI Excel插入图片
- 支付系统就该这么设计(万能通用),稳的一批!
- 【高级数理统计R语言学习】9 无序多分类分析
- 解决梯度消失和梯度弥散的方法
- 二相漏电断路器接线图_配电箱内断路器的接线方式,以及断路器1P、2P的含义解析!...
- Gamma Correction/Gamma校正/灰度校正/亮度校正(已更正) - 部分 DCC 中的线性工作流配置
- 命令行工具(Command line tools)
- 实验室GPU管理神器Determined
- IOS 内置浏览器,跳转前的http请求失效的问题
- bootstrap editable 动态改变列的编辑状态
热门文章
- springsecurity 登录失败_Spring Security 实战干货: 401和403状态
- python新版下载安装_各种版本的Python下载安装教程
- struts2 验证框架、国际化
- Hibernate3 r的SLF4J问题
- ad10怎么挖铺的铜_赣州“云星公园大观”的商铺,5米层高变4.5米,开发商挖地来凑?...
- 8屏 旌宇多屏管理软件_如何选择拼接屏,不能说的秘密,都在这!
- linux外部命令帮助,Linux的命令帮助
- php正则替换模板变量,php正则替换变量指定字符的方法
- mysql数据库语_MYSQL数据库常用语句
- oracle rman恢复表空间,Oracle数据库RMAN恢复之表空间和数据块介质的恢复