Issue
I've got a short question about OpenCV and finding specific shapes. On my PC I've got a pictrue with some shapes but I only want the contours of the rectangles:
Input file: src="https://i.stack.imgur.com/KWGMe.png" alt="Input File:">
What I did:
- Open my image and convert it into OpenCV Mat.
- Made some image proecssing [grayscale, blur]
- Found edges with Canny
- Found contours with "findContours"
- Draw rectangles around my contours with "boundingRect"
And this is where I stuck. I dont know how to eliminate the wrong contours. I tried it with iterate over my contours and remove the ones that aren't right. But I have no idea how to find the wrong contours. Are there any formulars I've to use or sth. like this? I found something with "arcLength" but I dont get the point of this.
Here is my code:
package main;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
import helper.ImageProcHelper;
public class Main {
public static void main(String[] args) throws Exception {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
File file = new File("C:\\Users\\Enrico Gründig\\Desktop\\Samples\\pic4.png");
Mat mat = new Mat(CvType.CV_8UC4);
Mat procMat = new Mat();
Mat hierarchy = new Mat();
Scalar color = new Scalar(0,0,255);
List<MatOfPoint> contours = new ArrayList<>();
try {
BufferedImage picture = ImageIO.read(file);
BufferedImage image = new BufferedImage(picture.getWidth(), picture.getHeight(), 5);
image.getGraphics().drawImage(picture, 0, 0, null);
System.out.println(image.getType());
mat = ImageProcHelper.ImageToMat(image);
Imgproc.cvtColor(mat, procMat, Imgproc.COLOR_RGBA2GRAY);
Imgproc.blur(procMat, procMat, new Size(3,3));
Imgproc.Canny(procMat, procMat, 127, 255);
//Konturen finden
Imgproc.findContours(procMat, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);
MatOfPoint2f[] contoursPoly = new MatOfPoint2f[contours.size()];
Rect[] boundRect = new Rect[contours.size()];
for(int i = 0; i < contours.size(); i++) {
contoursPoly[i] = new MatOfPoint2f();
Imgproc.approxPolyDP(new MatOfPoint2f(contours.get(i).toArray()), contoursPoly[i], 0.1, true);
boundRect[i] = Imgproc.boundingRect(new MatOfPoint(contours.get(i).toArray()));
}
for (int i = 0; i < contours.size(); i++) {
Imgproc.rectangle(mat, boundRect[i].tl(), boundRect[i].br(), color, 1);
}
image = ImageProcHelper.MatToImage(mat);
ImageIO.write((RenderedImage)image, "png", new File ("C:\\Users\\Enrico Gründig\\Desktop\\Samples\\output.png"));
} catch (IOException e) {
System.out.println("Error");
}
}
}
What is the point of this project:
I got an IP camera streaming a video. And with this project, I want to find all QR Codes in the stream, crop them and pass them to a decoder (e.g. ZXing). I tried this with only ZXing but I had problems with the angle, size and so on. Thats why I want to use OpenCV to find the codes and manipulate them to decrase the traffic from IP camera to decoder and (maybe) increase the hit ratio.
Thanks a lot for your help.
Solution
I don't have enough reputation to comment, but what you seem to be missing is a check to get the sides of each contour. In your code, you need to make use of contoursPoly[i].size() to differentiate between different shapes. Your code needs to end up looking something like this:
package main;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
import helper.ImageProcHelper;
public class Main {
public static void main(String[] args) throws Exception {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
File file = new File("C:\\Users\\Enrico Gründig\\Desktop\\Samples\\pic4.png");
Mat mat = new Mat(CvType.CV_8UC4);
Mat procMat = new Mat();
Mat hierarchy = new Mat();
Scalar color = new Scalar(0,0,255);
List<MatOfPoint> contours = new ArrayList<>();
try {
BufferedImage picture = ImageIO.read(file);
BufferedImage image = new BufferedImage(picture.getWidth(), picture.getHeight(), 5);
image.getGraphics().drawImage(picture, 0, 0, null);
System.out.println(image.getType());
mat = ImageProcHelper.ImageToMat(image);
Imgproc.cvtColor(mat, procMat, Imgproc.COLOR_RGBA2GRAY);
Imgproc.blur(procMat, procMat, new Size(3,3));
Imgproc.Canny(procMat, procMat, 127, 255);
//Konturen finden
Imgproc.findContours(procMat, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);
MatOfPoint2f[] contoursPoly = new MatOfPoint2f[contours.size()];
Rect[] boundRect = new Rect[contours.size()];
for(int i = 0; i < contours.size(); i++) {
contoursPoly[i] = new MatOfPoint2f();
Imgproc.approxPolyDP(new MatOfPoint2f(contours.get(i).toArray()), contoursPoly[i], 0.1, true);
boundRect[i] = Imgproc.boundingRect(new MatOfPoint(contours.get(i).toArray()));
}
for (int i = 0; i < contours.size(); i++) {
if (contoursPoly[i].size()>15){
Imgproc.rectangle(mat, boundRect[i].tl(), boundRect[i].br(), color, 1);
}
}
image = ImageProcHelper.MatToImage(mat);
ImageIO.write((RenderedImage)image, "png", new File ("C:\\Users\\Enrico Gründig\\Desktop\\Samples\\output.png"));
} catch (IOException e) {
System.out.println("Error");
}
}
}
I don't have Java setup with OpenCV so I haven't been able to test this code, but the idea came from this link. You might have to mess around with the "15" to differentiate between rectangle and circle.
Answered By - epistemophiliac
Answer Checked By - Cary Denson (JavaFixing Admin)