基于opencv的珠盘个数检测(c++)
目标:检测该图片上的个数与缺几个并标出来
效果图:
算法原理:
先加载原图然后进行转灰度,进行形态学梯度处理,接着二值化,在进行2次形态学处理,提取轮廓(计算面积->寻找最小包围矩形)然后在进行角度纠正,再映射到X,Y寻找缺点
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace cv;
using namespace std;
void Y_projection(Mat &warp, Mat &src, int max_gap, int &first, int &end);
int main(int argc, char** argv) {
Mat src = imread("E:/opencv/yuandian.jpg");
imshow("src", src);
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
//获取自定义核
Mat element = getStructuringElement(MORPH_RECT, Size(3, 3));
//形态学梯度(膨胀与腐蚀之差,保留物体的边缘)
Mat dst;
morphologyEx(gray, dst, MORPH_GRADIENT, element);
//imshow("xingtaixuetidu", dst);
//二值化
Mat binary;
threshold(dst, binary, 0, 255, THRESH_OTSU | THRESH_BINARY);
imshow("binary", binary);
//形态学处理
Mat element_1 = getStructuringElement(MORPH_ELLIPSE, Size(5, 5));
//开运算(先腐蚀在膨胀,消除小物体,在细点分离物体)
morphologyEx(binary, binary, MORPH_OPEN, element_1);
imshow("MORPH_OPEN", binary);
//闭运算(先膨胀在腐蚀,排除小型黑洞连接断点)
Mat elemetn_2 = getStructuringElement(MORPH_ELLIPSE, Size(10, 10));
morphologyEx(binary, binary, MORPH_CLOSE, elemetn_2);
imshow("MORPH_CLOSE", binary);
//提取轮廓
vector < vector<Point >> contours;
findContours(binary, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
//创建一个和原图像大小的Mat
Mat result = Mat::zeros(binary.size(), binary.type());
int total = 0;
for (size_t i = 0; i < contours.size(); i++) {
double area = contourArea(contours[i]);
if (area < 55) {
continue;
}
//寻找最小包围矩形
RotatedRect rrt = minAreaRect(contours[i]);
Point2f vertex[4];
rrt.points(vertex);
circle(result, rrt.center, 5, (255), -1);
total += 1;
}
imshow("result", result);
Mat warp;
Mat rotMat = getRotationMatrix2D(Point2f((result.rows - 1) / 2, (result.cols - 1) / 2), 177,-1);
warpAffine(result, warp, rotMat, result.size());
warpAffine(src, src, rotMat, src.size());
imshow("warp", warp);
imshow("src warp", src);
/////////
vector<int> bins;
for (int i = 0; i < warp.rows; i++) {
int found = 0;
for (int j = 0; j < warp.cols; j++) {
if (warp.at<uchar>(i, j) == 255) {
found += 1;
}
}
if (found > 0) {
bins.push_back(i);
}
}
/////////
vector<int> tbins;
for (int i = 0; i < (bins.size()-1); i++) {
int gap = bins[i + 1] - bins[i];
if (gap >= 15) {
tbins.push_back(bins[i + 1] - (gap/2));
}
}
int h = warp.rows - 1;
for (int i = 0; i < tbins.size(); i++) {
if (i == 0) {
Y_projection(warp, src, 50, i, tbins[i]);
}
else if(i==(tbins.size()-1)){
Y_projection(warp, src, 50, tbins[i],h);
}
else
{
int end = tbins[i] - 1;
Y_projection(warp, src, 50, tbins[i-1], end);
}
}
String number = "number:" + to_string(total);
putText(src, number,Size(50,50),FONT_HERSHEY_SIMPLEX,1.0,Scalar(0,0,255),2);
imshow("fianl_src", src);
waitKey(0);
return 0;
}
void Y_projection(Mat &warp,Mat &src,int max_gap,int &first,int &end) {
vector<int> y_bins;
for (int i = 0; i < warp.cols; i++) {
int found_y = 0;
for (int j = first; j < end; j++) {
if (warp.at<uchar>(j, i) == 255) {
found_y += 1;
}
}
if (found_y > 0) {
y_bins.push_back(i);
}
}
vector<int> y_tbins;
for (int i = 0; i < y_bins.size()-1; i++) {
int gap = y_bins[i + 1] - y_bins[i];
if (gap >= 7) {
y_tbins.push_back(y_bins[i + 1] - (gap/2));
}
if (gap >= 50) {
circle(src, Point(y_bins[i + 1] - (gap / 2),(end-(end-first)/2)), 5, Scalar(0,0,255), -1);
}
}
}