白旗製作所

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
  1. --/--/--(--) --:--:--|
  2. スポンサー広告

BufferedImageが遅い

Javaでの画像ファイルの読み書きにBufferedImageというクラスを用いているのですが
このクラスで画像のピクセルデータを読み出すのがとても遅いことに気づきました。

ちょっとピクセルごとに処理しようとすると、とてもじゃないけど待ってられないくらい遅くなります。
この問題は、こことかにあるように、BufferedImageの中身であるDataBufferクラスを取り出して
処理すれば早くなるはずです。また、データをint[]やbyte[]の配列にまで取り出してやっても早くなると思います。

そこで、画像のネガポジ反転処理(1ピクセルごとに、赤青緑データを255から引いた値にする)を例に、
以下の条件でどの程度早く出来るかを試してみました。
ただし、処理後の出力はBufferedImageクラスとします。

①1ピクセルごとにBufferedImage.getRGB(), setRGB()を実行して処理
②最初にBufferedImage.getRGBでデータをint[]型に変換してから処理
③BufferedImageをDataBufferByteに変換してから処理
④BufferedImageをbyte[]に変換してから処理 ※1
※3:ソースコードは最後に書いておきます。

1600×900のサイズのpngデータを用い、それぞれ100回ループした時の平均時間を計測しました。
結果、
①171.29ms
②158.88ms
③72.35ms
④85.04ms
となり、DataBuffereByteに変換して処理するのが最も高速となりました。

本来、byte[]に変換する④が高速になるはずなのですが、
再度BufferedImageに変換し直す処理がネックになっているようで、今回は多少遅くなってしまいました。
例えば出力がBufferedImageで無くても良い処理(画像マッチング等)などでは、
④の方が処理が早いのではないかと思います。※2

まあBufferedImageは読み書きに便利ですし、ARGBやABGRと言った並び順、alpha値の有無等の
画像ごとのデータ・タイプの差異を吸収してくれるため、遅くても仕方ない部分はあるかと思いますが、
ピクセル処理をする場合には少なくともDataBufferに変換してやったほうが、速く処理できるという結果となりました。
まあそれでもあまり速いとは言い難いですが、そこを求めるならそもそもjava使うなよということで…

※1
④のbyte配列だと処理速度は速いはずなのですが、データ範囲が0~255ではなく-128~127
(unsigned→signed)になってしまうため、少し工夫をしないと処理結果が他の関数と異なる場合があります。
まあめんどくさいので、多少処理速度が落ちてもint[]の配列にするのが楽で良いでしょうか。

※2
実際に同じ処理を、BufferedImageで返さないで10回ほどループさせた結果、
③1183ms
④1072ms
となり、byte配列にしたほうが処理速度は高速になりました。


※3
最後に時間計測に用いたソースを添付します。
ここここここを参考にさせていただきました。

①1ピクセルごとにBufferedImage.getRGB(), setRGB()を実行して処理

public static void negaposi_bufferedImageEach(BufferedImage image)
{
 int height = image.getHeight();
 int width = image.getWidth();
 int color = 0;
 int red, blue, green, alpha;
 //反転する
 for(int h=0; h<height; h++){
  for(int w=0; w<width; w++){
   color = image.getRGB(w, h);//色の読込
   //反転
   alpha = ImageUtility.alpha(color);
   red = 255-ImageUtility.red(color);
   blue = 255-ImageUtility.blue(color);
   green = 255-ImageUtility.green(color);
   image.setRGB(w, h, ImageUtility.argb(alpha, red, green, blue));
  }
 }
}


②最初にBufferedImage.getRGBでデータをint[]型に変換してから処理

public static void negaposi_bufferedImagePicelArray(BufferedImage image)
{
 int height = image.getHeight();
 int width = image.getWidth();
 int color = 0;
 int red, blue, green, alpha;
 //ピクセルデータをgetRGBを使ってint配列で取得
 int[] pixel = image.getRGB(0, 0, width, height, null, 0, width);
 //反転する
 for(int h=0; h<height; h++){
  for(int w=0; w<width; w++){
   color = pixel[h*width+w];//色の読込
   //反転
   alpha = ImageUtility.alpha(color);
   red = 255-ImageUtility.red(color);
   blue = 255-ImageUtility.blue(color);
   green = 255-ImageUtility.green(color);
   pixel[h*width+w] = ImageUtility.argb(alpha, red, green, blue);
  }
 }
 image.setRGB(0, 0, width, height, pixel, 0, width);
}


