python图像处理opencv_使用Python+OpenCV进行图像处理(二)| 视觉入门

【前言】图像预处理对于整个图像处理任务来讲特别重要。如果我们没有进行恰当的预处理,无论我们有多么好的数据也很难得到理想的结果。

本篇是视觉入门系列教程的第二篇。整个视觉入门系列内容如下:

基本的图像处理与滤波技术。

从特征检测到人脸检测。

图像分割与分水岭(Watershed)算法(TBU)

在边缘和轮廓检测中,噪声对检测的精度有很大的影响。因此,去除噪声和控制像素值的大小可以帮助模型聚焦于整体特征,获得更高的精度。对应的图像处理技术包括:模糊化(Blurring)、阈值化(thresholding)和形态转换(morphological transformation)。本篇我们将详细介绍这几个常见的图像预处理技术。(本文假设读者已经熟悉卷积的概念。)

模糊化(Blurring)

模糊化的目标是实现降噪。我们必须格外注意的是:如果我们把边缘检测算法应用到高分辨率的图像上,我们就会得到很多我们不感兴趣的检测结果;

8-1555989234.jpg

相反,如果我们把图像模糊太多,我们就会丢失数据。因此,我们需要找到一个适当的模糊量,从而不失去理想的边缘。

有多种技术用于实现模糊效果,在这里我们讨论OpenCV中常用的四种技术:平均模糊(Averaging blurring)、高斯模糊(Gaussian blurring)、中值模糊(median blurring)和双边滤波(bilateral filtering)。这四种技术应用一个共同的基本原理,即使用滤波器(内核)对图像进行卷积运算。不同的是,在四种模糊方法中使用的滤波器的值是不同的。

平均模糊(Average blurring)是取给定内核(kernel)区域下所有像素值的平均值替换中心的值。例如,假设给定一个大小为5X5的内核(kernel),我们计算卷积结果的平均值,并将结果放在给定区域的中心。示例如下:

7-1555989234.jpg

如果我们增加内核的大小,像素值将更加归一化。因此图像也会变得越来越模糊。让我们用下面的代码对比处理结果。(为了便于比较,将把原始图像加到结果中,进行对比显示。)

# 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()

1-1555989234.jpg

中值模糊(Medium blurring)和平均模糊(Average blurring)是一样的,只是它使用的是中值而不是平均值。正由于这个特性,当我们需要处理图像中突然出现的噪音时(如“椒盐噪音”),使用中值模糊(medium blurring)的效果要比平均模糊(average blurring)效果好。

6-1555989234.jpg

