2010-01-27 6 views
5

Me pregunto si existe una forma "inteligente" de dividir una imagen basada en ciertas características.Cómo separar una imagen en dos con java

Las imágenes son 300x57, blanco y negro (realmente en escala de grises, pero la mayoría de los colores son negros o blancos), se compone de dos características principales (llamémoslas blobs) separadas por espacio negro, cada burbuja varía ligeramente de ancho y altura, la posición de las manchas también varía, ¡las manchas NUNCA se superponen!

Esto es lo que una imagen "ve" como:

------------------------- 
----WWW---------WWWWW---- 
---WWWWWWW----WWWWWW----- 
-----WWWW-------WWW------ 
------------------------- 

La división resultante sería algo como esto:

------------  ------------- 
----WWW-----  ----WWWWW---- 
---WWWWWWW--  --WWWWWW----- 
-----WWWW---  ----WWW------ 
------------  ------------- 

Pasos planeo tomar con el fin de dividir la imagen:

  1. Escanee la imagen de un lado a otro.
  2. Determine los bordes de los blobs.
  3. Tome la distancia entre los dos bordes interiores.
  4. Divida la imagen en el medio de la distancia interior.
  5. Guarde las dos imágenes como archivos separados.

Sería bueno si normalizo los anchos de imagen, por lo que todas mis imágenes tienen un ancho uniforme cuando se guardan.

No tengo experiencia en la manipulación de imágenes, así que no sé cuál es una forma eficiente de hacerlo. Actualmente estoy usando una Imagen Buffered, obtengo el ancho/alto, iterando sobre cada píxel, etc. No hay una solución incorrecta para mi problema, pero estoy buscando una más eficiente (menos código + más rápido). También he estado buscando en java.awt.Graphics ...

Agradecería que obtuviera algunas ideas para formas más eficientes de realizar esta tarea. Quiero quedarme con las bibliotecas integradas de Java, ¿así que BufferedImage o Graphics2D son las cosas más eficientes para usar en este caso?

EDIT: Este es el código después de leer las sugerencias:

public void splitAndSaveImage(BufferedImage image) throws IOException 
{ 
    // Process image ------------------------------------------   
    int height = image.getHeight(); 
    int width = image.getWidth(); 
    boolean edgeDetected = false; 
    double averageColor = 0; 
    int threshold = -10; 
    int rightEdge = 0; 
    int leftEdge = 0; 
    int middle = 0; 

    // Scan the image and determine the edges of the blobs. 
    for(int w = 0; w < width; ++w) 
    {    
     for(int h = 0; h < height; ++h) 
     { 
      averageColor += image.getRGB(w, h); 
     } 

     averageColor = Math.round(averageColor/(double)height); 

     if(averageColor /*!=-1*/< threshold && !edgeDetected) 
     { 
      // Detected the beginning of the right blob 
      edgeDetected = true; 
      rightEdge = w; 
     }else if(averageColor >= threshold && edgeDetected) 
     { 
      // Detected the end of the left blob 
      edgeDetected = false; 
      leftEdge = leftEdge==0? w:leftEdge; 
     } 

     averageColor = 0; 
    } 

    // Split the image at the middle of the inside distance. 
    middle = (leftEdge + rightEdge)/2; 

    // Crop the image 
    BufferedImage leftImage = image.getSubimage(0, 0, middle, height); 

    BufferedImage rightImage = image.getSubimage(middle, 0, (width-middle), height); 

    // Save the image 
    // Save to file ------------------------------------------- 
    ImageIO.write(leftImage, "jpeg", new File("leftImage.jpeg")); 

    ImageIO.write(rightImage, "jpeg", new File("rightImage.jpeg")); 
} 
+0

Nota: después de que se detecta el borde derecho, simplemente se puede salir del bucle for. – Kiril

Respuesta

5

Una manera simple de hacer esto es sumar los valores de píxel en cada columna (bajando) para crear una única matriz (el mismo ancho que su imagen de entrada) de valores promedio. Comenzando en el medio de la matriz, busque el valor mínimo. Esta será la columna donde puedes dividir la imagen.

Esta columna probablemente no será el centro de la brecha entre sus blobs. Puedes hacer otra búsqueda externa desde esta columna, yendo primero a la izquierda para encontrar todas las columnas similares, y luego yendo a la derecha.

------------------------- 
----WWW---------WWWWW---- 
---WWWWWWW----WWWWWW----- 
-----WWWW-------WWW------ 
------------------------- 

col promedio:

---wwWWwww-----wWWWWww--- 

