openCV Java | Удалить капли по площади в двоичном изображении - PullRequest
0 голосов
/ 04 сентября 2018

У меня есть черно-белое изображение (только один канал, только 0 и 255), и я хотел бы удалить в нем маленькие капли, которые находятся ниже определенного порогового значения, используя openCV 3.4.2 в Java.

Теперь я уже нашел следующую ветку: Удаление больших двоичных объектов из двоичного изображения , что в значительной степени аналогично - но мне нужна помощь в переводе ответа в код Java. Кроме того, поскольку я хотел бы удалить черные пятна, я инвертирую изображение до и после обработки. РЕДАКТИРОВАТЬ: благодаря некоторым добрым предложениям мне удалось получить рабочий код и изменить его, так что теперь это MCVE.

Моя попытка до сих пор:

import java.util.ArrayList;

import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Scalar;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

public class contourCheck {

public static void main(String[] args) {

    // initialises openCV   
    System.loadLibrary(Core.NATIVE_LIBRARY_NAME);   

    // reads original black&white image
    Mat binary_image =  Imgcodecs.imread("C:/Users/MyName/Desktop/TestImages/testpic.png", Imgcodecs.CV_LOAD_IMAGE_GRAYSCALE);

    // creates temporary Mat
    Mat temp_image = binary_image;

    // inverts image
    Core.bitwise_not(temp_image,temp_image);

    // finds all contours in the image
    ArrayList<MatOfPoint> contours = new ArrayList<MatOfPoint>();
    Imgproc.findContours(temp_image, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_NONE);

    // deletes contours above minArea to keep only the contours that are supposed to be removed from the image
    int minArea = 10;
    for (int i = 0; i < contours.size(); i++) {
        double area = Imgproc.contourArea(contours.get(i));
        if (area > minArea) {
            contours.remove(i);
        }   
    } 
    System.out.println("number of contours remaining: " + contours.size());  
    for (int j = 0; j < contours.size(); j++) {

        if (j > 0) { // apparently temp_image gets also inverted, therefore it gets inverted here once again
            Core.bitwise_not(temp_image,temp_image);
        }
        // fills in small (<= minArea) contours with 0's  
        Imgproc.drawContours(temp_image, contours,j, new Scalar(0),Core.FILLED);

        // inverts image once again to get the original state
        Core.bitwise_not(temp_image,binary_image);

        // writes image with filtered contours
        Imgcodecs.imwrite("C:/Users/MyName/Desktop/TestImages/test/testpic_filtered" + j + ".png", binary_image);
    }
}
}

Теперь вот пример изображения, где я хотел бы удалить все черные пятна ниже minArea (= 10):

example binary image (

Что меня теперь удивляет, так это то, что некоторые очень большие компоненты удалены (например, огромный круг с несколькими маленькими кругами внутри или огромный прямоугольник), а меньшие сохранены. Есть ли ошибка в моем коде или я неправильно понимаю некоторые концепции здесь? Кроме того, почему bitwise_not также инвертирует temp_image, исходный Mat (Core.bitwise_not(temp_image,binary_image))?

Примечание: код создает изображение для каждого удаляемого контура, что означает создание 74 изображений.

Пример вывода (последнее изображение после удаления всех контуров):

image with contours removed

1 Ответ

0 голосов
/ 04 сентября 2018

Проблема заключается в следующем цикле:

for (int i = 0; i < contours.size(); i++) {
    double area = Imgproc.contourArea(contours.get(i));
    if (area > minArea) {
        contours.remove(i);
    }   
}

Обратите внимание, что вы перебираете contours, и в то же время вы иногда remove элементы. Помните, что remove сместит все последующие элементы вперед на одну позицию. Теперь вы увеличиваете индекс на каждой итерации. Это заставит вас пропустить контур, для каждого, который вы удалили. Поскольку ваша текущая цель - сохранить только достаточно малые контуры, эффект состоит в том, что некоторые из более крупных контуров проскальзывают.

Мое предложение для решения этой проблемы было бы принять несколько иной подход - вместо того, чтобы удалять нежелательные контуры, я бы создал новый экземпляр ArrayList<MatOfPoint> и заполнил его интересующими меня контурами и используйте это в дальнейшей обработке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...