如何忽略/删除触及图像边界的轮廓

时间:2016-11-15 16:57:11

标签: c++ opencv

我有以下代码使用cvThresholdcvFindContours来检测图像中的轮廓:

CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* contours = 0;

cvThreshold( processedImage, processedImage, thresh1, 255, CV_THRESH_BINARY );
nContours = cvFindContours(processedImage, storage, &contours, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE, cvPoint(0,0) );

我想以某种方式扩展此代码以过滤/忽略/删除任何触及图像边界的轮廓。但是我不确定如何解决这个问题。我应该过滤阈值图像还是之后可以过滤轮廓?希望有人知道一个优雅的解决方案,因为令人惊讶的是我无法通过谷歌搜索找到解决方案。

3 个答案:

答案 0 :(得分:5)

一般信息

  • 我正在使用OpenCV 3.0.0
  • 使用cv::findContours实际上会改变输入图像,因此请确保您在专门针对此功能的单独副本上工作,或者根本不进一步使用图像

更新2019-03-07:"由于此功能未修改opencv 3.2源图像。" (请参阅corresponding OpenCV documentation

一般解决方案

您需要知道的是轮廓是否有任何点接触图像边界。可以通过以下两个过程之一轻松提取此信息:

  • 检查轮廓的每个点的位置。如果它位于图像边界(x = 0或x = width - 1或y = 0或y = height - 1),则忽略它。
  • 在轮廓周围创建一个边界框。如果边界框位于图像边界,您也知道轮廓也是如此。

第二个解决方案的代码:

bool contourTouchesImageBorder(std::vector<cv::Point>& contour, cv::Size& imageSize)
{
    cv::Rect bb = cv::boundingRect(contour);

    bool retval = false;

    int xMin, xMax, yMin, yMax;

    xMin = 0;
    yMin = 0;
    xMax = imageSize.width - 1;
    yMax = imageSize.height - 1;

    // Use less/greater comparisons to potentially support contours outside of 
    // image coordinates, possible future workarounds with cv::copyMakeBorder where
    // contour coordinates may be shifted and just to be safe.
    if( bb.x <= xMin || 
        bb.y <= yMin ||
        bb.width >= xMax ||
        bb.height >= yMax)
    {
        retval = true;
    }

    return retval;
}

通过以下方式致电:

...
cv::Size imageSize = processedImage.size();
for (auto c: contours)
{
    if(contourTouchesImageBorder(c, imageSize))
    {
        // Do your thing...
        int asdf = 0;
    }
}
...

完整的C ++示例:

// Load example image
std::string path = "..\\ImageProcessingTest\\Testdata\\ContourBorderDetection\\";
//std::string filename = "circle3BorderDistance0.png";
std::string filename = "circleScenario2.png";
std::string fqn = path + filename;
cv::Mat img = cv::imread(fqn, CV_LOAD_IMAGE_GRAYSCALE);

cv::Mat processedImage;
img.copyTo(processedImage);

// Create copy for contour extraction since cv::findContours alters the input image
cv::Mat workingCopyForContourExtraction;
processedImage.copyTo(workingCopyForContourExtraction);


// Extract contours 
cv::findContours(workingCopyForContourExtraction, contours, cv::RetrievalModes::RETR_EXTERNAL, cv::ContourApproximationModes::CHAIN_APPROX_SIMPLE);

// Prepare image for contour drawing
cv::Mat drawing;
processedImage.copyTo(drawing);
cv::cvtColor(drawing, drawing, CV_GRAY2BGR);

// Draw contours
cv::drawContours(drawing, contours, -1, cv::Scalar(255, 255, 0), 1);

//cv::imwrite(path + "processedImage.png", processedImage);
//cv::imwrite(path + "workingCopyForContourExtraction.png", workingCopyForContourExtraction);
//cv::imwrite(path + "drawing.png", drawing);


cv::imshow("processedImage", processedImage);
cv::imshow("workingCopyForContourExtraction", workingCopyForContourExtraction);
cv::imshow("drawing", drawing);
cv::waitKey();

cv::Size imageSize = workingCopyForContourExtraction.size();
for (auto c: contours)
{
    if(contourTouchesImageBorder(c, imageSize))
    {
        // Do your thing...
        int asdf = 0;
    }
}

图像边界附近的轮廓检测问题

OpenCV似乎在正确查找图像边界附近的轮廓时遇到问题。

对于两个对象,检测到的轮廓是相同的(参见图像)。但是,在图像2中,检测到的轮廓不正确,因为物体的一部分位于x = 0,但轮廓位于x = 1。

这对我来说似乎是个错误。 这里有一个未解决的问题:https://github.com/opencv/opencv/pull/7516

似乎还有一个cv :: copyMakeBorder(https://github.com/opencv/opencv/issues/4374)的解决方法,但它看起来有点复杂。

如果你有点耐心,我建议等待OpenCV 3.2的发布,这应该会在接下来的1-2个月内发生。

示例图片

  • 物体触摸图像边框
  • 对象未触及图像边框
  • 物体触摸图像边框的轮廓
  • 对象没有触及图像边框的轮廓

Object touching image border

Object not touching image border

Contour for object touching image border

Contour for object not touching image border

答案 1 :(得分:1)

虽然这个问题是在C ++中,但同样的问题会影响Python中的openCV。 openCV&#39; 0像素&#39;的解决方案Python中的边界问题(也可能在C ++中使用)是在每个边框上用1个像素填充图像,然后使用填充图像调用openCV,然后删除边框。类似的东西:

img2 = np.pad(img.copy(), ((1,1), (1,1), (0,0)), 'edge')
# call openCV with img2, it will set all the border pixels in our new pad with 0
# now get rid of our border
img = img2[1:-1,1:-1,:]
# img is now set to the original dimensions, and the contours can be at the edge of the image

答案 2 :(得分:0)

如果有人在MATLAB中需要此功能,则此功能为

function [touch] = componentTouchesImageBorder(C,im_row_max,im_col_max)
    %C is a bwconncomp instance
    touch=0;
    S = regionprops(C,'PixelList');

    c_row_max = max(S.PixelList(:,1));
    c_row_min = min(S.PixelList(:,1));
    c_col_max = max(S.PixelList(:,2));
    c_col_min = min(S.PixelList(:,2));

    if (c_row_max==im_row_max || c_row_min == 1 || c_col_max == im_col_max || c_col_min == 1)
        touch = 1;
    end
end