使用OpenCV进行天空图像分析

3
我试图利用有限的OpenCV知识从相机/视频中获取的天空图像中提取云层。在下面的代码示例中,我将图像分成其通道(如BGR),通过蓝色和红色通道之间的差异获得灰度图像。然后,我对这个灰度图像进行阈值处理,以创建云区域掩码(实际上,它给出了天空区域并进行反转)。经过这些步骤,最后我将掩码放在实际图像上,根据阈值限制显示真正的云层区域。它对于白色/灰色的云层效果符合预期,但对于蓝色的云层则不行。
cv::Mat ch[3], img_gray, img_element, img_brdiff;
img_element = getStructuringElement(MORPH_RECT, Size(6, 6), Point(6, 6));
img_brdiff = cv::Mat::zeros(img_frame.size(), CV_8UC3);
cv::split(img_frame, ch);
cv::absdiff(ch[2], ch[0], img_brdiff);
cv::threshold(img_brdiff, img_gray, 35, 255, CV_THRESH_BINARY_INV);     
cv::morphologyEx(img_gray, img_gray, cv::MORPH_CLOSE, img_element);
//cv::medianBlur(img_gray, img_gray, 3);
img_frame.copyTo(img_final, img_gray);
cv::imshow("in", img_frame);
cv::imshow("out", img_final);

任何关于如何改进技术的想法?另外,在第二步中,我需要为每个云计算移动方向。
这是我的图像样本。

enter image description here

enter image description here

更新

在对天空特征如NBRR(标准化蓝红比)、饱和度等进行了进一步检查后,我得到了更好的结果,但在我看来仍不是最佳的...扩展的示例代码如下。

const int BINARY_TH = 35;
const double SAT_FIX_TH = 0.3;
const double NBRR_FIX_TH = 0.4;

cv::Mat ch[3], img_gray, img_element, img_brdiff;
img_element = getStructuringElement(MORPH_RECT, Size(6, 6), Point(6, 6));
img_brdiff = cv::Mat::zeros(img_frame.size(), CV_8UC3);
cv::split(img_frame, ch);
cv::absdiff(ch[2], ch[0], img_brdiff);
cv::threshold(img_brdiff, img_gray, BINARY_TH, 255, CV_THRESH_BINARY_INV);      
cv::morphologyEx(img_gray, img_gray, cv::MORPH_CLOSE, img_element);

double r, b, g, nbrr, sat;
for(int y = 0; y < img_frame.rows; y++)
{
    for(int x = 0; x < img_frame.cols; x++)
    {
        b = img_frame.at<Vec3b>(y,x)[0];
        g = img_frame.at<Vec3b>(y,x)[1];
        r = img_frame.at<Vec3b>(y,x)[2];

        nbrr = (b - r) / (b + r);
        sat = 1.0 - (std::min(b, std::min(g, r)) / std::max(b, std::max(g, r)));

        if( nbrr < NBRR_FIX_TH &&
            sat < SAT_FIX_TH
            )
            img_gray.at<uchar>(y, x) = (uchar)255;
    }
}

cv::medianBlur(img_gray, img_gray, 3);

img_frame.copyTo(img_final, img_gray);
cv::imshow("in", img_frame);
cv::imshow("out", img_final);

3
你最好去OpenCV论坛寻求帮助,因为这是一个非常广泛的问题,没有一个正确答案。 - chris
1个回答

0

我认为如果在梯度上使用阈值,你会得到更好的结果。你需要保留蓝色通道。我无法提供任何代码示例,因为我不知道如何使用opencv,但是网上肯定有很多示例。

你是否也尝试将图像转换为灰度?


在我的代码示例中,我将图像分割为其通道,例如BGR,并通过蓝色-红色通道差异获取灰度图像。然后,我在这个灰度图像上设置阈值,以创建云区域掩码(实际上,它给出了天空区域,我将其反转)。经过这些步骤,最后我将掩码放在实际图像上,根据阈值限制显示真实的云区域。啊,除了那些带有蓝色的云区域。 :) - rca
好的,感谢您的解释。我仍然认为您应该使用渐变而不是简单阈值,这应该可以在暗云上获得更好的结果。在计算梯度之前,您还可以对图像进行低通滤波以降低噪声水平,从而获得更清晰的结果。 - Antoine
谢谢您的关注。但是我不明白如何将渐变用作阈值?问题在于,云的某些部分看起来像天空,具有更高的蓝色密度,所以为什么它们不能在算法中被识别为云呢? - rca

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