使用OpenCV检测在背景图像上绘制的矩形

时间:2016-02-11 08:22:06

标签: c++ opencv image-processing

我正在尝试检测在图像上绘制的一些矩形(白色)。 (比如使用油漆或其他图像编辑工具)。 由于我是图像处理的初学者,我通过net和OpenCV示例程序搜索完成了这项工作,但无法让它完美地运行。我正在使用OpenCV C ++库。

我尝试过的算法

cv::Mat src = cv::imread(argv[1]);
cv::Mat gray;
cv::cvtColor(src, gray, CV_BGR2GRAY);
meanStdDev(gray, mu, sigma);
cv::Mat bw;
cv::Canny(gray, bw, mu.val[0] - sigma.val[0], mu.val[0] + sigma.val[0]);
std::vector<std::vector<cv::Point> > contours;
cv::findContours(bw.clone(), contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

std::vector<cv::Point> approx;
for (int i = 0; i < contours.size(); i++){
cv::approxPolyDP(cv::Mat(contours[i]), approx, cv::arcLength(cv::Mat(contours[i]), true)*0.02, true);
if (approx.size() >= 4 && approx.size() <= 6)
Rect boundRect = boundingRect( Mat(approx) );
rectangle( dst, boundRect.tl(), boundRect.br(), Scalar(255,255,255), 1, 8, 0 );}

仅检测到一个矩形。你可以指导一下这个链接吗?

输入图片:

输出图片:

3 个答案:

答案 0 :(得分:1)

我无法编译你的代码示例,因为在if-block中声明了boundRect但是矩形绘图(试图访问boundRect)在if-block之外,所以我调整了你的代码:

enum

使用输入图像我得到这个输出:

enter image description here

这可能不是你所期望的。看看canny输出图像(看看可视化调试的中间结果总是好的!),图像中有太多的结构,轮廓将涵盖所有这些,所以有一些将近似于多项式有4到6个元素。

相反,你必须变得更聪明一些。您可以尝试使用cv :: HoughLinesP提取直线并连接这些线。或者您可以尝试通过查找白色区域来分割图像(如果矩形始终为白色)。

int main(int argc, char* argv[])
{
    cv::Mat src = cv::imread("C:/StackOverflow/Input/rectangles.png");
    cv::Mat dst = src.clone();

    cv::Mat gray;
    cv::cvtColor(src, gray, CV_BGR2GRAY);

    // ADDED: missing declaration of mu and sigma
    cv::Scalar mu, sigma;
    meanStdDev(gray, mu, sigma);
    cv::Mat bw;
    cv::Canny(gray, bw, mu.val[0] - sigma.val[0], mu.val[0] + sigma.val[0]);

    // ADDED: displaying the canny output
    cv::imshow("canny", bw);


    std::vector<std::vector<cv::Point> > contours;
    cv::findContours(bw.clone(), contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

    std::vector<cv::Point> approx;
    for (int i = 0; i < contours.size(); i++){
        cv::approxPolyDP(cv::Mat(contours[i]), approx, cv::arcLength(cv::Mat(contours[i]), true)*0.02, true);
        if (approx.size() >= 4 && approx.size() <= 6)
        {
            // ADDED: brackets around both lines belonging to the if-block
            cv::Rect boundRect = cv::boundingRect(cv::Mat(approx));
            cv::rectangle(dst, boundRect.tl(), boundRect.br(), cv::Scalar(255, 255, 255), 3, 8, 0);
        }

    }

    // ADDED: displaying input and results
    cv::imshow("input", src);
    cv::imshow("dst", dst);
    cv::imwrite("C:/StackOverflow/Output/rectangles.png", dst);
    cv::waitKey(0);
    return 0;
}

给出了这个结果:

enter image description here

如您所见,白色附近还有其他明亮区域。多项式近似也没有多大帮助。

答案 1 :(得分:0)

你确定你只找到一个轮廓或者你只是绘制一个轮廓吗?它看起来并不像你在绘图程序中循环所以你只会绘制第一个找到的。

我有一个博客,早已死了,可能会为您提供一些好的指导:http://workingwithcomputervision.blogspot.co.uk/2012/09/game-player-step-2-finding-game-board.html

如果链接死亡,我相信这是与文章轮廓相关的文章中最相关的部分:

//Draw contours
for (int i = 0; i < contours.size(); i++) {
Scalar color = Scalar(0, 255, 0);
drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, Point());
}

我注意到你正在使用边界矩形进行绘图。这是另一个绘图例程,再次从上面的链接,这样做:

Rect bounds;
Mat drawing = Mat::zeros(purpleOnly.size(), CV_8UC3);
int j = 0;
for (int i = 0; i < contours.size(); i++) {
   if (arcLength(contours[i], true) > 500){
       Rect temp = boundingRect(contours[i]);
       rectangle(drawing, temp, Scalar(255, 0, 0), 2, 8);
       if (j == 0) {
           bounds = temp;
       } else {
           bounds = bounds | temp;
       }
       j++;
   }
}

请注意,我还会对轮廓的大小进行一些检查,以滤除噪音。

答案 2 :(得分:0)

通常,在HSV空间中分割颜色(甚至是白色)更容易。有适当的阈值:

inRange(hsv, Scalar(0, 0, 220), Scalar(180, 30, 255), mask);

我们不关心Hue,只保持低饱和度和高价值,我得到:

enter image description here

然后,您可以轻松找到连接的组件,并丢弃小于阈值th_blob_size的blob。得到的矩形是(绿色):

enter image description here

您最终可以应用其他过滤阶段来解决更困难的情况,但对于此图像,删除小blob就足够了。如果您需要更强大的功能,请发布其他图片。

代码:

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;


int main(int argc, char** argv)
{
    Mat3b img = imread("path_to_image");

    int th_blob_size = 100;

    Mat3b hsv;
    cvtColor(img, hsv, COLOR_BGR2HSV);

    Mat1b mask;
    inRange(hsv, Scalar(0, 0, 220), Scalar(180, 30, 255), mask);

    vector<vector<Point>> contours;
    findContours(mask.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);

    Mat3b res = img.clone();
    for (int i = 0; i < contours.size(); ++i)
    {
        // Remove small blobs
        if (contours[i].size() < th_blob_size)
        {
            continue;
        }

        Rect box = boundingRect(contours[i]);
        rectangle(res, box, Scalar(0,255,0), 1);
    }

    imshow("Result", res);
    waitKey();

    return 0;
}