如何去除重叠的轮廓
How to remove overlapping contours
我正在开发一个程序,试图从拼图中提取彩色方块。我从视频捕获中获取一帧,然后找到所有轮廓。然后我删除不是正方形的轮廓(这工作正常但正在寻找更好的方法)。我面临的主要问题是轮廓重叠。我使用 RETR_TREE
获取所有轮廓,但是当使用 RETR_EXTERNAL
时,轮廓变得更难检测。有什么方法可以改进正方形的检测吗?或者我可以删除图像中重叠轮廓的方法。
这是一张轮廓重叠的图像:
在这张图片中找到了 11 个轮廓,但我只想要 9 个。(我画了矩形以更容易地看到重叠部分)
。如何去除内部轮廓?
这是我的代码:
public Mat captureFrame(Mat capturedFrame){
Mat newFrame = new Mat();
capturedFrame.copyTo(newFrame);
//Gray
Mat gray = new Mat();
Imgproc.cvtColor(capturedFrame, gray, Imgproc.COLOR_RGB2GRAY);
//Blur
Mat blur = new Mat();
Imgproc.blur(gray, blur, new Size(3,3));
//Canny image
Mat canny = new Mat();
Imgproc.Canny(blur, canny, 20, 40, 3, true);
//Dilate image to increase size of lines
Mat kernel = Imgproc.getStructuringElement(1, new Size(3,3));
Mat dilated = new Mat();
Imgproc.dilate(canny,dilated, kernel);
List<MatOfPoint> contours = new ArrayList<>();
List<MatOfPoint> squareContours = new ArrayList<>();
//find contours
Imgproc.findContours(dilated, contours, new Mat(), Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_NONE);
//Remove contours that aren't close to a square shape.
//Wondering if there is a way I can improve this?
for(int i = 0; i < contours.size(); i++){
double area = Imgproc.contourArea( contours.get(i));
MatOfPoint2f contour2f = new MatOfPoint2f(contours.get(i).toArray());
double perimeter = Imgproc.arcLength(contour2f, true);
//Found squareness equation on wiki...
//https://en.wikipedia.org/wiki/Shape_factor_(image_analysis_and_microscopy)
double squareness = 4 * Math.PI * area / Math.pow(perimeter, 2);
//add contour to new List if it has a square shape.
if(squareness >= 0.7 && squareness <= 0.9 && area >= 3000){
squareContours.add(contours.get(i));
}
}
MatOfPoint2f approxCurve = new MatOfPoint2f();
for(int n = 0; n < squareContours.size(); n++){
//Convert contours(n) from MatOfPoint to MatOfPoint2f
MatOfPoint2f contour2f = new MatOfPoint2f( squareContours.get(n).toArray());
//Processing on mMOP2f1 which is in type MatOfPoint2f
double approxDistance = Imgproc.arcLength(contour2f, true)*0.02;
Imgproc.approxPolyDP(contour2f, approxCurve, approxDistance, true);
//Convert back to MatOfPoint
MatOfPoint points = new MatOfPoint( approxCurve.toArray());
// Get bounding rect of contour
Rect rect = Imgproc.boundingRect(points);
//length and width should be about the same
if(rect.height - rect.width < Math.abs(10)){
System.out.printf("%s , %s \n", rect.height, rect.width);
}
// draw enclosing rectangle (all same color, but you could use variable i to make them unique)
Imgproc.rectangle(newFrame, new Point(rect.x,rect.y), new Point(rect.x+rect.width,rect.y+rect.height),new Scalar (255, 0, 0, 255), 3);
}
return newFrame;
}
幸运的是,cv::findContours 还为我们提供了层次结构矩阵,您在代码段中忽略了它,层次结构对于 RETR_EXTERNAL
以外的所有模式都非常有用,您可以找到详细文档层次矩阵 here。
层次矩阵包含每个轮廓的 [下一个,上一个,First_Child,父] 格式的数据。现在你可以过滤轮廓,其逻辑如select only those contours where parent == -1
,这将消除父轮廓内的子轮廓。
要使用层级矩阵,您需要将 cv::findContours 调用为:
cv::Mat hierarchy;
Imgproc.findContours(dilated, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_NONE);
我正在开发一个程序,试图从拼图中提取彩色方块。我从视频捕获中获取一帧,然后找到所有轮廓。然后我删除不是正方形的轮廓(这工作正常但正在寻找更好的方法)。我面临的主要问题是轮廓重叠。我使用 RETR_TREE
获取所有轮廓,但是当使用 RETR_EXTERNAL
时,轮廓变得更难检测。有什么方法可以改进正方形的检测吗?或者我可以删除图像中重叠轮廓的方法。
这是一张轮廓重叠的图像:
在这张图片中找到了 11 个轮廓,但我只想要 9 个。(我画了矩形以更容易地看到重叠部分)
。如何去除内部轮廓? 这是我的代码:
public Mat captureFrame(Mat capturedFrame){
Mat newFrame = new Mat();
capturedFrame.copyTo(newFrame);
//Gray
Mat gray = new Mat();
Imgproc.cvtColor(capturedFrame, gray, Imgproc.COLOR_RGB2GRAY);
//Blur
Mat blur = new Mat();
Imgproc.blur(gray, blur, new Size(3,3));
//Canny image
Mat canny = new Mat();
Imgproc.Canny(blur, canny, 20, 40, 3, true);
//Dilate image to increase size of lines
Mat kernel = Imgproc.getStructuringElement(1, new Size(3,3));
Mat dilated = new Mat();
Imgproc.dilate(canny,dilated, kernel);
List<MatOfPoint> contours = new ArrayList<>();
List<MatOfPoint> squareContours = new ArrayList<>();
//find contours
Imgproc.findContours(dilated, contours, new Mat(), Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_NONE);
//Remove contours that aren't close to a square shape.
//Wondering if there is a way I can improve this?
for(int i = 0; i < contours.size(); i++){
double area = Imgproc.contourArea( contours.get(i));
MatOfPoint2f contour2f = new MatOfPoint2f(contours.get(i).toArray());
double perimeter = Imgproc.arcLength(contour2f, true);
//Found squareness equation on wiki...
//https://en.wikipedia.org/wiki/Shape_factor_(image_analysis_and_microscopy)
double squareness = 4 * Math.PI * area / Math.pow(perimeter, 2);
//add contour to new List if it has a square shape.
if(squareness >= 0.7 && squareness <= 0.9 && area >= 3000){
squareContours.add(contours.get(i));
}
}
MatOfPoint2f approxCurve = new MatOfPoint2f();
for(int n = 0; n < squareContours.size(); n++){
//Convert contours(n) from MatOfPoint to MatOfPoint2f
MatOfPoint2f contour2f = new MatOfPoint2f( squareContours.get(n).toArray());
//Processing on mMOP2f1 which is in type MatOfPoint2f
double approxDistance = Imgproc.arcLength(contour2f, true)*0.02;
Imgproc.approxPolyDP(contour2f, approxCurve, approxDistance, true);
//Convert back to MatOfPoint
MatOfPoint points = new MatOfPoint( approxCurve.toArray());
// Get bounding rect of contour
Rect rect = Imgproc.boundingRect(points);
//length and width should be about the same
if(rect.height - rect.width < Math.abs(10)){
System.out.printf("%s , %s \n", rect.height, rect.width);
}
// draw enclosing rectangle (all same color, but you could use variable i to make them unique)
Imgproc.rectangle(newFrame, new Point(rect.x,rect.y), new Point(rect.x+rect.width,rect.y+rect.height),new Scalar (255, 0, 0, 255), 3);
}
return newFrame;
}
幸运的是,cv::findContours 还为我们提供了层次结构矩阵,您在代码段中忽略了它,层次结构对于 RETR_EXTERNAL
以外的所有模式都非常有用,您可以找到详细文档层次矩阵 here。
层次矩阵包含每个轮廓的 [下一个,上一个,First_Child,父] 格式的数据。现在你可以过滤轮廓,其逻辑如select only those contours where parent == -1
,这将消除父轮廓内的子轮廓。
要使用层级矩阵,您需要将 cv::findContours 调用为:
cv::Mat hierarchy;
Imgproc.findContours(dilated, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_NONE);