检测图像中的十字 [英] Detecting crosses in an image

查看:273
本文介绍了检测图像中的十字的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个程序来检测探测设备的提示,并在探测期间分析颜色变化。输入/输出机制或多或少地存在。我现在需要的是肉体的实际检测:

I am working on a program to detect the tips of a probing device and analyze the color change during probing. The input/output mechanisms are more or less in place. What I need now is the actual meat of the thing: detecting the tips.

在下面的图片中,提示位于十字架的中心。我想到在一些阈值之后将BFS应用于图像,但是被卡住,不知道如何进行。阅读后,我转向OpenCV,它在图像中提供特征检测。然而,我被这些又一次使用的大量概念和技术所淹没,无法理解如何进行。

In the images below, the tips are at the center of the crosses. I thought of applying BFS to the images after some threshold'ing but was then stuck and didn't know how to proceed. I then turned to OpenCV after reading that it offers feature detection in images. However, I am overwhelmed by the vast amount of concepts and techniques utilized here and again, clueless about how to proceed.

我正在寻找正确的方法吗?你可以给我一些指针吗?

Am I looking at it the right way? Can you give me some pointers?


从短视频中提取的图像

Image extracted from short video


二进制版本,阈值设置为95

Binary version with threshold set at 95

推荐答案

模板匹配方法



这是一个简单的 matchTemplate 解决方案,类似于Guy Sirton提到的方法。

Template Matching Approach

Here is a simple matchTemplate solution, that is similar to the approach that Guy Sirton mentions.

只要您的目标没有太大的缩放或旋转,模板匹配就会起作用。

Template matching will work as long as you don't have much scaling or rotation occurring with your target.

这是我的模板使用:

Here is the template that I used:

以下是用于检测多个无阻碍交叉的代码:

Here is the code I used to detect several of the unobstructed crosses:

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char* argv[])
{
    string inputName = "crosses.jpg";
    string outputName = "crosses_detect.png";
    Mat img   = imread( inputName, 1);
    Mat templ = imread( "crosses-template.jpg", 1);

    int resultCols =  img.cols - templ.cols + 1;
    int resultRows = img.rows - templ.rows + 1;
    Mat result( resultCols, resultRows, CV_32FC1 );

    matchTemplate(img, templ, result, CV_TM_CCOEFF);
    normalize(result, result, 0, 255.0, NORM_MINMAX, CV_8UC1, Mat());

    Mat resultMask;
    threshold(result, resultMask, 180.0, 255.0, THRESH_BINARY);

    Mat temp = resultMask.clone();
    vector< vector<Point> > contours;
    findContours(temp, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(templ.cols / 2, templ.rows / 2));

    vector< vector<Point> >::iterator i;
    for(i = contours.begin(); i != contours.end(); i++)
    {
        Moments m = moments(*i, false);
        Point2f centroid(m.m10 / m.m00, m.m01 / m.m00);
        circle(img, centroid, 3, Scalar(0, 255, 0), 3);
    }

    imshow("img", img);
    imshow("results", result);
    imshow("resultMask", resultMask);

    imwrite(outputName, img);

    waitKey(0);

    return 0;
}

这导致了这个检测图像:

This results in this detection image:

此代码基本设置了一个阈值将交叉峰与图像的其余部分分开,然后检测所有这些轮廓。最后,它计算每个轮廓的质心以检测十字的中心。

This code basically sets a threshold to separate the cross peaks from the rest of the image, and then detects all of those contours. Finally, it computes the centroid of each contour to detect the center of the cross.

这是使用三角形检测的另一种方法。它似乎不像 matchTemplate 方法那么准确,但可能是您可以玩的替代方案。

Here is an alternative approach using triangle detection. It doesn't seems as accurate as the matchTemplate approach, but might be an alternative you could play with.

使用 findContours 我们检测到图像中的所有三角形,结果如下:

Using findContours we detect all the triangles in the image, which results in the following:

然后我注意到所有的三角形顶点集群在交叉中心,所以这些集群用于重点显示如下所示的交叉中心点:

Then I noticed all the triangle vertices cluster near the cross center, so then these clusters are used to centroid the cross center point shown below:

最后,以下是我以前使用的代码:

Finally, here is the code that I used to do this:

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <list>

using namespace cv;
using namespace std;

vector<Point> getAllTriangleVertices(Mat& img, const vector< vector<Point> >& contours);
double euclideanDist(Point a, Point b);

vector< vector<Point> > groupPointsWithinRadius(vector<Point>& points, double radius);
void printPointVector(const vector<Point>& points);
Point computeClusterAverage(const vector<Point>& cluster);

