如何在OpenCv中探测太空中的太阳?

时间:2011-11-21 21:49:45

标签: c++ opencv edge-detection

我需要从太空中探测太阳。

这些是输入图像的示例:

我在Morphologic过滤后(open操作两次)得到了这样的结果

以下是此处理的算法代码:

// Color to Gray
cvCvtColor(image, gray, CV_RGB2GRAY);

// color threshold
cvThreshold(gray,gray,150,255,CV_THRESH_BINARY);

// Morphologic open for 2 times
cvMorphologyEx( gray, dst, NULL, CV_SHAPE_RECT, CV_MOP_OPEN, 2);

对于这么简单的任务,处理不是太重了吗?如何找到太阳的中心?如果我找到白点,我会发现大地球的白点(第一个示例图像上的左上角)

请告诉我,我的进一步行动是为了探测太阳。

更新1:

尝试按公式获取centroid的算法:{x,y} = {M10/M00, M01/M00}

CvMoments moments;
cvMoments(dst, &moments, 1);
double m00, m10, m01;

m00 = cvGetSpatialMoment(&moments, 0,0);
m10 = cvGetSpatialMoment(&moments, 1,0);
m01 = cvGetSpatialMoment(&moments, 0,1);

// calculating centroid
float centroid_x = m10/m00;
float centroid_y = m01/m00;

    cvCircle( image, 
              cvPoint(cvRound(centroid_x), cvRound(centroid_y)), 
              50, CV_RGB(125,125,0), 4, 8,0);

地球在照片中,我得到了这样的结果:

因此,质心在地球上。 :(

更新2:

尝试cvHoughCircles

CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* circles = cvHoughCircles(dst, storage, CV_HOUGH_GRADIENT, 12, 
                                dst->width/2, 255, 100, 0, 35);

if ( circles->total > 0 ) {
    // getting first found circle
    float* circle = (float*)cvGetSeqElem( circles, 0 ); 

    // Drawing:
    // green center dot
    cvCircle( image, cvPoint(cvRound(circle[0]),cvRound(circle[1])), 
          3, CV_RGB(0,255,0), -1, 8, 0 ); 
    // wrapping red circle
    cvCircle( image, cvPoint(cvRound(circle[0]),cvRound(circle[1])), 
        cvRound(circle[2]), CV_RGB(255,0,0), 3, 8, 0 ); 
}

第一个例子:宾果游戏,但第二个例子:没有;(

我尝试了cvHoughCircles()的不同配置 - 无法找到适合我每张示例照片的配置。

UPDATE3:

matchTemplate方法对我有用(mevatron的回复)。它适用于大量测试。

3 个答案:

答案 0 :(得分:9)

如何尝试简单的matchTemplate方法。我使用了这个模板图像:
enter image description here

并且,它检测到我试过的3张太阳图像中的3张: enter image description here enter image description here enter image description here

这应该起作用,因为圆圈(在你的情况下是太阳)是旋转不变的,并且由于你离太阳太远,所以它应该是大致尺度不变的。因此,模板匹配在这里可以很好地工作。

最后,这是我用来执行此操作的代码:

#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[])
{
    /// Load image and template
    string inputName = "sun2.png";
    string outputName = "sun2_detect.png";
    Mat img   = imread( inputName, 1 );
    Mat templ = imread( "sun_templ.png", 1 );

    /// Create the result matrix
    int result_cols =  img.cols - templ.cols + 1;
    int result_rows = img.rows - templ.rows + 1;

    Mat result( result_cols, result_rows, CV_32FC1 );

    /// Do the Matching and Normalize
    matchTemplate(img, templ, result, CV_TM_CCOEFF);
    normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());

    Point maxLoc;
    minMaxLoc(result, NULL, NULL, NULL, &maxLoc);

    rectangle(img, maxLoc, Point( maxLoc.x + templ.cols , maxLoc.y + templ.rows ), Scalar(0, 255, 0), 2);
    rectangle(result, maxLoc, Point( maxLoc.x + templ.cols , maxLoc.y + templ.rows ), Scalar(0, 255, 0), 2);

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

    imwrite(outputName, img);

    waitKey(0);

    return 0;
}

希望您觉得有帮助!

答案 1 :(得分:2)

颜色分割方法

对图像进行颜色分割,以识别黑色背景上的对象。您可以根据其区域识别太阳(鉴于此唯一标识它,并且不会在图像上大致变化)。 更复杂的方法可以计算图像矩,例如胡物品的瞬间。有关这些功能,请参阅this page

使用您选择的分类算法对找到的对象进行实际分类。最简单的方法是手动指定阈值,分别为。结果适用于所有(大多数)对象/图像组合的值范围。

您可以从原始时刻计算实际位置,如for the circular sun the position is equal to the center of mass

Centroid: {x, y } = { M10/M00, M01/M00 }

边缘地图方法

另一种选择是边缘地图的圆形霍夫变换,这有望返回一些候选圆(按位置和半径)。您可以根据预期的半径选择太阳圆(如果幸运的话,最多只有一个)。

答案 2 :(得分:2)

对代码的一个简单补充是根据对象的大小过滤掉对象。如果你总是希望地球比太阳大得多,或者太阳在每张照片中都有相同的区域,你可以按区域过滤它。

尝试Blob detector执行此任务。

请注意,应用形态学开/关而不是简单的侵蚀或扩张可能是好的,所以你的太阳在处理之前和之后将具有几乎相同的区域。