我有以下代码使用cvThreshold和检测图像中的轮廓cvFindContours:
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) );
Run Code Online (Sandbox Code Playgroud)
我想以某种方式扩展此代码,以过滤/忽略/删除任何接触图像边界的轮廓。但是我不确定如何去做。我应该过滤阈值图像还是之后过滤轮廓?希望有人知道一个优雅的解决方案,因为令人惊讶的是我无法通过谷歌搜索提出一个解决方案。
cv::findContours实际上会改变输入图像,因此请确保您是专门为此功能使用单独的副本,或者根本不要进一步使用该图像。更新2019-03-07:“由于此功能未修改opencv 3.2源映像。” (请参阅相应的OpenCV文档)
您只需要了解轮廓的任何点是否触及图像边界即可。可以通过以下两个过程之一轻松提取此信息:
第二种解决方案的代码:
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;
}
Run Code Online (Sandbox Code Playgroud)
通过以下方式调用:
...
cv::Size imageSize = processedImage.size();
for (auto c: contours)
{
if(contourTouchesImageBorder(c, imageSize))
{
// Do your thing...
int asdf = 0;
}
}
...
Run Code Online (Sandbox Code Playgroud)
完整的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;
}
}
Run Code Online (Sandbox Code Playgroud)
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个月内完成。
范例图片
小智 5
虽然这个问题是在 C++ 中出现的,但同样的问题也会影响 Python 中的 openCV。Python 中 openCV“0 像素”边框问题的解决方案(也可能在 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
Run Code Online (Sandbox Code Playgroud)