Javaに関する様々な情報をご紹介します。

Javaに関する様々な情報をご紹介します。
評価

0

JPEGの書き出し

JPEG画像を読み込んで別ファイルに出力するという操作を行いたくて、以下のように書きました。

//画像情報の取得
try {        
        FileInputStream fis = new FileInputStream(fileName);
        JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(fis);
        image = decoder.decodeAsBufferedImage();
        fis.close();
    } catch (IOException e) {
        System.err.println("Can't open the file.");
    }

//** 中略 **//

//画像情報の出力
try{
        FileOutputStream fos = new FileOutputStream(fileName);
        JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(fos);

        JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(image);
        param.setQuality((float)1.0, false);
        encoder.setJPEGEncodeParam(param);    

        encoder.encode(image);
        fos.close();
    }
    catch (IOException e){
        System.out.println("Can't save the new file.");
    }


これで、実際に入出力はできるのですが、圧縮されるのかオリジナルとは違うものが出力されてしまいます。
コピーではなく、画素値の変更が一切ないように出力したい場合はどうしたらいいでしょうか?

10

回答

79603

閲覧

10件の回答

評価

0

JPEGは非可逆圧縮圧縮を行うため元のデータと全く同じにはなりません。
setQualityで最高品質の1.0を指定した場合でもイメージが劣化する旨ドキュメントに記載されています。

元データと同じ物を作りたければ、単にファイルのコピーでいいと思います。

評価

0

誤:非可逆圧縮圧縮
正:非可逆圧縮

評価

0

まず、そのプログラムで1度出力したものを読み込んでみた場合も異なるものが出力されますか?
(中略の部分に何があるのかも判らないし、コードは妥当なものだとして挙げられた部分も検証とかしていませんが)

挙げられたコードの範囲でも変わることになる可能性があるところは
>param.setQuality((float)1.0, false);
だろうと思います。

因みにJPEG画像って、情報部(マーカーと呼んでた筈)とイメージ部があって、イメージ部が展開(解凍)されてビットマップデータ(画像)になり、表示されたりします。
そのとき、APIによって、環境に合わせて調整される・・ということもあるかもしれませんね。(実際には生のまま、描画の際に調整されたりだろうと思うけど)

ファイルに書き出すとき、詳しい流れは忘れたけど、そのビットマップ画像は量子化され、ハフマンとかで符号化、圧縮されます。(今は改良されたりして違ってきているかもだけど)
かつて算術なんとか(可逆)とかもあったけど、ライセンスの関係で一般には採用されていないし、通常非可逆を前提としているので、オリジナルと同じでなくてもよいというのがあるし、変わっているという範囲が何処までなのかも微妙に判らなかったりですけど、情報部(マーカー)も(処理できれば)オリジナルと異なっていてもいいし、オリジナルとは異なるものが入っていたりもするでしょう。
後者は同じプログラムでサイド出力すれば付加情報とか変化する場合のものを除いて、一致するものと思いますけど、そもそも厳密な一致を求めるものではないのではないかと思います。

厳密な一致を求めたいなら、たぶん別の手段を考えたりするのが妥当ではないかと。

評価

0

>tamaさん
画素値を一部だけ変更してそれ以外は変化なしで出力したかったので。

>まあさん
いずれ画素値を変更するつもりで作っていますが、とりあえずということで上記のプログラムは読み込んで吐き出すだけのものです。
1度出力したものを読み込むと、先と違う画素値が得られるので困っていました。


JPEGだと非可逆は無理なようですね。。
違うファイル形式の使用も検討してみます。
ありがとうございました。

評価

0

>違うファイル形式の使用も検討してみます。

違うものと言っても、ビットマップくらいしかなかったと思うけど・・。
例えば、ビットマップはデカイというところで、ビットマップで圧縮を持ってるものというのが、どうなっているのかは、あるらしいということしか知らないので、どんなのかは判りませんし、
Javaですぐに扱えるように予め用意されているものは?というのもありそうですけど。

評価

0

pngで良いでしょう。

評価

0

ああ、あと

JPEGやGIFで出力したときに、どうなっているかはシビアに考えても仕方ないことで、出力し直した(再生成)場合に変化する・・それは劣化という形で現れる・・というのは画像を扱っている人には承知のもので、扱ったことはないけどPNGでも同様でしょう。

そこの辺りは、単に"先と違う画素値が得られる"とかいうことより、気にすることになるかもですね。

評価

0

>コピーではなく、画素値の変更が一切ないように
見落としてました^^;

>違うファイル形式の使用も検討してみます。
は色々ありますよ、TIFFやJPEG2000やLossless JPEGなど。
256階調でよければGIFも可逆圧縮ですね。

画素の値を厳密に見る画像処理や医療画像ではJPEGは使われませんね、レポート用の保存などで使われるくらいです。
どうしてもJPEGを使いたい場合は何回保存したかや、その時のクオリティ値を別途保存したりします。

評価

0

>医療画像ではJPEGは使われませんね

それは以前携わった気がする。。
でも、まだ世の中が画像に対してあまり詳しくない頃だったと思うけど。

評価

0

一応書いておくと、pngも可逆です。
ファイルとしては別のものができるけど、画素レベルでは同じものになります。
Webで使う場合、まだブラウザの対応が完全ではない、という面はありますが。

質問から6ヶ月以上経過しているので、回答を書き込むことはできません。