Python_OpenCV入门到精通——基础篇(看这一篇就足够了!!!)

Python_OpenCV入门到精通——基础篇(看这一篇就足够了!!!)

此文章一共分为四篇

第一篇 入门篇

第二篇 基础篇(本篇)

第三篇 进阶篇

第四篇 项目篇

在这篇系列文章中,我将带你从“完全小白”一路打怪升级到能动手做真实项目
不堆术语、不讲空话,只用你能跑通的代码 + 能看懂的图解 + 踩过的坑总结
手把手拆解 OpenCV 的核心逻辑。点个关注不迷路!!!

目录

第6章 绘制图形和文字

6.1 线段的绘制

6.2 矩形的绘制

6.3 圆形的绘制

6.4 多边形的绘制

6.5 文字的绘制

6.6 动态绘制图形

6.7 小结

第7章 图像的几何变换

7.1 缩放

7.2 翻转

7.3 仿射变换

7.4 透视

7.5 小结

第8章 图像的阈值处理

8.1 阈值处理函数

8.2 “非黑即白”的图像

8.3 零处理

8.4 截断处理

8.5 自适应处理

8.6 Otsu方法

8.7 阈值处理的作用

8.8 小结

第9章 图像的运算

9.1 掩模

9.2 图像的加法运算

9.3 图像的位运算

9.4 合并图像

9.5 小结

本篇介绍绘制图形和文字、图像的几何变换、图像的阈值处理和图像的运算。 学习完这一部分内容后,读者不仅能够直观地看到运用OpenCV处理图像后的效 果,还能够了解OpenCV程序的编码步骤和注意事项。

第6章 绘制图形和文字

OpenCV提供了许多绘制图形的方法,包括绘制线段的line()方法、绘制矩形的 rectangle()方法、绘制圆形的circle()方法、绘制多边形的polylines()方法和绘制文字的 putText()方法。本章将依次对上述各个方法进行讲解,并使用上述方法绘制相应的图 形。

6.1 线段的绘制

OpenCV提供了用于绘制线段的line()方法,使用这个方法即可绘制长短不一、粗细 各异、五颜六色的线段。line()方法的语法格式如下:

img = cv2.line(img, pt1, pt2, color, thickness)

img:画布。

pt1:线段的起点坐标。

pt2:线段的终点坐标。

color:绘制线段时的线条颜色。

thickness:绘制线段时的线条宽度。

注意 :线条颜色是RGB格式的,例如红色的RGB值是(255, 0, 0)。但是在OpenCV中,RGB图 像的通道顺序被转换成B→G→R,因此(0, 0, 255)代表的是红色。

示例 : 绘制线段并拼成一个“王”字。

注意这里的坐标形式是(x,y),不是(y,x)

6.2 矩形的绘制

OpenCV提供了用于绘制矩形的rectangle()方法,使用这个方法既可以绘制矩形边 框,也可以绘制实心矩形。rectangle()方法的语法格式如下:

img = cv2.rectangle(img, pt1, pt2, color, thickness)

img:画布。

pt1:矩形的左上角坐标。

pt2:矩形的右下角坐标。

color:绘制矩形时的线条颜色。

thickness:绘制矩形时的线条宽度。

示例: 绘制一个矩形边框。

如果想要填充图6.4中的矩形边框,使之变成实心矩形,应该如何修改上述代码呢?

在rectangle()方法的语法格式中,thickness表示绘制矩形时的线条宽度。当 thickness的值为-1时,即可绘制一个实心矩形。也就是说,只需要把代 码中的最后一个参数20修改为-1,就能够绘制一个实心矩形

正方形是特殊的矩形,因此使用rectangle()方法不仅能绘制矩形,还能绘制正方 形。

6.3 圆形的绘制

OpenCV提供了用于绘制圆形的circle()方法,这个方法与rectangle()方法的功能相 同,既可以绘制圆形边框,也可以绘制实心圆形。circle()方法的语法格式如下:

img = cv2.circle(img, center, radius, color, thickness)

img:画布。

center:圆形的圆心坐标。

radius:圆形的半径。

color:绘制圆形时的线条颜色。

thickness:绘制圆形时的线条宽度。

示例: 绘制“交通灯”

绘制圆形和绘制线段或者矩形一样容易,但是绘制圆形要比绘制线段或者矩形多一些 趣味。例如,绘制同心圆、绘制随机圆等。

例子: 绘制同心圆

例子: 绘制27个随机实心圆

6.4 多边形的绘制

OpenCV提供了绘制多边形的polylines()方法,使用这个方法绘制的多边形既可以是 闭合的,也可以是不闭合的。polylines()方法的语法格式如下:

img = cv2.polylines(img, pts, isClosed, color, thickness)

img:画布。

pts:由多边形各个顶点的坐标组成的一个列表,这个列表是一个numpy的数 组类型。

isClosed:如果值为True,表示一个闭合的多边形;如果值为False,表示一 个不闭合的多边形。 

color:绘制多边形时的线条颜色。

thickness:绘制多边形时的线条宽度。

示例: 绘制一个等腰梯形边框。

注意: 在绘制一个等腰梯形边框时,需按顺时针(即(100, 50),(200, 50),(250, 250) 和(50, 250))或者逆时针(即(100, 50),(50, 250),(250, 250)和(200, 50))给出 等腰梯形4个顶点的坐标,否则无法绘制一个等腰梯形边框。

不闭合的情况

6.5 文字的绘制