③BufferedImageをDataBufferByteに変換してから処理

public static void negaposi_dataBufferByte(BufferedImage image)
{
 int height = image.getHeight();
 int width = image.getWidth();
 int[] ints = new int[width * height];
 int red, blue, green, alpha, count=0;
 //ピクセルデータをDataBufferByteで取得
 DataBufferByte buf = ImageUtility.convertARGB(image);
 DataBuffer iBuf = new DataBufferInt(ints, width*height);
 //反転する
 for(int i=0; i<buf.getSize(); i+=4, count++){
  alpha = buf.getElem(i);
  red = 255-buf.getElem(i+1);
  green = 255-buf.getElem(i+2);
  blue = 255-buf.getElem(i+3);
  iBuf.setElem(count, ImageUtility.argb(alpha, red, green, blue));
 }
 //BufferedImageに戻す
 int[] bandMasks = new int[] { 0x00ff0000, 0x0000ff00, 0x000000ff };
 WritableRaster writableRaster = Raster.createPackedRaster(iBuf, width, height, width, bandMasks, null);
 image.setData(writableRaster);
}

※以下のBufferedImageをDataBufferByteに変換するコードは省略してますが、
 実際はBufferedImageのtypeごとに別個の処理をすることになります。
public static DataBufferByte convertARGB(BufferedImage image)
{
 int alpha=1, count=0, color=0;
 DataBufferByte rBuf=null;
 DataBuffer iBuf = image.getRaster().getDataBuffer();
 switch(image.getType()){
 case BufferedImage.TYPE_INT_ARGB:
  rBuf = new DataBufferByte(iBuf.getSize()*4);
  for(int i=0; i<iBuf.getSize(); i++){
   color = iBuf.getElem(i);
   rBuf.setElem( i*4+0, color>>24&0xff );
   rBuf.setElem( i*4+1, color>>16&0xff );
   rBuf.setElem( i*4+2, color>>8&0xff );
   rBuf.setElem( i*4+3, color&0xff );
  }
  break;
 }
 return rBuf;
}

④BufferedImageをbyte[]に変換してから処理

public static void negaposi_Array(BufferedImage image)
{
 int height = image.getHeight();
 int width = image.getWidth();
 int red, blue, green, alpha, count=0;
 //ピクセルデータをByte配列で取得
 byte[] bufa = ImageUtility.convertByteARGB(image);
 int[] iBuf = new int[width*height];
 //反転する
 for(int i=0; i<bufa.length; i+=4, count++){
  alpha = bufa[i];
  red = 255-bufa[i+1];
  green = 255-bufa[i+2];
  blue = 255-bufa[i+3];
  iBuf[count] = ImageUtility.argb(alpha, red, green, blue);
 }
 //BufferedImageに戻す
 int[] bandMasks = new int[] { 0x00ff0000, 0x0000ff00, 0x000000ff };
 WritableRaster writableRaster = Raster.createPackedRaster(new DataBufferInt(iBuf, width*height), width, height, width,  bandMasks, null);
 image.setData(writableRaster);
}

※以下の処理も③同様省略してます。
public static byte[] convertByteARGB(BufferedImage image)
{
 int alpha=1, count=0, color=0;
 byte[] rBuf=null;
 DataBuffer iBuf = image.getRaster().getDataBuffer();
 switch(image.getType()){
 case BufferedImage.TYPE_INT_ARGB:
  rBuf = new byte[iBuf.getSize()*4];
  for(int i=0; i<iBuf.getSize(); i++){
   color = iBuf.getElem(i);
   rBuf[ i*4+0] = (byte)(color>>24&0xff );
   rBuf[ i*4+1] = (byte)(color>>16&0xff );
   rBuf[ i*4+2] = (byte)(color>>8&0xff );
   rBuf[ i*4+3] = (byte)(color&0xff );
  }
  break;
 }
 return rBuf;
}





  1. 2014/10/22(水) 00:52:52|
  2. Java
  3. | トラックバック:0
  4. | コメント:0

javaで、型の違う配列での四則演算がしたい

javaで、型の違う配列での四則演算をやりたくていろいろ調べたんですが、やっぱそのままじゃ無理そう。

なぜこんなことをやろうと思ったかといえば、
このブログの最初の記事でmatrixクラスとかを作っていたんですが、
int[][]型のmatrixでも、double[][]型のmatrixでも、1つの関数とかで
同じ挙動を実現したいと思ったのが発端。

