① OpenCV-Python教程:19.轮廓属性
1图像矩
帮你计算一些属性,比如重心,面积等。
函数cv2.moments()会给你一个字典,包含所有矩值
你可以从这个里面得到有用的数据比如面积,重心等。重心可以用下面的式子得到:
2.轮廓面积
轮廓面积由函数cv2.contourArea()得到或者从矩里得到M['m00']
3.轮廓周长
可以用cv2.arcLength()函数得到。第二个参数指定形状是否是闭合的轮廓(如果传True)。或者只是一个曲线。
4.轮廓近似
这会把轮廓形状近似成别的边数少的形状,边数由我们指定的精确度决定。这是Douglas-Peucker算法的实现。
要理解这个,假设你试图找一个图像里的方块,但是由于图像里的一些问题,你得不到一个完美的方块,只能得到一个“坏方块”。现在你可以使用这个函数来近似,第二个参数叫epsilon,是从轮廓到近似轮廓的最大距离。是一个准确率参数,好的epsilon的选择可以得到正确的输出。
在下面第二个图像里,绿线显示了epsilon = 10% of arc length 的近似曲线。第三个图像显示了epsilon = 1% of the arc length。第三个参数指定曲线是否闭合。
5.凸形外壳
凸形外壳和轮廓近似类似,但是还不一样(某些情况下两个甚至提供了同样的结果)。这儿,cv2.convexHull()函数检查凸面曲线缺陷并修复它。一般来说,凸面曲线总是外凸的,至少是平的,如果它内凹了,这就叫凸面缺陷。比如下面这张图,红线显示了手的凸形外壳。双向箭头显示了凸面缺陷,是轮廓外壳的最大偏差。
参数详情:
·points 是我们传入的轮廓
·hull 是输出,一般我们不用传
·clockwise: 方向标示,如果是True,输出凸形外壳是顺时针方向的。否则,是逆时针的。
·returnPoints:默认是True。然后会返回外壳的点的坐标。如果为False,它会返回轮廓对应外壳点的索引。
所以要获得凸形外壳,下面
但是如果你想找到凸面缺陷,你需要传入returnPoints = False。我们拿上面的矩形图形来说,首先我找到他的轮廓cnt,现在用returnPoints = True来找他的凸形外壳,我得到下面的值:[[[234 202]], [[51 202]], [51 79]], [[234 79]]] 是四个角的点。如果你用returnPoints = False,我会得到下面的结果:[[129], [67], [0], [142]]. 这是轮廓里对应点的索引,比如cnt[129] = [234, 202]],这和前面结果一样。
6.检查凸面
有一个函数用来检查是否曲线是凸面, cv2.isContourConvex().它返回True或False。
7.边界矩形
有两种边界矩形
7.a.正边界矩形
这个矩形不考虑对象的旋转,所以边界矩形的面积不是最小的,函数是cv2.boundingRect()。
假设矩形左上角的坐标是(x,y), (w, h)是它的宽和高
7.b.渲染矩形
这个边界矩形是用最小面积画出来的,所以要考虑旋转。函数是cv2.minAreaRect()。它返回一个Box2D结构,包含了(左上角(x,y),(width, height),旋转角度)。但是要画这个矩形我们需要4个角。这四个角用函数cv2.boxPoints()得到
8.最小闭包圆
我们找一个目标的外接圆可以用函数cv2.minEnclosingCircle().这个圆用最小面积完全包围目标。
9.椭圆
用一个椭圆来匹配目标。它返回一个旋转了的矩形的内接椭圆
10. 直线
类似的我们可以匹配一根直线,下面的图像包含一系列的白色点,我们可以给它一条近似的直线。
END
② 图片处理-opencv-10.图像锐化与边缘检测
Roberts算子又称为交叉微分算法,它是基于交叉差分的梯度算法,通过局部差分计算检测边缘线条。常用来处理具有陡峭的低噪声图像,当图像边缘接近于正45度或负45度时,该算法处理效果更理想。其缺点是对边缘的定位不太准确,提取的边缘线条较粗。
Prewitt是一种图像边缘检测的微分算子,其原理是利用特定区域内像素灰度值产生的差分实现边缘检测。由于Prewitt算子采用3 3模板对区域内的像素值进行计算,而Robert算子的模板为2 2,故Prewitt算子的边缘检测结果在水平方向和垂直方向均比Robert算子更加明显。Prewitt算子适合用来识别噪声较多、灰度渐变的图像。
dst = filter2D(src, ddepth, kernel[, dst[, anchor[, delta[, borderType]]]])
RSobel算子是一种用于边缘检测的离散微分算子,它结合了高斯平滑和微分求导。该算子用于计算图像明暗程度近似值,根据图像边缘旁边明暗程度把该区域内超过某个数的特定点记为边缘。Sobel算子在Prewitt算子的基础上增加了权重的概念,认为相邻点的距离远近对当前像素点的影响是不同的,距离越近的像素点对应当前像素的影响越大,从而实现图像锐化并突出边缘轮廓。Sobel算子的边缘定位更准确,常用于噪声较多、灰度渐变的图像。
Sobel算子根据像素点上下、左右邻点灰度加权差,在边缘处达到极值这一现象检测边缘。对噪声具有平滑作用,提供较为精确的边缘方向信息。因为Sobel算子结合了高斯平滑和微分求导(分化),因此结果会具有更多的抗噪性,当对精度要求不是很高时,Sobel算子是一种较为常用的边缘检测方法。
dst = Sobel(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]])
在进行Sobel算子处理之后,还需要调用convertScaleAbs()函数计算绝对值,并将图像转换为8位图进行显示
dst = convertScaleAbs(src[, dst[, alpha[, beta]]])
拉普拉斯(Laplacian)算子是n维欧几里德空间中的一个二阶微分算子,常用于图像增强领域和边缘提取。它通过灰度差分计算邻域内的像素,基本流程是:判断图像中心像素灰度值与它周围其他像素的灰度值,如果中心像素的灰度更高,则提升中心像素的灰度;反之降低中心像素的灰度,从而实现图像锐化操作。在算法实现过程中,Laplacian算子通过对邻域中心像素的四方向或八方向求梯度,再将梯度相加起来判断中心像素灰度与邻域内其他像素灰度的关系,最后通过梯度运算的结果对像素灰度进行调整。
Laplacian算子分为四邻域和八邻域,四邻域是对邻域中心像素的四方向求梯度,八邻域是对八方向求梯度。当邻域内像素灰度相同时,模板的卷积运算结果为0;当中心像素灰度高于邻域内其他像素的平均灰度时,模板的卷积运算结果为正数;当中心像素的灰度低于邻域内其他像素的平均灰度时,模板的卷积为负数。对卷积运算的结果用适当的衰弱因子处理并加在原中心像素上,就可以实现图像的锐化处理。
dst = Laplacian(src, ddepth[, dst[, ksize[, scale[, delta[, borderType]]]]])
由于Sobel算子在计算相对较小的核的时候,其近似计算导数的精度比较低,比如一个33的Sobel算子,当梯度角度接近水平或垂直方向时,其不精确性就越发明显。Scharr算子同Sobel算子的速度一样快,但是准确率更高,尤其是计算较小核的情景,所以利用3*3滤波器实现图像边缘提取更推荐使用Scharr算子
Scharr算子又称为Scharr滤波器,也是计算x或y方向上的图像差分,在OpenCV中主要是配合Sobel算子的运算而存在的。Scharr算子的函数原型如下所示,和Sobel算子几乎一致,只是没有ksize参数.
dst = Scharr(src, ddepth, dx, dy[, dst[, scale[, delta[, borderType]]]]])
Canny边缘检测算子(多级边缘检测算法)是一种被广泛应用于边缘检测的标准算法,其目标是找到一个最优的边缘检测解或找寻一幅图像中灰度强度变化最强的位置。最优边缘检测主要通过低错误率、高定位性和最小响应三个标准进行评价。
Canny算子的实现步骤如下:
edges = Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradient]]])
LOG(Laplacian of Gaussian)边缘检测算子也称为Marr&Hildreth算子,它根据图像的信噪比来求检测边缘的最优滤波器。该算法首先对图像做高斯滤波,然后再求其拉普拉斯(Laplacian)二阶导数,根据二阶导数的过零点来检测图像的边界,即通过检测滤波结果的零交叉(Zero crossings)来获得图像或物体的边缘。
LOG算子该综合考虑了对噪声的抑制和对边缘的检测两个方面,并且把Gauss平滑滤波器和Laplacian锐化滤波器结合了起来,先平滑掉噪声,再进行边缘检测,所以效果会更好。 该算子与视觉生理中的数学模型相似,因此在图像处理领域中得到了广泛的应用。它具有抗干扰能力强,边界定位精度高,边缘连续性好,能有效提取对比度弱的边界等特点。
③ 如何使用Opencv对图像进行颜色特征提取
打开小画家,将色卡打开,点击吸管工具,移动鼠标到红色块上方,松开鼠标,此时,吸管工具已经提取了红色色块信息。然后点击右方的“编辑颜色”,弹出颜色选择对话框,此时,右下角有刚才习惯的红绿蓝颜色信息,如红152,绿18,蓝15。这个就是颜色对应的RGB色彩值。
创建VC控制台工程,加入所需头文件和库文件。注意要事先将opencv 头文件和库文件添加到工程属性中,作者使用opencv2.4+VC2010示范。
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <stdio.h>
#include <vector>
#include <math.h>
#pragma comment (lib,"opencv_core244d.lib")
#pragma comment (lib,"opencv_highgui244d.lib")
#pragma comment (lib,"opencv_imgproc244d.lib")
定义一个颜色常量target,默认值即为刚才吸管工具提取的红色色块的RGB值分量,和一个计算与颜色标准值色差的函数,暂定为色差在10以内即为相近颜色。这里cv::Vec3b 变量对应一个数组值, Vec3b[0] 对应blue,Vec3b[1]对应green,Vec3b[2]对应Red,即BGR。
接着添加以下代码,目的为读取源图片,根据图片大小做一定的缩放,然后定义同样大小的一个灰度图,用于存贮计算结果值。核心算法如下,遍历源图片,色差(与红色色块比较)低于30的即为需要提取的颜色值,将此像素位置记录并写入到灰度图中,以黑色显示,否则设置为白色。然后将处理前后图片显示出来。
实际商业应用中也是可以如法炮制的,比如下图的药片,大小,颜色不统一,如何提取识别黄色小药片在何处或计算其数量呢。
笔者尝试使用吸管工具提取其BGR值为(22,184,245),将色差由10调整为30,可以正确检测此药片。如图。
④ OpenCV-Python教程:57.图像修复
基础
你们可能家里都会有一些老照片已经有黑点啊,划痕啊等。你有想过修复它们么?我们不能简单的在绘图工具里把他们擦除了就完了。因为这样只是把黑色的东西变成白色的而已,实际上没用。在这种情况下,会用到一种技术叫图像修复。基本的思想很简单:用周围的像素替换坏掉的像素,这样看上去就和周围一样了。比如下面这张:
很多算法被设计来干这个,OpenCV提供了两个,可以用同一个函数来访问: cv2.inpaint()
第一个算法是基于论文" An Image Inpainting Technique Based on the Fast Marching Method"。 是基于快速匹配方法的。假设图像里的一个区域要修复。算法从这个区域的边界开始,逐渐地进入区域,把边界内的所有东西填充上。它取要修复的部分周围的一个像素周围的一小片邻居。这个像素被周围已知的像素的标准加权和替换掉。选择权重是很重要的。要修复的点周围像素的权重较高。和正常边界近的,还有在边界轮廓上的像素的权重较高。当像素被修复以后,它会通过快速匹配方法(FMM)移动到最近的像素。FMM保证那些已知像素周围的像素首先被修复,所以这个就像人工启发式的操作一样。这个算法使用标志cv2.INPAINT_TELEA开启。
第二个算法基于论文" Navier-Stokes, Fluid Dynamics, and Image and Video Inpainting ".这个算法基于流体动力学和偏微分方程。基本原则是启发式。它首从已知区域先沿着边缘到未知区域访问(由于边缘应该是连续的)。在匹配边要修复区域边界的梯度向量时持续画等值线(把相同亮度的点用线连起来,类似于轮廓线)。这时候用到流体动力学。之后会填充颜色以减小最小方差。这个算法用标志cv2.INPAINT_NS启用。
编码
我们需要创建和输入图像相同大小的掩图,需要修复的区域对应的像素要非0.剩下的就简单了。我的图像被一些黑色划痕给破坏了(实际上是我自己加的)。我用绘图工具对应的标记出来。
看下面的结果。第一个图片是输入图像,第二个是掩图,第三个是用第一种算法的结果,最后一张是第二种算法的结果。
END