OpenCV提供了用于绘制文字的putText()方法,使用这个方法不仅能够设置字体的 样式、大小和颜色,而且能够使字体呈现斜体的效果,还能够控制文字的方向,进而使文 字呈现垂直镜像的效果。putText()方法的语法格式如下:

img = cv2.putText(img, text, org, fontFace, fontScale, color, thickness, lineType, bottomLeftOrigin)

img:画布。

text:要绘制的文字内容。

org:文字在画布中的左下角坐标。

fontFace:字体样式,可选参数如表所示。

fontScale:字体大小。

color:绘制文字时的线条颜色。

thickness:绘制文字时的线条宽度。

lineType:线型。(线型指的是线的产生算法,有4和8两个值,默认值为8)

bottomLeftOrigin:绘制文字时的方向。(有True和False两个值,默认值为False)

说明: 使用putText()方法时,thickness、lineType和bottomLeftOrigin是可选参数,有 无均可。

示例 : 绘制文字“hello OpenCV”

不借助其他库或者模块,使用putText()方法绘制中文时,即把实例6.8的第11行代 码中的hello OpenCV修改为中文后会显示乱码,因此,只介绍绘制英文的相关内容。

FONT_HERSHEY_TRIPLEX 这个文字更好看一点

6.5.1 文字的斜体效果

FONT_ITALIC可以与其他文字类型一起使用,使字体在呈现指定字体样式效果的同时,也呈现斜体效果。

示例: 绘制指定字体样式的文字并呈现斜体效果

6.5.2 文字的垂直镜像效果

在putText()方法的语法格式中,有一个控制绘制文字时的方向的参数,即 bottomLeftOrigin,其默认值为False。当bottomLeftOrigin为True时,文字将呈现垂 直镜像效果。

6.5.3 在图像上绘制文字

这个就是图像来源变了,其他的完全一样

6.6 动态绘制图形

前面主要讲解的是如何在画布上绘制静态的图形,如线段、矩形、正方形、圆形、多 边形和文字等。那么,能不能让这些静态的图形动起来呢?如果能,又该怎么做呢?

示例 : 弹球动画

在一个宽、高都为200像素的纯白色图像中,绘制一个半径为20像素的纯蓝色小 球。让小球做匀速直线运动,一旦圆形碰触到图像边界则开始反弹(反弹不损失动能)。想要实现这个功能需要解决两个问题:如何计算运动轨迹和如何实现动画。下面分别介绍 这两个问题的解决思路。

1.通过图像坐标系计算运动轨迹

小球在4个方向的速度

由此可以得出,只需要改变速度的正负号小球就可以改变移动方向,所以在程序中可 以将小球的横坐标速度和纵坐标速度设定成一个不变的值,每次小球碰到左右边界,就更 改横坐标速度的正负号,碰到上下边界,就更改纵坐标速度的正负号。

2.通过time模块实现动画效果

Python自带一个time时间模块,该模块提供了一个sleep()方法可以让当前线程休眠 一段时间,其语法格式如下:

time.sleep(seconds)

seconds:休眠时间,单位为s,可以是小数,如1/10表示(1/10)s。

动画实际上是由多幅画面在短时间内交替放映实现的视觉效果。每一幅画面被称为一 帧,所谓的60帧就是指1s放映了60幅画面。使用time模块每(1/60)s计算一次小球的 移动轨迹,并将移动后的结果绘制到图像上,这样1s有60幅图像交替放映,就可以看到 弹球的动画效果了。

6.7 小结

不论是绘制图形,还是绘制文字,都需要创建画布,这个画布可以是一幅图像。需要确定线条的颜色时,要特别注意颜色的表示方式,即(B, G, R)。当绘制矩形、圆形和多 边形时,通过设置线条宽度,既可以绘制图形的边框,又可以绘制被填充的图形。但是, 在绘制多边形的过程中,要按照顺时针或者逆时针的方向,标记多边形各个顶点的坐标。 此外,OpenCV提供的用于绘制图形的方法,不仅可以绘制静态的图形,还可以绘制动 态的图形。

第7章 图像的几何变换

几何变换是指改变图像的几何结构,例如大小、角度和形状等,让图像呈现出缩放、 翻转、映射和透视效果。这些几何变换操作都涉及复杂、精密的计算,OpenCV将这些 计算过程封装成非常灵活的方法,开发者只需修改一些参数,就能实现图像的变换效果。 本章将介绍几种常见的几何变换效果及其实现方法。

7.1 缩放

“缩”表示缩小,“放”表示放大,通过OpenCV提供的resize()方法可以随意更改 图像的大小比例,其语法如下:

dst = cv2.resize(src, dsize, fx, fy, interpolation)

src:原始图像。

dsize:输出图像的大小,格式为(宽,高),单位为像素。

fx:可选参数。水平方向的缩放比例。

fy:可选参数。垂直方向的缩放比例。 interpolation:可选参数。缩放的插值方式。在图像缩小或放大时需要删减或 补充像素,该参数可以指定使用哪种算法对像素进行增减。建议使用默认值。

返回值说明: dst:缩值之后的图像。

resize()方法有两种使用方式,一种是通过dsize参数实现缩放,另一种是通过fx和fy 参数实现缩放,下面分别介绍。

7.1.1 dsize参数实现缩放

dsize参数的格式是一个元组,例如(100, 200),表示将图像按照宽100像素、高 200像素的大小进行缩放。如果使用dsize参数,就可以不写fx和fy参数。

例子:将图像按照指定宽高进行缩放。

