- PR -

BufferedImageからpixel情報をRGB個別に取得する

投稿者投稿内容
いっし〜
会議室デビュー日: 2006/11/07
投稿数: 11
投稿日時: 2006-11-08 19:44
こんにちは。
現在、bmpファイルを読み込み・画素単位の処理を施し・jpeg画像として出力するプログラムを組んでいます。
bmpファイルの読み込みはImageIOを使用してできたのですが、読み込んだBufferedImageから画素情報を取得するところでつまづいています。
WritableRasterを作成し、DataBufferIntのgetElem()でどうやらpixel情報は取得できたようなのですが、どうもデータがおかしいような気がします。
【おかしい点】
2×2画素bmpファイルの画素値が
[0][0] 0 0 0
[0][1] 255 0 0
[1][0] 0 255 0
[1][1] 0 0 255
(行・列・R・G・B順)
となっているのに、getElem()で取得した情報を表示させてみると
[0]0
[1]16711680
[2]255
[3]65280
となっています。
RGBの値がまとめてひとつの要素に入っているのだとしても、つじつまがあいません。
よろしければご教授願います。

プログラムは下記のように作成しました。

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Iterator;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import javax.imageio.stream.ImageOutputStream;
import java.util.Locale;

public class pixcelTest {

public static void main(String[] args) throws Exception{
int xsize = 2;
int ysize = 2;
int[] pixel = new int[xsize*ysize];

/*bmpファイル読み込み処理*/
FileInputStream fis = new FileInputStream("boo.bmp");
BufferedImage image = ImageIO.read(fis);//読み込んだイメージ
BufferedImage cpyImage = new BufferedImage(xsize, ysize, BufferedImage.TYPE_INT_RGB);//コピー用イメージ
Graphics2D g2d = cpyImage.createGraphics();
g2d.drawImage(image, 0, 0, xsize, ysize, null);//読み込んだイメージをコピー
fis.close();

WritableRaster ras = cpyImage.getRaster();
DataBufferInt buffer = (DataBufferInt)(ras.getDataBuffer());
//pixel情報取得
for(int i = 0; i < xsize*ysize; i++)
pixel2[i] = buffer.getElem(i);
//pixel情報表示
for(int i = 0; i < xsize*ysize; i++)
System.out.println(pixel2[i]);


/*jpegファイル書き出し処理*/
ImageWriter writer = null;
Iterator itr = ImageIO.getImageWritersByFormatName("jpeg");
if(itr.hasNext()){
writer = (ImageWriter) itr.next();
}
ImageOutputStream ios = ImageIO.createImageOutputStream(new File("boo.jpg"));
writer.setOutput(ios);
JPEGImageWriteParam iwparam = new JPEGImageWriteParam(Locale.getDefault());
iwparam.setCompressionMode(iwparam.MODE_EXPLICIT);
iwparam.setCompressionQuality(0.5f);
writer.write(null, new IIOImage(cpyImage, null, null), iwparam);
ios.flush();
writer.dispose();
ios.close();
}
}
nagise
ぬし
会議室デビュー日: 2006/05/19
投稿数: 1141
投稿日時: 2006-11-09 09:17
引用:

いっし〜さんの書き込み (2006-11-08 19:44) より:
[0]0
[1]16711680
[2]255
[3]65280
となっています。
RGBの値がまとめてひとつの要素に入っているのだとしても、つじつまがあいません。



16進数に直すと
16711680 -> 0x00FF0000
255 -> 0x000000FF
65280 -> 0x0000FF00
となりますから、あまりおかしいようには思わないですが。

どのように辻褄が合わないとお考えですか?
いっし〜
会議室デビュー日: 2006/11/07
投稿数: 11
投稿日時: 2006-11-09 11:44
nagiseさん、お返事ありがとうございます。
なるほど、16進数に変換するのですね。まったく頭から抜け落ちていました。

引用:

nagiseさんの書き込み (2006-11-09 09:17) より:

どのように辻褄が合わないとお考えですか?


単純にRGBが連続した値として格納されているのなら、
[1]16711680 → R:167 G:116 B:80
のようになっているのかと思い、そうなると読み込んだファイルの画素値と一致しないために「おかしいな」と思っていました。