高斯模糊(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()

9-1555989235.jpg

阈值化(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()

9-1555989235-1.jpg

0-1555989235.jpgia_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()

3-1555989236.jpg

如上图所示,左边为原始图像与二进制阈值化结果图。对比二进制阈值化结果图与右上方两张结果图(由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的二阶导数,数学表达式如下。

1-1555989236.png

让我们通过下方代码更直观的看看这些处理后图像是什么样的。

# 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()

9-1555989236.jpg

如上图所示,第一幅和第二幅图像均含有一个方向图样。在第一张图中,我们可以清楚地看到垂直方向上的边缘。在第二幅图中,我们可以看到水平线。第三幅和第四幅图像,两个方向的边缘都凸显出来了。

形态转换(Morpgological transformations)

通过滤波操作来转换图像的形态的技术称为形态变换(morphological transformation)。首先,让我们了解下腐蚀(erosion)和扩张(dilation)。

腐蚀(Erosion) 是一种缩小图形形态的技术,通常被应用在灰度图上。过滤器的形状可以是矩形、椭圆和交叉形状。通过过滤器删除给定区域下的全部0值。

9-1555989236-1.jpg

代码实现如下:

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()

10-1555989237.jpg

上图形象的展示出不同滤波器下的不同缩放结果。我们可以看到三张分别使用基础(方形)滤波器、椭圆形滤波器和交叉滤波器处理过的结果图。可以看出其分别以“圆形”、“线性”和“对角线”的方式进行收缩。

扩张(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()

6-1555989237.jpg

开闭运算是侵蚀和扩张的混合形式。开运算是指先进行侵蚀,然后对侵蚀结果进行扩张操作。相对应的,闭运算是指先进行扩张,再进行侵蚀。

0-1555989237.jpg

正如上图所示,闭运算一般用于检测图形的整体轮廓,开运算用于检测图形的子模式(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()

2-1555989237.jpg

注意,原图中的手在分别使用开闭操作进行处理时会产生不同的结果。梯度滤波(MORPH_CGRADIENT)运算是计算扩张结果图与腐蚀结果图之差。顶帽(Top-hat)运算(MORPH_TOPHAT)是计算开运算结果图与原始图像之差,黑帽(Black Hot)运算(MORPH_BLACKHAT)是计算闭运算结果图与原始图像之差。形态学运算详细介绍参看(https://homepages.inf.ed.ac.uk/rbf/HIPR2/morops.htm)。

总结与展望

本篇介绍了OpenCV中几项比较常用的运算。下篇将介绍轮廓检测和人脸检测等检测技术。欢迎批评指正。

6-1555989238.gif

你也许还想看:

欢迎扫码关注:

9-1555989238.jpeg

觉得赞你就点在看,多谢大佬4-1555989238.gif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/245209.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

html文本设置float,css怎么float(浮动)?

在css中,浮动是一种使元素脱离文档流的方法,会使元素向左或向右移动,其周围的元素也会重新排列。Float(浮动),往往是用于图像,但它在布局时一样非常有用。浮动是一种非常有用的布局方式,它能够改变页面中对…

字符动图_手把手教你做一个python+matplotlib的炫酷的数据可视化动图

1.数据可视化动图,是数据可视化的高级显示,最近很流行。2.比如下面将告诉你如何制作一个如下的数据可视化动图。3.例:3.1 准备一组数据,虚拟的csv资料,对应关系如下4个项目:namegroupyearvaluename&#xf…

runtime 分类结构体_iOS 读懂runtime基础(一)

目录前言本文会详细描述Objective-C运行时的各对象数底层据结构、类和原类、消息传递与转发、动态方法等技术方案. 文中底层代码实现均来自Apple open source; 本文篇幅较长, 文中描述加之有个人的一点理解, 主要用作记录和学习之用, 文笔粗陋, 技术菜鸡, 如有错误或不妥之处, …

weblogic jms消息 删除_消息队列与消息中间件概述:消息中间件核心概念与技术选型...

什么是消息?“消息”是在两台计算机间传送的数据单位。消息可以非常简单,例如只包含文本字符串;也可以更复杂,可能包含嵌入对象。什么是队列?队列(Queue)队列是一种先进先出(FIFO)的数据结构。什么是消息队列&#xff…

mod游戏什么意思计算机,MOD运算

mod运算,即求余运算,是在整数运算中求一个整数 x 除以另一个整数y的余数的运算,且不考虑运算的商。在计算机程序设计中都有MOD运算,其格式为: mod(nExp1,nExp2),即是两个数值表达式作除法运算后的余数。中文…

伽马分布极大似然估计_一文通俗解释极大似然估计

我们都知道机器学习的大致流程是通过建立一个合理的模型学习现有数据集,然后通过该模型去完成特定的任务。其中每个模型都包含自身的一组特定参数,而这组参数决定着模型的本身。但这里存在一个很关键的一个问题,就是我们如何去找到一组参数使…

html json解析插件,jQuery插件jsonview展示json数据

本文实例为大家分享了jQuery插件jsonview展示json数据的具体代码,供大家参考,具体内容如下项目中要展示json数据,自己写一套html来展示太麻烦,可以使用jquery的插件jsonview来解决这个问题。首先,去jquery官网下载最新…

python3.5安装pygame_python怎么安装pygame

Pygame 是一种流行的 Python 包,用于编写游戏-鼓励学生学习编程,同时创建有趣的东西。 Pygame 在新窗口中显示图形,因此它将 无法在 WSL 的命令行方法下运行。 但是,如果您通过本教程中所述的 Microsoft Store 安装了 Python&…

所属的用户_关于chmod(变更用户对此文件的相关权限)超详细说明,小白秒懂

Linux下一切都是文件,通过ls -l或者别名ll可以查看文件的详细信息:drwxr-xr-x 第一个字符d指的是目录文件;第2-4个字符rwx:指的是u(user,owner)对这个文件具有可读可写可执行的权限;第5-7字符r-x:指的是g(group)对这个文件具有可读可执行权限&#xff1b…

台式计算机计量单位,计算机的计量单位以及常见的数据类型

为什么会写这篇文章,面试官问数据类型占字节大小干什么。实际开发中,你肯定计算预估过一些数据具体要占多少磁盘,或者是内存。如果你没有不去在意这些东西,很多服务器资源,都会被无形的浪费掉。我们知道计算机的世界其…

cad线性标注命令_CAD常用标注快捷键和命令

点击上方 “CAD自学网 ” → 点击右上角“...” → 点选“设为星标 ★ ”为CAD自学网加上星标,即可及时收到干货啦!左下角阅读原文看CAD视频站长推荐:1、CAD2014快速精通进阶提高教程:点击查看 2、室内设计全屋定制全套视频教程&…

计算机怎么设置网络共享,局域网共享设置,教您电脑怎么设置局域网共享

前两天,遇到位朋友说他刚买了台新的电脑,加上原来家里原有的两台电脑了,就三台了,现在想要三台电脑都能够进行一个共享职员的这么设置,就是不知道如何在局域网里怎么设置共享,下面,小编就来跟大…

python中的类装饰器应用场景_Python 自定义装饰器使用写法及示例代码

1、Python装饰器简介 python的装饰器就是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。简单的说装饰器就是一个用来返回函数的函数。 它经常用于有切面需求的场景,比如&…

html引用外部导入式css文件夹,css文件内引用外部资源文件的相对路径

1.default.css文件内容(位于css文件夹下):.ClassName .ClassName .ClassName.page-sidebar .sidebar-search .submit {--该图片相对于css文件所在的位置。不是使用本css文件的html文件位置。background-image: url(../image/search-icon.png);}2.使用本css文件的htm…

京东五星电器送扫地机器人_家电也流行“套餐”,京东五星电器吹响国庆家装“集结号”...

“70吋超薄激光电视、大容量多门法式冰箱、烘干一体滚筒洗衣机、蒸烤一体机、扫地机器人……”一系列的新房采购清单让宿迁的谭小姐直呼头痛,“挑选品牌和型号,比较价格,还要想着跟装修风格是否搭配,好浪费时间。”在线下实体店迎…

c++设置单元格填充色_格式函数text,设置自定义格式的万金油

在日常的数据处理统计中,我们知道,数据格式标准统一,是很重要的前提,如果数据混乱,那么,在后期的数据处理分析,excel就会耍脾气出错的。所以设置好数据格式,是相当重要的。今天&…

生成html_听说你不会用Python将字符串生成PDF?来,我教你!

这是恋习Python推荐的第118篇好文来源:Python爬虫与算法作者:jclian笔者在今天的工作中,遇到了一个需求,那就是如何将Python字符串生成PDF。比如,需要把Python字符串‘这是测试文件’生成为PDF, 该PDF中含有文字‘这是…

大学计算机基础python第二次作业_python第二次作业-titanic数据集练习

一、读入titanic.xlsx文件,按照教材示例步骤,完成数据清洗。 titanic数据集包含11个特征,分别是: Survived:0代表死亡,1代表存活 Pclass:乘客所持票类,有三种值(1,2,3) Name:乘客姓名 Sex:乘客性别 Age:乘客…

大一计算机上机试题2017,2017历年全国计算机二级ACCESS上机试题及答案

可通过实际操作,锻炼Access的学习能力,动手能力,编程思想锻炼等,也可以作为access 数据库国际二级考试使用呢历年Access操作试题解析(2017.5.8整理,可能为2015年以前的)第一套一、基本操作题(计30分)(1)新建数据库"学生.mdb",将考生文件夹中的…

怎么做手机的上下滑动_手机视频恢复怎么做?删除时间较久的找回方法

手机视频恢复?话说到手机视频误删的情况对于一些用户来说是常态,手机视频多又杂,经常全选删除,不小心多选了没有发现就给一起删除了!事隔多个月以后突然要用到,却怎么也找不到了,最近删除相册也…