我的工作基于带有点阵的图像(图1),最终结果如图4所示。我将逐步解释我的工作。
图。 1原始图像
第1步:检测每个对象的边缘,包括点和我想要删除的“响铃”以获得更好的性能。边缘检测的结果如图2所示。我使用Canny边缘探测器,但它对一些浅灰色点不起作用。 我的第一个问题是如何关闭点的轮廓并尽可能减少其他噪音?
图。 2边缘检测
第2步:扩充每个对象。我找不到填补洞的好方法,所以我直接扩张它们。如图3所示,孔似乎扩大得太多,其他噪音也是如此。 我的第二个问题是如何填充或扩大孔洞以使它们以相同/相似的尺寸填充圆圈?
图。 3扩张
第3步:查找并绘制每个点的质心。如图4所示,由于粗略图像处理,存在“环”的标记,并且一些点以两个白色像素示出。 所需的结果应该只显示一个点的点和一个白色像素。
图。 4:群众中心
以下是这3个步骤的代码。任何人都可以帮助我的工作做得更好吗?
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>
#include <cv.h>
#include <highgui.h>
using namespace std;
using namespace cv;
// Global variables
Mat src, edge, dilation;
int dilation_size = 2;
// Function header
void thresh_callback(int, void*);
int main(int argc, char* argv)
{
IplImage* img = cvLoadImage("c:\\dot1.bmp", 0); // dot1.bmp = Fig. 1
// Perform canny edge detection
cvCanny(img, img, 33, 100, 3);
// IplImage to Mat
Mat imgMat(img);
src = img;
namedWindow("Step 1: Edge", CV_WINDOW_AUTOSIZE);
imshow("Step 1: Edge", src);
// Apply the dilation operation
Mat element = getStructuringElement(2, Size(2 * dilation_size + 1, 2 * dilation_size + 1),
Point(dilation_size, dilation_size)); // dilation_type = MORPH_ELLIPSE
dilate(src, dilation, element);
// imwrite("c:\\dot1_dilate.bmp", dilation);
namedWindow("Step 2: Dilation", CV_WINDOW_AUTOSIZE);
imshow("Step 2: Dilation", dilation);
thresh_callback( 0, 0 );
waitKey(0);
return 0;
}
/* function thresh_callback */
void thresh_callback(int, void*)
{
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
// Find contours
findContours(dilation, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
// Get the moments
vector<Moments> mu(contours.size());
for(int i = 0; i < contours.size(); i++) {
mu[i] = moments(contours[i], false);
}
// Get the mass centers
vector<Point2f> mc(contours.size());
for(int i = 0; i < contours.size(); i++) {
mc[i] = Point2f(mu[i].m10/mu[i].m00 , mu[i].m01/mu[i].m00);
}
// Draw mass centers
Mat drawing = Mat::zeros(dilation.size(), CV_8UC1);
for( int i = 0; i< contours.size(); i++ ) {
Scalar color = Scalar(255, 255, 255);
line(drawing, mc[i], mc[i], color, 1, 8, 0);
}
namedWindow("Step 3: Mass Centers", CV_WINDOW_AUTOSIZE);
imshow("Step 3: Mass Centers", drawing);
}
答案 0 :(得分:10)
您可以采取一些措施来改善效果。要减少图像中的噪点,可以在应用Canny算子之前应用中值模糊。这是一种常见的去噪技术。另外,请尽量避免使用C API和IplImage
。
cv::Mat img = cv::imread("c:\\dot1.bmp", 0); // dot1.bmp = Fig. 1
cv::medianBlur(img, img, 7);
// Perform canny edge detection
cv::Canny(img, img, 33, 100);
这可以显着减少边缘图像中的噪点:
为了更好地保留点的原始大小,您可以使用较小的内核而不是扩张来执行形态闭合的几次迭代。这也会减少点与圆的连接:
// This replaces the call to dilate()
cv::morphologyEx(src, dilation, MORPH_CLOSE, cv::noArray(),cv::Point(-1,-1),2);
这将使用3x3内核执行两次迭代,使用cv::noArray()
。
结果更清晰,点完全填满:
保持管道的其余部分不变,最终得出结果。圆圈中仍然存在一些虚假的质量中心,但比原始方法少得多:
如果您想尝试完全从结果中删除圆圈,可以尝试使用cv::HoughCircles()
并调整参数,直到获得良好的结果。这可能有一些困难,因为整个圆圈在图像中不可见,只有片段,但我建议您尝试一下。如果您确实检测到最里面的圆圈,则可以将其用作遮罩来过滤外部质量中心。
答案 1 :(得分:5)
如何关闭点的轮廓? 使用填充绘图选项的drawContours方法(CV_FILLED或厚度= -1)
降低噪音? 使用blurring(低通滤波)方法之一。
相似尺寸? 在扩张后使用侵蚀= morphological closing。
一个圆圈的一个点,没有外环的输出? 找到所有contour areas的平均值。 erase contours与此值有很大差异。输出剩余的中心。
Aurelius已经提到了其中大部分内容,但由于这个问题非常有趣,我可能会在有足够时间时尝试发布一个完整的解决方案。祝你好运。