はてなブックマークに追加

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
はてなブックマークに追加

高速ぼかしフィルタ

通常のぼかしフィルタは、フィルタをかける画像が大きくなればなるほど、またフィルタのオペレータが大きくなればなるほど 処理時間は指数関数的に増大してしまいます。特にゲームではリアルタイム性が求められるため、 高速なぼかしフィルタのアルゴリズムが必要となります。ここではopenCVを用いて高速ぼかしフィルタのアルゴリズムを説明します。

単純にぼかしフィルタを使用すると、下図のようにフィルタが1px右へ移動したときに赤色のゾーンを再計算する必要があります。 高速化するために、この部分の計算結果を保存しておき、次の計算の時に使用しようというのが基本的なアイデアです。

opencv1.jpg

まずは縦列の値C0, C1, C2を先に計算し、それをフィルタ移動先でも再利用します。縦列の値Ciは 各ピクセルの足し算p(i,j-1)+p(i,j)+p(i,j+1)で求めることが出来ます。 フィルタをかけた後の値Sは(C0+C1+C2)/9で求めることが出来ます。

opencv2.jpg

フィルタが移動後の値はS-C0+C3で求めることが出来ます。この方法で従来は9回必要だった計算が 6回で済むため、高速化が図れます。今回は3x3のフィルタを用いましたが、フィルタが大きくなればなるほど この効果は大きくなってきます。残念ながら、この高速化の手法はガウシアンフィルタなどには適応出来ません。 コード例では5x5のフィルタの例を示しています。

4536px x 3024pxの画像に対して通常のぼかしフィルタをかけた場合4.56秒でした。それに対して 高速ぼかしフィルタでは2.12秒と2倍以上の高速化が図れました。 (実行環境:MacBook, Intel Core2Duo 2GHz, 2Gbyte Memory. )

ピクセルにアクセスするためのマクロ定義はこちら。ピクセルアクセスのためのマクロ

CvScalar C0, C1, C2, C3, C4, S;

inline CvScalar apply5x5FastBlurFilterWithImage(IplImage *img, int x, int y)
{
	CvScalar color;
	
	S.val[0] -= C0.val[0];
	S.val[1] -= C0.val[1];
	S.val[2] -= C0.val[2];	
	
	C0 = C1;
	C1 = C2;
	C2 = C3;
	C3 = C4;
	
	C4.val[0] = PIXVALR(img, x+2, y-2) + PIXVALR(img, x+2, y-1) + PIXVALR(img, x+2, y) + PIXVALR(img, x+2, y+1) + PIXVALR(img, x+2, y+2);   	
	C4.val[1] = PIXVALG(img, x+2, y-2) + PIXVALG(img, x+2, y-1) + PIXVALG(img, x+2, y) + PIXVALG(img, x+2, y+1) + PIXVALG(img, x+2, y+2);  
	C4.val[2] = PIXVALB(img, x+2, y-2) + PIXVALB(img, x+2, y-1) + PIXVALB(img, x+2, y) + PIXVALB(img, x+2, y+1) + PIXVALB(img, x+2, y+2);  
	
	S.val[0] += C4.val[0];
	S.val[1] += C4.val[1];
	S.val[2] += C4.val[2];	
	
	color.val[0] = S.val[0] / 25;
	color.val[1] = S.val[1] / 25;
	color.val[2] = S.val[2] / 25;
	
	return color;
}

