• 締切済み
  • 困ってます

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

共感・応援の気持ちを伝えよう!

  • 回答数1
  • 閲覧数1377
  • ありがとう数0

みんなの回答

  • 回答No.1
  • kmee
  • ベストアンサー率55% (1857/3366)

TYPE_BYTE_INDEXED というのどういう画像フォーマットだか、ご存じですね? 念の為に書いておくと、各画素を0から255までの色番号で表現し、各色番号がどんな色になるかを別途パレットで指定するものです。 その構成上、パレットに無い色は出せません。 使用されている色が256色までだったら、元の画像を再現できます。 それを越えると、表現できない色が出てきます。パレット中の近い色に置き換える、とか、タイルパターンやディザを使う、といった減色処理が必要になります。減色処理が悪いと、著しく劣化したように見えます。 添付画像を見た印象では、この減色処理の結果ではないか、と思われます。 インデックスカラー同士の合成、というのはいろいろと面倒です。 ・同じパレットでなければ、色番号の変更だけでは処理できず、一旦色にして、該当する合成先の色番号に変換する必要があります ・アルファチャンネルがあるとさらに複雑です。合成の結果、パレットに無い色が生れる可能性があります。 汎用にやろうとしたら「一旦フルカラーで処理→パレットにあうように減色」とするのが確実でしょう。drawImageの実装は調べてませんが、Graphicsが汎用的に使われるクラスなので、おそらくこのような汎用的な処理をしているものと思われます。 このプログラムで言えば、途中の処理に使うbiはTYPE_4BYTE_ABGRにして、書き出す直前に256色に減色→書き出しというのが常套手段と思われます。 Javaでの減色方法は面倒なので調べてません。

共感・感謝の気持ちを伝えよう!

質問者からの補足

アドバイスありがとうございます。 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からの一連の処理で逆に使ってなかった色が誕生するのが???です。。 このあたり、アドバイスいただいたようにアルファチャンネルとの絡みがあるのかもしれませんが。 減色も合成も何もしていないので、単純に読んだファイルをそのまま書き出せれば良いだけなイメージでいたのですが、簡単にはいきませんね。。

