web-dev-qa-db-ja.com

ImageIOは他の方法とはわずかに異なるRGB値を読み取ります

Java(&実際には Paint.NET )を使用すると、ImageMagick、Gimp、Python、およびOctaveを使用する場合とは異なるRGBが得られることがわかりました。最後の4つはすべて互いに同意しているので、私は正しいと思います。

これらの例では、次のテストイメージを使用しています:_http://farm3.static.flickr.com/2811/9177301733_9836174725_o.jpg_

ピクセルのテスト_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)
_

何が得られますか?

Imagemagickを使用した簡単なテストは次のとおりです。

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

出力:

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

ここにいくつかのJavaおよびpythonコードがあり、これも問題を示しています。

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

そして、これが対応するpythonスクリプトです:

_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]
_

この12monkeysライブラリ を使用してみましたが、それで問題が解決することを期待していますが、サイコロはありません。 Javaを使用して正しいRGB値を抽出する方法は他にありますか?確かに私はこの問題を抱えている最初の人ではありません!

更新

getRaster().getSample()を試しましたが、同じ無効な結果が得られました:System.out.println(raster.getSample(4144, 2768, 0)+","+ raster.getSample(4144, 2768, 1)+","+ raster.getSample(4144, 2768, 2));出力:_125,107,69_

より詳しい情報

これは、画像の左上にある最初の9(3x3の正方形)ピクセルに対して3つの異なるツールによってデコードされるRGB値を示す出力です。ご覧のとおり、PythonとImageMagickは一致しています。Java一致する場合があります。Xをここに配置しましたJava =同意しない...:

_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)
_

Javaが一部のピクセルに異なる値を与えるのはなぜですか?あるいは、ネイティブJavaコードを使用して正しい値を生成する別の(高速)方法はありますか?

更新2016-09-26:

この問題を示すコードをコミットし、それをgithub( imageio-test )にプッシュして、さまざまなマシンで簡単にテストできるようにしました。 JavaはOSXとUbuntuLinuxの両方で一貫していたが、一貫性がなかったのはPython、ImageMagick、Octaveでした。つまり、Linuxボックスでは、すべてのツールが互いに一致しています。 、したがって、私は今、Javaはずっと正しかったと思っています、そしてそれはOSXで間違った結果を与えている他のツールです!私はまだ本当に理由を理解していません、そして私はしていません」どの値が正しいかについて具体的な証拠はありませんが、どこかで取得しています...

30
matt burns

実際、私は問題を好転させたいと思います。そして、非常に多くの異なるプラットフォームとツールが実際に同じ値を生成することに驚いていると言います。 :-)

JPEGは損失があります

まず第一に、JPEGは不可逆画像圧縮方式です。これは、元のデータを正確に再現することは不可能であることを意味します。または、必要に応じて、いくつかの異なるピクセル値がすべて何らかの方法で「正しい」場合があります。

すべてのJPEGソフトウェアが同じソースファイルからまったく同じ値を生成しないという技術的な理由は、通常、値の丸め/クランプが異なるか、パフォーマンスを向上させるための浮動小数点演算の整数近似です。他のバリエーションは、たとえば、サブサンプリングされたクロマ値を復元するために適用されるさまざまな補間アルゴリズムに起因する場合があります(つまり、より滑らかな画像は目に心地よく見えるかもしれませんが、必ずしもより正確であるとは限りません)。

別の 優れた回答 同様の質問に対する「JPEG標準では、デコーダーの実装でビットごとに同一の出力画像を生成する必要はありません」、および引用符 Wikipedia JPEGエントリ

[...]deコーディングの精度要件[...];参照アルゴリズムからの出力は、以下を超えてはなりません。

  • ピクセルコンポーネントごとに最大1ビットの差
  • 各8×8ピクセルブロックの平均二乗誤差が低い
  • 各8×8ピクセルブロックの平均誤差が非常に低い
  • 画像全体の平均二乗誤差が非常に低い
  • 画像全体の平均誤差が非常に低い

(上記はリファレンス実装についてのみ説明していることに注意してください)。

ただし、運が良ければ、すべてのソフトウェア/ツールが実際には(の一部のバージョン) libjpeg を使用することになります。それらはすべてlibjpegを使用しているため、表示される違いの原因はJPEGデコードに関連していない可能性があります。

色空間

すべてのソフトウェアがJPEGファイルをRGB値を使用する表現に変換する場合でも、この表現に使用する色空間に違いがある可能性があります。

使用しているすべてのソフトウェアが実際にRGB値を sRGB色空間 で表示しているようです。これはおそらく、主流のコンピューティングで使用される最も標準的で広く使用されている色空間であるため、結局のところ驚くことではありません。色空間は常にsRGBであるため、表示される違いの原因はおそらく色空間ではありません。

ICCプロファイルとカラーマッチング

次に考えられる色の違いの原因は、カラーマッチング(カラーマッチングモジュール、CMMまたはカラーマネジメントシステム、CMSによって行われる)が100%正確な科学ではないことです(たとえば、 ブラックポイント補正に関するこのドキュメントを参照) または Little CMSブログ )からのより技術的な投稿のいくつかを読んでください。

