OpenCV - удалить неправильные контуры

У меня есть короткий вопрос об OpenCV и поиске конкретных форм. На моем ПК у меня есть картинка с некоторыми фигурами, но мне нужны только контуры прямоугольников:

Входной файл: Входной файл:

Каким должен быть мой вывод: Каким должен быть мой вывод

На самом деле мой вывод: Каков мой вывод на самом деле


Что я сделал:

  1. Откройте мое изображение и конвертируйте его в OpenCV Mat.
  2. Произведена обработка изображения [оттенки серого, размытие]
  3. Найденные преимущества с Canny
  4. Найденные контуры с помощью «findContours»
  5. Нарисуйте прямоугольники вокруг моих контуров с помощью «boundingRect».

И тут я застрял. Я не знаю, как устранить неправильные контуры. Я попробовал перебрать мои контуры и удалить те, которые неверны. Но я понятия не имею, как найти неправильные контуры. Есть ли какие-либо формулы, которые я должен использовать или что-то в этом роде. так? Я нашел что-то с «arcLength», но не понимаю смысла этого.


Вот мой код:

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");
        }   
    }
}

В чем суть этого проекта:

У меня есть IP-камера, которая транслирует видео. И в этом проекте я хочу найти все QR-коды в потоке, обрезать их и передать декодеру (например, ZXing). Я пробовал это только с ZXing, но у меня были проблемы с углом, размером и так далее. Вот почему я хочу использовать OpenCV, чтобы найти коды и манипулировать ими, чтобы уменьшить трафик от IP-камеры к декодеру и (возможно) увеличить коэффициент попадания.

Пример QR-кода: Образец QR-кода

Это должно быть моим выводом: Это должен быть мой вывод

Это мой вывод: Это мои выходные данные


Спасибо большое за вашу помощь.


person Enrico Gründig    schedule 15.10.2018    source источник
comment
Проблема вверху связана с обработкой как внешних, так и внутренних контуров. Мне кажется, вы должны заботиться только о внешних. | Во втором случае вы получаете более одного контура и обрабатываете их по отдельности — вот почему вы получаете так много разных блоков. Кажется, вы хотите найти ограничивающую рамку, которая подходит всем из них - простым способом было бы поместить все вершины из всех контуров в один список и найти для этого ограничивающий прямоугольник.   -  person Dan Mašek    schedule 15.10.2018


Ответы (1)


У меня недостаточно репутации, чтобы комментировать, но вам, похоже, не хватает проверки, чтобы получить стороны каждого контура. В вашем коде вам нужно использовать контурыPoly[i].size(), чтобы различать разные формы. Ваш код должен выглядеть примерно так:

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");
        }   
    }
}

У меня нет установки Java с OpenCV, поэтому я не смог протестировать этот код, но идея пришла из этого ссылка. Возможно, вам придется возиться с «15», чтобы различать прямоугольник и круг.

person epistemophiliac    schedule 15.10.2018