Java での画像フィルタの例
このページでは,画像ファイルの読み出しと書き込みや,画像処理を行う Java プログラムの見本を示す. ImageMagick と JMagick を使って簡単に作ることができる.
【関連する外部ページ】
http://kyle-in-jp.blogspot.com/search/label/JMagick : JMagick を使った画像処理プログラムの見本が載ったすばらしい Web ページ
必要となるソフトウェア
- Eclipse のWebページの記述に従って,Eclipse のインストールが終わっていること.
- 「Java から ImageMagick の機能を使う(JMagick を使用)」のWebページの記述に従って,JMagick のインストール等が終わっていること.
サンプルプログラム
要点
- Eclipse でのパッケージ名は次のようにしている.
- パッケージ名: hoge.hoge.com
- メインとなるクラス名: HelloWorld
- JMagick を使った画像ファイルの読み込み(簡単です).
次のように書く.FILENAME がファイル名.width, height に縦横のサイズが入ります.
image = new MagickImage( new ImageInfo( FILENAME ) ); int width = (int)image.getDimension().getWidth(); int height = (int)image.getDimension().getHeight(); - 読み込んだ画像ファイルの画素値を,Java 配列にコピー(簡単です).
次のように書く.
byte bytes[] = new byte[ width * height * 4 ]; // red, green, blue, alpha image.dispatchImage(0, 0, width, height, "RGBA", bytes); - Java 配列を、画像ファイルに書き込む
次のように書く.「image.setFileName("c:\\R\\sample.png");」でファイル名を変えます(元のファイルを上書きしたくないので).
image.constituteImage(width, height, "RGBA", filter( width, height, bytes ) ); image.setFileName("c:\\R\\sample.png"); image.writeImage(new ImageInfo());
実行手順は, 「Java から ImageMagick の機能を使う(JMagick を使用)」 のWebページを見てください.
package hoge.hoge.com;
import magick.ImageInfo;
import magick.MagickException;
import magick.MagickImage;
public class HelloWorld {
final private static String FILENAME = "C:\\R\\hoge.jpg";
// 縦横 2M だけ大きいサイズの真っ黒の画像の中心に,画像 bytes を貼り付け
// 各画素は4バイト(1バイトが4つ分)であること.
private static double[] imagepaste( final int width, final int height, byte bytes[], final int M ) {
double data[] = new double[ (width + (2 * M)) * (height * (2 * M)) * 4 ]; // red, green, blue, alpha
for( int i=0; i<height; i++ ) {
for( int j=0; j<width; j++ ) {
final int from = (i* width * 4) + (j*4);
final int to = ((width+(2*M))*M*4) + (i*(width+(2*M))*4) + (M*4) + (j*4);
data[ to ] = bytes[ from ]; // red
data[ to + 1 ] = bytes[ from + 1 ]; // green
data[ to + 2 ] = bytes[ from + 2 ]; // blue
data[ to + 3 ] = bytes[ from + 3 ]; // alpha
}
// 左右の余白を黒でうめる
for( int j=0; j<M; j++) {
final int to = ((width+(2*M))*M*4) + (i*(width+(2*M))*4) + (j*4);
data[ to ] = 0; // red
data[ to + 1 ] = 0; // green
data[ to + 2 ] = 0; // blue
data[ to + 3 ] = 0; // alpha
}
for( int j=0; j<M; j++) {
final int to = ((width+(2*M))*M*4) + (i*(width+(2*M))*4) + (M*4) + (width*4) + (j*4);
data[ to ] = 0; // red
data[ to + 1 ] = 0; // green
data[ to + 2 ] = 0; // blue
data[ to + 3 ] = 0; // alpha
}
}
// 上を黒で埋める
for( int i=0; i<M; i++ ) {
for( int j=0; j<(width+(2*M)); j++ ) {
final int to = (i*(width+(2*M))*4) + (j*4);
data[ to ] = 0; // red
data[ to + 1 ] = 0; // green
data[ to + 2 ] = 0; // blue
data[ to + 3 ] = 0; // alpha
}
}
// 下を黒で埋める
for( int i=0; i<M; i++ ) {
for( int j=0; j<(width+(2*M)); j++ ) {
final int to = ((width+(2*M))*(M+height)*4) + (i*(width+(2*M))*4) + (j*4);
data[ to ] = 0; // red
data[ to + 1 ] = 0; // green
data[ to + 2 ] = 0; // blue
data[ to + 3 ] = 0; // alpha
}
}
return data;
}
// 画像の隅、縦横 2M 画素を切り抜く
// 各画素は4バイト(1バイトが4つ分)であること.imagepaste の逆操作
private static byte[] imageclip( final int width, final int height, double data[], final int M ) {
byte bytes[] = new byte[ width * height * 4 ]; // red, green, blue, alpha
for( int i=0; i<height; i++ ) {
for( int j=0; j<width; j++ ) {
final int from = ((width+(2*M))*M*4) + (i*(width+(2*M))*4) + (M*4) + (j*4);
final int to = (i * width * 4) + (j*4);
bytes[ to ] = (byte) data[ from ]; // red
bytes[ to + 1 ] = (byte) data[ from + 1 ]; // green
bytes[ to + 2 ] = (byte) data[ from + 2 ]; // blue
bytes[ to + 3 ] = (byte) data[ from + 3 ]; // alpha
}
}
return bytes;
}
private static byte[] filter( int width, int height, byte bytes[] ) {
// 余白つきの画像を生成
final int M = 1;
double data[] = imagepaste( width, height, bytes, /* margin */ M );
// フィルタ本体
byte data2[] = new byte[ (width + 2) * (height * 2) * 4 ]; // red, green, blue, alpha
for( int i=0; i<height; i++ ) {
for( int j=0; j<width; j++ ) {
final int at = ((width+(2*M))*M*4) + (i*(width+(2*M))*4) + (M*4) + (j*4); // 余白の分だけずれる
final int atP = at - ((width+(2*M))*4);
final int atN = at + ((width+(2*M))*4);
final double red = (double) (
data[ atP - 4 ] + data[ atP ] + data[ atP + 4 ] +
data[ at - 4 ] + data[ at ] + data[ at + 4 ] +
data[ atN - 4 ] + data[ atN ] + data[ atN + 4 ] ) / 9;
final double green = (double) (
data[ atP - 3 ] + data[ atP + 1 ] + data[ atP + 5 ] +
data[ at - 3 ] + data[ at + 1 ] + data[ at + 5 ] +
data[ atN - 3 ] + data[ atN + 1 ] + data[ atN + 5 ] ) / 9;
final double blue = (double) (
data[ atP - 2 ] + data[ atP + 2 ] + data[ atP + 6 ] +
data[ at - 2 ] + data[ at + 2 ] + data[ at + 6 ] +
data[ atN - 2 ] + data[ atN + 2 ] + data[ atN + 6 ] ) / 9;
final double alpha = (double) (
data[ atP - 1 ] + data[ atP + 3 ] + data[ atP + 7 ] +
data[ at - 1 ] + data[ at + 3 ] + data[ at + 7 ] +
data[ atN - 1 ] + data[ atN + 3 ] + data[ atN + 7 ] ) / 9;
final int to = (i* width *4) + (j*4);
data2[ to ] = (byte) red;
data2[ to + 1 ] = (byte) green;
data2[ to + 2 ] = (byte) blue;
data2[ to + 3 ] = (byte) alpha;
}
}
return data2;
}
public static void main(String[] args) {
MagickImage image;
try {
// 画像ファイル読み込み
image = new MagickImage( new ImageInfo( FILENAME ) );
int width = (int)image.getDimension().getWidth();
int height = (int)image.getDimension().getHeight();
// 画像データを,配列にコピー
byte bytes[] = new byte[ width * height * 4 ]; // red, green, blue, alpha
image.dispatchImage(0, 0, width, height, "RGBA", bytes);
// フィルタ(filter)の実行結果を書き戻す
image.constituteImage(width, height, "RGBA", filter( width, height, bytes ) );
image.setFileName("c:\\R\\sample.png");
image.writeImage(new ImageInfo());
} catch (MagickException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}
}
}