こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

締切り済みの質問

8bit透過PNGを書き出すと劣化?する

最後にソースを記載しますが、やりたいことは
8bitの透過PNG画像を複数枚読み込み、重ねた1枚の画像を出力する、ということなのですが、どうにも画質が劣化?してしまって解決法が見出せずにいます。

以下のソースで読み込んでいるPNG画像は8bitの透過PNG画像で、BufferedImage.TYPE_BYTE_INDEXEDのBufferedImageに書きだして保存しているだけ(のつもり)です。

それだけで読み込んだもの、書きだしたものの品質が大きく違ってしまっています。

Javaで画像を扱ったことがほとんど無いので、そもそもTYPE_BYTE_INDEXEDを使うとそういう結果になるのは仕方のないことなのか、というのもわかりません。
ちなみにTYPE_4BYTE_ABGRを使うと劣化なく出力されますが、32bitなので。。。

どうにか読んだ8bit透過PNG画像をそのままの品質で書きだす方法はないものでしょうか?

アドバイスをお願いいたします。。

※添付画像は左側が下記ソース中のsrc.pngにあたるもの、右側がdest.pngにあたるものです。
画像添付がjpegしかできなかったので透過していませんが、円形以外の白色部分は実際には透過しています。

-以下ソースです--------------------
public class ImageTest {

public static void main() {
BufferedImage pileupImg = null;
try {
pileupImg = ImageIO.read(new File("C:/src.png"));
} catch (IOException e) {
return;
}

PixelGrabber pg = new PixelGrabber(pileupImg, 0, 0, -1, -1, true);
try {
pg.grabPixels();
} catch (InterruptedException e){}

Image img = Toolkit.getDefaultToolkit().createImage(new MemoryImageSource(256, 256, (int[])pg.getPixels(), 0, 256));
BufferedImage bi = new BufferedImage(256, 256, BufferedImage.TYPE_BYTE_INDEXED, (IndexColorModel)pileupImg.getColorModel());
Graphics g = bi.getGraphics();
g.drawImage(img, 0, 0, null);
try {
ImageIO.write(bi, "png", new File("C:/dest.png"));
} catch (IOException e) {}

return;
}
}

投稿日時 - 2011-03-13 06:13:25

QNo.6589485

困ってます

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(1)

ANo.1

TYPE_BYTE_INDEXED というのどういう画像フォーマットだか、ご存じですね?
念の為に書いておくと、各画素を0から255までの色番号で表現し、各色番号がどんな色になるかを別途パレットで指定するものです。

その構成上、パレットに無い色は出せません。
使用されている色が256色までだったら、元の画像を再現できます。
それを越えると、表現できない色が出てきます。パレット中の近い色に置き換える、とか、タイルパターンやディザを使う、といった減色処理が必要になります。減色処理が悪いと、著しく劣化したように見えます。

添付画像を見た印象では、この減色処理の結果ではないか、と思われます。


インデックスカラー同士の合成、というのはいろいろと面倒です。
・同じパレットでなければ、色番号の変更だけでは処理できず、一旦色にして、該当する合成先の色番号に変換する必要があります
・アルファチャンネルがあるとさらに複雑です。合成の結果、パレットに無い色が生れる可能性があります。

汎用にやろうとしたら「一旦フルカラーで処理→パレットにあうように減色」とするのが確実でしょう。drawImageの実装は調べてませんが、Graphicsが汎用的に使われるクラスなので、おそらくこのような汎用的な処理をしているものと思われます。


このプログラムで言えば、途中の処理に使うbiはTYPE_4BYTE_ABGRにして、書き出す直前に256色に減色→書き出しというのが常套手段と思われます。
Javaでの減色方法は面倒なので調べてません。

投稿日時 - 2011-03-13 11:27:55

補足

アドバイスありがとうございます。

TYPE_BYTE_INDEXEDがカラーパレットを使用して描画するというのは認識しております。

> ・同じパレットでなければ、色番号の変更だけでは処理できず、一旦色にして、該当する合成先の色番号に変換する必要があります

ここはBufferedImageのコンストラクタ引数の4番目に元画像(8bitインデックスカラー画像)のカラーモデルを指定していることで元画像のカラーパレットをそのまま使用できると思っています。


> ・アルファチャンネルがあるとさらに複雑です。合成の結果、パレットに無い色が生れる可能性があります。

このあたりがあやしいかも。。。
アルファチャンネルありますし。

> このプログラムで言えば、途中の処理に使うbiはTYPE_4BYTE_ABGRにして、書き出す直前に256色に減色→書き出しというのが常套手段と思われます。

ここで仰られている「256色の減色→書き出し」は当方では「BufferedImageをTYPE_BYTE_INDEXEDで扱う」の認識でいるのですが、別の方法があるのでしょうか。。
結局途中をフルカラー(TYPE_4BYTE_ABGR)にしても最終的な書き出し方法が
1.TYPE_BYTE_INDEXEDのBufferedImage生成
2.1のBufferedImageからGraphics取得
3.GraphicsにdrawImage
4.保存
を経ると最初の添付画像の通りになってしまうのです。


元画像は透過色、アウトラインの黒(0,0,0)、円形塗りつぶしの灰色(240,240,240)しか使っていないのに、同じカラーパレット(getColorModel)を引数に与えたBufferedImageからの一連の処理で逆に使ってなかった色が誕生するのが???です。。
このあたり、アドバイスいただいたようにアルファチャンネルとの絡みがあるのかもしれませんが。

減色も合成も何もしていないので、単純に読んだファイルをそのまま書き出せれば良いだけなイメージでいたのですが、簡単にはいきませんね。。

投稿日時 - 2011-03-14 03:38:50

あなたにオススメの質問