将一个图像按照宽100像素、高100像素的大小进行缩小,再按照宽400像素、高 400像素的大小进行放大,代码如下:

7.1.2 fx和fy参数实现缩放

使用fx参数和fy参数控制缩放时,dsize参数值必须使用None,否则fx和fy失效。 fx参数和fy参数可以使用浮点值,小于1的值表示缩小,大于1的值表示放大。其计算公式为:

新图像宽度 = round(fx × 原图像宽度)

新图像高度 = round(fy × 原图像高度)

例子:将图像按照指定比例进行缩放。

7.2 翻转

水平方向被称为X轴,垂直方向被称为Y轴。图像沿着X轴或Y轴翻转之后,可以呈现 出镜面或倒影的效果,如图7.3和图7.4所示。

OpenCV通过cv2.flip()方法实现翻转效果,其语法如下:

dst = cv2.flip(src, flipCode)

src:原始图像。

flipCode:翻转类型,类型值及含义如表所示。

dst:翻转之后的图像。

7.3 仿射变换

仿射变换是一种仅在二维平面中发生的几何变形,变换之后的图像仍然可以保持直线 的“平直性”和“平行性”,也就是说原来的直线变换之后还是直线,平行线变换之后还 是平行线。常见的仿射变换效果如图7.6所示,包含平移、旋转和倾斜。

OpenCV通过cv2. warpAffine()方法实现仿射变换效果,其语法如下:

dst = cv2.warpAffine(src, M, dsize, flags, borderMode, borderValue)

src:原始图像。

M:一个2行3列的矩阵,根据此矩阵的值变换原图中的像素位置。

dsize:输出图像的尺寸大小。

flags:可选参数,插值方式,建议使用默认值。

borderMode:可选参数,边界类型,建议使用默认值。

borderValue:可选参数,边界值,默认为0,建议使用默认值。

M也被叫作仿射矩阵,实际上就是一个2×3的列表,其格式如下:

M = [[a, b, c],[d, e, f]]

图像做何种仿射变换,完全取决于M的值,仿射变换输出的图像按照以下公式进行计 算:

新x = 原x × a + 原y × b + c

新y = 原x × d + 原y × e + f

原x和原y表示原始图像中像素的横坐标和纵坐标,新x与新y表示同一个像素经过仿 射变换后在新图像中的横坐标和纵坐标。

M矩阵中的数字采用32位浮点格式,可以采用两种方式创建M。

1)创建一个全是0的M,代码如下:

2)创建M的同时赋予具体值,代码如下:

通过设定M的值就可以实现多种仿射效果,下面分别介绍如何实现图像的平移、旋转 和倾斜。

7.3.1 平移

平移就是让图像中的所有像素同时沿着水平或垂直方向移动。实现这种效果只需要 将M的值按照以下格式进行设置:

M = [[1, 0, 水平移动的距离],[0, 1, 垂直移动的距离]]

若水平移动的距离为正数,图像向右移动,若为负数,图像向左移动;若垂直移动的 距离为正数,图像向下移动,若为负数,图像向上移动;若水平移动的距离和垂直移动的 距离的值为0,图像不发生移动。

例子: 让图像向右下方平移。

7.3.2 旋转

让图像旋转也是通过M矩阵实现的,但得出这个矩阵需要做很复杂的运算,于是 OpenCV提供了getRotationMatrix2D()方法自动计算旋转图像的M矩阵。 getRotationMatrix2D()方法的语法如下:

M = cv2.getRotationMatrix2D(center, angle, scale)

center:旋转的中心点坐标。

angle:旋转的角度(不是弧度)。正数表示逆时针旋转,负数表示顺时针旋 转。

scale:缩放比例,浮点类型。如果取值1,表示图像保持原来的比例。

返回值说明:

M:getRotationMatrix2D()方法计算出的仿射矩阵。

例子: 让图像逆时针旋转

7.3.3 倾斜

OpenCV需要定位图像的3个点来计算倾斜效果,3个点的位置如图7.11所示这3 个点分别是“左上角”点A、“右上角”点B和“左下角”点C。OpenCV会根据这3个点 的位置变化来计算其他像素的位置变化。因为要保证图像的“平直性”和“平行性”,所 以不需要“右下角”的点做第4个参数,右下角这个点的位置根据A、B、C 3点的变化自 动计算得出。

说明

“平直性”是指图像中的直线在经过仿射变换之后仍然是直线。“平行性”是指图像中的平行线在经过仿射变换之后仍然是平行线

让图像倾斜也是需要通过M矩阵实现的,但得出这个矩阵需要做很复杂的运算,于是 OpenCV提供了getAffineTransform()方法来自动计算倾斜图像的M矩阵。 getRotationMatrix2D()方法的语法如下:

M = cv2.getAffineTransform(src, dst)

src:原图3个点坐标,格式为3行2列的32位浮点数列表,例如:[[0, 1], [1, 0], [1, 1]]。

dst:倾斜图像的3个点坐标,格式与src一样。 返回值说明:

M:getAffineTransform()方法计算出的仿射矩阵。

例子: 让图像向右倾斜。

如果让图像向左倾斜,不能只通过移动点A来实现,还需要通过移动点B和点C来实 现,3个点的修改方式如下:(通过改变BC 两点)

7.4 透视

如果说仿射是让图像在二维平面中变形,那么透视 就是让图像在三维空间中变形。从不同的角度观察物 体,会看到不同的变形画面,例如,矩形会变成不规则 的四边形,直角会变成锐角或钝角,圆形会变成椭圆, 等等。这种变形之后的画面就是透视图。