int main(int argc, char* argv[])
{
    Mat img   = imread("crosses.jpg", 1);
    double resizeFactor = 0.5;
    resize(img, img, Size(0, 0), resizeFactor, resizeFactor);

    Mat momentImg = img.clone();

    Mat gray;
    cvtColor(img, gray, CV_BGR2GRAY);

    adaptiveThreshold(gray, gray, 255.0, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 19, 15);
    imshow("threshold", gray);
    waitKey();

    vector< vector<Point> > contours;
    findContours(gray, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);

    vector<Point> allTriangleVertices = getAllTriangleVertices(img, contours);

    imshow("img", img);
    imwrite("shape_detect.jpg", img);
    waitKey();

    printPointVector(allTriangleVertices);
    vector< vector<Point> > clusters = groupPointsWithinRadius(allTriangleVertices, 10.0*resizeFactor);
    cout << "Number of clusters: " << clusters.size() << endl;

    vector< vector<Point> >::iterator cluster;
    for(cluster = clusters.begin(); cluster != clusters.end(); ++cluster)
    {
        printPointVector(*cluster);

        Point clusterAvg = computeClusterAverage(*cluster);
        circle(momentImg, clusterAvg, 3, Scalar(0, 255, 0), CV_FILLED);
    }

    imshow("momentImg", momentImg);
    imwrite("centroids.jpg", momentImg);
    waitKey();

    return 0;
}

vector<Point> getAllTriangleVertices(Mat& img, const vector< vector<Point> >& contours)
{
    vector<Point> approxTriangle;
    vector<Point> allTriangleVertices;
    for(size_t i = 0; i < contours.size(); i++)
    {
        approxPolyDP(contours[i], approxTriangle, arcLength(Mat(contours[i]), true)*0.05, true);
        if(approxTriangle.size() == 3)
        {
            copy(approxTriangle.begin(), approxTriangle.end(), back_inserter(allTriangleVertices));
            drawContours(img, contours, i, Scalar(0, 255, 0), CV_FILLED);

            vector<Point>::iterator vertex;
            for(vertex = approxTriangle.begin(); vertex != approxTriangle.end(); ++vertex)
            {
                circle(img, *vertex, 3, Scalar(0, 0, 255), 1);
            }
        }
    }

    return allTriangleVertices;
}

double euclideanDist(Point a, Point b)
{
    Point c = a - b;
    return cv::sqrt(c.x*c.x + c.y*c.y);
}

vector< vector<Point> > groupPointsWithinRadius(vector<Point>& points, double radius)
{
    vector< vector<Point> > clusters;
    vector<Point>::iterator i;
    for(i = points.begin(); i != points.end();)
    {
        vector<Point> subCluster;
        subCluster.push_back(*i);

        vector<Point>::iterator j;
        for(j = points.begin(); j != points.end(); )
        {
            if(j != i &&  euclideanDist(*i, *j) < radius)
            {
                subCluster.push_back(*j);
                j = points.erase(j);
            }
            else
            {
                ++j;
            }
        }

        if(subCluster.size() > 1)
        {
            clusters.push_back(subCluster);
        }

        i = points.erase(i);
    }

    return clusters;
}

Point computeClusterAverage(const vector<Point>& cluster)
{
    Point2d sum;
    vector<Point>::const_iterator point;
    for(point = cluster.begin(); point != cluster.end(); ++point)
    {
        sum.x += point->x;
        sum.y += point->y;
    }

    sum.x /= (double)cluster.size();
    sum.y /= (double)cluster.size();

    return Point(cvRound(sum.x), cvRound(sum.y));
}

void printPointVector(const vector<Point>& points)
{
    vector<Point>::const_iterator point;
    for(point = points.begin(); point != points.end(); ++point)
    {
        cout << "(" << point->x << ", " << point->y << ")";
        if(point + 1 != points.end())
        {
            cout << ", ";
        }
    }
    cout << endl;
}

我在以前的实现中修复了一些错误,并清理了代码位。我也用各种调整大小的因素进行了测试,似乎表现相当好。然而,在我达到四分之一刻度之后,它开始有麻烦正确检测三角形,所以这对于极小的十字架可能不会很好。另外,似乎在时刻函数中有一个错误,就像它返回的一些有效的集群(-NaN,-NaN)一样。所以,我相信准确性有点好转。它可能需要更多的调整,但总体而言,我认为这应该是一个很好的起点。

I fixed a few bugs in my previous implementation, and cleaned the code up a bit. I also tested it with various resize factors, and it seemed to perform quite well. However, after I reached a quarter scale it started to have trouble properly detecting triangles, so this might not work well for extremely small crosses. Also, it appears there is a bug in the moments function as for some valid clusters it was returning (-NaN, -NaN) locations. So, I believe the accuracy is a good bit improved. It may need a few more tweaks, but overall I think it should be a good starting point for you.

我认为如果四周的黑色边框三角形有点厚/尖锐,如果三角形本身的阴影较少。

I think my triangle detection would work better if the black border around the triangles was a bit thicker/sharper, and if there were less shadows on the triangles themselves.

希望有帮助!

这篇关于检测图像中的十字的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