Lecture de l'ImageIO des valeurs RVB légèrement différentes des autres méthodes

j'ai découvert que J'obtiens un RVB différent en utilisant Java (&en fait paint.NET ) que J'utilise ImageMagick, Gimp, Python, et Octave. Les 4 derniers sont d'accord et je suppose que c'est vrai.

pour ces exemples, j'utilise cette image test: http://farm3.static.flickr.com/2811/9177301733_9836174725_o.jpg

Test de pixel 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)

qu'est-ce qui se passe?

voici un test rapide avec imagemagick:

convert image.jpg -crop 1x1+4144+2768 -depth 8 txt:

sortie:

# ImageMagick pixel enumeration: 1,1,65535,srgb
0,0: (32896,27242,17219)  #806A43  srgb(128,106,67)

voici du code java et python qui démontre aussi le problème:

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

et voici le script python correspondant:

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]

j'ai essayé d'utiliser cette bibliothèque de 12monskeys dans l'espoir que cela corrigerait mais pas de dés. Autres idées pour extraire les valeurs RVB correctes à l'aide de java? Je ne suis sûrement pas la première personne à avoir ce problème!

mise à Jour

j'ai essayé getRaster().getSample() mais j'ai eu le même résultat invalide: System.out.println(raster.getSample(4144, 2768, 0)+","+ raster.getSample(4144, 2768, 1)+","+ raster.getSample(4144, 2768, 2)); sortie: 125,107,69

plus d'information

Voici une sortie qui montre quelles valeurs RVB sont décodées par trois outils différents pour les 9 (3x3 carrés) premiers pixels en haut à gauche de l'image. Comme vous pouvez le voir, Python et ImageMagick sont à l'unisson. Java correspond parfois. J'ai mis un X où java n'est pas d'accord...:

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)

Pourquoi Java donner des valeurs différentes pour certains pixels? Alternativement, y a-t-il une autre façon (rapide) de générer des valeurs correctes en utilisant du code Java natif?

mise à jour 2016-09-26:

j'ai commis mon code qui démontre ce problème et l'ai poussé à github ( imageio-test ) afin que je puisse facilement le tester sur différents machine. Il s'avère que Java était cohérent entre OSX et Ubuntu Linux, mais C'était Python, ImageMagick et Octave qui étaient incohérents. En d'autres termes, sur la Linux box, tous les outils sont d'accord entre eux, et donc, je pense maintenant que java avait raison depuis le début, et ce sont les autres outils qui donnent des résultats incorrects sur OSX! Je ne comprends toujours pas vraiment pourquoi et je n'ai aucune preuve concrète quant aux valeurs qui sont les bonnes, mais je m'en sors...

28
demandé sur matt burns 2016-09-23 00:39:32

4 réponses

en fait, j'aimerais retourner le problème, et dire que je suis surpris que tant de plates-formes et d'outils différents produisent réellement les mêmes valeurs. :- )

JPEG avec perte est

tout d'abord, JPEG est une méthode de compression d'image avec perte. Cela signifie que la reproduction des données exactes de l'original n'est pas possible . Ou, si vous le souhaitez, plusieurs valeurs de pixels différentes peuvent toutes être "correctes" d'une certaine manière.

les raisons techniques pour lesquelles tous les logiciels JPEG ne produisent pas exactement les mêmes valeurs à partir du même fichier source, sont typiquement des arrondis/serrage des valeurs différentes, ou des approximations entières d'opérations à virgule flottante pour une meilleure performance. D'autres variations peuvent provenir de différents algorithmes d'interpolation appliqués pour restaurer les valeurs de chroma sous-échantillonnées, par exemple (i.e. une image plus lisse peut sembler plus agréable à l'oeil, mais n'est pas nécessairement plus correcte).

un autre excellente réponse à une question similaire déclare que "la norme JPEG n'exige pas que les implémentations de décodage produisent bit-for-bit des images de sortie identiques " , et cite L'entrée JPEG Wikipedia :