たとえば以下のようなメソッドを考えます。

public static double[][] plus(double[][] matrix, double[][] target)
{
double[][] result = new double[matrix.length][matrix[0].length];

for(int i=0; i<matrix.length; i++ )
for(int j=0; j<matrix[0].length; j++ )
result[i][j] = matrix[i][j]+target[i][j];
return result;
}



この処理を、以下のようにint[][]配列でも使いたいのです。
int[][] ho = {1, 2};
int[][] ge = {3, 4};
double[][] hoge = plus(ho,ge);

かといって、関数のオーバーロードを型の種類ごとに用意するのは
保守性が悪くなりますし、コード量も多くなりますので、
なるべくどの型でも使える関数みたいなものを用意したいです。

ただ、どの型でも使える関数というのが、どうもjavaでは容易ではないようです。
配列であってもキャストで(int[][])double[][]と書ければ良いんですがね。

候補として考えたのは以下の3つ。
①Objecti[][]型を使う
②Generic型を使う
③型変換メソッドを作る

他にもラムダ式を用いるとかがあると思いますが
java 1.5上での開発のためここでは考えません。

①、②の方法ではObject型やジェネリック型自体に四則演算がないので、
そのままでは使えません。配列要素の入れ替えとかなら出来ると思います。

結局、ちょっとオーバーヘッドが大きいけど、③の型変換メソッドを作って
入れるのが一番手っ取り早いし実現性も高いかなと思います。


#そもそもこんなmatrixのメソッドはdouble以外で使わなければいいんですが、
 使いたくなるシチュエーションがあるんですよね。。。。





  1. 2014/10/20(月) 01:13:52|
  2. Java
  3. | トラックバック:0
  4. | コメント:0

MATLABからjavaクラス、jarファイルを扱う

ちょっと便利そうだな-と思い、MATLABからjavaクラスを呼び出してみたので方法をメモしておきます。

参考:http://www.mathworks.co.jp/jp/help/matlab/matlab_external/creating-and-using-java-objects.html
MATLAB-高度なソフトウェア開発-外部プログラミング言語インターフェイス-Java ライブラリ-javaオブジェクトの作成と使用
http://lunran.sakura.ne.jp/wordpress/category/matlab/
Matlab - Lunran Studio


①javaクラスをmatlabが対応しているバージョンでビルドしておきます。
以下のコマンドでmatlabが対応しているjavaバージョンを調べます。
version -java

その後、eclipseのプロジェクト→プロパティ→Java Build Path→ライブラリー→JREシステム・ライブラリー
の実行環境を、上記バージョンに変更してビルドしておきます。

②コンストラクタを初期化メソッドにしておきます。
javaクラスにコンストラクタがあると、うちの環境では以下の様なエラーを吐いて使えなかったので、
No constructor ClassName with matching signature found

コンストラクタは初期化メソッドに代替しておきます。
private void ClassName()
{
  ...
}


