Tengo una imagen en blanco y negro (un solo canal, de 0 y sólo 255 de) y me gustaría para eliminar pequeñas manchas en los mismos que están por debajo de un cierto umbral área usando OpenCV 3.4.2 en Java.
Ahora ya me encontré con el siguiente hilo: Eliminación de manchas de una imagen binaria , que es más o menos la misma - pero necesito ayuda para traducir la respuesta en código Java. También, ya que me gustaría para eliminar puntos negros, que invertir la imagen antes y después del procesamiento. EDIT: gracias a algunas sugerencias tipo que lograron obtener un código de trabajo y modificar lo que ahora se trata de una MCVE.
Mi intento hasta el momento:
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);
}
}
}
Ahora aquí está una imagen ejemplo en el que me gustaría para eliminar todos los puntos negros que están por debajo minArea
(= 10):
Lo que me sorprende ahora es que algunos de los muy grandes componentes se eliminan (por ejemplo, el gran círculo con algunos pequeños círculos en el interior o la enorme rectángulo), mientras que los más pequeños se mantienen. ¿Hay un error en mi código o estoy entendiendo mal algunos conceptos aquí? Además, ¿por qué bitwise_not también invertido temp_image
, la fuente Mat ( Core.bitwise_not(temp_image,binary_image)
)?
Nota: el código crea una imagen para cada contorno que se elimina, lo que significa que 74 imágenes se crean.
Ejemplo de salida (última imagen después de eliminar todos los contornos):
Las mentiras de problemas en el siguiente bucle:
for (int i = 0; i < contours.size(); i++) {
double area = Imgproc.contourArea(contours.get(i));
if (area > minArea) {
contours.remove(i);
}
}
Aviso, que permite repetir contours
, y al mismo tiempo, a veces remove
elementos. Hay que recordar que remove
se desplazará todos los elementos posteriores hacia delante en una posición. Ahora, se incrementa el índice en cada iteración. Esto provocará que se salte un contorno, por cada uno que haya eliminado. Dado que su objetivo actual es mantener únicamente las pequeñas contornos suficientes, el efecto es que algunos de los más grandes se deslizan a través.
Mi sugerencia para resolver este problema sería la de adoptar un enfoque ligeramente diferente - en lugar de eliminar los contornos indeseables, me gustaría crear una nueva instancia de ArrayList<MatOfPoint>
, y rellenarla con los contornos que estoy interesado y el uso que, en su posterior procesamiento.