2013-07-19 101 views
7

我的工作是基于具有点阵的图像(图1),最终结果如图4所示。我将逐步解释我的工作。Opencv:边缘检测,扩张和质心绘图

图1个原始图像

enter image description here

步骤1:检测的每个对象的边缘,包括我想删除获得更好的性能的点和“环”。而边缘检测的结果如图2所示。我使用Canny边缘检测器,但对于一些浅灰色的点不能很好地工作。 我的第一个问题是如何关闭点的轮廓并尽可能减少其他噪点?

图2的边缘检测

enter image description here

步骤2:扩张每一个对象。我没有找到填补漏洞的好方法,所以我直接扩张它们。如图3所示,空洞似乎过大,其他噪音也会增大。 我的第二个问题是如何填充或扩大孔,以使它们以相同/相似的大小填充圆圈?

图3扩张

enter image description here

步骤3:查找和绘制每个点的质心。如图4所示,由于粗略的图像处理,存在“环”的标记,并且一些点以两个白色像素示出。 想要的结果应该只显示一个点的点和一个白色像素。

图4:质谱中心

enter image description here

这是我对这些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

你有没有尝试过[这里](http://stackoverflow.com/questions/1716274/fill-the-holes-in-opencv)呢? – William

回答

9

您可以通过几件事来改善您的结果。要减少图像中的噪点,可以在应用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); 

这显著减少你的边缘图像噪点的数量: Canny result

为了更好地留住你点的原始尺寸,你可以用较小的内核,而不是执行形态闭合的几个迭代扩张。这也将减少与圆的点的接合:

// This replaces the call to dilate() 
cv::morphologyEx(src, dilation, MORPH_CLOSE, cv::noArray(),cv::Point(-1,-1),2); 

这将3×3的内核,通过使用cv::noArray()指示执行两个迭代。

结果是更清洁,并且点被完全填充:

Closing result

离开其余管道未修改给出了最终的结果。还有从圆一些虚假的质心,但比原来的方法相当少:

Mass centers

如果您想尝试删除结果中圈完全,你可以尝试使用cv::HoughCircles()和调整参数,直到你得到一个好的结果。这可能会有一些困难,因为整个圆圈在图片中不可见,只有细分,但我建议您尝试一下。如果您确实检测到最内层的圆圈,则可以将其用作掩模来滤除外部质心。

+0

谢谢,你确实改进了我的工作。另一个问题:我注意到cv :: Canny和cvCanny之间有一点区别。为什么他们创建了两个类似的函数,哪一个(前缀为'cv ::'和'cv'的函数)应该更经常使用? – WangYudong

+0

两者之间应该没有区别 - 它们在内部使用相同的实现。 'cv :: Canny()'与C++ API中的'cv :: Mat'一起使用,您应该更喜欢它。 'cvCanny()'来自使用'IplImage'的C API,并且已被弃用。 – Aurelius

5

如何关闭点的轮廓?使用drawContours方法具有填充绘图选项(CV_FILLED或厚度= -1)

降低噪声?使用blurring(低通滤波)方法之一。

similar similar?膨胀后使用侵蚀= morphological closing

一个点为一个圆圈,输出没有外环?找到所有的平均值contour areaserase contours与此值有很大差异。输出其余的中心。

Aurelius已经提到了其中的大部分,但由于这个问题很安静,所以我会在有足够时间的时候尝试并发布一个完整的解决方案。祝你好运。