常用图像处理

opencv安装可以参考这篇文章

斑点检测

斑点

图像特征点检测包括角点和斑点,斑点(Blob)是指二维图像中和周围颜色有颜色差异和灰度差异的区域,因为斑点代表的是一个区域,所以其相对于单纯的角点,具有更好的稳定性和更好的抗干扰能力.斑点通常是指与周围有着颜色和灰度差别的区域

在实际地图中,往往存在着大量这样的斑点,如一颗树是一个斑点,一块草地是一个斑点,一栋房子也可以是一个斑点。由于斑点代表的是一个区域,相比单纯的角点,它的稳定性要好,抗噪声能力要强,所以它在图像配准上扮演了很重要的角色。

同时有时图像中的斑点也是我们关心的区域,比如在医学与生物领域,我们需要从一些X光照片或细胞显微照片中提取一些具有特殊意义的斑点的位置或数量。

原理

opencv提供了SimpleBlobDetector函数用来检测斑点

  1. 阈值:通过使用以minThreshold开始的阈值对源图像进行阈值处理,将源图像转换为多个二进制图像。这些阈值以thresholdStep递增,直到maxThreshold。因此,第一个阈值为minThreshold,第二个阈值为minThreshold + thresholdStep,第三个阈值为minThreshold + 2 x thresholdStep,依此类推;
  2. 分组:在每个二进制图像中,连接的白色像素被分组在一起。我们称这些二进制blob;
  3. 合并:计算二进制图像中二进制斑点的中心,并合并比minDistBetweenBlob更近的斑点;
  4. 中心和半径计算:计算并返回新合并的Blob的中心和半径。

选项

SimpleBlobDetector_create可按颜色,大小和形状来过滤斑点类型:

按颜色:首先需要设置filterByColor =True。设置blobColor = 0可选择较暗的blob,blobColor = 255可以选择较浅的blob。

按大小:可以通过设置参数filterByArea = 1以及minArea和maxArea的适当值来基于大小过滤blob。例如。设置minArea = 100将滤除所有少于100个像素的斑点。

按圆度:这只是测量斑点距圆的距离。例如。正六边形的圆度比正方形高。要按圆度过滤,请设置filterByCircularity =1。然后为minCircularity和maxCircularity设置适当的值。圆度定义为()。圆的为圆度为1,正方形的圆度为PI/4,依此类推。下面公式S表示面积,C表示周长

\[ afa(正圆)=\frac{4\times\pi\times S}{C\times C}=1 \\ afa(正方形)=\frac{4\times\pi\times S}{C\times C}=\frac{\pi}{4} \]

按凸性:凸度定义为(斑点的面积/凸包的面积)。形状的“凸包”是最紧密的凸形,它完全包围了该形状,用不严谨的话来讲,给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边形,它能包含点集中所有的点。直观感受上,凸性越高则里面“奇怪的部分”越少。要按凸度过滤,需设置filterByConvexity = true,minConvexity、maxConvexity应该属于[0,1],而且maxConvexity> minConvexity。

按惯性比:这个词汇比较抽象。我们需要知道Ratio可以衡量形状的伸长程度。简单来说。对于圆,此值是1,对于椭圆,它在0到1之间,对于直线,它是0。按惯性比过滤,设置filterByInertia = true,并设置minInertiaRatio、maxInertiaRatio同样属于[0,1]并且maxConvexity> minConvexity。

20221108173749

SimpleBlobDetector::Params::Params()
{
thresholdStep = 10; //二值化的阈值步长,即公式1的t
minThreshold = 50; //二值化的起始阈值,即公式1的T1
maxThreshold = 220; //二值化的终止阈值,即公式1的T2
//重复的最小次数,只有属于灰度图像斑点的那些二值图像斑点数量大于该值时,该灰度图像斑点才被认为是特征点
minRepeatability = 2;
//最小的斑点距离,不同二值图像的斑点间距离小于该值时,被认为是同一个位置的斑点,否则是不同位置上的斑点
minDistBetweenBlobs = 10;

filterByColor = true; //斑点颜色的限制变量
blobColor = 0; //表示只提取黑色斑点;如果该变量为255,表示只提取白色斑点

filterByArea = true; //斑点面积的限制变量
minArea = 25; //斑点的最小面积
maxArea = 5000; //斑点的最大面积

filterByCircularity = false; //斑点圆度的限制变量,默认是不限制
minCircularity = 0.8f; //斑点的最小圆度
//斑点的最大圆度,所能表示的float类型的最大值
maxCircularity = std::numeric_limits<float>::max();

filterByInertia = true; //斑点惯性率的限制变量
minInertiaRatio = 0.1f; //斑点的最小惯性率
maxInertiaRatio = std::numeric_limits<float>::max(); //斑点的最大惯性率

filterByConvexity = true; //斑点凸度的限制变量
minConvexity = 0.95f; //斑点的最小凸度
maxConvexity = std::numeric_limits<float>::max(); //斑点的最大凸度
}

