我尝试用我有限的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);
答案 0 :(得分:0)
我认为通过在渐变上使用阈值可以获得更好的效果。你必须为此保留蓝色通道。我不能给你任何代码示例,因为我不知道如何使用opencv,但必须有大量的在线示例。
您是否也尝试过以灰度转换图像?