如图7.14所示从图像的底部观察图7.15(a),眼 睛距离图像底部较近,所以图像底部宽度不变,但眼睛距离图像顶部较远,图像顶部宽度 就会等比缩小,于是观察者就会看到如图7.15(b)所示的透视效果。

OpenCV中需要通过定位图像的4个点计算透视效果,4个点的位置如图7.16所示。 OpenCV根据这4个点的位置变化来计算其他像素的位置变化。透视效果不能保证图像 的“平直性”和“平行性”。

OpenCV通过warpPerspective()方法来实现透视效果,其语法如下:

dst = cv2.warpPerspective(src, M, dsize, flags, borderMode, borderValue)

src:原始图像。

M:一个3行3列的矩阵,根据此矩阵的值变换原图中的像素位置。

dsize:输出图像的尺寸大小。

flags:可选参数,插值方式,建议使用默认值。

borderMode:可选参数,边界类型,建议使用默认值。

borderValue:可选参数,边界值,默认为0,建议使用默认值。

warpPerspective()方法也需要通过M矩阵计算透视效果,但得出这个矩阵需要做很 复杂的运算,于是OpenCV提供了getPerspectiveTransform()方法自动计算M矩阵。 getPerspectiveTransform()方法的语法如下:

M = cv2.getPerspectiveTransform(src, dst,)

src:原图4个点坐标,格式为4行2列的32位浮点数列表,例如:[[0, 0], [1, 0], [0, 1],[1, 1]]。

dst:透视图的4个点坐标,格式与src一样。

例子 :模拟从底部观察图像得到的透视效果。

7.5 小结

图像的缩放有2种方式:一种是设置dsize参数,另一种是设置fx参数和fy参数。

图像 的翻转有3种方式,沿X轴翻转、沿Y轴翻转和同时沿X轴、Y轴翻转,这3种方式均由 flipCode参数的值决定。

图像的仿射变换取决于仿射矩阵,采用不同的仿射矩阵(M), 就会使图像呈现不同的仿射效果。此外,图像的透视仍然要依靠M矩阵实现。因此,读者 只要熟练掌握并灵活运用M矩阵,就能够得心应手地对图像进行几何变换操作。

第8章 图像的阈值处理

阈值是图像处理中一个很重要的概念,类似一个“像素值的标准线”。所有像素值都 与这条“标准线”进行比较,最后得到3种结果:像素值比阈值大、像素值比阈值小或像 素值等于阈值。程序根据这些结果将所有像素进行分组,然后对某一组像素进行“加 深”或“变淡”操作,使得整个图像的轮廓更加鲜明,更容易被计算机或肉眼识别。下面 将对阈值的相关内容进行详细的介绍。

8.1 阈值处理函数

在图像处理的过程中,阈值的使用使得图像的像素值更单一,进而使得图像的效果更 简单。首先,把一幅彩色图像转换为灰度图像,这样图像的像素值的取值范围即可简化为 0~255。然后,通过阈值使得转换后的灰度图像呈现出只有纯黑色和纯白色的视觉效果。例如,当阈值为127时,把小于127的所有像素值都转换为0(即纯黑色),把大于 127的所有像素值都转换为255(即纯白色)。虽然会丢失一些灰度细节,但是会更明显 地保留灰度图像主体的轮廓。

OpenCV提供的threshold()方法用于对图像进行阈值处理,threshold()方法的语法 如下:

retval, dst = cv2.threshold(src, thresh, maxval, type)

src:被处理的图像,可以是多通道图像。

thresh:阈值,阈值在125~150取值的效果最好。

maxval:阈值处理采用的最大值。

type:阈值处理类型。常用类型和含义如表8.1所示。

返回值说明:

retval:处理时采用的阈值。

dst:经过阈值处理后的图像。

8.2 “非黑即白”的图像

二值化处理和反二值化处理使得灰度图像的像素值两极分化,灰度图像呈现出只有纯 黑色和纯白色的视觉效果。

8.2.1 二值化处理

二值化处理也叫二值化阈值处理,该处理让图像仅保留两种像素值,或者说所有像素 都只能从两种值中取值。 进行二值化处理时,每一个像素值都会与阈值进行比较,将大于阈值的像素值变为最 大值,将小于或等于阈值的像素值变为0,计算公式如下:

if 像素值 阈值: 像素值 = 0

if 像素值 > 阈值: 像素值 = 最大值

通常二值化处理是使用255作为最大值,因为灰度图像中255表示纯白色,能够很清 晰地与纯黑色进行区分,所以灰度图像经过二值化处理后呈现“非黑即白”的效果。 例如,图8.1是一个由白到黑的渐变图,最左侧的像素值为255(表现为纯白色), 右侧的像素值逐渐递减,直到最右侧的像素值为0(表现为纯黑色)。像素值的变化如图 8.2所示。

示例:灰度图二值化

灰度图可以这样处理,彩色图像也可以进行二值化处理

8.2.2 反二值化处理

反二值化处理也叫反二值化阈值处理,其结果为二值化处理的相反结果。将大于阈值 的像素值变为0,将小于或等于阈值的像素值变为最大值。原图像中白色的部分变成黑 色,黑色的部分变成白色。计算公式如下:

if 像素值 阈值: 像素值 = 最大值

if 像素值 > 阈值: 像素值 = 0

示例:对图像进行反二值化处理。

可见这种方法对于那种偏黑色的图片效果比较好,彩色图像经过反二值化处理后,因为各通道的颜色分量值不同,会呈现“混乱”的效 果