[...] exigences de précision pour le de codage [...]; la sortie de l'algorithme de référence ne doit pas dépasser:

  • un maximum d'un bit de différence pour chaque composante de pixel
  • erreur carrée moyenne faible sur chaque bloc de 8×8 pixels
  • erreur Moyenne Très faible sur chaque bloc de 8×8 pixels
  • erreur carrée moyenne très faible sur toute l'image
  • erreur moyenne extrêmement faible sur toute l'image

(notez que ce qui précède parle de la application de référence seulement).

cependant, avec un peu de chance, il semble que tous vos logiciels/outils finissent par utiliser (une certaine version de) libjpeg . Parce qu'ils utilisent tous libjpeg, la source des différences que vous voyez n'est probablement pas liée au décodage JPEG.

Les Espaces De Couleur

même si tous vos logiciels convertissent le fichier JPEG en une représentation utilisant des valeurs RGB, il pourrait y avoir des différences dans l'espace de couleur qu'ils utilisent pour cette représentation.

il semble que tous les logiciels que vous utilisez affichent en fait les valeurs RGB dans l'espace de couleur sRGB . Il s'agit probablement de l'espace de couleur le plus standard et le plus largement utilisé dans le mainstream computing, de sorte que ce n'est pas une surprise après tout. Comme l'espace de couleur est toujours sRGB, la source des différences que vous voyez est probablement pas la couleur de l'espace.

profils ICC et correspondance des couleurs

la prochaine source possible de différences de couleur, est que l'appariement des couleurs (comme fait par un Module D'appariement des couleurs, CMM ou système de gestion des couleurs, CMS) n'est pas une science exacte à 100% (Voir par exemple ce document sur la compensation de point noir ou lire certains des postes plus techniques de la petit CMS blog ).

très probablement le logiciel tournant sur Mac OS X utilise le CMM D'Apple, alors que Java utilise toujours Little CMS (D'OpenJDK 7 ou Oracle JDK/JRE 8), et la plupart des logiciels sur la plate-forme Linux utiliseront probablement aussi L'open source Little CMS (selon la page D'accueil de Little CMS, "vous pouvez trouver peu de CMS dans la plupart des distributions Linux"). Le logiciel sur Windows va probablement dévier légèrement aussi bien (je n'ai pas été en mesure de vérifier si Paint.Net utilise Petit CMS, Windows ' construit en CMM ou autre chose). Et bien sûr, en utilisant le CMM D'Adobe (c.-à-d. Photoshop) probablement déviant aussi.

encore une fois, avec un peu de chance, une grande partie du logiciel que vous avez testé utilise le même moteur CMM ou CMS, petit CMS , donc encore une fois, vous aurez beaucoup de résultats égaux. Mais il semble que certains des logiciels que vous avez testés utilisent des CMM différents, et est une source probable des légères différences de couleur.

en résumé

les différentes valeurs de pixels que vous voyez sont toutes "correctes". les différences proviennent de différentes implémentations ou approximations d'algorithmes dans le logiciel, mais cela ne signifie pas nécessairement qu'une valeur est correcte et que les autres sont fausses.

PS: Si vous devez reproduire exactement les mêmes valeurs sur plusieurs plateformes, utilisez la même pile d'outils/mêmes algorithmes sur toutes les plateformes.

18
répondu haraldK 2017-05-23 11:55:16

selon mon commentaire, la principale différence entre les diverses applications/bibliothèques que vous avez utilisées pour récupérer la valeur de couleur pixel est qu'elles utilisent toutes différentes versions de libjpeg - au moins sur Mac OSX.

lorsque vous consultez votre projet Github sur certaines versions D'Ubuntu, vous verrez que toutes les valeurs sont rapportées de la même manière. Dans ces cas, Python ImageMagick et Java JDK/JRE utilisent les mêmes libjpeg mise en œuvre.

