目标:检测工具盒中工件的个数及缺失工件的位置。
思路:灰度转换,形态学处理,二值化,轮廓处理,拟合圆,位置分析等。
代码:
// CountElement.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
void Y_projection(Mat &warp, Mat &src, int max_gap, int &first, int &end);
int main()
{
std::cout << "Hello World!\n";
Mat srcImage = imread("2.jpg");
namedWindow("原始图");
imshow("原始图", srcImage);
Mat grayImage;
cvtColor(srcImage, grayImage, COLOR_RGB2GRAY);
//namedWindow("grayImage");
//imshow("grayImage", grayImage);
Mat kernal = getStructuringElement(MORPH_RECT, Size(3, 3));
Mat gradientImage;
morphologyEx(grayImage, gradientImage, MORPH_GRADIENT, kernal);
namedWindow("gradientImage形态学梯度");
imshow("gradientImage形态学梯度", gradientImage);
Mat thresholdImage;
threshold(gradientImage, thresholdImage, 0, 255, THRESH_OTSU);
namedWindow("二值化OTSU图");
imshow("二值化OTSU图", thresholdImage);
//imwrite("thresholdImage.jpg", thresholdImage);
kernal = getStructuringElement(MORPH_ELLIPSE, Size(5, 5));
Mat openImage;
morphologyEx(thresholdImage, openImage, MORPH_OPEN, kernal);
//namedWindow("openImage");
//imshow("openImage", openImage);
kernal = getStructuringElement(MORPH_ELLIPSE, Size(10, 10));
Mat closeImage;
morphologyEx(openImage, closeImage, MORPH_CLOSE, kernal);
namedWindow("closeImage");
imshow("closeImage", closeImage);
/*Mat cannyImage;
Canny(closeImage, cannyImage, 50, 100);
namedWindow("cannyImage");
imshow("cannyImage", cannyImage);*/
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours(closeImage, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
Mat resultImage = Mat::zeros(grayImage.rows, grayImage.cols, grayImage.type());
int total = 0;
for (int i = 0; i < contours.size(); i++)
{
double area = contourArea(contours[i]);
if (area < 55) continue;
total++;
RotatedRect rect = minAreaRect(contours[i]);
//Point2f rectVertex[4];
//rect.points(rectVertex);
circle(resultImage, rect.center, 5, Scalar(255),-1);
}
vector<Point> pts;
for (int i = 0; i < resultImage.rows; i++)
{
for (int j = 0; j < resultImage.cols; j++)
{
if (resultImage.ptr<uchar>(i)[j] == 255)
{
pts.push_back(Point(j, i));
}
}
}
RotatedRect rect = minAreaRect(pts);
Point2f rectVertex[4];
rect.points(rectVertex);
for (int i = 0; i < 4; i++)
{
putText(resultImage, to_string(i), rectVertex[i], FONT_HERSHEY_SIMPLEX, 1.0, Scalar(200));
line(resultImage, rectVertex[i], rectVertex[(i + 1) % 4], Scalar(100), 2, 8);
}
cout << "角度为:" << rect.angle;
cout << "宽度为:" << rect.size.width;
cout << "高度为:" << rect.size.height;
namedWindow("resultImage");
imshow("resultImage", resultImage);
Mat matrix = getRotationMatrix2D(rect.center, rect.angle, 1);
warpAffine(resultImage, resultImage, matrix, resultImage.size());
warpAffine(srcImage, srcImage, matrix, srcImage.size());
//-----------------------------------------------------------------
//计算每行工具所在的行数
vector<int> bins;
vector<int> tbins;
for (int i = 0; i < resultImage.rows; i++) {
int found = 0;
for (int j = 0; j < resultImage.cols; j++) {
if (resultImage.at<uchar>(i, j) == 255) {
found += 1;
}
}
if (found > 0) {
cout << i<<endl;
bins.push_back(i);
}
}
for (int i = 0; i < (bins.size() - 1); i++) {
int gap = bins[i + 1] - bins[i];
if (gap >= 15) {
cout << "tbins: " << bins[i + 1] - (gap / 2)<<endl;
tbins.push_back(bins[i + 1] - (gap / 2));
}
}
//-----------------------------------------------------------------
//-----------------------------------------------------------------
//逐行排查缺失工具的位置
Mat dstImage;
srcImage.copyTo(dstImage);
int h = resultImage.rows - 1;
for (int i = 0; i < tbins.size(); i++) {
if (i == 0)
{
//第一排工具所占行数从0到第一个位置
Y_projection(resultImage, dstImage, 50, i, tbins[i]);
}
else if (i == (tbins.size() - 1))
{
//最后一排工具所占行数从最后一个位置到图片最后一行
Y_projection(resultImage, dstImage, 50, tbins[i], h);
}
else
{
//中间行工具所占行数为上下两个位置之间
int end = tbins[i] - 1;
Y_projection(resultImage, dstImage, 50, tbins[i - 1], end);
}
}
//-----------------------------------------------------------------
//namedWindow("resultImage");
//imshow("resultImage", resultImage);
putText(dstImage, "numvers: "+to_string(total),Point(50,50), FONT_HERSHEY_SIMPLEX, 1.0, Scalar(255,255,255));
namedWindow("dstImage");
imshow("dstImage", dstImage);
waitKey(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 >= 15) {
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,255,255), -1);
}
}
}