8.3 零处理

低于阈值零处理也叫低阈值零处理,该处理将低于或等于阈值的像素值变为0,大于 阈值的像素值保持原值,计算公式如下:

if 像素值 阈值: 像素值 = 0

if 像素值 > 阈值: 像素值 = 原值

示例:对图像进行低于阈值零处理。

图像经过低于阈值零处理后,颜色深的位置会彻底变黑,颜色浅的位置不受影响。彩色图像经过低于阈值零处理后,会让深颜色区域的颜色变得更深,甚至变黑

8.3.2 超出阈值零处理

超出阈值零处理也叫超阈值零处理,该处理将大于阈值的像素值变为0,小于或等于 阈值的像素值保持原值。计算公式如下:

if 像素值 阈值: 像素值 = 原值

if 像素值 > 阈值: 像素值 = 0

示例:对图像进行超出阈值零处理

图像经过超出阈值零处理后浅颜色区域彻底变黑,深颜色区域则不受影响。但彩色图 像经过超出阈值零处理后,浅颜色区域的颜色分量取相反的极值,也呈现出一种“混 乱”的效果

8.4 截断处理

截断处理也叫截断阈值处理,该处理将图像中大于阈值的像素值变为和阈值一样的 值,小于或等于阈值的像素保持原值,其公式如下:

if 像素 阈值: 像素 = 原值

if 像素 > 阈值: 像素 = 阈值

示例:对图像进行截断处理

图像经过截断处理后,整体颜色都会变暗。彩色图像经过截断处理后,在降低亮度的 同时还会让浅颜色区域的颜色变得更浅

8.5 自适应处理

前面已经依次对cv2.THRESH_BINARY、cv2.THRESH_BINARY_INV,cv2.THRESH_TOZERO、cv2.THRESH_TOZERO_INV和cv2.THRESH_TRUNC这5 种阈值处理类型进行了详解。因为图8.1是一幅色彩均衡的图像,所以直接使用一种阈值 处理类型就能够对图像进行阈值处理。很多时候图像的色彩是不均衡的,如果只使用一种 阈值处理类型,就无法得到清晰有效的结果。

例子:使用常用的5种阈值处理类型对色彩不均衡的图像

从左到右依次是,二值化处理,反二值化处理,低于阈值处理,高于阈值处理,截断处理

从视觉上看,对于色彩不均衡的图像,虽然使用截断处理的效果是5种阈值处理类型中效果比较好的,但是有些轮廓依然模糊不清(例如,图8.22中的手部轮廓),使用程 序继续对其进行处理仍然很困难。这时,需要进一步简化图像。

OpenCV提供了一种改进的阈值处理技术:图像中的不同区域使用不同的阈值。把 这种改进的阈值处理技术称作自适应阈值处理也称自适应处理,自适应阈值是根据图像中 某一正方形区域内的所有像素值按照指定的算法计算得到的。与前面讲解的5种阈值处理 类型相比,自适应处理能更好地处理明暗分布不均的图像,获得更简单的图像效果。

OpenCV提供了adaptiveThresHold()方法对图像进行自适应处理, adaptiveThresHold()方法的语法如下:

dst = cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C)

src:被处理的图像。需要注意的是,该图像需是灰度图像。

maxValue:阈值处理采用的最大值。 

adaptiveMethod:自适应阈值的计算方法。自适应阈值的计算方法及其含义 如表8.2所示。



thresholdType:阈值处理类型;需要注意的是,阈值处理类型需是 cv2.THRESH_BINARY或cv2.THRESH_BINARY_INV中的一个。

blockSize:一个正方形区域的大小。例如,5指的是5×5的区域。

C:常量。阈值等于均值或者加权值减去这个常量。

返回值说明:

dst:经过阈值处理后的图像。

示例:使用自适应处理的效果

个人觉得左边的更好一点,与前面讲解的5种阈值处理类型的处理结果相比,自适应处理保留了图像中更多的细 节信息,更明显地保留了灰度图像主体的轮廓。

注意: 使用自适应阈值处理图像时,如果图像是彩色图像,那么需要先将彩色图像转换为灰度图像;否则,运行程序时会出现如图8.25所示的错误提示。

8.6 Otsu方法

前面在讲解5种阈值处理类型的过程中,每个实例设置的阈值都是127,这个127是 笔者设置的,并不是通过算法计算得到的。对于有些图像,当阈值被设置为127时,得到 的效果并不好,这时就需要一个个去尝试,直到找到最合适的阈值。

逐个寻找最合适的阈值不仅工作量大,而且效率低。为此,OpenCV提供了Otsu方 法。Otsu方法能够遍历所有可能的阈值,从中找到最合适的阈值。

Otsu方法的语法与threshold()方法的语法基本一致,只不过在为type传递参数时, 要多传递一个参数,即cv2.THRESH_OTSU。cv2.THRESH_OTSU的作用就是实现 Otsu方法的阈值处理。Otsu方法的语法如下:

retval, dst = cv2.threshold(src, thresh, maxval, type)

src:被处理的图像。需要注意的是,该图像需是灰度图像。

thresh:阈值,且要把阈值设置为0。

maxval:阈值处理采用的最大值,即255。

type:阈值处理类型。除在表8.1中选择一种阈值处理类型外,还要多传递一个 参数,即cv2.THRESH_OTSU。例如, cv2.THRESH_BINARY+cv2.THRESH_OTSU。

返回值说明:

retval:由Otsu方法计算得到并使用的最合适的阈值。

