OpenCV --- 图像预处理(七)
OpenCV — 图像预处理(七)
文章目录
- OpenCV --- 图像预处理(七)
- 十七,图像轮廓特征查找
- 17.1 外接矩形
- 17.2 最小外接矩形
- 17.3 最小外接圆
十七,图像轮廓特征查找
- 图像轮廓特征查找其实就是他的外接轮廓。
- 应用:
- 图像分割
- 形状分析
- 物体检测与识别
- 根据轮廓点进行,所以要先找到轮廓。
- 先灰度化、二值化。目标物体白色,非目标物体黑色,选择合适的儿值化方式。
- 有了轮廓点就可以找到最上、最下、最左、最右的四个坐标, X m i n 、 X m a x 、 Y m i n 、 Y m a x X_{min}、X_{max}、Y_{min}、Y_{max} Xmin、Xmax、Ymin、Ymax。就可以绘制出矩形。
17.1 外接矩形
boundingRect(轮廓点)
num = cv.imread('txycl/images/num.png')
# 灰度图
gray = cv.cvtColor(num, cv.COLOR_BGR2GRAY)
# 二值化处理
_,binary = cv.threshold(gray, 127, 255, cv.THRESH_OTSU+cv.THRESH_BINARY_INV)
# 查找轮廓
contours, hierarchy = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
# 绘制轮廓
cv.drawContours(num, contours, -1, (0, 0, 255), 2)
cv.imshow('num', num)
# cv.waitKey(0)
# cv.destroyAllWindows()
# 外接矩形x,y,w,h = cv.boundingRect(contours[0])轮廓点
for cnt in contours:x, y, w, h = cv.boundingRect(cnt)cv.rectangle(num, (x, y), (x + w, y + h), (0, 255, 0), 2)# 显示效果
cv.imshow('num2', num)
cv.waitKey(0)
cv.destroyAllWindows()
17.2 最小外接矩形
- 最小外接矩形就是在外接矩形的基础上基于凸包进行旋转卡壳法
- 假如凸包如下
- 以其中的一条边为起始边,然后按照逆时针方向计算每个凸包点与起始边的距离,并将距离最大的点记录下来。
- 以a、b两点为起始边,并计算出e点离起始边最远,那么e到起始边的距离就是一个矩形的高度,因此我们只需要再找出矩形的宽度即可。对于矩形的最右边,以向量 a b ‾ \overline{{a b}} ab为基准,然后分别计算凸包点在向量 a b ‾ \overline{{a b}} ab上的投影的长度,投影最长的凸包点所在的垂直于起始边的直线就是矩形最右边所在的直线。
d点就是在向量 a b ‾ \overline{{a b}} ab上投影最长的凸包点,那么通过d点垂直于直线ab的直线就是矩形的右边界所在的直线。矩形的左边界的也是这么计算的,不同的是使用的向量不是 a b ‾ \overline{{a b}} ab而是 b a ‾ \overline{{b a}} ba
如上图所示,h点垂直于ab的直线就是以ab为起始边所计算出来的矩形所在的左边界所在的直线。其中矩形的高就是e点到直线ab的距离,矩形的宽是h点在向量上 b a ‾ \overline{{b a}} ba的投影加上d点在向量 a b ‾ \overline{{a b}} ab上的投影减去ab的长度,即:
w i d t h = ∣ b h ‾ ∣ × cos a + ∣ a d ‾ ∣ × cos θ − ∣ a b ‾ ∣ w i d t h=|\overline{{{b h}}}|\times\cos a+|\overline{{{a d}}}|\times\cos\theta-|\overline{{{a b}}}| width=∣bh∣×cosa+∣ad∣×cosθ−∣ab∣
于是我们就有了以ab为起始边所构成的外接矩形的宽和高,这样就可以得到该矩形的面积。然后再以bc为起始边,并计算其外接矩形的面积。也就是说凸多边形有几个边,就要构建几次外接矩形,然后找到其中面积最小的矩形作为该凸多边形的最小外接矩形
- API
contours, hierarchy = cv2.findContours(image_np_thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours为二值图像上查找所有的外部轮廓
rect = cv2.minAreaRect(cnt)
传入的cnt参数为contours中的轮廓,可以遍历contours中的所有轮廓,然后计算出每个轮廓的小面积外接矩形
- rect 是计算轮廓最小面积外接矩形:rect 结构通常包含中心点坐标
(x, y)
、宽度width
、高度height
和旋转角度angle
cv2.boxPoints(rect).astype(int)
cv2.boxPoints(rect)返回 是一个形状为 4行2列的数组,每一行代表一个点的坐标(x, y),顺序按照逆时针或顺时针方向排列
将最小外接矩形转换为边界框的四个角点,并转换为整数坐标
cv2.drawContours(image, contours, contourIdx, color, thickness)
- image:原图像,一般为 numpy 数组,通常为灰度或彩色图像。
- contours:一个包含多个轮廓的列表,可以用上一个api得到的 [box]
- contourIdx:要绘制的轮廓索引。如果设置为
-1
,则绘制所有轮廓。 - color:轮廓的颜色,可以是 BGR 颜色格式的三元组,例如
(0, 0, 255)
表示红色。 - thickness:轮廓线的粗细,如果是正数,则绘制实线;如果是 0,则绘制轮廓点;如果是负数,则填充轮廓内部区域。
#最小轮廓矩形
# 读取图像
num = cv.imread("txycl/images/num.png")
# 灰度化
gray = cv.cvtColor(num, cv.COLOR_BGR2GRAY)
# 二值化
thresh, binary = cv.threshold(gray, 127, 255, cv.THRESH_OTSU + cv.THRESH_BINARY_INV)
# 查找轮廓
cnts, h = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
# 遍历每个轮廓
for cnt in cnts:# 获取外接矩形x, y, w, h = cv.boundingRect(cnt)# 绘制外接矩形cv.rectangle(num, (x, y), (x + w, y + h), (255, 0, 0), 2, cv.LINE_AA)# 获取凸包hull = cv.convexHull(cnt)# 绘制凸包cv.polylines(num, [hull], True, (0, 0, 255), 2, cv.LINE_AA)# 获取最小外接矩形rect = cv.minAreaRect(cnt)box = cv.boxPoints(rect).astype(np.int32)# 绘制最小外接矩形cv.drawContours(num, [box], -1, (0, 255, 0), 2, cv.LINE_AA)
# 显示图像
cv.imshow("num", num)
cv.waitKey(0)
cv.destroyAllWindows()
17.3 最小外接圆
寻找最小外接圆使用的算法是Welzl算法。Welzl算法基于一个定理:希尔伯特圆定理表明,对于平面上的任意三个不在同一直线上的点,存在一个唯一的圆同时通过这三个点,且该圆是最小面积的圆(即包含这三个点的圆中半径最小的圆,也称为最小覆盖圆
- API
cv2.minEnclosingCircle(points) -> (center, radius)
参数说明:
points
:输入参数图片轮廓数据
返回值:
center
:一个包含圆心坐标的二元组(x, y)
。radius
:浮点数类型,表示计算得到的最小覆盖圆的半径。
cv2.circle(img, center, radius, color, thickness)
img
:输入图像,通常是一个numpy数组,代表要绘制圆形的图像。center
:一个二元组(x, y)
,表示圆心的坐标位置。radius
:整型或浮点型数值,表示圆的半径长度。color
:颜色标识,可以是BGR格式的三元组(B, G, R)
,例如(255, 0, 0)
表示红色。thickness
:整数,表示圆边框的宽度。如果设置为-1
,则会填充整个圆。
#最小外接圆
# 读取图像
num = cv.imread("txycl/images/num.png")
# 灰度化
gray = cv.cvtColor(num, cv.COLOR_BGR2GRAY)
# 二值化
thresh, binary = cv.threshold(gray, 127, 255, cv.THRESH_OTSU + cv.THRESH_BINARY_INV)
# 查找轮廓
cnts, h = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
# 遍历每个轮廓
for cnt in cnts:# 获取外接圆(x, y), radius = cv.minEnclosingCircle(cnt)# 绘制外接圆center = (int(x), int(y))radius = int(radius)cv.circle(num, center, radius, (255, 0, 0), 2, cv.LINE_AA)#显示图像
cv.imshow("num", num)
cv.waitKey(0)
cv.destroyAllWindows()