Mac OS Xで実行されているソフトウェアはAppleのCMMを使用している可能性がありますが、Javaは常にLittleCMSを使用しています(OpenJDK7またはOracleJDK/JRE 8から)。Linuxプラットフォーム上のほとんどのソフトウェアはオープンソースのLittleCMSも使用する可能性があります(LittleCMSのホームページ「ほとんどのLinuxディストリビューションでLittleCMSを見つけることができます」による)。Windows上のソフトウェアもわずかに異なる可能性があります(Paintかどうかを確認できませんでした.NetはLittleCMS、Windowsの組み込みCMMなどを使用します。もちろん、AdobeのCMM(つまり、Photoshop)の使用も同様に逸脱する可能性があります。

繰り返しになりますが、運が良ければ、テストしたソフトウェアの多くは同じCMMまたはCMSエンジンを使用します Little CMS なので、同じ結果が得られます。ただし、テストしたソフトウェアの中には異なるCMMを使用しているものがあり、わずかな色の違いの原因である可能性があります。

要約すれば

表示されるさまざまなピクセル値はすべて「正しい」です。違いは、ソフトウェアのアルゴリズムのさまざまな実装または近似に起因しますが、必ずしもそれを意味するわけではありません。 1つの値が正しく、他の値が間違っています。

PS:複数のプラットフォーム間でまったく同じ値を再現する必要がある場合は、すべてのプラットフォームで同じツールスタック/同じアルゴリズムを使用してください。

19
haraldK

私のコメントによると、ピクセルカラー値を取得するために使用したさまざまなアプリケーション/ライブラリの主な違いは、少なくともMac OSXではすべて異なるバージョンの libjpeg -を使用していることです。

Githubプロジェクトを特定のバージョンのUbuntuでチェックアウトすると、すべての値が全面的に同じように報告されていることがわかります。このような場合、python ImageMagickとJava JDK/JREは同じlibjpeg実装を使用しています。

Macでは、jpeg経由でhomebrewをインストールした場合、またはPillow経由でpipをインストールした場合、libjpeg v9(libjpeg.9.dylib)、一方Java 7と8のJDKには、まったく異なる独自のlibjpegがバンドルされています。

Octaveはそのjpeg依存関係をリストします as libjpeg8-dev

GIMP、Inkscape、Scribusなども独自にバンドルされています。私の場合、GIMPにはpythonおよびImageMagickと同じバージョンがバンドルされており、同様の値を説明します(つまり、/Applications/GIMP.app/Contents/Resources/lib/libjpeg.9.dylib

アプリ間で値が同じであることを保証する場合は、次のオプションがあります。

  1. 同じプラットフォーム/スタックに固執する(@haraldkによって提案されているように)-Linuxプラットフォームでの開発/実行に固執し、すべてが同じlibjpegバージョン
  2. Javaコードを他のアプリが使用しているのと同じバージョンにバインドします-すなわち:load libjpeg.9.dylibそしてそれをあなたのJavaアプリから使用してください。しかし、あなたがそれをどのように行うかは100%わかりません。
  3. 正しいものを使用するようにJDKを再コンパイルします- この回答 によって参照されるオプションは、openjdkを使用し、目的に対してコンパイルすることですより達成可能に聞こえるlibjpegのバージョン。

オプション2と3は、オプション1の非常に難しいバージョンであることを認めます。

注:
@ haraldkの結論はほとんど同じなので、私は間違いなく賛成票を投じています。

また、さまざまなiccプロファイルを使用して遊んだところ、まったく異なる答えが得られました。したがって、それを警戒する価値があります。

Libjpegの実装にさらに重点を置いた回答を追加したかったのは、それが特定のインスタンスであなたを捕らえていると信じているからです。

更新

実際、@ haraldkの回答には、CMMとLittleCMSの違いというもう1つの大きな違いがあります。彼が言うように:JavaはLittleCMSを使用します、 これはUbuntuでも使用されます

私は実際、それがここでの答えである可能性が高いと思います。

8
ndtreviv

色の一貫性とICCプロファイル

Javaは、画像をアップロードするときにカラープロファイルを尊重しません。また、OSが異なればRGBカラーの処理も異なります。

Oracleが_import Java.awt.color_について書いていることは次のとおりです。

通常、ColorまたはColorModelは、入力、表示、または出力プロファイルのいずれかであるICCプロファイルに関連付けられます。他のタイプのICCプロファイルがあります。抽象プロファイル、デバイスリンクプロファイル、および名前付きカラープロファイル。これらには、色、画像、またはデバイスの色空間を表すのに適切な情報が含まれていません。不適切なICCプロファイルからICC_ColorSpaceオブジェクトを作成しようとするとエラーになります。

ICCプロファイルは、プロファイルの色空間(モニターなど)からプロファイル接続空間(PCS)への変換を表します。画像または色にタグを付けるための対象のプロファイルには、ICCプロファイル形式仕様で定義されているデバイスに依存しないスペース(1つのCIEXYZスペースと2つのCIELabスペース)の1つであるPCSがあります。対象となるほとんどのプロファイルには、可逆変換があるか、双方向に進む変換を明示的に指定します。 PCSからプロファイルのネイティブスペースへの変換を必要とする方法でICC_ColorSpaceオブジェクトが使用され、変換を正しく実行するためのデータが不十分な場合、ICC_ColorSpaceオブジェクトは、指定されたタイプの色空間(TYPE_RGB、TYPE_CMYKなど)で出力を生成します。 。)ただし、出力データの特定のカラー値は未定義になります。

_ICC_ColorSpace_クラスの詳細は、デフォルトの色空間で描画したり、既知の色空間でインポートされた画像を操作および表示したりする単純なアプレットでは重要ではありません。せいぜい、そのようなアプレットはColorSpace.getInstance()を介してデフォルトの色空間の1つを取得する必要があります。 (docs.Oracle.comからの抜粋)https://docs.Oracle.com/javase/7/docs/api/Java/awt/color/ICC_ColorSpace .html

Javaでの色空間変換

色空間変換は、画像の読み取りと書き込みの両方の宛先タイプによって制御されます。ラスターが読み取られると、色空間変換は実行されず、宛先タイプはすべて無視されます。この場合、宛先タイプが指定されていると、すべてのリスナーに警告が送信されます。ラスターが書き込まれると、任意の宛先タイプがバンドの解釈に使用されます。これにより、JFIFまたはAdobeヘッダーが書き込まれたり、フレームおよびスキャンヘッダーに異なるコンポーネントIDが書き込まれたりする可能性があります。メタデータオブジェクトに存在する値が宛先タイプと一致しない場合、宛先タイプが使用され、すべてのリスナーに警告が送信されます。 (docs.Oracle.comからの抜粋)https://docs.Oracle.com/javase/7/docs/api/javax/imageio/metadata/doc -files/jpeg_metadata.html

便利なリンク

RGB変換に触れる情報を見てください。 Rolf W.Rasmussenによる正規化されたfloat/intカラーコンポーネントにはいくつかの問題があります。

http://opensource.Apple.com/source/gcc3/gcc3-1041/libjava/Java/awt/image/ColorModel.Java

_The Sad Story of PNG Gamma “Correction”_を読んでください(問題は、JPEGとTIFFが同じ「病気」に苦しんでいることです)。

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

S.O.を見てください役職。あなたのための可能な解決策があります:

In Java画像をsRGBに変換すると、画像が明るすぎます

すべての試みを行っても色に一貫性がない場合は、画像をsRGBプロファイルに変換してみてください。ただし、埋め込みはしないでください。

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

また、ケニー・ハントの本がお役に立てば幸いです

enter image description here

...そして次のコード(www.physicsforums.comで公開)を使用すると、さまざまな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); 
    } 
}
_