dst:经过阈值处理后的图像。

例子 :在图上实现Otsu方法的阈值处理

可见,二值化处理最适合的阈值就是114.0

8.7 阈值处理的作用

阈值处理在计算机视觉技术中占有十分重要的位置,它是很多高级算法的底层处理逻 辑之一。因为二值图像会忽略细节,放大特征,而很多高级算法要根据物体的轮廓来分析 物体特征,所以二值图像非常适合做复杂的识别运算。在进行识别运算之前,应先将图像 转为灰度图像,再进行二值化处理,这样就得到了算法所需要的物体(大致)轮廓图像。

下面通过一个实例来演示通过阈值处理获取物体轮廓的方法

例子:利用阈值处理勾勒楼房和汽车的轮廓。

读取一幅图像,先将图像转为灰度图像,再将图像分别进行二值化处理和反二值化处 理

从后面两幅图像可以看到,二值化处理后,图片只有纯黑和纯白两种颜色,图像中的 楼房边缘变得更加鲜明,更容易被识别。地面因为颜色较深,所以大面积被涂黑,这样白 色的汽车就与地面形成了鲜明的反差。二值化处理后的汽车轮廓在肉眼看来可能还不够明显,但反二值化处理后的汽车轮廓与地面的反差就非常大。高级图像识别算法可以根据这 种鲜明的像素变化来搜寻特征,最后达到识别物体分类的目的。

8.8 小结

OpenCV提供了一个可以快速抠出图像主体线条的工具,这个工具就是阈值。在阈 值的作用下,一幅彩色图像被转换为只有纯黑和纯白的二值图像。然而,灰度图像经5种 阈值处理类型处理后,都无法得到图像主体的线条。为此,OpenCV提供了一种改进的 阈值处理技术,即自适应处理,其关键在于对图像中的不同区域使用不同的阈值。有了这 种改进的阈值处理技术,得到图像主体的线条就不再是一件难以实现的事情了。

第9章 图像的运算

图像是由像素组成的,像素又是由具体的正整数表示的,因此图像也可以进行一系列 数学运算,通过运算可以获得截取、合并图像等效果。OpenCV提供了很多图像运算方 法,经过运算的图像可以呈现出很多有趣的视觉效果。下面将对OpenCV中一些常用的 图像运算方法进行介绍。

9.1 掩模

前面的章节出现过“掩模”这个参数,当时建议大家不使用这个参数。掩模到底有什 么用呢?这一节将介绍掩模的概念。

外科医生在给患者做手术时,会为患者盖上手术洞巾,类似图9.1,这样医生就只在 这个预设好的孔洞部位进行手术。

手术洞巾不仅有利于医生定位患处、暴露手术视野,还 可以对非患处起到隔离、防污的作用。 同样,当计算机处理图像时,图像也如同一名“患者”一样,有些内容需要处理,有 些内容不需要处理。通常计算机处理图像时会把所有像素都处理一遍,但如果想让计算机 像外科大夫那样仅处理某一小块区域,那就要为图像盖上一张仅暴露一小块区域的“手术 洞巾”。像“手术洞巾”那样能够覆盖原始图像、仅暴露原始图像“感兴趣区 域”(ROI)的模板图像就被叫作掩模。

掩模,也叫作掩码,英文为mask,在程序中用二值图像来表示:0值(纯黑)区域 表示被遮盖的部分,255值(纯白)区域表示暴露的部分(某些场景下也会用0和1当作 掩模的值)。

例如,图9.2是一幅小猫的原始图像,图9.3是原始图像的掩模,掩模覆盖原始图像 之后,可以得到如图9.4所示的结果。

如果调换了掩模中黑白区域,如图9.5所示,掩模覆盖原始图像之后得到的结果如图所示。

在使用OpenCV处理图像时,通常使用numpy库提供的方法创建掩模图像,下面通 过一个实例演示如何创建掩模图像。

示例:创建3通道掩模图像。

掩模在图像运算过程中充当了重要角色,通过掩模才能看到最直观的运算结果,接下 来将详细介绍图像运算的相关内容。

9.2 图像的加法运算

图像中每一个像素都有用整数表示的像素值,2幅图像相加就是让相同位置像素值相 加,最后将计算结果按照原位置重新组成一幅新图像。原理如图9.9所示。

在开发程序时通常不会使用“+”运算符对图像做加法运算,而是用OpenCV提供的 add()方法,该方法的语法如下:

dst = cv2.add(src1, src2, mask, dtype)

src1:第一幅图像。

src2:第二幅图像。

mask:可选参数,掩模,建议使用默认值。

dtype:可选参数,图像深度,建议使用默认值。

dst:相加之后的图像。如果相加之后值的结果大于255,则取255。

示例:分别使用“+”和add()方法计算图像和。

读取一幅图像,让该图像自己对自己做加法运算,分别使用“+”运算符和add()方 法,观查两者相加结果的不同,具体代码如下:

上述代码的运行结果如图所示。从结果可以看出,“+”运算符的计算结果如果 超出了255,就会取相加和除以255的余数,也就是取模运算,像素值相加后反而变得更 小,由浅色变成了深色;而add()方法的计算结果如果超过了255,就取值255,很多浅 颜色像素彻底变成了纯白色。

下面通过一个实例演示如何使用加运算修改图像颜色。

示例:模拟三色光叠加得白光。

图像的加法运算中也可以使用掩模,下面通过一个实例介绍掩模的使用方法。

示例:利用掩模遮盖相加结果

