7

我的工作基于带有点阵列的图像(图 1),最终结果如图 4 所示。我将逐步解释我的工作。

图 1 原始图像

在此处输入图像描述

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

图 2 边缘检测

在此处输入图像描述

第 2 步:膨胀每个对象。没找到填洞的好方法,所以直接扩孔。如图 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);
}
4

2 回答 2

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()和调整参数,直到获得良好的结果。这可能会有一些困难,因为整个圆圈在图像中不可见,只有部分,但我建议您尝试一下。如果您确实检测到了最内圈,则可以将其用作掩码以过滤掉外部质心。

于 2013-07-19T16:28:56.757 回答
5

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

减少噪音?使用其中一种模糊(低通滤波)方法。

大小相似?膨胀后使用腐蚀=形态闭合

一圈一点,不带外圈输出?找到所有轮廓区域的平均值。擦除与该值有很大差异的轮廓。输出剩余的中心。

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

于 2013-07-19T18:30:45.687 回答