sur le Mac, si vous avez installé jpeg via homebrew , ou Pillow via pip , alors vous remarquerez qu'ils utilisent libjpeg v9 ( libjpeg.9.dylib ), alors que Java 7 et 8 JDKs viennent avec leur propre libjpeg empaqueté qui sont très différents.

Octave dresse la liste de ses jpeg dépendances comme libjpeg8-dev .

GIMP, Inkscape, Scribus etc sont également fournis avec leur propre. Dans mon cas, GIMP est livré avec la même version que python et ImageMagick, ce qui expliquerait les valeurs similaires (i.e.: /Applications/GIMP.app/Contents/Resources/lib/libjpeg.9.dylib )

si vous voulez garantir que les valeurs soient les mêmes pour toutes les applications, vous avez les options:

  1. coller à la même plateforme/pile (comme suggéré par @haraldk) - coller avec le développement / l'exécution de vos trucs sur les plateformes Linux qui garantissent tous les utiliser la même libjpeg version
  2. liez votre code Java à la même version que les autres applications utilisent - c'est à dire: chargez libjpeg.9.dylib et utilisez celle de votre application Java. Je ne suis pas sûr à 100% comment vous feriez ça.
  3. Recompile your JDK to use the right one - une option référencée par cette réponse est d'utiliser openjdk et de le compiler contre la version désirée de libjpeg, qui sonne plus réalisable.

j'admets que les options 2 et 3 sont des versions plus dures de l'option 1!

Note:

Je vote définitivement la réponse de @haraldk parce que sa conclusion est à peu près la même.

j'ai aussi joué avec différents profils icc, et ils donnent des réponses totalement différentes. Donc il vaut mieux se méfier de cela.

je viens de j'ai voulu ajouter une réponse qui mettait davantage l'accent sur l'implémentation de libjpeg, parce que je pense que c'est ce qui vous surprend dans votre cas spécifique.

mise à Jour

en fait, il y a une autre différence majeure notée dans la réponse de @haraldk, soit la différence entre CMM et Little CMS. Comme il le dit: Java utilise Little CMS, qui est également utilisé par Ubuntu

je pense que c'est plus probable la réponse ici.

6
répondu ndtreviv 2017-05-23 12:10:51

l'uniformité des Couleurs et les Profils ICC

Java ne respecte pas le profil de couleur lors du téléchargement d'une image. Aussi différents OS sont différemment Gérer les couleurs RGB.

voici ce Qu'Oracle écrit à propos de import java.awt.color :

typiquement, une couleur ou un modèle de couleur serait associé à un profil ICC qui est soit un profil d'entrée, d'affichage ou de sortie. Il existe d'autres types de profils ICC, par exemple: les profils abstraits, les profils de lien de périphérique et les profils de couleur nommés, qui ne contiennent pas d'informations appropriées pour représenter l'espace de couleur d'une couleur, d'une image ou d'un périphérique. Tenter de créer un objet ICC_ColorSpace à partir d'un profil ICC inapproprié est une erreur.

les profils ICC représentent les transformations de l'espace couleur du profil (par exemple un moniteur) vers un espace de connexion de profil (PCS). Profils d'intérêt pour le marquage des images ou des couleurs ont un PC qui est un des espaces indépendants de l'appareil (un espace CIEXYZ et deux espaces CIELab) définis dans la spécification ICC Profile Format. La plupart des profils d'intérêt ont soit des transformations inversibles, soit explicitement des transformations allant dans les deux sens. Si un objet ICC_ColorSpace est utilisé d'une manière nécessitant une conversion des PC vers l'espace natif du profil et qu'il y a des données inadéquates pour effectuer correctement la conversion, L'objet ICC_ColorSpace produira une sortie dans le type spécifié de l'espace de couleur (par exemple TYPE_RGB, TYPE_CMYK,etc.), mais les valeurs de couleur spécifiques des données de sortie ne seront pas définies.

