画像を読み取り、変換(サイズ、フォーマット)して、書き戻すメソッドがあります。これは常に非常にうまく機能しましたが、明らかにいくつかのメタデータ(IPTC)を含むいくつかのJPEG画像(通信社から)に出くわしました。それらの画像を変換するとき、色はすべて間違っています。私の最初の推測は、それらはCMYK画像ですが、そうではないということでした。
画像を小さいJPEGに変換するかPNGに変換するかは関係なく、常に同じように見えるため、問題は読み取りに起因する必要があります。
最初は、ImageIO.read()
を使用して画像を読み取りました。 ImageIO.getImageReadersByMIMEType()
を介して実際のImageReader
を取得し、ImageReader#setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata)
のignoreMetadata
パラメーターを設定してメタデータを無視するようにリーダーに指示しようとしましたが、成功。
次に、メタデータなしのバージョンの画像を作成しました(Fireworksを使用)。その画像は正しく変換されます。
私が見つけた唯一の違いは、機能していない画像では、リーダーの変数colorSpaceCode
の値が2であるということです。作業イメージの場合、値は3です。両方の画像に2であるoutColorSpaceCode
もあります。
読者のソースコメント はsetImageDataネイティブコードコールバックによって設定されるだけです。変更されたIJG + NIFTY色空間コード私は今本当に立ち往生しています。だからどんな助けでも大歓迎です。
here に移動し、[ダウンロード]をクリックすると、元の画像(〜3 MB)を取得できます。下の左の画像は私が元の画像から得たものを示し、右はそれがどのように見えるべきかを示しています。
少なくとも結果の画像がJPEGでもある場合は、今すぐ解決策を見つけました。最初に(バイト配列imageDataから)画像を読み取り、最も重要なのはメタデータも読み取ります。
InputStream is = new BufferedInputStream(new ByteArrayInputStream(imageData));
Image src = null;
Iterator<ImageReader> it = ImageIO.getImageReadersByMIMEType("image/jpeg");
ImageReader reader = it.next();
ImageInputStream iis = ImageIO.createImageInputStream(is);
reader.setInput(iis, false, false);
src = reader.read(0);
IIOMetadata imageMetadata = reader.getImageMetadata(0);
今、私はいくつかの変換を行います(つまり、サイズを縮小します)...そして最後に結果をJPEG画像として書き戻します。ここで最も重要なのは、元の画像から取得したメタデータを新しいIIOImage
に渡すことです。
Iterator<ImageWriter> iter = ImageIO.getImageWritersByMIMEType("image/jpeg");
ImageWriter writer = iter.next();
ImageWriteParam iwp = writer.getDefaultWriteParam();
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
iwp.setCompressionQuality(jpegQuality);
ImageOutputStream imgOut = new MemoryCacheImageOutputStream(out);
writer.setOutput(imgOut);
IIOImage image = new IIOImage(destImage, null, imageMetadata);
writer.write(null, image, iwp);
writer.dispose();
残念ながら、PNG画像を作成すると、(メタデータを渡しても)間違った色が表示されますが、それでも問題はありません。
同様の問題がありました。返されるBufferedImage
は、透明なピクセルがある場合に基づいたレンディションであり、ほとんどのpng/gifタイプのファイルに当てはまります。ただし、jpegに変換する場合は、このフラグをfalseに設定する必要があります。変換が適切に処理されるメソッドを作成する必要があるかもしれません。つまり:
public static BufferedImage toBufferedImage(Image image) {
...
}
そうでなければ、その「マルニッシュ」の倍音が保存された結果になります。 :)
私も同様の問題を抱えていました。
Image image = Java.awt.Toolkit.getDefaultToolkit().getImage(path);
の代わりに
Image image = javax.imageio.ImageIO.read(new File(path));
私はこの問題に遭遇していましたが、実際にこれを処理するサードパーティのライブラリを見つけました。 https://github.com/haraldk/TwelveMonkeys
文字通り、私がしなければならなかったのは、これを私のMaven依存関係に含めることだけで、奇妙な色で出てきたjpegが正常に読み込まれ始めました。コードの行を変更する必要すらありませんでした。
これが「悪い」画像を良い画像に変換するアルゴリズムですが、画像が正しくレンダリングされないかどうかを自動的に検出する方法が見つからないため、それでも役に立ちません。
画像が(眼球以外で)悪くなるかどうかを検出する方法を誰かが見つけた場合は、お知らせください。 (たとえば、このいわゆるcolorSpaceCode
値はどこで取得できますか?!)
private static void fixBadJPEG(BufferedImage img)
{
int[] ary = new int[img.getWidth() * img.getHeight()];
img.getRGB(0, 0, img.getWidth(), img.getHeight(), ary, 0, img.getWidth());
for (int i = ary.length - 1; i >= 0; i--)
{
int y = ary[i] >> 16 & 0xFF; // Y
int b = (ary[i] >> 8 & 0xFF) - 128; // Pb
int r = (ary[i] & 0xFF) - 128; // Pr
int g = (y << 8) + -88 * b + -183 * r >> 8; //
b = (y << 8) + 454 * b >> 8;
r = (y << 8) + 359 * r >> 8;
if (r > 255)
r = 255;
else if (r < 0) r = 0;
if (g > 255)
g = 255;
else if (g < 0) g = 0;
if (b > 255)
b = 255;
else if (b < 0) b = 0;
ary[i] = 0xFF000000 | (r << 8 | g) << 8 | b;
}
img.setRGB(0, 0, img.getWidth(), img.getHeight(), ary, 0, img.getWidth());
}
ここでは問題ないようです:
import Java.awt.image.BufferedImage;
import Java.net.URL;
import Java.io.File;
import javax.imageio.ImageIO;
import javax.swing.*;
class TestImage {
public static void main(String[] args) throws Exception {
URL url = new URL("http://i.stack.imgur.com/6vy74.jpg");
BufferedImage origImg = ImageIO.read(url);
JOptionPane.showMessageDialog(null,new JLabel(new ImageIcon(origImg)));
File newFile = new File("new.png");
ImageIO.write(origImg, "png", newFile);
BufferedImage newImg = ImageIO.read(newFile);
JOptionPane.showMessageDialog(null,new JLabel(
"New",
new ImageIcon(newImg),
SwingConstants.LEFT));
}
}
画像をバイト配列からBase64に変換しようとしたときに、同様の問題が発生しました。問題の原因はアルファチャンネルの画像のようです。アルファチャネルを使用して画像を保存すると、アルファチャネルも保存され、画像の読み取りに使用される一部の外部プログラムは、4つのチャネルをCMYKとして解釈します。
BufferedImageのアルファチャネルを削除することにより、簡単な回避策を見つけました。これはばかげているかもしれませんが、それは確かに私にとってはうまくいきました。
//Read the image from a byte array
BufferedImage bImage = ImageIO.read(new ByteArrayInputStream(byteArray));
//Get the height and width of the image
int width = bImage.getWidth();
int height = bImage.getHeight();
//Get the pixels of the image to an int array
int [] pixels=bImage.getRGB(0, 0,width,height,null,0,width);
//Create a new buffered image without an alpha channel. (TYPE_INT_RGB)
BufferedImage copy = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
//Set the pixels of the original image to the new image
copy.setRGB(0, 0,width,height,pixels,0,width);