pixeles - recorrer imagen pixel por pixel java
ImageIO lee valores RGB ligeramente diferentes a otros métodos (4)
Consulte here : WARNING: Color space tagged as sRGB, without an embedded color profile. Windows and Mac browsers and apps treat the colors randomly.
WARNING: Color space tagged as sRGB, without an embedded color profile. Windows and Mac browsers and apps treat the colors randomly.
Edición: En cuanto a errores de redondeo y variación de implementación por versión; Simplemente no son el caso de esta imagen. Hay algo mágico en la Mac que hace que el azul y el verde se vean más brillantes en una curva de coincidencia de colores. Corrija el espacio de color, la coincidencia de color dará el mismo resultado. Yo voté a favor de la respuesta de Andy Fedoroff, pero también noté que en realidad nadie te ha dado una solución ... Llegaste a la conclusión de que Java es correcto. Ve con eso. Libjpeg no ha cambiado en mucho tiempo. Es estable y reproduce los colores de manera confiable en muchas plataformas y entornos. No se han realizado cambios significativos (de ninguna manera) para descodificar el jpeg estándar envejecido.
Edit 2: Intentaremos crear un ejemplo que genere los mismos valores que el perfil de Mac en función de su proyecto. Necesita el perfil ICC de fábrica de su Mac en Library/ColorSync/Profiles
.
Aquí es donde estoy. Aquí hay un ejemplo con el perfil sRGB ICC v4 aplicado. Esto es técnicamente aplicando sRGB sobre sRGB, pero está explicando el concepto.
private ICC_Profile cp = ICC_Profile.getInstance("src/test/resources/sRGB_ICC_v4_Appearance.icc");
private ICC_ColorSpace cs = new ICC_ColorSpace(cp);
private int[] getRGBUsingImageIO2(File file, int x, int y) throws IOException {
BufferedImage image = ImageIO.read(file);
ColorConvertOp cco = new ColorConvertOp( cs, null );
BufferedImage result = cco.filter( image, null );
int javaRGB = result.getRGB(x, y);
int javaRed = (javaRGB >> 16) & 0xFF;
int javaGreen = (javaRGB >> 8) & 0xFF;
int javaBlue = (javaRGB >> 0) & 0xFF;
return new int[]{javaRed, javaGreen, javaBlue};
}
Image IO 1 : [0, 0] = [145, 146, 164] Image IO 2 : [0, 0] = [145, 147, 165] Image IO 1 : [1, 0] = [137, 138, 156] Image IO 2 : [1, 0] = [137, 139, 157] Image IO 1 : [2, 0] = [148, 147, 161] Image IO 2 : [2, 0] = [148, 148, 162] Image IO 1 : [0, 1] = [150, 153, 168] Image IO 2 : [0, 1] = [150, 154, 169] Image IO 1 : [1, 1] = [138, 141, 156] Image IO 2 : [1, 1] = [138, 142, 157] Image IO 1 : [2, 1] = [145, 147, 159] Image IO 2 : [2, 1] = [145, 148, 160] Image IO 1 : [0, 2] = [154, 160, 172] Image IO 2 : [0, 2] = [154, 161, 173] Image IO 1 : [1, 2] = [146, 152, 164] Image IO 2 : [1, 2] = [146, 153, 165] Image IO 1 : [2, 2] = [144, 148, 157] Image IO 2 : [2, 2] = [144, 149, 158]
¿Podrías asignar tu perfil de color a tu repositorio de pruebas de imagen?
Descubrí que estoy obteniendo RGB diferente al usar Java (y en realidad paint.NET ) que al usar ImageMagick, Gimp, Python y Octave. Los últimos 4 están todos de acuerdo entre sí y por eso asumo que son correctos.
Para estos ejemplos, estoy usando esta imagen de prueba: http://farm3.static.flickr.com/2811/9177301733_9836174725_o.jpg
Prueba de píxel x=4144 y=2768
R G B
Java = (125, 107, 69)
Paint.NET = (125, 107, 69)
ImageMagick = (128, 106, 67)
Python = (128, 106, 67)
Octave = (128, 106, 67)
Gimp = (128, 106, 67)
¿Lo que da?
Aquí hay una prueba rápida usando imagemagick:
convert image.jpg -crop 1x1+4144+2768 -depth 8 txt:
salida:
# ImageMagick pixel enumeration: 1,1,65535,srgb
0,0: (32896,27242,17219) #806A43 srgb(128,106,67)
Aquí hay algunos códigos java y python que también demuestran el problema:
import org.apache.commons.io.FileUtils;
import org.junit.Test;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
public class ImageIOTest {
@Test
public void can_read_file() throws IOException, InterruptedException, URISyntaxException {
File tempFile = File.createTempFile("image", "jpg");
FileUtils.copyURLToFile(new URL("http://farm3.static.flickr.com/2811/9177301733_9836174725_o.jpg"), tempFile);
BufferedImage image = ImageIO.read(tempFile);
int javaRGB = image.getRGB(4144, 2768);
int javaRed = (javaRGB >> 16) & 0xFF;
int javaGreen = (javaRGB >> 8) & 0xFF;
int javaBlue = (javaRGB >> 0) & 0xFF;
System.out.printf("rgb: (%d, %d, %d)", javaRed, javaGreen, javaBlue);
}
}
Y aquí está la secuencia de comandos de python correspondiente:
from PIL import Image
import sys, urllib, cStringIO
file = cStringIO.StringIO(urllib.urlopen("http://farm3.static.flickr.com/2811/9177301733_9836174725_o.jpg").read())
im = Image.open(file)
pix = im.load()
print pix[4144, 2768]
He intentado usar esta biblioteca de 12monkeys con la esperanza de que eso lo arregle, pero no los dados. ¿Alguna otra idea de cómo puedo extraer los valores RGB correctos usando java? ¡Seguramente no soy la primera persona en tener este problema!
Actualizar
Intenté getRaster().getSample()
pero obtuve el mismo resultado no válido: System.out.println(raster.getSample(4144, 2768, 0)+","+ raster.getSample(4144, 2768, 1)+","+ raster.getSample(4144, 2768, 2));
Salida: 125,107,69
Más información
Aquí hay algunos resultados que muestran qué valores RGB se decodifican mediante tres herramientas diferentes para los primeros 9 (3x3 cuadrados de) píxeles en la parte superior izquierda de la imagen. Como puedes ver, Python y ImageMagick están al unísono. Java a veces coincide. He puesto una X donde Java no está de acuerdo ...
Tool [x, y] = (R , G , B )
ImageIO : [0, 0] = (86, 90, 93)
Python : [0, 0] = (86, 90, 93)
ImageMagick : [0, 0] = (86, 90, 93)
ImageIO : [1, 0] = (86, 90, 93)
Python : [1, 0] = (86, 90, 93)
ImageMagick : [1, 0] = (86, 90, 93)
ImageIO : [2, 0] = (90, 91, 95) X
Python : [2, 0] = (88, 92, 95)
ImageMagick : [2, 0] = (88, 92, 95)
ImageIO : [0, 1] = (85, 93, 95)
Python : [0, 1] = (85, 93, 95)
ImageMagick : [0, 1] = (85, 93, 95)
ImageIO : [1, 1] = (85, 93, 95) X
Python : [1, 1] = (87, 92, 95)
ImageMagick : [1, 1] = (87, 92, 95)
ImageIO : [2, 1] = (87, 92, 95)
Python : [2, 1] = (87, 92, 95)
ImageMagick : [2, 1] = (87, 92, 95)
ImageIO : [0, 2] = (83, 93, 94)
Python : [0, 2] = (83, 93, 94)
ImageMagick : [0, 2] = (83, 93, 94)
ImageIO : [1, 2] = (83, 93, 94) X
Python : [1, 2] = (84, 92, 94)
ImageMagick : [1, 2] = (84, 92, 94)
ImageIO : [2, 2] = (83, 91, 93)
Python : [2, 2] = (83, 91, 93)
ImageMagick : [2, 2] = (83, 91, 93)
¿Por qué Java está dando diferentes valores para algunos píxeles? Alternativamente, ¿hay otra forma (rápida) de generar valores correctos utilizando código Java nativo?
ACTUALIZACIÓN 2016-09-26:
Confirmé mi código que demuestra este problema y lo envié a github ( imageio-test ) para poder probarlo fácilmente en diferentes máquinas. Resulta que Java era consistente tanto en OSX como en Ubuntu Linux, pero era Python, ImageMagick y Octave que eran inconsistentes. En otras palabras, en el cuadro de Linux, todas las herramientas están de acuerdo entre sí, y por lo tanto, ahora estoy pensando que Java siempre estuvo bien, ¡y son las otras herramientas las que están dando resultados incorrectos en OSX! Todavía no entiendo realmente por qué y no tengo ninguna prueba concreta de qué valores son los correctos, pero estoy llegando a alguna parte ...
Según mi comentario, la principal diferencia entre las diversas aplicaciones / bibliotecas que ha usado para recuperar el valor de color de píxel es que todas están usando diferentes versiones de libjpeg , al menos en Mac OSX.
Cuando revise su proyecto Github en ciertas versiones de Ubuntu, verá que todos los valores se informan de la misma manera en todo el tablero. En estos casos, Python ImageMagick y Java JDK / JRE están utilizando la misma implementación libjpeg.
En la Mac, si instaló jpeg
través de homebrew
o Pillow
través de pip
, notará que están usando libjpeg v9 ( libjpeg.9.dylib
), mientras que los Java 7 y 8 JDK vienen con sus propios paquetes de libjpeg que son bastante diferente.
Octave enumera sus dependencias jpeg como libjpeg8-dev
.
GIMP, Inkscape, Scribus, etc. también vienen con su propio paquete. En mi caso, GIMP viene con la misma versión que python e ImageMagick, lo que explicaría los valores similares (es decir: /Applications/GIMP.app/Contents/Resources/lib/libjpeg.9.dylib
)
Si desea garantizar que los valores sean los mismos en todas las aplicaciones, tiene opciones:
- Se adhieren a la misma plataforma / pila (como lo sugiere @haraldk) : apéguese a desarrollar / ejecutar sus cosas en plataformas Linux que garanticen que todas ellas usen la misma versión de libjpeg
- Enlace su código Java a la misma versión que las otras aplicaciones están utilizando , por ejemplo: cargue
libjpeg.9.dylib
ylibjpeg.9.dylib
desde su aplicación Java. Aunque no estoy 100% seguro de cómo harías eso. - Recompile su JDK para usar el correcto : una opción a la que hace referencia esta respuesta es usar openjdk y compilarlo con la versión deseada de libjpeg, que suena más factible.
¡Admito que las opciones 2 y 3 son versiones más difíciles de la opción 1!
Nota:
Definitivamente estoy votando la respuesta de @ haraldk porque su conclusión es bastante parecida.
También jugué con el uso de diferentes perfiles icc, y me dan respuestas totalmente diferentes. Así que vale la pena tener cuidado con eso.
Solo quería agregar una respuesta que agregue más énfasis en la implementación de libjpeg, porque creo que eso es lo que lo está atrapando en su instancia específica.
Actualizar
En realidad, hay otra diferencia importante que se nota en la respuesta de @ haraldk, que es la diferencia entre CMM y Little CMS. Como él dice: Java usa Little CMS, que también es usado por Ubuntu
En realidad creo que es más probable que sea la respuesta aquí.
En realidad, me gustaría cambiar el problema y decir que me sorprende que tantas plataformas y herramientas diferentes produzcan los mismos valores. :-)
JPEG tiene pérdidas
En primer lugar, JPEG es un método de compresión de imágenes con pérdida. Esto significa que no es posible reproducir los datos exactos del original. O, si lo desea, varios valores de píxeles diferentes pueden ser todos "correctos" de alguna manera.
Las razones técnicas por las que no todo el software JPEG produce los mismos valores exactos del mismo archivo de origen, generalmente son diferentes redondeos / ajustes de valores, o aproximaciones de enteros de operaciones de punto flotante para un mejor rendimiento. Otras variaciones pueden provenir de diferentes algoritmos de interpolación aplicados para restaurar los valores cromáticos del submuestreo, por ejemplo (es decir, una imagen más suave puede parecer más agradable a la vista, pero no es necesariamente más correcta).
Otra excelente respuesta a una pregunta similar dice que "El estándar JPEG no requiere que las implementaciones del decodificador produzcan imágenes de salida idénticas bit a bit" , y cita la entrada JPEG de Wikipedia :
requisitos de [...] precisión para la codificación [...]; la salida del algoritmo de referencia no debe exceder:
- un máximo de un bit de diferencia para cada componente de píxel
- bajo error cuadrático medio en cada bloque de 8 × 8 píxeles
- error medio muy bajo en cada bloque de 8 × 8 píxeles
- Error cuadrático medio muy bajo en toda la imagen.
- Error medio extremadamente bajo en toda la imagen.
(Tenga en cuenta que lo anterior se refiere solo a la implementación de referencia ).
Sin embargo, con un poco de suerte, parece que todos sus programas / herramientas realmente terminan usando (alguna versión de) libjpeg . Debido a que todos usan libjpeg, lo más probable es que la fuente de las diferencias que usted ve no esté relacionada con la decodificación JPEG.
Espacios de color
Incluso si todo su software convierte el archivo JPEG en una representación utilizando valores RGB, podría haber diferencias en el espacio de color que utilizan para esta representación.
Parece que todo el software que está utilizando realmente muestra los valores RGB en el espacio de color sRGB . Este es probablemente el espacio de color más estándar y más utilizado en la informática general, por lo que no es una sorpresa, después de todo. Como el espacio de color siempre es sRGB, lo más probable es que la fuente de las diferencias que usted ve no sea el espacio de color.
Perfiles ICC y combinación de colores.
La siguiente fuente posible de diferencias de color es que la concordancia de color (como lo hace un Módulo de concordancia de color, CMM o Sistema de administración de color, CMS) no es una ciencia exacta al 100% (consulte, por ejemplo, este documento sobre compensación de puntos negros o lea algunos de Las publicaciones más técnicas del blog Little CMS ).
Lo más probable es que el software que se ejecuta en Mac OS X esté usando el CMM de Apple, mientras que Java está usando Little CMS siempre (desde OpenJDK 7 o Oracle JDK / JRE 8), y la mayoría del software en la plataforma Linux también usará el código abierto de Little CMS ( de acuerdo con la página de inicio de Little CMS, "Puede encontrar Little CMS en la mayoría de las distribuciones de Linux"). Es probable que el software en Windows también se desvíe un poco (no he podido verificar si Paint.Net usa Little CMS, el CMM integrado de Windows o algo más). Y, por supuesto, el uso de CMM de Adobe (es decir, Photoshop) probablemente también se desviará.
Nuevamente, con un poco de suerte, gran parte del software que probaste usa el mismo motor CMM o CMS, Little CMS , así que nuevamente tendrás muchos resultados iguales. Pero parece que parte del software que probaste usa diferentes CMM y es una fuente probable de las ligeras diferencias de color.
En resumen
Los diferentes valores de píxeles que ve son todos "correctos". Las diferencias se derivan de diferentes implementaciones o aproximaciones de algoritmos en el software, pero eso no significa necesariamente que un valor sea correcto y los otros estén equivocados.
PD: si necesita reproducir exactamente los mismos valores en múltiples plataformas, use la misma pila de herramientas / los mismos algoritmos en todas las plataformas.
Consistencia del color y perfiles ICC
Java no respeta el perfil de color al cargar una imagen. También diferentes sistemas operativos son diferentes para manejar los colores RGB.
Aquí está lo que Oracle escribe sobre import java.awt.color
:
Normalmente, un Color o ColorModel se asociaría con un Perfil ICC que es un perfil de entrada, visualización o salida. Hay otros tipos de perfiles ICC, por ejemplo, perfiles abstractos, perfiles de enlace de dispositivo y perfiles de color con nombre, que no contienen información adecuada para representar el espacio de color de un color, imagen o dispositivo. Intentar crear un objeto ICC_ColorSpace a partir de un perfil ICC inapropiado es un error.
Los perfiles ICC representan transformaciones del espacio de color del perfil (por ejemplo, un monitor) a un espacio de conexión de perfil (PCS). Los perfiles de interés para etiquetar imágenes o colores tienen un PCS que es uno de los espacios independientes del dispositivo (un espacio CIEXYZ y dos espacios CIELab) definidos en la Especificación de formato de perfil ICC. La mayoría de los perfiles de interés tienen transformaciones invertibles o especifican explícitamente las transformaciones en ambas direcciones. Si se utiliza un objeto ICC_ColorSpace de una manera que requiera una conversión de PCS al espacio nativo del perfil y no haya datos adecuados para realizar la conversión correctamente, el objeto ICC_ColorSpace producirá resultados en el tipo de espacio de color especificado (por ejemplo, TYPE_RGB, TYPE_CMYK, etc.) .), pero los valores de color específicos de los datos de salida serán indefinidos.
Los detalles de la clase ICC_ColorSpace
no son importantes para applets simples, que dibujan en un espacio de color predeterminado o manipulan y muestran imágenes importadas con un espacio de color conocido. A lo sumo, tales applets necesitarían obtener uno de los espacios de color predeterminados a través de ColorSpace.getInstance()
. (extracto de docs.oracle.com) https://docs.oracle.com/javase/7/docs/api/java/awt/color/ICC_ColorSpace.html
Transformaciones del espacio de color en Java
Las transformaciones del espacio de color son controladas por el tipo de destino para la lectura y escritura de imágenes. Cuando se leen los Rasters, no se realiza ninguna transformación del espacio de color y se ignora cualquier tipo de destino. Se envía una advertencia a los oyentes si se especifica un tipo de destino en este caso. Cuando se escriben rásteres, cualquier tipo de destino se utiliza para interpretar las bandas. Esto puede hacer que se escriba un encabezado JFIF o Adobe, o que se escriban diferentes identificadores de componente en los encabezados de marco y escaneo. Si los valores presentes en un objeto de metadatos no coinciden con el tipo de destino, se usa el tipo de destino y se envía una advertencia a los oyentes. (extracto de docs.oracle.com) https://docs.oracle.com/javase/7/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html
Enlaces útiles
Mira la información tocando la conversión RGB. Hay algunos problemas en los componentes de color float / int normalizados de Rolf W. Rasmussen:
http://opensource.apple.com/source/gcc3/gcc3-1041/libjava/java/awt/image/ColorModel.java
Lea la The Sad Story of PNG Gamma “Correction”
(el problema es que JPEG y TIFF sufren la misma "enfermedad").
https://hsivonen.fi/png-gamma/
Mira la publicación de SO Hay una posible solución para ti:
En Java, convertir una imagen a sRGB hace que la imagen sea demasiado brillante.
Si aún tiene colores inconsistentes después de todos los intentos, intente convertir las imágenes a un perfil sRGB, pero no las incruste:
https://imageoptim.com/color-profiles.html
Además, espero que el libro de Kenny Hunt pueda ser útil .
... y el siguiente código (publicado en www.physicsforums.com) le permite ver cómo se ven varios RGB:
import java.awt.*;
import javax.swing.*;
public class RGB {
public static void main(String[] args) {
JFrame frame = new JFrame("RGB");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
RGBpanel panel = new RGBpanel();
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
}
}
class RGBpanel extends JPanel {
public RGBpanel() {
setPreferredSize(new Dimension(300,300));
int red = Integer.parseInt(JOptionPane.showInputDialog("Enter red value"));
int green = Integer.parseInt(JOptionPane.showInputDialog("Enter green value"));
int blue = Integer.parseInt(JOptionPane.showInputDialog("Enter blue value"));
Color colour = new Color(red,green,blue);
setBackground(colour);
}
}
Resumen
El problema de la inconsistencia de color proviene de los perfiles de color. Intente asignar un perfil de color uniforme a todas las imágenes ya sea manualmente (en Photoshop) o mediante programación (en Java).