Dependiendo de la forma en el espacio en blanco es (valor de píxel en cuanto a) entre las dos manchas, se puede establecer el valor de umbral bastante bajo. Si hay algo de ruido, tendrá que ser un poco más alto.

Encontrar el valor de umbral correcto puede ser una tarea ardua, a menos que pueda determinarlo algorítmicamente.

+0

+1: Creo que esto funcionaría muy bien y es muy simple. –

+0

Yah, eso es lo que terminé haciendo. Publicaré el código para que otros lo vean. – Kiril

1

No estoy al tanto de un edge detectionalgorithm que no requiere iteración a través de los píxeles, por lo que su actual enfoque puede ser óptimo. Dependiendo de otros factores, es posible que pueda aprovechar ImageJ, que tiene una amplia colección de analytical plugins.

Adición: Dada una preferencia para evitar dependencias externas, BufferedImage es una buena opción. Una vez que identifica los bordes, el método getSubimage() es conveniente. Es posible que pueda utilizar uno de los métodos RastergetPixels() de manera efectiva en la convolución. ImageIO puede escribir los resultados.

+0

@trashgod Buen punto ... ¿Son BufferedImage y Graphics2D las mejores cosas para usar en la biblioteca de Java (no quiero ir al exterior)? – Kiril

+0

Sí; He agregado algunos detalles arriba. – trashgod

1

No creo que haya ninguna razón para hacer otra cosa que no sea escanear cada línea y detener cuando haya obtenido una transición blanca-> negro-> blanca (¡no es necesario escanear toda la línea!). Si puede adivinar la posición de los blobs, podría refinarlo un poco al seleccionar un punto de partida en el centro de la imagen y luego buscar desde allí a la izquierda y a la derecha. Pero dudo seriamente que valga la pena el esfuerzo.

Tampoco es necesario ejecutar primero un algoritmo de detección de bordes en la imagen. ¡Solo escanea las líneas!

EDITAR: Sr. Berna señaló que esto no funcionará con objetos cóncavos.

+0

Esto se rompe cuando el blob es cóncavo. Si la parte superior de la mancha tiene forma de dedos, puede obtener una transición blanca -> negra -> blanca sin encontrar el borde de la primera mancha. –

+0

@Mr. Berna: Es cierto. Solo funcionará (confiablemente) para blobs convexos. –

1

¿Importa la separación entre las manchas? Si no necesita equilibrar el espacio en blanco, se necesitaría menos trabajo para encontrar una línea blanca vertical entre los blobs. Compruebe si la línea vertical central tiene solo píxeles blancos. Si la línea central tiene un píxel negro, escanee hacia la izquierda y hacia la derecha para la primera línea que solo tiene píxeles blancos. Para verificar las situaciones en las que ambos blobs están a un lado del centro, escanee una línea horizontal para intervalos negro-blanco-negro. Si la línea vertical seleccionada está dentro de un intervalo blanco rodeado de intervalos negros, sabrá que hay al menos un blob en cada lado de la división de la imagen.

Fallar estos controles requeriría escanear líneas adicionales, pero para todas las imágenes bien formadas, donde las manchas se centran en las mitades derecha e izquierda de la imagen, este método tomará solo dos escaneos de línea. Este método puede llevar más tiempo para otras imágenes, o incluso romper, para las imágenes de bordes. Esto se rompería en este ejemplo:

------------------------- 
----WWW----WWWWWWWWWW---- 
---WWWWWWW----WWWWWW----- 
-----WWWWWWWW---WWW------ 
------------------------- 

Pero la pregunta parece indicar que esta situación es imposible. Si la razón detrás de esta división de imagen requiere el procesamiento de cada imagen, necesitará un método de retroceso. No necesitaría un método de retroceso si las cajas de borde pueden rechazarse. Una vez que el escaneo encuentre que la imagen queda fuera de los rangos aceptables, puede dejar de verificar la imagen. Por ejemplo, si no se puede encontrar una línea blanca vertical en el tercio central de la imagen, es posible que pueda rechazar la imagen. O simplemente puede utilizar este método como una optimización, ejecutando esta comprobación en solo dos líneas para encontrar y dividir las imágenes bien formadas, y luego pasar las imágenes mal formadas a un algoritmo más completo.

+0

Me gusta la idea de escanear la línea horizontal ... Voy a experimentar con ella.En mi muestra de más de 1000 imágenes, no he tenido una imagen en la que porciones de dos blobs ocupen la misma línea vertical, las manchas siempre han estado separadas por unas pocas líneas blancas. – Kiril

Cuestiones relacionadas