opencv-052-二值图像分析(使用几何矩计算轮廓中心与横纵比过滤)

知识点

对图像二值图像的每个轮廓,可以计算轮廓几何矩,根据几何矩可以计算图像的中心位置,估计得到中心位置可以计算中心矩、然后再根据中心矩可以计算胡矩。

几何矩

API

1
2
3
4
5
6
Moments cv::moments(
InputArray array,
bool binaryImage = false
)
array是输入的图像轮廓点集合
输出图像的几何矩。

代码(c++,python)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main() {
Mat src = imread("../images/stuff.jpg");
if (src.empty()) {
cout << "could not load image.." << endl;
}
imshow("input", src);

// 去噪声与二值化
Mat dst, gray, binary;
Canny(src, binary, 80, 160);

Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
dilate(binary, binary, k);

// 轮廓发现与绘制
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(binary, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point());
for (size_t t = 0; t < contours.size(); t++) {
// 最小外接轮廓
RotatedRect rrt = minAreaRect(contours[t]);
float w = rrt.size.width;
float h = rrt.size.height;
Point2f pts[4];
rrt.points(pts);

// 计算横纵比
float ratio = min(w,h) / max(w,h);
Point2f cpt = rrt.center;
circle(src, cpt, 2, Scalar(255,0,0), 2);
if(ratio > 0.9){
circle(src, cpt, 2, Scalar(255,0,0), 2);
// 绘制旋转矩形
for (int i = 0; i < 4; i++) {
line(src, pts[i % 4], pts[(i + 1) % 4], Scalar(0, 0, 255), 2);
}
}
if(ratio < 0.5){
circle(src, cpt, 2, Scalar(255,0,0), 2);
// 绘制旋转矩形
for (int i = 0; i < 4; i++) {
line(src, pts[i % 4], pts[(i + 1) % 4], Scalar(0, 255, 0), 2);
}
}
}

imshow("contours", src);

waitKey(0);
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import cv2 as cv
import numpy as np


def canny_demo(image):
t = 80
canny_output = cv.Canny(image, t, t * 2)
cv.imshow("canny_output", canny_output)
cv.imwrite("D:/canny_output.png", canny_output)
return canny_output


src = cv.imread("D:/images/stuff.jpg")
cv.namedWindow("input", cv.WINDOW_AUTOSIZE)
cv.imshow("input", src)
binary = canny_demo(src)
k = np.ones((3, 3), dtype=np.uint8)
binary = cv.morphologyEx(binary, cv.MORPH_DILATE, k)

# 轮廓发现
out, contours, hierarchy = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
for c in range(len(contours)):
rect = cv.minAreaRect(contours[c])
cx, cy = rect[0]
ww, hh = rect[1]
ratio = np.minimum(ww, hh) / np.maximum(ww, hh)
print(ratio)
mm = cv.moments(contours[c])
m00 = mm['m00']
m10 = mm['m10']
m01 = mm['m01']
cx = np.int(m10 / m00)
cy = np.int(m01 / m00)
box = cv.boxPoints(rect)
box = np.int0(box)
if ratio > 0.9:
cv.drawContours(src, [box], 0, (0, 0, 255), 2)
cv.circle(src, (np.int32(cx), np.int32(cy)), 2, (255, 0, 0), 2, 8, 0)
if ratio < 0.5:
cv.drawContours(src, [box], 0, (255, 0, 255), 2)
cv.circle(src, (np.int32(cx), np.int32(cy)), 2, (0, 0, 255), 2, 8, 0)


# 显示
cv.imshow("contours_analysis", src)
cv.imwrite("D:/contours_analysis.png", src)
cv.waitKey(0)
cv.destroyAllWindows()

结果

代码地址

github