9.3 图像的位运算

位运算是二进制数特有的运算操作。图像由像素组成,每个像素可以用十进制整数表 示,十进制整数又可以转化为二进制数,所以图像也可以做位运算,并且位运算是图像数 字化技术中一项重要的运算操作。

OpenCV提供了几种常用的位运算方法,具体如表所示。

接下来将详细介绍这些方法的含义及使用方式。

9.3.1 按位与运算

与运算就是按照二进制位进行判断,如果同一位的数字都是1,则运算结果的相同位 数字取1,否则取0。

OpenCV提供bitwise_and()方法来对图像做与运算,该方法的语法如下:

dst = cv2.bitwise_and(src1, src2, mask)

src1:第一幅图像。

src2:第二幅图像。

mask:可选参数,掩模。

图像做与运算时,会把每一个像素值都转为二进制数,然后让两幅图像相同位置的两 个像素值做与运算,最后把运算结果保存在新图像的相同位置上,运算过程如图所 示。

(1)如果某像素与纯白像素做与运算,结果仍然是某像素的原值

(2)如果某像素与纯黑像素做与运算,结果为纯黑像素

由此可以得出:如果原图像与掩模进行与运算,原图像仅保留掩模中白色区域覆盖的 内容,其他区域全部变成黑色。下面通过一个实例演示掩模在与运算过程的作用。

9.3.2 按位或运算

或运算也是按照二进制位进行判断,如果同一位的数字都是0,则运算结果的相同位 数字取0,否则取1。

OpenCV提供bitwise_or()方法来对图像做或运算,该方法的语法如下:

dst = cv2.bitwise_or(src1, src2, mask)

或运算有以下两个特点。

(1)如果某像素与纯白像素做或运算,结果为纯白像素

(2)如果某像素与纯黑像素做或运算,结果仍然是某像素的原值

9.3.3 按位取反运算

取反运算是一种单目运算,仅需一个数字参与运算就可以得出结果。取反运算也是按 照二进制位进行判断,如果运算数某位上数字是0,则运算结果的相同位的数字就取1, 如果这一位的数字是1,则运算结果的相同位的数字就取0。

OpenCV提供bitwise_not()方法来对图像做取反运算,该方法的语法如下:

dst = cv2.bitwise_not(src, mask)

src:参与运算的图像。

mask:可选参数,掩模。

图像经过取反运算后呈现与原图颜色完全相反的效果,下面通过一个实例演示掩膜在取反运算过程中的作用。

示例:图片进行取反(别有一番风味)

9.3.4 按位异或运算

异或运算也是按照二进制位进行判断,如果两个运算数同一位上的数字相同,则运算 结果的相同位数字取0,否则取1。

OpenCV提供bitwise_xor()方法对图像做异或运算,该方法的语法如下:

dst = cv2.bitwise_xor(src, mask)

src:参与运算的图像。

mask:可选参数,掩模。

图像做异或运算的过程如图9.27所示

异或运算有两个特点。

(1)如果某像素与纯白像素做异或运算,结果为原像素的取反结果

(2)如果某像素与纯黑像素做异或运算,结果仍然是某像素的原值

由此可以得出:如果原图像与掩模进行异或运算,掩模白色区域覆盖的内容呈现取反 效果,黑色区域覆盖的内容保持不变。下面通过一个实例演示掩模在异或运算过程的作 用。

示例:图片演示

异或运算还有一个特点:执行一次异或运算得到一个结果,再对这个结果执行第二次 异或运算,则还原成最初的值。利用这个特点可以实现对图像内容的加密和解密。下面通 过一个实例,利用异或运算的特点对图像数据进行加密和解密。

示例:对图像进行加密、解密。

这个就很有用了,通过这个密钥图像,可以加密传递图像

9.4 合并图像

在处理图像时经常会遇到需要将两幅图像合并成一幅图像,合并图像也分2种情况:

①两幅图像融合在一起;

②每幅图像提供一部分内容,将这些内容拼接成一幅图像。

OpenCV分别用加权和和覆盖两种方式来满足上述需求。本节将分别介绍如何利用代码 实现加权和和覆盖效果。

9.4.1 加权和

多次曝光技术是指在一幅胶片上拍摄几个影像,最后冲印出的相片同时具有多个影像 的和信息

OpenCV通过计算加权和的方式,按照不同的权重取两幅图像的像素之和,最后组 成新图像。加权和不会像纯加法运算那样让图像丢失信息,而是在尽量保留原有图像信息 的基础上把两幅图像融合到一起。

OpenCV通过addWeighted()方法计算图像的加权和,该方法语法如下:

dst = cv2.addWeighted(src1, alpha, src2, beta, gamma)

src1:第一幅图像。

alpha:第一幅图像的权重。

src2:第二幅图像。

beta:第二幅图像的权重。

gamma:在和结果上添加的标量。该值越大,结果图像越亮,相反则越暗。可 以是负数。

例子:利用计算加权和的方式实现多次曝光效果。

读取两幅不同的风景照片,使用addWeighted()方法计算两幅图像的加权和,两幅图 像的权重都为0.6,标量为0,查看处理之后的图像是否为多次曝光效果,具体代码如 下:

9.4.2 覆盖

覆盖图像就是直接把前景图像显示在背景图像中,前景图像挡住背景图像。覆盖之后 背景图像会丢失信息,不会出现加权和那样的“多次曝光”效果。