関連するQ&A

  • byte[]からBufferedImageを生成

    初めまして、よろしくお願いします。 画像データがbyte[]に格納されています。 この画像データ(byte[])からBufferedImageを生成したいのですが どうしたらよろしいでしょうか? byte[]、BufferedImageで検索してみましたが、 ずばりこれというものは見つかりませんでした。 よろしくお願いします。

    • ベストアンサー
    • Java
  • BufferedImageをbyte[]に変換ってできますか?

    画像データを保持しているBufferedImageオブジェクト から、その内容をbyte[]で取得する方法はあるので しょうか? また、Image⇒byte[]も可能でしょうか?

  • BufferedImageへの変換エラーです

     こんにちは。java初心者です、宜しくお願いします。  BufferedImageに変換したいんですが、引数の取り方が分かりません。  "srcimage1"を"BufferedImage" 、"Image"で取ってやっても"BufferedImageにキャスト出来ま せん"という意味のエラー表示が出るばかりでお手上げです。    どなたか詳しい方、エラーの原因についてどうか宜しくお願いします。 ========================================= public class BufferedImage1 extends Applet { static BufferedImage srcimage1 , newimg ; static Image srcimage0 ; //static BufferedImage srcimage0 ; static BufferedImage source ; static BufferedImage target ; static Graphics g ; static Graphics2D g2 ; static Image img ; public void init() { this.setLocation( 0 , 0 ) ; setBackground( Color.red ) ; g2 = (Graphics2D)g ; srcimage0 = getImage( getDocumentBase() , "a.gif" ) ; srcimage1 = ( BufferedImage )srcimage0 ; newimg = change(srcimage1 , new BufferedImage(100 , 100 , BufferedImage.TYPE_4BYTE_ABGR_PRE)) ; } public void paint(Graphics g) { g2.drawImage(newimg , 0 , 0 , null ) ; } public BufferedImage change( BufferedImage source , BufferedImage target) { this.source = source ; this.target = target ; Graphics2D g2 = target.createGraphics() ; g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION , RenderingHints.VALUE_INTERPOLATION_BICUBIC) ; double scalex = (double) target.getWidth() / source.getWidth() ; double scaley = (double) target.getHeight() / source.getHeight() ; AffineTransform xform = AffineTransform.getScaleInstance(scalex , scaley) ; g2.drawRenderedImage(source , xform) ; g2.dispose() ; return target ; } }

    • ベストアンサー
    • Java
  • drawImageメソッドの使い方

    import com.sun.image.codec.jpeg.JPEGImageDecoder; import com.sun.image.codec.jpeg.JPEGImageEncoder; import com.sun.image.codec.jpeg.JPEGCodec; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.awt.image.BufferedImage; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Shape; import java.awt.geom.*; public class EdgeDetection extends Object { public static void main(String[] args) { BufferedImage in_bi = null; BufferedImage out_bi = new BufferedImage(134, 181, 1); Graphics2D outg2 = out_bi.createGraphics(); Shape s = new Line2D.Float(10.0f, 50.0f, 90.0f, 150.0f); outg2.drawImage(s, 0, 0, 0); JPEGImageEncoder ie = null; try { ie = JPEGCodec.createJPEGEncoder(new FileOutputStream(args[0])); } catch(FileNotFoundException e) { System.err.println("ファイルが見つかりません [write]"); System.err.println("Edge_" + args[0]); System.exit(253); } try { ie.encode(out_bi); } catch(IOException e) { System.err.println("書き込みに失敗しました"); System.exit(252); } } } ------------------------------------------------------------- このようなソースを作成してコンパイルを行ったのですが、35行目の drawImageでエラーが発生します。見直したところ drawImageの引数に間違いがあるとも思えないのですが、 他に何かエラーになるような個所があるのでしょうか? 開発キットはJDK1.3です。宜しくお願いします。

    • ベストアンサー
    • Java
  • 任意の文字列をJAVAで画像化

    表題にある通り任意の文字列をJAVAで画像化したいと思い 下記サンプルを検索して見つけたのですが作成される画像は 真っ白な画像で文字列は表示されませんでした。 任意の文字列を画像として出力するにはどうすればよいでしょうか。 import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.File; import javax.imageio.ImageIO; public class Test5 { public static void main(String[] args) { String str = "abc123"; new Test5().Create(str); System.out.println(str); } public void Create(String str) { int w=60; int h=17; try { //受け取った文字列を画像化 BufferedImage image=new BufferedImage(w,h,BufferedImage.TYPE_INT_RGB); Graphics2D g2d=image.createGraphics(); g2d.setBackground(Color.WHITE); g2d.clearRect(0,0,w,h); g2d.setColor(Color.BLACK); g2d.drawString(str,0,0); ImageIO.write(image, "JPEG", new File("c:\\test.jpg")); } catch(Exception e) { e.printStackTrace(); } } }

    • ベストアンサー
    • Java
  • 画像の縮小のプログラムのエラーについて教えて下さい

     java初心者です、宜しくお願いします。  画像の縮小のプログラムを色々と他のサンプルコードとかを参考にしながら 書きましたが、黒い四角形がWindowに表示されるだけです。  プログラム自体のエラーは表示されません。  一体どこが悪いのでしょうか、宜しくお願いします。 ===================================================== public class reSizedIImage extends Applet { Image img ; private MediaTracker tracker; BufferedImage bufimg ; BufferedImage re_img ; public void init() { setBackground( Color.white ) ; img = Toolkit.getDefaultToolkit().getImage( "a.jpg" ) ; tracker = new MediaTracker( this ) ; tracker.addImage( img , 1 ) ; try { // tracker.waitForID( 1 ) ; } catch ( Exception ex ) { ex.printStackTrace() ; } int imgw = img.getWidth( this ) ; int imgh = img.getHeight( this ) ; System.out.println(" imgw = " + imgw + " imgh = " + imgh ) ; int re_width = 300 ; int re_height = 300 ; bufimg = new BufferedImage( img.getWidth( this ) , img.getHeight( this ) , BufferedImage.TYPE_INT_RGB ) ; re_img = new BufferedImage( re_width , re_height , BufferedImage.TYPE_INT_RGB ) ; AffineTransformOp ato = new AffineTransformOp ( AffineTransform.getScaleInstance ( ( double )re_width / img.getWidth( this ) , ( double ) re_height / img.getHeight( this ) ) , null ) ; ato.filter( bufimg , re_img ) ; int re_w = re_img.getWidth(this); int re_h = re_img.getHeight(this); System.out.println(" re_img_w = " + re_w + " re_img_h = " + re_h ) ; } public void paint( Graphics g ) { g.drawImage( re_img , 200 , 200 , this ) ; } }

    • ベストアンサー
    • Java
  • Fireworksで8bitの透過PNGの書出し

    Fireworksで、8bitの透過PNGの書き出しをしたいのですが、 透過させたくない白い部分まで透過してしまい、うまくいきません。 かなりのFireworks初心者で、きれいに書き出す方法をネットで調べてみたのですが、 分からず困っております。 作業詳細 ---------------- 最終的に欲しいデータ : Fireworks形式のPNG(8bit) 作業環境:Windows XP、Fireworks CS3、Photoshop CS3 作業手順: (1)Photoshopにて複数の画像を作成  ドロップシャドウなどを適用している透過画像もあるので、  「Webおよびデバイス用に保存」でPNG-24で書き出します。 (2)作成した画像をFireworksで、ある決まったテンプレート(PNG)にレイアウトします。 (3)書き出し設定  「最適化」WindowにてPNG 8 を選択。    (3)「書き出し」からスライスの書き出しを行います。 ---------------- すると、PNG32では問題なく書き出せるのですが、 PNG 8 の場合は、透過させたくない白い部分まで色が抜けて透過してしまいました。 試しにPNG24で書き出そうとしたところ、今度は透過させたい部分まで透過されなくなり、 スライスの範囲通りに四角い形になってしまいました。 PhotoshopのPNG8ではドロップシャドウの部分などがきれいに透過されないことと、 最終的にFireworkのテンプレートも必要なので、 Photoshopだけで作業は完結できません。 説明が下手で恐縮ですが、 8bitの透過PNG(Fireworks)の書き出し方をお教えいただけますでしょうか。 よろしくお願いいたします。

  • 画像の重ね合わせがうまくできません・・

    はじめまして。 画像を重ね合わせるプログラムを作成したいのですが、 期待通りにならないため悩んでおります。 Aという画像を下地に、Bという画像の特定RGBのドットを透過させて合成した画像を作りたいのですが、 どうもうまくいかず、半透明な絵ができてしまいます。 (Aがうっすらと見え、Bが重なるかんじ) 合成部分のコーディングは以下です。 AlphaCompositeの使い方が間違っているのか、AlphaCompositeではなく 別のクラスを使うのか、そもそも APIではできなくてドットを一個一個書い ていく必要があるのか・・? ご存じの方がいましたら、なにとぞご教授、お願いいたします。 public BufferedImage mergeTwice(BufferedImage baseImage, BufferedImage layImage) { // BufferedImage を Graphic2D に変換 Graphics2D baseGra = baseImage.createGraphics(); Graphics2D layGra = layImage.createGraphics(); // 2つの画像を合成 AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f); baseGra.setComposite( composite ); baseGra.drawImage(layImage, 0, 0, null); AlphaComposite composite2 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f); return baseImage; }

  • イメージのリサイズが表示されない

    JAVA初心者です、宜しくお願い致します。 イメージをリサイズするコードを書きましたが、うまく表示されません。 コードにはエラーは出ていません、原因が分かりません。 ご教示宜しく致します。 ====================================================================== public class reSizedIImage extends Applet { Image img ; private MediaTracker tracker; BufferedImage bufimg ; BufferedImage re_img ; public void init() { setBackground( Color.white ) ; img = Toolkit.getDefaultToolkit().getImage( "a.jpg" ) ; tracker = new MediaTracker( this ) ; tracker.addImage( img , 1 ) ; try { //イメージのロードが完了するまで待機 tracker.waitForID( 1 ) ; } catch ( Exception ex ) { ex.printStackTrace() ; } int imgw = img.getWidth( this ) ; int imgh = img.getHeight( this ) ; System.out.println(" imgw = " + imgw + " imgh = " + imgh ) ; int re_width = 300 ; int re_height = 300 ; bufimg = new BufferedImage( img.getWidth( this ) , img.getHeight( this ) , BufferedImage.TYPE_INT_RGB ) ; re_img = new BufferedImage( re_width , re_height , BufferedImage.TYPE_INT_RGB ) ; AffineTransformOp ato = new AffineTransformOp ( AffineTransform.getScaleInstance ( ( double )re_width / img.getWidth( this ) , ( double )re_height / img.getHeight( this ) ) , null ) ; ato.filter( bufimg , re_img ) ; int re_w = re_img.getWidth(this); int re_h = re_img.getHeight(this); System.out.println(" re_img_w = " + re_w + " re_img_h = " + re_h ) ; } public void paint( Graphics g ) { g.drawImage( re_img , 200 , 200 , this ) ; } }

    • ベストアンサー
    • Java
  • Javaでの画面コピーの取得方法

    Javaで画面コピーを取得する方法が分かりません。 ロボットで画面コピーをクリップボードにコピーして、クリップボードから画像を取得する方法は実現できました。 Robot robot = new Robot(); robot.keyPress(KeyEvent.VK_PRINTSCREEN); robot.keyRelease(KeyEvent.VK_PRINTSCREEN); BufferedImage image = (BufferedImage)clipboard.getContents(null);//画像データ クリップボードを経由しないで画面コピーを取得する方法はありますか?

    • ベストアンサー
    • Java