inline CvScalar blurFront5x5(IplImage *img, int x, int y)
{
	CvScalar color;
	
	C0.val[0] = PIXVALR(img, x-2, y-2) + PIXVALR(img, x-2, y-1) + PIXVALR(img, x-2, y) + PIXVALR(img, x-2, y+1) + PIXVALR(img, x-2, y+2);   
	C1.val[0] = PIXVALR(img, x-1, y-2) + PIXVALR(img, x-1, y-1) + PIXVALR(img, x-1, y) + PIXVALR(img, x-1, y+1) + PIXVALR(img, x-1, y+2);   
	C2.val[0] = PIXVALR(img, x  , y-2) + PIXVALR(img, x  , y-1) + PIXVALR(img, x  , y) + PIXVALR(img, x  , y+1) + PIXVALR(img, x  , y+2);   
	C3.val[0] = PIXVALR(img, x+1, y-2) + PIXVALR(img, x+1, y-1) + PIXVALR(img, x+1, y) + PIXVALR(img, x+1, y+1) + PIXVALR(img, x+1, y+2);   
	C4.val[0] = PIXVALR(img, x+2, y-2) + PIXVALR(img, x+2, y-1) + PIXVALR(img, x+2, y) + PIXVALR(img, x+2, y+1) + PIXVALR(img, x+2, y+2);   	
	
	C0.val[1] = PIXVALG(img, x-2, y-2) + PIXVALG(img, x-2, y-1) + PIXVALG(img, x-2, y) + PIXVALG(img, x-2, y+1) + PIXVALG(img, x-2, y+2);   
	C1.val[1] = PIXVALG(img, x-1, y-2) + PIXVALG(img, x-1, y-1) + PIXVALG(img, x-1, y) + PIXVALG(img, x-1, y+1) + PIXVALG(img, x-1, y+2);   
	C2.val[1] = PIXVALG(img, x  , y-2) + PIXVALG(img, x  , y-1) + PIXVALG(img, x  , y) + PIXVALG(img, x  , y+1) + PIXVALG(img, x  , y+2);   
	C3.val[1] = PIXVALG(img, x+1, y-2) + PIXVALG(img, x+1, y-1) + PIXVALG(img, x+1, y) + PIXVALG(img, x+1, y+1) + PIXVALG(img, x+1, y+2);   
	C4.val[1] = PIXVALG(img, x+2, y-2) + PIXVALG(img, x+2, y-1) + PIXVALG(img, x+2, y) + PIXVALG(img, x+2, y+1) + PIXVALG(img, x+2, y+2);  
	
	C0.val[2] = PIXVALB(img, x-2, y-2) + PIXVALB(img, x-2, y-1) + PIXVALB(img, x-2, y) + PIXVALB(img, x-2, y+1) + PIXVALB(img, x-2, y+2);   
	C1.val[2] = PIXVALB(img, x-1, y-2) + PIXVALB(img, x-1, y-1) + PIXVALB(img, x-1, y) + PIXVALB(img, x-1, y+1) + PIXVALB(img, x-1, y+2);   
	C2.val[2] = PIXVALB(img, x  , y-2) + PIXVALB(img, x  , y-1) + PIXVALB(img, x  , y) + PIXVALB(img, x  , y+1) + PIXVALB(img, x  , y+2);   
	C3.val[2] = PIXVALB(img, x+1, y-2) + PIXVALB(img, x+1, y-1) + PIXVALB(img, x+1, y) + PIXVALB(img, x+1, y+1) + PIXVALB(img, x+1, y+2);   
	C4.val[2] = PIXVALB(img, x+2, y-2) + PIXVALB(img, x+2, y-1) + PIXVALB(img, x+2, y) + PIXVALB(img, x+2, y+1) + PIXVALB(img, x+2, y+2);  
	
	S.val[0] = (C0.val[0]+C1.val[0]+C2.val[0]+C3.val[0]+C4.val[0]);
	S.val[1] = (C0.val[1]+C1.val[1]+C2.val[1]+C3.val[1]+C4.val[1]);
	S.val[2] = (C0.val[2]+C1.val[2]+C2.val[2]+C3.val[2]+C4.val[2]);
	return color;
}

int main(int argc, char** argv)
{
	CvScalar color;
    IplImage* tmp = cvLoadImage("test.jpg");	
	IplImage* result = cvCreateImage(cvGetSize(tmp), IPL_DEPTH_8U, 3);
	cvNamedWindow("w1",  CV_WINDOW_AUTOSIZE);	

	for(int y = 2; y < tmp->height-2; ++y){
		blurFront5x5(tmp, 2, y);
		for(int x = 2; x < tmp->width-2; ++x){
			color = apply5x5FastBlurFilterWithImage(tmp, x, y);
			PIXVALR(result, x, y) = color.val[0];
			PIXVALG(result, x, y) = color.val[1];
			PIXVALB(result, x, y) = color.val[2];			
		}
	}

    cvShowImage("w1", result);
	cvWaitKey(0);
    return 0;
}
スポンサーサイト

コメントの投稿

非公開コメント

開発アプリ

iDOF 色影 ラテアート ぱすてる

プロフィール

hokuson

Author:hokuson
京都在住。iPhoneアプリ「色影」や「iDOF」の開発者。アプリのレビューとかもしてみる。博士後期課程@R大学。ついに就職活動なるものをしなければいけないらしい。誰か雇ってください。笑。

カレンダー
03 | 2017/04 | 05
- - - - - - 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 - - - - - -
カテゴリ
最新コメント
RSSリンクの表示
リンク
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。