我试图利用有限的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);
任何关于如何改进技术的想法?另外,在第二步中,我需要为每个云计算移动方向。
这是我的图像样本。
更新
在对天空特征如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);