les détails de la classe ICC_ColorSpace ne sont pas importants pour les applets simples, qui dessinent dans un espace de couleur par défaut ou manipulent et affichent des images importées avec un espace de couleur connu. Au plus, ces applets auraient besoin d'obtenir un des espaces de couleur par défaut via ColorSpace.getInstance() . (extrait de docs.oracle.com) https://docs.oracle.com/javase/7/docs/api/java/awt/color/ICC_ColorSpace.html

Palette Transformations en Java

les transformations Colorspace sont contrôlées par le type de destination pour la lecture et l'écriture des images. Lorsque les Rasters sont lus, aucune transformation colorspace n'est effectuée, et tout type de destination est ignoré. Un avertissement est envoyé à tous les auditeurs si un type de destination est indiqué dans ce cas. Quand on écrit Rasters, n'importe quel type de destination est utilisé pour interpréter les bandes. Cela peut conduire à l'écriture D'un en-tête JFIF ou Adobe, ou à l'écriture de différents noms de composants sur les en-têtes de cadre et de numérisation. Si les valeurs présentes dans un objet de métadonnées ne correspondent pas au type de destination, le type de destination est utilisé et un avertissement est envoyé à tous les auditeurs. (extrait de docs.oracle.com) https://docs.oracle.com/javase/7/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html

Liens utiles

regardez l'information touchant la conversion RGB. Il y a quelques problèmes sur les composants de couleur normalisés float/int par Rolf W. Rasmussen:

http://opensource.apple.com/source/gcc3/gcc3-1041/libjava/java/awt/image/ColorModel.java

Lire The Sad Story of PNG Gamma “Correction” (Le problème est que JPEG et TIFF souffrent de la même"maladie").

https://hsivonen.fi/png-gamma /

regardez s. o. post. Il y a une solution possible pour vous:

en Java convertir une image en sRGB rend l'image trop lumineuse

si vous avez encore des couleurs incohérentes après toutes les tentatives, essayez de convertir les images au profil sRGB, mais ne les incorporez pas:

https://imageoptim.com/color-profiles.html

aussi, j'espère que le livre de Kenny Hunt pourrait être utile .

enter image description here

...et le code suivant (publié à www.physicsforums.com) permet de voir à quoi ressemblent les différents 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); 
    } 
}

Récapitulation

le problème de l'incohérence des couleurs provient des profils de couleur. Essayez d'assigner un profil de couleur uniforme à toutes les images, que ce soit manuellement (dans Photoshop) ou par programmation (en Java).

5
répondu Gigantic 2017-09-06 16:19:21

voir ici : WARNING: Color space tagged as sRGB, without an embedded color profile. Windows and Mac browsers and apps treat the colors randomly.

modifier: en ce qui concerne les erreurs d'arrondissement et la variation de mise en œuvre par version; ils ne sont tout simplement pas le cas pour cette image. Il y a une certaine magie qui se passe avec le Mac qui rend le bleu et le vert plus lumineux sur une courbe de correspondance de couleur. Corriger l'espace de couleur, la correspondance de couleur donnera le même résultat. J'ai voté en faveur de la réponse D'Andy Fedoroff, mais je remarque aussi que personne ne vous a donné de solution... Vous avez en conclusion, Java est correct. Aller avec qui. Libjpeg n'a pas changé depuis longtemps. Il est stable et reproduit les couleurs de manière fiable sur de nombreuses plateformes et environnements. Aucun changement significatif (de quelque manière que ce soit) n'a été apporté au décodage du jpeg standard âgé.

Edit 2: Allons essayer de créer un exemple qui produit les mêmes valeurs que le Mac profil selon votre type de projet. Besoin de votre Mac's factory ICC profile de Library/ColorSync/Profiles .

voilà où je suis. Voici un exemple avec le profil sRGB ICC v4 appliqué. C'est techniquement appliquer sRGB sur sRGB, mais ça explique le concept.

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] 

pourriez-vous transmettre votre profil de couleur à votre imageio-test repo?

4
répondu TylerY86 2016-10-02 17:25:21