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

スポンサーサイト

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

DCT変換と逆DCT変換

openCVを使ってDCT変換と逆DCT変換するサンプルソースです。 cvDCTを使用した場合、画像中心に高周波成分が表示され、周辺に向かうにつれて低周波成分になっていきます。 したがって、下図に示すフィルタはどちらも低周波成分を通し高周波成分をカットするローパスフィルタとして 機能します。左側のフィルタは理想的なLPF、右側のフィルタはガウシアンフィルタになります。

opencv3.jpg

下図に理想LPFとガウシアンフィルタを使用したそれぞれの場合の結果を示します。理想LPFを使用した場合 周波数ドメインの関数に不連続な部分ができてしまうため、変換後の画像にリンギングが生じています。 それに対してガウシアンフィルタを使用した場合には、フィルタ関数が連続関数で記述可能なため、リンギングは生じていません。(ガウシアンの係数によっては生じる場合もある)

opencv4.jpg
ピクセルにアクセスするためのマクロ定義はこちら。ピクセルアクセスのためのマクロ
int main(int argc, char ** argv)
{
	IplImage *invImage;
	CvMat *dct,*idct;
	
	IplImage *image = cvLoadImage("lena.jpg", CV_LOAD_IMAGE_GRAYSCALE);
	IplImage *mask = cvLoadImage("mask1.jpg", CV_LOAD_IMAGE_GRAYSCALE);
	invImage = cvCreateImage( cvGetSize(image), IPL_DEPTH_8U, 1);

	dct = cvCreateMat(image->height, image->width, CV_64FC1);
	idct = cvCreateMat(image->height, image->width, CV_64FC1);
	
	// DCT
	int x, y;
	for(y=0; yheight; y++){
		for(x=0; xwidth; x++){
			cvmSet(dct,
				   y,
				   x,
				   (double)(unsigned char)(image->imageData[image->widthStep
															* y + x]));
		}
	}
	
	cvDCT( dct, dct, CV_DXT_FORWARD);
	
	// マスク値をDCT結果に乗算
	for(int x = 0; x < dct->width; ++x){
		for(int y = 0; y < dct->height; ++y){
			double scale = PIXVAL(mask, x, y) / 255.0;
			double value = cvmGet(dct, y, x) * scale;
			cvmSet(dct, y, x, value);
		}
	}
	
	// 逆変換
	cvDCT( dct, idct, CV_DXT_INVERSE);
	for(y=0; yheight; y++){
		for(x=0; xwidth; x++){
			invImage->imageData[invImage->widthStep * y + x] = cvmGet(idct,y,x);
		}
	}

	cvWaitKey(0);
	return 0;
}
スポンサーサイト
はてなブックマークに追加

高速ぼかしフィルタ

通常のぼかしフィルタは、フィルタをかける画像が大きくなればなるほど、またフィルタのオペレータが大きくなればなるほど 処理時間は指数関数的に増大してしまいます。特にゲームではリアルタイム性が求められるため、 高速なぼかしフィルタのアルゴリズムが必要となります。ここでは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;
}
はてなブックマークに追加

ピクセルアクセスのためのマクロ

ピクセルにアクセスする場合に便利なマクロ群。
グレースケール画像の場合にはPIXVAL(画像名, x座標, y座標)でアクセス出来る。また、カラー画像(RGB)の場合にはPIXVALR, PIXVALG, PIXVALBを使用することで、各色のピクセルにアクセスすることが出来る。
#define PIXVAL(iplimagep, x, y) (*(uchar *)((iplimagep)->imageData + (y) * (iplimagep)->widthStep + (x)))
#define PIXVALB(iplimagep, x, y) (*(uchar *)((iplimagep)->imageData + (y) * (iplimagep)->widthStep + (x)*3))
#define PIXVALG(iplimagep, x, y) (*(uchar *)((iplimagep)->imageData + (y) * (iplimagep)->widthStep + (x)*3+1))
#define PIXVALR(iplimagep, x, y) (*(uchar *)((iplimagep)->imageData + (y) * (iplimagep)->widthStep + (x)*3+2))
はてなブックマークに追加

セピア変換

cvsepia.jpg

openCVを使ってカラー画像をセピア色に変換するサンプルソースです。
変換アルゴリズムとても単純で、まず入力画像をグレースケールに変換したのち、
変換画像の赤成分をアップ、青成分をダウンするだけです。

得られる写真の色合いはグレースケールにした時の彩度と
色調補正するときの赤青の増減値でほぼ決まります。

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


int main(int argc, char** argv)
{
IplImage *img = cvLoadImage("fw.jpg");
IplImage *gray = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);

const int darkness = 50;

// グレースケール化
cvCvtColor(img, gray, CV_BGR2GRAY);
for(int x = 0; x < gray->width; ++x){
for(int y = 0; y < gray->height; ++y){
int g = PIXVAL(gray, x, y) - darkness;
PIXVAL(gray, x, y) = (g > 0)?g:0;
}
}
cvCvtColor(gray, img, CV_GRAY2RGB);

// セピア色をつける
for(int x = 0; x < img->width; ++x){
for(int y = 0; y < img->height; ++y){
int r = PIXVALR(img, x, y)+30;
int b = PIXVALB(img, x, y)-30;
PIXVALR(img, x, y) = (r < 256)? r : 255;
PIXVALB(img, x, y) = (b > 0) ? b : 0;
}
}

// 表示
cvNamedWindow("window1", CV_WINDOW_AUTOSIZE);
cvShowImage("window1", img);

cvWaitKey(0);

cvReleaseImage(&img);
cvReleaseImage(&gray);
return 0;
}

開発アプリ

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

プロフィール

hokuson

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

カレンダー
10 | 2017/11 | 12
- - - 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ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。