2013年9月2日月曜日

Java でフルカラー画像をグレースケールに変換

Java でフルカラー Jpeg を 8bit グレースケールの PNG に変換した際に嵌った記録・・・

元となる画像(1024 x 768)は、
これから、8bit グレースケールの PNG を生成することとする。

共通処理として、

画像読み込み

public static BufferedImage loadImage(String filename) {
  try (FileInputStream in = new FileInputStream(filename)) {
    return ImageIO.read(in);
  }
}

画像出力

public static void outputImage(String filename, BufferedImage image) throws IOException {
  ImageWriter writer = ImageIO.getImageWritersByFormatName("png").next();
  writer.setOutput(ImageIO.createImageOutputStream(new File(filename)));
  writer.write(image);
  writer.dispose();
}
の 2 つを用意。

変換処理として、書き出し時に JPEGImageWriteParam の PNG 版のようなもので指定して変換できるかと、探したが見つからなかった。
com.sun.imageio.plugins.png 以下にいくつか PNG ようのクラスがあるようだ。
が、よく分からなかったので、タイプ指定で TYPE_BYTE_GRAY を指定した BufferedImage を新たに生成。
それにデータをコピーすることに。

変換処理は、2 パターン。

パターン1Graphics2D#drawImage() を使用

public static BufferedImage grayScale1(BufferedImage image) throws Exception {
  int w = image.getWidth(), h = image.getHeight();
  BufferedImage output = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
  Graphics2D g2d = output.createGraphics();
  g2d.drawImage(image, 0, 0, w, h, null);
  return output;
}

パターン 2BufferedImage#setData() を使用

public static BufferedImage grayScale2(BufferedImage image) throws Exception {
  int w = image.getWidth(), h = image.getHeight();
  BufferedImage output = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
  output.setData(image.getData());
  return output;
}

パターン1、パターン2 をそれぞれ呼び出し

try {
  BufferedImage org = loadImage("org.jpg");
  outputImage("output1.png", grayScale1(org));
  outputImage("output2.png", grayScale2(org));
} catch (Exception e) { e.printStackTrace(); } 

パターン1はいい感じ。
パターン2はデカッ
色が減った分、横に4倍引き伸ばされた感じ?画像データ配列の変換がおかしくなったようだ。

パターン1


パターン2



BufferedImage の setRGB() の説明を読むと、
デフォルトモデルがイメージ ColorModel と一致しない場合は色変換が行われます。
と書かれていたのでこちらを使うようにしてみた。


パターン 3BufferedImage#setRGB() を使用

public static BufferedImage grayScale3(BufferedImage image) throws Exception {
  int w = image.getWidth(), h = image.getHeight();
  BufferedImage output = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
  output.setRGB(0, 0, w, h, image.getRGB(0, 0, w, h, null, 0, w), 0, w);
  return output;
} 

パターン1にくらべかなり黒っぽくなった。

パターン3


今回は、パターン1がよさそうだ。

実は、実際嵌った時の画像では、パターン1の場合、元画像の白の部分がグレーがかってしまいパターン3で出力したほうがはっきりしたものになった。
元画像によって変えるのも大変だが・・・

Graphics2D の場合、パラメータを色々指定できるのでそのあたりをいじってみても変わるかもしれない。

0 件のコメント:

コメントを投稿