③.classファイルのあるパスをダイナミッククラスパスに追加します。(jarファイルも同様)
javaclasspath('C:\workspace\ClassTest\bin\');

以下のコマンドでダイナミッククラスパスに追加されたかを確認しておきます。
javaclasspath

④使用例1:クラスをインスタンス化してみます。
instanceName = javaObject('ClassName');
以下の方法でもOKです。
instanceName = ClassName;

④使用例2:クラスのメソッドを使用してみます。
return_value = javaMethod('methodName', instanceName, arg1, arg2, arg3);
以下の方法でもOKです。
return_value = instanceName.methodName(arg1, arg2, arg3);


これでjavaクラスもMATLABで使いまわせるようになりました。
  1. 2014/08/29(金) 11:44:46|
  2. Java
  3. | トラックバック:0
  4. | コメント:0

eclipseのエディタ設定

eclipseのエディタの設定を自分好みに少し設定変更したのでメモしておきます。
#eclipseのバージョンはKepler Service Release 2です。

変更点は
①行番号表示をさせる。
②フォントを等幅フォントにする。
の2点です。

eclipseはデフォルトでconsolasというフォントになっていましたが、
日本語(全角)が英数字(半角)2文字と等幅にならず、ずれてしまうため、
ずれないようにMeiryoKe_Consoleという別のフォントを利用します。
また、太文字も等幅で表示させるため、JStyleというプラグインも入れます。



①エディタの行番号表示
メニューバー→ウインドウ→設定→一般→エディター→テキスト・エディター
→『行番号の表示』のチェックを入れる。

②等幅フォントの導入
全角・半角にかかわらず等幅なフォントとして、MeiryoKe_Consoleを導入します。

1.
JStyleをインストール。
http://mergedoc.sourceforge.jp/index.html#/jstyle.html

2.
メニューバー→ウインドウ→設定→一般→JStyle
→『太字を通常文字と同じ幅で表示する』にチェックを入れる。
+ついでに、『改行』『タブ』『全角スペース』にチェックを入れ、『タブ』のマークを『^』に変更。

3.
以下HPを参考に、MeiryoKe_Consoleフォントを作成してインストール。
http://pc-zero.jp/technic/meiryoke_console.html


4.
メニューバー→ウインドウ→設定→一般→外観→色とフォント
→『Java Editor Text Font』と『Javadoc display font』を
 MeiryoKe_Consoleの10ポイントに設定。


以上を設定すると下図のような感じの表示になり、
日本語コメントだろうが、キーワードの太文字だろうが等幅で行番号とともに表示されるようになります。

新しいビットマップ イメージ

  1. 2014/04/30(水) 20:26:10|
  2. Java
  3. | トラックバック:0
  4. | コメント:0

Javaで行列演算

せっかくなので今更ながらブログ作りました。
ぼちぼちなんか書いていきます。

とりあえず、先日作ったJava用の行列計算クラスでも置いておきます。
行列型のようなもので、内部に2次元配列と、入出力メソッド、
積和、転置、逆、行列式等、基本的な行列演算メソッドを備えています。
なぜJavaで作ったのかは謎。

こちらにお世話になりました。
http://blogtool.flatlabs.net/source.html
もうちょっとインデント調整できないかな。


ダウンロードはこちらから。↓
Matrix.java_jpn
Matrix.java_eng



public class Matrix{

//内部で扱われる2次元配列
private double[][] matrix;

//コンストラクタ(行列サイズの初期化)
public Matrix(int rowSize, int columnSize)
{
matrix = new double[rowSize][columnSize];
}

//------------------------------------------------------------------------//
// メッセージ関数 //
//------------------------------------------------------------------------//
//行サイズを取得
public int getRowSize()
{
return matrix.length;
}

//列サイズを取得
public int getColumnSize()
{
return matrix[0].length;
}

//行列のサイズを取得
public int[] getSize()
{
int[] size = {matrix[0].length, matrix.length};
return size;
}

//行列の要素を取得
public double get(int row, int column)
{
return matrix[row][column];
}
//行列の値を設定
public double[][] get()
{
return matrix;
}

//行列の要素に値を設定
public void set(int row, int column, double value)
{
matrix[row][column] = value;
}
//行列の値を設定
public void set(double[][] value)
{
matrix = value;
}

//行列のある行の値を取得
public double[] getRow(int row)
{
return matrix[row];
}

//行列のある行に値を設定
public void setRow(int row, double[] value)
{
matrix[row] = value;
}

//行列のある列の値を取得
public double[] getColumn(int column)
{
return matrix[column];
}

//行列のある列に値を設定
public void setColumn(int column, double[] value)
{
matrix[column] = value;
}

//行列の値を表示
public void print( String name )
{
int i,j,row, column;
row = getRowSize();
column = getColumnSize();

System.out.println( name + "= " ) ;

for( i=0; i<row; i++ ){
System.out.print( " " ) ;
for( j=0; j<column; j++ ){
System.out.print( matrix[i][j] + " " ) ;
}
System.out.println("");
}
}

//------------------------------------------------------------------------//
// 行列の演算関数群 //
//------------------------------------------------------------------------//
//行列の和
public Matrix plus(Matrix target)
{
int i,j,row, column;
row = getRowSize();
column = getColumnSize();
Matrix result = new Matrix(row,column);

for( i=0; i<row; i++ ){
for( j=0; j<column; j++ ){
result.set(i, j, (matrix[i][j]+target.get(i,j)) );
}
}
return result;
}
public Matrix plus(double value)
{
int i,j,row, column;
row = getRowSize();
column = getColumnSize();
Matrix result = new Matrix(row,column);

for( i=0; i<row; i++ ){
for( j=0; j<column; j++ ){
result.set(i, j, (matrix[i][j]+value) );
}
}
return result;
}

//行列の差
public Matrix minus(Matrix target)
{
int i, j, row, column;
row = getRowSize();
column = getColumnSize();
Matrix result = new Matrix(row,column);

for( i=0; i<row; i++ ){
for( j=0; j<column; j++ ){
result.set(i, j, (matrix[i][j]-target.get(i,j)) );
}
}
return result;
}
public Matrix minus(double value)
{
int i, j, row, column;
row = getRowSize();
column = getColumnSize();
Matrix result = new Matrix(row,column);

for( i=0; i<row; i++ ){
for( j=0; j<column; j++ ){
result.set(i, j, (matrix[i][j]-value) );
}
}
return result;
}

//行列の積
public Matrix multiply(Matrix target)
{
int i, j, k, row, column;
double temp;

row = getRowSize();
column = getColumnSize();
Matrix result = new Matrix(row,row);

for( i=0; i<row; i++ ){
for( j=0; j<row; j++ ){
temp = 0;
for ( k=0; k<column; k++ ){
temp += matrix[i][k] * target.get(k,j);
}
result.set(i, j, temp);
}
}
return result;
}
public Matrix multiply(double value)
{
int i, j, row, column;
row = getRowSize();
column = getColumnSize();
Matrix result = new Matrix(row,column);

for( i=0; i<row; i++ ){
for( j=0; j<column; j++ ){
result.set(i, j, (matrix[i][j]*value) );
}
}
return result;
}

//各々の配列要素の積
public Matrix multiplyEach(Matrix target)
{
int i, j, row, column;
row = getRowSize();
column = getColumnSize();
Matrix result = new Matrix(row,column);

for( i=0; i<row; i++ ){
for( j=0; j<column; j++ ){
result.set(i, j, (matrix[i][j]*target.get(i, j)) );
}
}
return result;
}

//割り算
public Matrix division(double value)
{
int i, j, row, column;
row = getRowSize();
column = getColumnSize();
Matrix result = new Matrix(row,column);

for( i=0; i<row; i++ ){
for( j=0; j<column; j++ ){
result.set(i, j, (matrix[i][j]/value) );
}
}
return result;
}

//転置行列
public Matrix transpose()
{
int i, j, row, column;
row = getRowSize();
column = getColumnSize();
Matrix result = new Matrix(row,column);

for( i=0; i<row; i++ ){
for( j=0; j<column; j++ ){
result.set(i, j, matrix[j][i]);
}
}
return result;
}

//行列式
public double determinant()
{
int row, column;
row = getRowSize();
column = getColumnSize();

//正方行列でない
if(row != column){ return 0.0; }
//行列が1*1
else if(row == 1){ return matrix[0][0]; }
//行列が2*2
else if(row == 2){ return (matrix[0][0]*matrix[1][1]-matrix[0][1]*matrix[1][0]); }
//行列が3*3以上
else{
int i;
double det=0;
Matrix cofactorMatrix = new Matrix(row-1,column-1);

for( i=0; i<row; i++ ){
cofactorMatrix = spritMatrix(i,0);
if( i%2 == 0) det += matrix[i][0]*cofactorMatrix.determinant();
else det -= matrix[i][0]*cofactorMatrix.determinant();
}
return det;
}
}

//逆行列
public Matrix inverse()
{
int i, j, row, column;
row = getRowSize();
column = getColumnSize();
Matrix result = new Matrix(row,column);

for( i=0; i<row; i++ ){
for( j=0; j<column; j++ ){
if( (i+j)%2 == 0) result.set(i, j, spritMatrix(i,j).determinant() );
else result.set(i, j, (-1)*spritMatrix(i,j).determinant() );
}
}
return result.transpose().division(determinant());
}

//行列の要素を指定すると、その行・列を排除した行列を返す
private Matrix spritMatrix(int row, int column)
{
int i, j, m=0, n=0;
int rowSize = getRowSize();
int columnSize = getColumnSize();
Matrix result = new Matrix(rowSize-1,columnSize-1);

m=0;
for( i=0; i<rowSize; i++ ){
if(i!=row){
n=0;
for( j=0; j<columnSize; j++ ){
if(j!=column){
result.set(m, n, matrix[i][j]);
n++;
}
}
m++;
}
}
return result;
}
}
//file end.-------------------------------------------------------------------//

  1. 2012/12/10(月) 23:31:40|
  2. Java
  3. | トラックバック:0
  4. | コメント:0
前のページ
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。