EntryNavi : « AndroidのProgressBar / SeekBarでsetProgressDrawable / setThumbを2回以上すると消える | メイン | Galaxy Sでカメラアプリを使用する時の注意点 »

CategoryNavi : メイン -> コンピューター -> 開発 -> Java(Android)

Androidででっかい画像ファイルを扱う

Androidのアプリで2Mピクセルとか以上の大きいファイルを扱おうとするとOutOfMemoryの例外で落ちてしまう場合があります。
もちろん端末のメモリサイズにも大きく影響するので一概にどれだけと言えないのですが・・・。

通常、画像を扱う場合「Bitmap」クラスを使います。
何も考えずに使ってしまうと画像をそのままのサイズでメモリに展開してしまってメモリオーバーになるわけです。
ですが、予めサイズなどの情報だけを取得してリサイズして展開することができます。

そんな方法があるよって事を以前から聞いていて実際の方法をどこかのサイトで見たのですが迷子になったので残しておきます。

サンプルの内容
1.画像サイズを取得してリサイズ時の係数を計算
  「BitmapFactory.Options」を使用して「BitmapFactory.decodeFile」の動作を調節します。
  「inJustDecodeBounds」パラメータで画像の情報だけをOptionsに入れてくれます。
  「inSampleSize」へ指定する値を計算します。
  inSampleSize = 1 の時、等倍
  inSampleSize > 1 小さくなる
  inSampleSize < 1 1と同じ動作(つまり等倍)
  んで、例えば、4の時、一辺が1/4になるので面積は1/16になります。
2.実際に画像を読み込む
  ImageViewとかにデータを渡す場合はここまででいいかも。
  自前でCanvasに描画する場合とかは続きが必要
3.表示エリアのサイズに合わせて具体的にサイズ調節
  1ではアバウトにしかサイズ調節できないので。


/**
 * ビットマップをロードする
 * @param file 開くビットマップのファイルクラス
 * @param view_width 表示領域の幅
 * @param view_height 表示領域の高さ
 * @return
 */
private Bitmap loadBitmap(File file, int view_width, int view_height){
	Bitmap ret = null;
    	
	if(file == null){
	}else{
		//画像
		BitmapFactory.Options option = new BitmapFactory.Options();
		Bitmap src = null;
		int sample_size = 0;
			
		//実際に読み込まないで情報だけ取得する
		//んで、スケールを決める
		option.inJustDecodeBounds = true;	
		BitmapFactory.decodeFile(file.getAbsolutePath(), option);
		if((option.outWidth * option.outHeight) > 1048576){
			//1Mピクセル超えてる
			double out_area = (double)(option.outWidth * option.outHeight) / 1048576.0;
			sample_size = (int) (Math.sqrt(out_area) + 1);
		}else{
			//小さいのでそのまま
			sample_size = 1;
		}
		
		//実際に読み込むモード
		option.inJustDecodeBounds = false;	
		//スケーリングする係数 
		option.inSampleSize = sample_size;
		//画像を読み込む
		src = BitmapFactory.decodeFile(file.getAbsolutePath(), option);
		if(src == null){
		}else{
			int src_width = src.getWidth();
			int src_height = src.getHeight();
			
			//表示利用域に合わせたサイズを計算
			float scale = getFitScale(view_width, view_height, src_width, src_height);
			
			//リサイズマトリクス
			Matrix matrix = new Matrix();
			matrix.postScale(scale, scale);
				
			//ビットマップ作成
			ret = Bitmap.createBitmap(src, 0, 0, src_width, src_height, matrix, true);
		}
	}

	return ret;
}

/**
  * ベストフィットなスケーリング率を求める
  * @param dest_width 目的のサイズ(幅)
  * @param dest_height 目的のサイズ(高さ)
  * @param src_width 元のサイズ(幅)
  * @param src_height 元のサイズ(高さ)
  * @return
  */
 public static float getFitScale(int dest_width, int dest_height
    		, int src_width, int src_height){
	float ret = 0;
    	
	if(dest_width < dest_height){
		//縦が長い
		if(src_width < src_height){
			//縦が長い
			ret = (float)dest_height / (float)src_height;
    			
			if((src_width * ret) > dest_width){
				//縦に合わせると横がはみ出る
				ret = (float)dest_width / (float)src_width;
			}
		}else{
			//横が長い
			ret = (float)dest_width / (float)src_width;
		}
	}else{
		//横が長い
		if(src_width < src_height){
			//縦が長い
			ret = (float)dest_height / (float)src_height;
		}else{
			//横が長い
			ret = (float)dest_width / (float)src_width;

			if((src_height * ret) > dest_height){
				//横に合わせると縦がはみ出る
				ret = (float)dest_height / (float)src_height;
			}
		}
	}
    	
	return ret;
}

<Category : Java(Android)>

コメント (2)

KID:

ども、ついったーの@blueberrystreamです。なんどかblog記事拝見させてもらってます。

Xperiaとかでも、その端末で撮った写真画像をそのままのスケールでロードするとメモリ不足でクラッシュするってくらいAndroidでの画像の扱いはシビア(他の組み込み系はもっとシビア?!)な感じらしいですねー。うちもdev phone1ではクラッシュしないのにIS01で画像関係でクラッシュしまくって泣きました…。
コード、今度アプリ作るときに参考にさせてもらいます!

あ、あと、ちまっとぐぐったらこんなのが出てきました。

AndroidでBitmapFactoryを使ってサイズの大きな画像を読み込むサンプル - hoge256ブログ http://www.hoge256.net/2009/08/432.html

コードが短すぎてイマイチこわい感じですが…なにかの足しにでも。。

あやね:

>KIDさん
どもども、いつもお世話になってます^^

組み込み系だともともとメモリに余裕がなかったりしますから単純には扱えない部分もあるでしょうね。
Androidに限って言えばその時の他のアプリの動作状況とかGCの動作状況でもかわるでしょうから難しいですね。

実は、この記事の内容を実際に調べたのってしばらく前でして、たしか紹介していただいたブログを参考につくったきがします。
参考にしたところが行方不明になってたので記事にしたんですよー。
ありがとうございますw

コメントを投稿

検索

Google

サイドフィード

track feed 理ろぐ
人気ブログランキング - 理ろぐ
Powered by
Movable Type 3.34