0

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: Input File:

What my output should be: What my output should be

What my output actually is: What my output actually is


What I did:

  1. Open my image and convert it into OpenCV Mat.
  2. Made some image proecssing [grayscale, blur]
  3. Found edges with Canny
  4. Found contours with "findContours"
  5. 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.

QR Code Sample: QR Code Sample

This should be my output: This should be my output

This is my output: This is my output


Thanks a lot for your help.

Jeru Luke
  • 20,118
  • 13
  • 80
  • 87
  • Problem at the top is due to processing both external as well as internal contours. Seems to me you only should care about external ones. | Second case is that you get more than one contour and handle them individually -- that's why you get so many different boxes. What you seem to want is to find a bounding box that fits all of them -- simple way would be to put all the vertices from all the contours into a single list and find a bounding rectangle for that. – Dan Mašek Oct 15 '18 at 18:03

1 Answers1

0

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.

epistemophiliac
  • 829
  • 8
  • 20