实例

# -*- coding:utf-8 -*-
import cv2
import numpy as np

# Read image
im = cv2.imread("blob.jpg", cv2.IMREAD_GRAYSCALE)

# 设置SimpleBlobDetector_Params参数
params = cv2.SimpleBlobDetector_Params()
# 改变阈值
params.minThreshold = 10
params.maxThreshold = 200
# 通过面积滤波
params.filterByArea = True
params.minArea = 1500
# 通过圆度滤波
params.filterByCircularity = True
params.minCircularity = 0.1
# 通过凸度滤波
params.filterByConvexity = True
params.minConvexity = 0.87
# 通过惯性比滤波
params.filterByInertia = True
params.minInertiaRatio = 0.01
# 创建一个检测器并使用默认参数
detector = cv2.SimpleBlobDetector_create(params)
# 检测blobs.
key_points = detector.detect(im)

# 绘制blob的红点
draw_image = cv2.drawKeypoints(im, key_points, np.array([]), (0, 0, 255),
cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

# Show blobs
cv2.imshow("key_points", draw_image)
cv2.waitKey(0)

20221108174037

凸包提取

convexhull函数:计算出图像的凸包,根据图像的轮廓点,通过函数convexhull转化成凸包的点坐标,从而画出图像的凸包。

 void convexHull(InputArray points,OutputArray hull,bool clockwise =  false,bool returnPoints = true)

//InputArray points: 得到的点集,一般是用图像轮廓函数求得的轮廓点

//OutputArray hull: 输出的是凸包的二维xy点的坐标值,针对每一个轮廓形成的

//bool clockwise = false: 表示凸包的方向,顺时针或者逆时针

//bool returnPoint = true: 表示返回点还是点地址的索引

二值化

图像的二值化就是将图像上的像素点的灰度值设置为0或255,这样将使整个图像呈现出明显的黑白效果。在数字图像处理中,二值图像占有非常重要的地位,图像的二值化使图像中数据量大为减少,从而能凸显出目标的轮廓。OpenCV中提供了函数cv::threshold();

参数说明

  • src:源图像,可以为8位的灰度图,也可以为32位的彩色图像。(两者由区别)
  • dst:输出图像
  • thresh:阈值
  • maxval:dst图像中最大值
  • type:阈值类型,可以具体类型如下:

20221118112327

效果图

20221118112607

轮廓提取

findContours可以提取轮廓

  • src:源图像
  • mode:轮廓的检索方式
  • method:一般用 cv.CHAIN_APPROX_SIMPLE,就表示用尽可能少的像素点表示轮廓
  • contours:图像轮廓坐标
  • hierarchy:[Next, Previous, First Child, Parent],文中有详细解释

drawContours可以附加轮廓到图像上

理解轮廓

20221118113845

图中总共有8条轮廓,2和2a分别表示外层和里层的轮廓,3和3a也是一样。从图中看得出来:

  • 轮廓0/1/2是最外层的轮廓,我们可以说它们处于同一轮廓等级:0级
  • 轮廓2a是轮廓2的子轮廓,反过来说2是2a的父轮廓。轮廓2a算一个等级:1级
  • 同样3是2a的子轮廓,轮廓3处于一个等级:2级
  • 类似的,3a是3的子轮廓,等等…………

hierarchy

如果我们打印出cv.findContours()函数的返回值hierarchy,会发现它是一个包含4个值的数组:[Next, Previous, First Child, Parent],他们每个值都表示在数组的索引

  • Next:与当前轮廓处于同一层级的下一条轮廓,如果没有与它同一层级的下一条轮廓,此时Next=-1
  • Previous:与当前轮廓处于同一层级的上一条轮廓,同理
  • First Child:当前轮廓的第一条子轮廓,如果没有则为0
  • Parent:当前轮廓的父轮廓

RETR_LIST

这是最简单的一种寻找方式,它不建立轮廓间的子属关系,也就是所有轮廓都属于同一层级。这样,hierarchy中的后两个值[First Child, Parent]都为-1。

因为没有从属关系,所以轮廓0的下一条是1,1的下一条是2……

如果你不需要轮廓层级信息的话,cv.RETR_LIST更推荐使用,因为性能更好

RETR_TREE

建立完整的轮廓等级

20221118114703

RETR_EXTERNAL

这种方式只寻找最高层级的轮廓,也就是它只会找到前面我们所说的3条0级轮廓

RETR_CCOMP

它把所有的轮廓只分为2个层级,不是外层的就是里层的。

20221118114901

图中括号里面1代表外层轮廓,2代表里层轮廓。比如说对于轮廓2,Next就是4,Previous是1,它有里层的轮廓3,所以First Child=3,但因为只有两个层级,它本身就是外层轮廓,所以Parent=-1。