Javaを使用してgif画像をjpegに変換したいのですが。ほとんどの画像でうまく機能しますが、単純な透明なgif画像があります。
入力gif画像http://img292.imageshack.us/img292/2103/indexedtestal7.gif
[画像が欠落している場合:それは周囲に透明なピクセルがある青い円です]
次のコードを使用してこの画像を変換すると:
File file = new File("indexed_test.gif");
BufferedImage image = ImageIO.read(file);
File f = new File("indexed_test.jpg");
ImageIO.write(image, "jpg", f);
このコードは例外をスローせずに機能しますが、無効なjpeg画像になります。
[画像が欠落している場合:IE jpegを表示できない場合、Firefoxは無効な色で画像を表示します。]
Java 1.5を使用しています。
また、サンプルgifをgimpを使用してpngに変換し、そのpngをJavaコードの入力として使用してみました。結果は同じです。
JDKのバグですか?サードパーティのライブラリなしで、できれば画像を正しく変換するにはどうすればよいですか?
更新:
回答は、jpeg変換が透明度を正しく処理できないことを示し(これはまだバグだと思います)、透明なピクセルを事前定義された色に置き換えるための回避策を提案します。提案された方法は両方とも非常に複雑なので、より単純な方法を実装しました(回答として投稿します)。この回避策(Markusによる)で最初に公開された回答を受け入れます。どちらの実装が良いかわかりません。私は最も単純なものを選びますが、それでも機能しないgifを見つけました。
Java 6(そして5もそうだと思います)の場合:
BufferedImage bufferedImage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_RGB);
g = bufferedImage.createGraphics();
//Color.WHITE estes the background to white. You can use any other color
g.drawImage(image, 0, 0, bufferedImage.getWidth(), bufferedImage.getHeight(), Color.WHITE, null);
質問の更新ですでに述べたように、透明なピクセルを事前定義された色に置き換える簡単な方法を実装しました。
public static BufferedImage fillTransparentPixels( BufferedImage image,
Color fillColor ) {
int w = image.getWidth();
int h = image.getHeight();
BufferedImage image2 = new BufferedImage(w, h,
BufferedImage.TYPE_INT_RGB);
Graphics2D g = image2.createGraphics();
g.setColor(fillColor);
g.fillRect(0,0,w,h);
g.drawRenderedImage(image, null);
g.dispose();
return image2;
}
そして私はこのようにjpeg変換の前にこのメソッドを呼び出します:
if( inputImage.getColorModel().getTransparency() != Transparency.OPAQUE) {
inputImage = fillTransparentPixels(inputImage, Color.WHITE);
}
3か月遅れましたが、非常によく似た問題が発生しています(gifをロードすることすらできませんが、単に透明な画像を生成します-たとえば、背景なし、色付きの形状-jpegに保存すると、すべての色が台無しになるだけでなく、背景)
Java2d-interest listのこのかなり古いスレッド でこのコードを見つけました。簡単なテストの後、muchであるため、共有したいと思いました。 )ソリューションよりもパフォーマンスが高い:
final WritableRaster raster = img.getRaster();
final WritableRaster newRaster = raster.createWritableChild(0, 0, img.getWidth(), img.getHeight(), 0, 0, new int[]{0, 1, 2});
// create a ColorModel that represents the one of the ARGB except the alpha channel
final DirectColorModel cm = (DirectColorModel) img.getColorModel();
final DirectColorModel newCM = new DirectColorModel(cm.getPixelSize(), cm.getRedMask(), cm.getGreenMask(), cm.getBlueMask());
// now create the new buffer that we'll use to write the image
return new BufferedImage(newCM, newRaster, false, null);
残念ながら、私はそれが何をするのか正確に理解しているとは言えません;)
問題は(少なくともpngからjpgへの変換では)、jpgは透明度をサポートしていないため、配色が同じではないことです。
私たちが成功したのは、これらの線に沿ったものです(これは、コードのさまざまなビットから取得されます。したがって、フォーマットの粗雑さはご容赦ください)。
File file = new File("indexed_test.gif");
BufferedImage image = ImageIO.read(file);
int width = image.getWidth();
int height = image.getHeight();
BufferedImage jpgImage;
//you can probably do this without the headless check if you just use the first block
if (GraphicsEnvironment.isHeadless()) {
if (image.getType() == BufferedImage.TYPE_CUSTOM) {
//coerce it to TYPE_INT_ARGB and cross fingers -- PNGs give a TYPE_CUSTOM and that doesn't work with
//trying to create a new BufferedImage
jpgImage = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
} else {
jpgImage = new BufferedImage(width, height, image.getType());
}
} else {
jgpImage = GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().getDefaultConfiguration().
createCompatibleImage(width, height, image.getTransparency());
}
//copy the original to the new image
Graphics2D g2 = null;
try {
g2 = jpg.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2.drawImage(image, 0, 0, width, height, null);
}
finally {
if (g2 != null) {
g2.dispose();
}
}
File f = new File("indexed_test.jpg");
ImageIO.write(jpgImage, "jpg", f);
これは、pngからjpgおよびgifからjpgで機能します。そして、透明なビットがあった場所に白い背景が表示されます。これを変更するには、drawImageを呼び出す前にg2で画像を別の色で塗りつぶします。
タイプBufferedImage.TYPE_INT_ARGBのBufferedImageを作成し、JPEGに保存すると、奇妙な結果になります。私の場合、色はオレンジ色に削られています。また、生成された画像が無効であり、他のリーダーが画像の読み込みを拒否する場合もあります。
ただし、タイプBufferedImage.TYPE_INT_RGBの画像を作成する場合は、それをJPEGに保存すると問題なく機能します。
したがって、これはJava JPEGイメージライターのバグだと思います-透明性なしでできることだけを書き込む必要があります(.NET GDI +のように)。または最悪の場合、例外をスローします。 「透明度のある画像は書き込めません」などの意味のあるメッセージ。
JPEGは透明度をサポートしていません。したがって、円の色を正しく取得した場合でも、エンコーダーやレンダラーに応じて、背景は黒または白になります。
BufferedImage originalImage = ImageIO.read(getContent());
BufferedImage newImage = new BufferedImage(originalImage.getWidth(), originalImage.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
for (int x = 0; x < originalImage.getWidth(); x++) {
for (int y = 0; y < originalImage.getHeight(); y++) {
newImage.setRGB(x, y, originalImage.getRGB(x, y));
}
}