ここからさらにRGB各値を取得するには、
Rなら0x00FF0000
Gなら0x0000FF00
Bなら0x000000FF
で、論理和をとることで可能になりそうですね。
nagiseさん、本当にありがとうございます。

もう1つ、WritableRasterを作成してDataBufferIntのgetElem()を使用する方法のほかに、ColorクラスのgetRGB()を使用する方法でRGB各値の取得に成功しました。
ソースコードは以下です。

int[][] pRED = new int[ysize][xsize];
int[][] pGREEN = new int[ysize][xsize];
int[][] pBLUE = new int[ysize][xsize];

for(int i=0; i<ysize ;i++){
for(int j=0; j<xsize ;j++){
Color pixel = new Color(image.getRGB(j, i));
pRED[i][j] = pixel.getRed();
pGREEN[i][j] = pixel.getGreen();
pBLUE[i][j] = pixel.getBlue();
}
}

現在この2つの取得法があるのですが、どちらを使ったほうがよいなどの優劣はあるのでしょうか?
sawat
大ベテラン
会議室デビュー日: 2006/08/02
投稿数: 112
投稿日時: 2006-11-09 13:48
引用:

現在この2つの取得法があるのですが、どちらを使ったほうがよいなどの優劣はあるのでしょうか?


Colorオブジェクトを使う場合は、ループ内で画像のピクセル数分のColorオブジェクトを生成する(そして、すぐ破棄される)ことになります。
扱う画像ファイルが小さければ問題はありませんが、巨大な画像を扱う場合はGCが無駄に発生したりして、処理性能に影響が出てしまうかもしれません。
nekoyama
ベテラン
会議室デビュー日: 2005/03/12
投稿数: 71
投稿日時: 2006-11-09 15:42
java.awt.image.RescaleOpで目的の処理は実装できませんか?
必要ないならば、そんなクラスもあると参考までに。
いっし〜
会議室デビュー日: 2006/11/07
投稿数: 11
投稿日時: 2006-11-09 23:02
sawatさん、未記入さん、コメントありがとうございます。

引用:

sawatさんの書き込み (2006-11-09 13:48) より:

扱う画像ファイルが小さければ問題はありませんが、巨大な画像を扱う場合はGCが無駄に発生したりして、処理性能に影響が出てしまうかもしれません。


なるほど、まだ試験用の2×2サイズしか扱っていないので検討してみる必要がありそうです。
巨大な画像というのが一般的にどのていどのサイズなのかはわかりませんが、扱う画像サイズは720×540です。

引用:

未記入さんの書き込み(2006-11-09 15:42)より:

java.awt.image.RescaleOpで目的の処理は実装できませんか?


そのようなクラスもあるのですね、勉強になります。
明日にでも試してみようと思います。このクラスで目的の処理が実装できたときには、御報告いたします。

お二方とも、本当にありがとうございます。
tori31001
会議室デビュー日: 2006/03/11
投稿数: 6
投稿日時: 2006-11-10 13:33
RasterのgetPixels()なら
最初から論理和とってビットシフトされたデータが取得できますよ。
いっし〜
会議室デビュー日: 2006/11/07
投稿数: 11
投稿日時: 2006-11-10 17:21
tori31001さん、アドバイスありがとうございます。

引用:

tori31001さんの書き込み (2006-11-10 13:33) より:

RasterのgetPixels()なら最初から論理和とってビットシフトされたデータが取得できますよ。



早速試してみました。…が、なにやら配列の宣言がおかしいらしく、
java.lang.ArrayIndexOutOfBoundsException: Coordinate out of bounds!
とのエラー表示が出力されてしまいました。
作成したソースコードは以下です。
xsize:画像の横サイズ
ysize:画像の縦サイズ
です。

WritableRaster ras = image.getRaster();
int[] pixel = new int[ysize*xsize*3];
for(int i = 0; i < ysize; i++){
for(int j = 0; j < xsize*3; j++){
ras.getPixels(j, i, xsize, ysize, pixel);
}
}

用意したint型配列にRGB順で各値を挿入してくれるのかと思い、縦×横×3(RGB分)の領域を確保したのですが、どうやらちがっているようです。(ちなみに縦×横の領域確保でも同様のエラーメッセージがでます。)

もうすこし格闘してみます。

スキルアップ/キャリアアップ(JOB@IT)