要約

色の不一致の問題は、カラープロファイルに起因します。手動(Photoshopの場合)またはプログラム(Javaの場合)にかかわらず、すべての画像に均一なカラープロファイルを割り当てるようにしてください。

6
ARGeo

ここ を参照してください:WARNING: Color space tagged as sRGB, without an embedded color profile. Windows and Mac browsers and apps treat the colors randomly.

編集:丸め誤差とバージョンごとの実装の差異に関する限り。この画像には当てはまりません。 Macには、カラーマッチングカーブで青と緑を明るくする魔法があります。色空間を修正すると、カラーマッチングで同じ結果が得られます。 Andy Fedoroffの回答に賛成票を投じましたが、実際に解決策を提供した人がいないことにも気づきました... Javaが正しいという結論に達しました。それで行きなさい。 Libjpegは長い間変わっていません。安定しており、多くのプラットフォームや環境で確実に色を再現します。古くなった標準jpegのデコードに(何らかの形で)重要な変更は加えられていません。

編集2:プロジェクトに基づいてMacプロファイルと同じ値を生成する例を作成しようとしています。 Library/ColorSync/ProfilesのMacのファクトリICCプロファイルが必要です

これが私がいるところです。これは、sRGB ICCv4プロファイルが適用された例です。これは技術的にはsRGBよりもsRGBを適用していますが、概念を説明しています。

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};
}
画像IO 1:[0、0] = [145、146、164] 
画像IO 2:[0、0] = [ 145、147、165] 
画像IO 1:[1、0] = [137、138、156] 
画像IO 2:[ 1、0] = [137、139、157] 
画像IO 1:[2、0] = [148、147、161] 
画像IO 2:[2、0] = [148、148、162] 
画像IO 1:[0、1] = [150、153、168] 
画像IO 2:[0、1] = [150、154、169] 
画像IO 1:[1、1] = [138、141、 156] 
画像IO 2:[1、1] = [138、142、157] 
画像IO 1:[2、1] = [145、147、159] 
画像IO 2:[2、1] = [145、148、160] 
画像IO 1 :[0、2] = [154、160、172] 
画像IO 2:[0、2] = [154、161、173] 
画像IO 1:[1、2] = [146、152、164] 
画像IO 2:[1、2] = [146、153、165] 
画像IO 1:[2、2] = [144、148、157] 
画像IO 2:[2、2] = [144、 149、158] 

カラープロファイルをimageio-testリポジトリにコミットできますか?

4
TylerY86