OpenCV没有提供覆盖操作的方法,开发者可以直接用修改图像像素值的方式实现 图像的覆盖、拼接效果:从A图像中取像素值,直接赋值给B图像的像素,这样就能在B 图像中看到A图像的信息了。

示例:将小猫图像覆盖到沙滩图像上。

读取小猫原始图像,将原始图像中75~400行、120~260列的像素单独保存成一幅 小猫图像,并将小猫图像缩放成70×160大小。读取沙滩图像,将小猫图像覆盖到沙滩 图像(100, 200)的坐标位置。覆盖过程中将小猫图像的像素逐个赋值给沙滩图像中对应位 置的像素 

如果前景图像是4通道(含alpha通道)图像,就不能使用上面例子中直接替换整个 区域的方式覆盖背景图像了。因为前景图像中有透明的像素,透明的像素不应该挡住背 景,所以在给背景图像像素赋值时应排除所有透明的前景像素。下面通过一个实例来演示 如何在覆盖过程中排除4通道图像的透明区域。

示例:拼接禁止吸烟图像。

禁止图像由一个红圈和一个斜杠组成,这个图像是4通道图像,格式为PNG。将禁止 图像覆盖到吸烟图像上时要注意:不要把前景图像的透明像素覆盖到背景图像上。覆盖之 前要遍历前景图像中的每一个像素,如果像素的alpha通道值为0,表示该像素是透明像 素,就要停止操作该像素,实现的具体代码如下:

9.5 小结

读者朋友要明确关于掩模的3个问题:0和255这2个值在掩模中各自发挥的作用;通 过这2个值,掩模的作用又是什么;如何创建一个掩模。掌握了掩模后,就能够利用掩模 遮盖图像相加后的结果。掩模除了应用于图像的加法运算外,还应用于图像的位运算。一 个掩模应用于图像的位运算的典型实例就是对图像进行加密、解密。本章除了上述内容, 还讲解了合并图像的2种方式:加权和、覆盖。读者朋友要熟练掌握这两种方式。

Read more

哈希表的介绍和使用

哈希表的介绍和使用

今天,我们来介绍的是哈希表,哈希表主要用于对数据的出现次数统计,查重。利用的容器主要有vector、map/set、ordered_map/ordered_set等。   下面我们来看几道例题: class Solution { public:     vector<int> twoSum(vector<int>& nums, int target) {         unordered_map<int,int> hash;         for(int i=0;i<nums.size();i++){             int x=target-nums[i];             if(hash.

By Ne0inhk
【数据结构-初阶】详解线性表(5)---队列

【数据结构-初阶】详解线性表(5)---队列

🎈主页传送门:良木生香 🔥个人专栏:《C语言》 《数据结构-初阶》 《程序设计》 🌟人为善,福随未至,祸已远行;人为恶,祸虽未至,福已远离 上期回顾:在上一篇文章(【数据结构-初阶】详解栈和队列(1)---栈)中我们讲到了在顺序表与链表之外的另一种线性表---栈,知道了这是一种具有先进后出和后进先出特点的数据结构,既然有先进后出,那么肯定就有先进先出的数据结构,所以这就是我们今天要讲的------队列 一、队列的概念 既然我们想要实现先进先出的效果,那肯定就不像栈那样有一端是堵起来的,想必应该是两端都开口吧。嗯,事实确实如此。 队列:是只允许在一端进行数据的插入操作,在另一端进行数据的删除操作的一种特殊的线性表,其具有先进先出FIFO(first in first out)的结构特点. 入队列:进行插入操作的一端叫做队尾 出队列:进行删除操作的一端叫做队头 下面是队列的示意图: 名字叫做队列,其实就像我们排队一样,先排的人先得服务,后排的人后得到服务,在队列中,先进来的元素先得到操作,

By Ne0inhk
【算法】滑动窗口(一)-长度最小的子数组

【算法】滑动窗口(一)-长度最小的子数组

目录 一、题目介绍 二、算法原理 1.排必然非结果情况 1.1.2区域 (1)预证区 (2)已证区 2.滑动窗口 三、提交代码 一、题目介绍 209. 长度最小的子数组 - 力扣(LeetCode) 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 target 的长度最小的 子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。 示例 1: 输入:target = 7, nums = [2,3,

By Ne0inhk
【算法】堆排序

【算法】堆排序

算法系列四:堆排序 一、原理 1.大根堆得最大值 2.新堆得接下来最大值 二、实现 1.end 2.升序降序 三、性质 1.时间复杂度 2.空间复杂度 3.稳定性 一、原理 1.大根堆得最大值 将待排序数组所有数据创建成大根堆排列,排出数组首元素为数组中的最大值,将数组最大值排放到数组末尾总体完成它一个数据的排列 2.新堆得接下来最大值 交换排序好后整体堆的结构被破坏,因为数组中的最大值找到并放到了数组末尾完成了它数据的排列,接下来重新维护出堆得最大值排序就是要得此最大值之外剩余数据中的最大值即第二大的值接着放到第一大值前面这样来进行排序的,所以接下来继续维护出堆得最大值就去包含已排好序最大值之外剩下的接下来的最大值,所以回到堆结构被破坏,此时要再维护出堆得接下来的最大值维护的堆就不再包含那些得到交换下来的过去最大值了,不包含它们以后,去维护的此时新堆就为交换到堆顶数据一个破坏点其它全堆的结构,用一次向下调整就能维护出继续出接下来最大值再往后放成最大值们的升序排放,最后,所有数据最大值、剩余数据最大值、再剩余数据最大值、所有再剩余数据最大值都堆排比

By Ne0inhk