如何在OpenCv中从太空天空中检测太阳?

9
我需要从太空天空中探测太阳。
以下是输入图像的示例: 经过形态学滤波(两次“开”运算)之后,我得到了以下结果: 这是该处理的算法代码:
// 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:

尝试通过公式获得质心的算法:{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 ); 
}

第一个例子:bingo,但是第二个例子 - 不行;(

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

更新3:

matchTemplate方法对我有用(来自mevatron的响应)。它可以使用大量的测试。

3个回答

11
试试简单的 matchTemplate 方法,我使用了这个模板图像:
enter image description here 它可以检测到我尝试的所有 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;
}

希望你能找到有帮助的内容!


2
实际上,这个工作的原因与提到的不同。首先,在这些图像中没有任何类似使用的模板。此外,这比最初的形态学方法更有可能慢几倍。 - mmgp
2
确实是很好的观点 :) 但是,由于我假设宇宙飞船在我们的太阳系中,所以我相信只有一个太阳会出现 :D - mevatron
我猜如果HUD(飞行员用户界面)的任何部分覆盖在太阳上,这个方法就会失败。除非模板匹配给出匹配程度的分数,这样你就可以设定一个阈值来筛选前几个结果,这样即使有些遮挡也可能会起作用。 - Rui Marques

2

您的代码中可以添加一个简单的功能,根据对象的大小进行筛选。如果您总是期望地球比太阳大得多,或者每张图片中太阳的面积几乎相同,那么可以通过面积进行过滤。

尝试使用Blob 检测器来完成此任务。

请注意,最好应用形态学开/闭运算而不是简单的腐蚀或膨胀,这样在处理前后太阳的面积几乎相同。


谢谢,我认为这对我很有用,但是我的OpenCV版本没有blob检测器,并且我在C++方面是新手(我将无法编译最新版本)。 - Larry Foobar
从sourceforge上获取最新的OpenCV http://sourceforge.net/projects/opencvlibrary/ 使用它就像使用自增运算符一样简单 :) 它已经编译好了。另外..你添加了使用命名空间cv吗?你怎么没有blob检测器呢? - Sam

2

颜色分割方法

对图像进行颜色分割以识别黑色背景上的物体。您可以根据太阳的面积(给定这一点可以唯一地识别它,即不会在图像之间大幅度变化)来识别太阳。 更复杂的方法可以计算对象的图像矩,例如hu矩。有关这些特征,请参见此页面

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

您可以从原始矩计算实际位置,如对于圆形太阳,位置等于重心

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

边缘图方法

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


我认为使用图像矩无法解决这个问题。如果屏幕上只有太阳,那么太阳的中心是“重心”点。但是如果地球占据了屏幕的一部分(例如图像#1左上角),那么这部分比太阳更“重”,重心将会偏移。我已经尝试过CircleHough,但它并不能适用于我所有的示例照片... - Larry Foobar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接