画像の拡大と縮小について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
アバター
みけCAT
記事: 6734
登録日時: 15年前
住所: 千葉県
連絡を取る:

画像の拡大と縮小について

#1

投稿記事 by みけCAT » 14年前

Windows Vista Home Premium SP2 32ビット
Dev-C++ 4.9.9.2
gcc version 3.4.2 (mingw-special)
です。

画像の拡大と縮小について質問があります。
自作の画像を扱うライブラリ(添付)を使用し、画像を拡大縮小して表示するソフトを作りました。
うまく動いてはいるのですが、画像を大きく拡大すると、少し重いようです。
また、比較のためにStretchBltでの拡大縮小も実装しましたが、
自前のプログラムでは、StretchBltに比べて画像が少し上にあるように見えます。

もうすこしいい(書き方が美しい、実行が速い、綺麗な画像が出力される、のいずれか)
コードにするにはどうすればいいでしょうか?
アドバイスをいただけると嬉しいです。
言語はC言語でお願いします。
よろしくお願いします。

自作のライブラリは、
bmpGetWidth(bmp)で画像の幅を取得します。
bmpGetHeight(bmp)で画像の高さを取得します。
bmpGetXY(bmp,x,y).r
bmpGetXY(bmp,x,y).g
bmpGetXY(bmp,x,y).b
でそれぞれ画像の(x,y)要素(左上が(0,0))の赤、緑、青成分にアクセス(取得、設定)できます。

拡大縮小の部分のコードです。
プロジェクト全体は添付しました。

コード:

#include "gstretch.h"

bmp_t* bmp_stretch(const bmp_t* bmp,int width,int height,int hokanflag) {
	bmp_t* bmp2;
	bmp_t* bmp3;
	int i,j,k;
	int prev,next,hokan;
	int rsum,gsum,bsum;
	if(width<=0 || height<=0 || bmp==NULL)return NULL;
	if(hokanflag) {
		/*補間有効モード*/
		bmp2=bmpMake(width,bmpGetHeight(bmp));
		if(bmp2==NULL)return NULL;
		if(width>bmpGetWidth(bmp)) {
			/*拡大なので補間*/
			for(i=0;i<bmpGetHeight(bmp);i++) {
				for(j=0;j<width;j++) {
					prev=j*bmpGetWidth(bmp)/width;
					next=prev+1;
					if(next>=bmpGetWidth(bmp))next=bmpGetWidth(bmp)-1;
					hokan=((j<<10)*bmpGetWidth(bmp)/width)%1024;
					bmpGetXY(bmp2,j,i).r=(
						bmpGetXY(bmp,prev,i).r*(1024-hokan)+
						bmpGetXY(bmp,next,i).r*hokan
						)>>10;
					bmpGetXY(bmp2,j,i).g=(
						bmpGetXY(bmp,prev,i).g*(1024-hokan)+
						bmpGetXY(bmp,next,i).g*hokan
						)>>10;
					bmpGetXY(bmp2,j,i).b=(
						bmpGetXY(bmp,prev,i).b*(1024-hokan)+
						bmpGetXY(bmp,next,i).b*hokan
						)>>10;
				}
			}
		} else if(width<bmpGetWidth(bmp)) {
			/*縮小なので平均を取る*/
			for(i=0;i<bmpGetHeight(bmp);i++) {
				for(j=0;j<width;j++) {
					prev=j*bmpGetWidth(bmp)/width;
					next=(j+1)*bmpGetWidth(bmp)/width;
					rsum=gsum=bsum=0;
					for(k=prev;k<next;k++) {
						rsum+=bmpGetXY(bmp,k,i).r;
						gsum+=bmpGetXY(bmp,k,i).g;
						bsum+=bmpGetXY(bmp,k,i).b;
					}
					bmpGetXY(bmp2,j,i).r=rsum/(next-prev);
					bmpGetXY(bmp2,j,i).g=gsum/(next-prev);
					bmpGetXY(bmp2,j,i).b=bsum/(next-prev);
				}
			}
		} else {
			/*同じ大きさなので単純にコピー*/
			for(i=0;i<bmpGetHeight(bmp);i++) {
				for(j=0;j<width;j++) {
					bmpGetXY(bmp2,j,i).r=bmpGetXY(bmp,j,i).r;
					bmpGetXY(bmp2,j,i).g=bmpGetXY(bmp,j,i).g;
					bmpGetXY(bmp2,j,i).b=bmpGetXY(bmp,j,i).b;
				}
			}
		}
		bmp3=bmpMake(width,height);
		if(bmp3==NULL) {
			bmpFree(bmp2);
			return NULL;
		}
		if(height>bmpGetHeight(bmp2)) {
			/*拡大なので補間*/
			for(i=0;i<width;i++) {
				for(j=0;j<height;j++) {
					prev=j*bmpGetHeight(bmp2)/height;
					next=prev+1;
					if(next>=bmpGetHeight(bmp2))next=bmpGetHeight(bmp2)-1;
					hokan=((j<<10)*bmpGetHeight(bmp2)/height)%1024;
					bmpGetXY(bmp3,i,j).r=(
						bmpGetXY(bmp2,i,prev).r*(1024-hokan)+
						bmpGetXY(bmp2,i,next).r*hokan
						)>>10;
					bmpGetXY(bmp3,i,j).g=(
						bmpGetXY(bmp2,i,prev).g*(1024-hokan)+
						bmpGetXY(bmp2,i,next).g*hokan
						)>>10;
					bmpGetXY(bmp3,i,j).b=(
						bmpGetXY(bmp2,i,prev).b*(1024-hokan)+
						bmpGetXY(bmp2,i,next).b*hokan
						)>>10;
				}
			}
		} else if(height<bmpGetHeight(bmp)) {
			/*縮小なので平均を取る*/
			for(i=0;i<width;i++) {
				for(j=0;j<height;j++) {
					prev=j*bmpGetHeight(bmp2)/height;
					next=(j+1)*bmpGetHeight(bmp2)/height;
					rsum=gsum=bsum=0;
					for(k=prev;k<next;k++) {
						rsum+=bmpGetXY(bmp2,i,k).r;
						gsum+=bmpGetXY(bmp2,i,k).g;
						bsum+=bmpGetXY(bmp2,i,k).b;
					}
					bmpGetXY(bmp3,i,j).r=rsum/(next-prev);
					bmpGetXY(bmp3,i,j).g=gsum/(next-prev);
					bmpGetXY(bmp3,i,j).b=bsum/(next-prev);
				}
			}
		} else {
			/*同じ大きさなので単純にコピー*/
			for(i=0;i<width;i++) {
				for(j=0;j<height;j++) {
					bmpGetXY(bmp3,i,j).r=bmpGetXY(bmp2,i,j).r;
					bmpGetXY(bmp3,i,j).g=bmpGetXY(bmp2,i,j).g;
					bmpGetXY(bmp3,i,j).b=bmpGetXY(bmp2,i,j).b;
				}
			}
		}
		/*中間データを開放*/
		bmpFree(bmp2);
	} else {
		/*補間無効モード*/
		bmp3=bmpMake(width,height);
		if(bmp3==NULL)return NULL;
		for(i=0;i<width;i++) {
			for(j=0;j<height;j++) {
				int x=i*bmpGetWidth(bmp)/width;
				int y=j*bmpGetHeight(bmp)/height;
				bmpGetXY(bmp3,i,j).r=bmpGetXY(bmp,x,y).r;
				bmpGetXY(bmp3,i,j).g=bmpGetXY(bmp,x,y).g;
				bmpGetXY(bmp3,i,j).b=bmpGetXY(bmp,x,y).b;
			}
		}
	}
	return bmp3;
}
添付ファイル
gstretch.zip
画像の拡大縮小のプログラムです。
(60.76 KiB) ダウンロード数: 203 回
libbmpio.zip
画像を扱うライブラリです。
(19.87 KiB) ダウンロード数: 155 回
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

ISLe
記事: 2650
登録日時: 15年前
連絡を取る:

Re: 画像の拡大と縮小について

#2

投稿記事 by ISLe » 14年前

ピクセルフォーマットを固定してリニアにアクセスすると高速に処理できるのですが、bmp_tに抽象化されている現在のコードでの高速化は難しいと思います。
同じピクセルを何度も取得しないようにすればある程度は速くなると思います。

全部のソースを見てないので想像ですが、たぶん座標を切り捨てているのでStretchBltと比べてズレるのではないでしょうか。

アバター
みけCAT
記事: 6734
登録日時: 15年前
住所: 千葉県
連絡を取る:

Re: 画像の拡大と縮小について

#3

投稿記事 by みけCAT » 14年前

ISLe さんが書きました:ピクセルフォーマットを固定してリニアにアクセスすると高速に処理できるのですが、
これは、例えばこのライブラリでメモリ上にビットマップファイルデータを作成し、
その中の画像データにアクセスするといったことでしょうか?
ISLe さんが書きました:全部のソースを見てないので想像ですが、たぶん座標を切り捨てているのでStretchBltと比べてズレるのではないでしょうか。
四捨五入などをするべきということでしょうか?

ほかの方の意見も聞いてみたいと思います。
オフトピック
こういうのを抽象化というのか…ふむふむ。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

ISLe
記事: 2650
登録日時: 15年前
連絡を取る:

Re: 画像の拡大と縮小について

#4

投稿記事 by ISLe » 14年前

みけCAT さんが書きました:
ISLe さんが書きました:ピクセルフォーマットを固定してリニアにアクセスすると高速に処理できるのですが、
これは、例えばこのライブラリでメモリ上にビットマップファイルデータを作成し、
その中の画像データにアクセスするといったことでしょうか?
32ビット型整数の配列にA8R8G8B8の形式でピクセルを入れるといった感じのことです。
整数型の配列をポインタでアクセスすると速いです。
かなり昔ですがこの掲示板でピクセルの半透明合成の話題があったのですけど似たようなことをやってました。
みけCAT さんが書きました:
ISLe さんが書きました:全部のソースを見てないので想像ですが、たぶん座標を切り捨てているのでStretchBltと比べてズレるのではないでしょうか。
四捨五入などをするべきということでしょうか?
小さな白黒のタイル画像を表示してみたのですが、単純な誤差の問題のようですね。
あとアンチエイリアス処理が片方向にしか行われていないせいで左上に寄った感じになるようです。

たろ

Re: 画像の拡大と縮小について

#5

投稿記事 by たろ » 14年前

ISLeさんの「半透明合成の話題」というのは、こちらでしょうか。

[画像処理]レイヤー合成の高速化について
http://dixq.net/forum/viewtopic.php?f=3&t=6122

このトピックを立てたものです。
ISLeさんお世話になりました。いつもありがとうございます。

みけCATさんの参考になれば幸いです。

1ピクセルにアクセスする GetXY(x,y) を使うのをやめて、ポインタ変数で直接ピクセルにアクセスする。
ポインタで配列を順次アクセスするような処理に変えることで速くなるはず。という話だと思います。

画像の拡大縮小の高速化については私も興味があります。
以前StretchBltやStretchDIBitsよりきれいで速いものを自作したいと試行錯誤しました。
勝てたかどうか怪しいままですが・・。

アバター
みけCAT
記事: 6734
登録日時: 15年前
住所: 千葉県
連絡を取る:

Re: 画像の拡大と縮小について

#6

投稿記事 by みけCAT » 14年前

とりあえず配列に変えてみました。

コード:

bmp_t* bmp_stretch(const bmp_t* bmp,int width,int height,int hokanflag) {
	bmp_t* bmp2;
	int bmpwidth,bmpheight;
	int i,j,k;
	int srcy,dsty;
	int prev,next,hokan;
	int rsum,gsum,bsum;
	int* data;
	int* data2;
	int* data3;
	if(width<=0 || height<=0 || bmp==NULL)return NULL;
	bmpwidth=bmpGetWidth(bmp);
	bmpheight=bmpGetHeight(bmp);
	/*作業用メモリ確保*/
	data=HeapAlloc(GetProcessHeap(),0,bmpwidth*bmpheight*sizeof(int));
	data2=HeapAlloc(GetProcessHeap(),0,width*bmpheight*sizeof(int));
	data3=HeapAlloc(GetProcessHeap(),0,width*height*sizeof(int));
	if(data==NULL || data2==NULL || data3==NULL) {
		if(data)HeapFree(GetProcessHeap(),0,data);
		if(data2)HeapFree(GetProcessHeap(),0,data2);
		if(data3)HeapFree(GetProcessHeap(),0,data3);
	}
	/*作業データの転送*/
	for(i=0;i<bmpheight;i++) {
		dsty=i*bmpwidth;
		for(j=0;j<bmpwidth;j++) {
			data[dsty+j]=
				bmp->data[dsty+j].r |
				(bmp->data[dsty+j].g<<8) |
				(bmp->data[dsty+j].b<<16);
		}
	}
	if(hokanflag) {
		/*補間有効モード*/
		if(width>bmpwidth) {
			/*拡大なので補間*/
			for(i=0;i<bmpheight;i++) {
				srcy=i*bmpwidth;
				dsty=i*width;
				for(j=0;j<width;j++) {
					prev=j*bmpwidth/width;
					next=prev+1;
					if(next>=bmpwidth)next=bmpwidth-1;
					hokan=((j<<8)*bmpwidth/width)&0xff;
					data2[dsty+j]=((
						(data[srcy+prev] & 0x00ff00ff)*(256-hokan)+
						(data[srcy+next] & 0x00ff00ff)*hokan
						)>>8) & 0x00ff00ff;
					data2[dsty+j]+=((
						(data[srcy+prev] & 0x0000ff00)*(256-hokan)+
						(data[srcy+next] & 0x0000ff00)*hokan
						)>>8) & 0x0000ff00;
				}
			}
		} else if(width<bmpwidth) {
			/*縮小なので平均を取る*/
			for(i=0;i<bmpheight;i++) {
				srcy=i*bmpwidth;
				dsty=i*width;
				for(j=0;j<width;j++) {
					prev=j*bmpwidth/width;
					next=(j+1)*bmpwidth/width;
					rsum=gsum=bsum=0;
					for(k=prev;k<next;k++) {
						rsum+=(data[srcy+k] & 0x000000ff);
						gsum+=(data[srcy+k] & 0x0000ff00)>>8;
						bsum+=(data[srcy+k] & 0x00ff0000)>>16;
					}
					data2[dsty+j]=
						(rsum/(next-prev)) |
						((gsum/(next-prev))<<8) |
						((bsum/(next-prev))<<16);
				}
			}
		} else {
			/*同じ大きさなので単純にコピー*/
			dsty=bmpwidth*bmpheight;
			for(i=0;i<dsty;i++) {
				data2[i]=data[i];
			}
		}
		if(height>bmpheight) {
			/*拡大なので補間*/
			for(i=0;i<width;i++) {
				for(j=0;j<height;j++) {
					prev=j*bmpheight/height;
					next=prev+1;
					if(next>=bmpheight)next=bmpheight-1;
					hokan=((j<<8)*bmpheight/height)&0xff;
					data3[j*width+i]=((
						(data2[prev*width+i] & 0x00ff00ff)*(256-hokan)+
						(data2[next*width+i] & 0x00ff00ff)*hokan
						)>>8) & 0x00ff00ff;
					data3[j*width+i]+=((
						(data2[prev*width+i] & 0x0000ff00)*(256-hokan)+
						(data2[next*width+i] & 0x0000ff00)*hokan
						)>>8) & 0x0000ff00;
				}
			}
		} else if(height<bmpheight) {
			/*縮小なので平均を取る*/
			for(i=0;i<width;i++) {
				for(j=0;j<height;j++) {
					prev=j*bmpheight/height;
					next=(j+1)*bmpheight/height;
					rsum=gsum=bsum=0;
					for(k=prev;k<next;k++) {
						rsum+=(data2[k*width+i] & 0x000000ff);
						gsum+=(data2[k*width+i] & 0x0000ff00)>>8;
						bsum+=(data2[k*width+i] & 0x00ff0000)>>16;
					}
					data3[j*width+i]=
						(rsum/(next-prev)) |
						((gsum/(next-prev))<<8) |
						((bsum/(next-prev))<<16);
				}
			}
		} else {
			/*同じ大きさなので単純にコピー*/
			dsty=width*height;
			for(i=0;i<dsty;i++) {
				data3[i]=data2[i];
			}
		}
	} else {
		/*補間無効モード*/
		for(i=0;i<height;i++) {
			dsty=i*width;
			for(j=0;j<width;j++) {
				int x=j*bmpwidth/width;
				int y=i*bmpheight/height;
				data3[dsty+j]=data[y*bmpwidth+x];
			}
		}
	}
	/*bmpデータに変換*/
	bmp2=bmpMake(width,height);
	if(bmp2!=NULL) {
		for(i=0;i<height;i++) {
			dsty=i*width;
			for(j=0;j<width;j++) {
				bmp2->data[dsty+j].r=(data3[dsty+j] & 0x000000ff);
				bmp2->data[dsty+j].g=(data3[dsty+j] & 0x0000ff00)>>8;
				bmp2->data[dsty+j].b=(data3[dsty+j] & 0x00ff0000)>>16;
			}
		}
	}
	/*メモリ開放*/
	HeapFree(GetProcessHeap(),0,data);
	HeapFree(GetProcessHeap(),0,data2);
	HeapFree(GetProcessHeap(),0,data3);
	/*データを返す*/
	return bmp2;
}
速度を計測してみると、

コード:

拡大 旧方式 補間有効:10998ms
拡大 旧方式 補間無効:6225ms
拡大 新方式 補間有効:7691ms
拡大 新方式 補間無効:4071ms
縮小 旧方式 補間有効:2340ms
縮小 旧方式 補間無効:281ms
縮小 新方式 補間有効:3354ms
縮小 新方式 補間無効:1248ms
等倍 旧方式 補間有効:8783ms
等倍 旧方式 補間無効:7613ms
等倍 新方式 補間有効:4804ms
等倍 新方式 補間無効:5102ms
という結果が出ました。(計測プログラムは添付しました)
拡大と等倍では若干速くなっていますが、縮小は遅くなっています。
きちんとポインタや共用体を使ったほうがいいのでしょうか?
添付ファイル
gstretchbench.zip
計測プログラムです。
(29.06 KiB) ダウンロード数: 133 回
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

ISLe
記事: 2650
登録日時: 15年前
連絡を取る:

Re: 画像の拡大と縮小について

#7

投稿記事 by ISLe » 14年前

みけCAT さんが書きました:拡大と等倍では若干速くなっていますが、縮小は遅くなっています。
きちんとポインタや共用体を使ったほうがいいのでしょうか?
最初の回答に書いたようにリニアにアクセスするのが高速化のポイントなので、整数型の配列にするのはそのための準備でしかありません。

内部的には常にリニアにA8R8B8G8形式でピクセル情報を持ち、入出力時にBMPファイル形式に変換するような設計にすると描画する度にフォーマット変換する必要が無くなります。
縮小が遅くなっているのはほとんどがフォーマット変換に掛かっている時間かと思います。

あと、これも繰り返しになりますが、描画したピクセル情報をキャッシュして同じピクセルを何度もサンプリングしないように工夫するのも効果的かと思います。

アバター
みけCAT
記事: 6734
登録日時: 15年前
住所: 千葉県
連絡を取る:

Re: 画像の拡大と縮小について

#8

投稿記事 by みけCAT » 14年前

ポインタを加算していくコードにしてみました。
結果は、補間有効の拡大では少しだけ速くなりましたが、その他は変わらないか、むしろ遅くなりました。

コード:

bmp_t* bmp_stretch(const bmp_t* bmp,int width,int height,int hokanflag) {
	bmp_t* bmp2;
	int bmpwidth,bmpheight;
	int i,j,k;
	int srcy,dsty;
	int prev,next,hokan;
	int rsum,gsum,bsum;
	int* data;
	int* data2;
	int* data3;
	int* data4;
	int* srcptr;
	int* dstptr;
	rgb_t* rgbptr;
	if(width<=0 || height<=0 || bmp==NULL)return NULL;
	bmpwidth=bmpGetWidth(bmp);
	bmpheight=bmpGetHeight(bmp);
	/*作業用メモリ確保*/
	data=HeapAlloc(GetProcessHeap(),0,bmpwidth*bmpheight*sizeof(int));
	data2=HeapAlloc(GetProcessHeap(),0,width*bmpheight*sizeof(int));
	data3=HeapAlloc(GetProcessHeap(),0,width*bmpheight*sizeof(int));
	data4=HeapAlloc(GetProcessHeap(),0,width*height*sizeof(int));
	if(data==NULL || data2==NULL || data3==NULL || data4==NULL) {
		if(data)HeapFree(GetProcessHeap(),0,data);
		if(data2)HeapFree(GetProcessHeap(),0,data2);
		if(data3)HeapFree(GetProcessHeap(),0,data3);
		if(data4)HeapFree(GetProcessHeap(),0,data4);
		return NULL;
	}
	/*作業データの転送*/
	dsty=bmpwidth*bmpheight;
	rgbptr=bmp->data;
	dstptr=&data[0];
	for(i=0;i<dsty;i++) {
		*dstptr=
			rgbptr->r |
			(rgbptr->g<<8) |
			(rgbptr->b<<16);
		rgbptr++;
		dstptr++;
	}
	if(hokanflag) {
		/*補間有効モード*/
		/*横方向のリサイズ*/
		if(width>bmpwidth) {
			/*拡大なので補間*/
			dstptr=&data2[0];
			for(i=0;i<bmpheight;i++) {
				srcptr=&data[i*bmpwidth];
				for(j=0;j<width;j++) {
					prev=j*bmpwidth/width;
					next=prev+1;
					if(next>=bmpwidth)next=bmpwidth-1;
					hokan=((j<<8)*bmpwidth/width)&0xff;
					*dstptr=((
						(srcptr[prev] & 0x00ff00ff)*(256-hokan)+
						(srcptr[next] & 0x00ff00ff)*hokan
						)>>8) & 0x00ff00ff;
					*dstptr+=((
						(srcptr[prev] & 0x0000ff00)*(256-hokan)+
						(srcptr[next] & 0x0000ff00)*hokan
						)>>8) & 0x0000ff00;
					dstptr++;
				}
			}
		} else if(width<bmpwidth) {
			/*縮小なので平均を取る*/
			dstptr=&data2[0];
			for(i=0;i<bmpheight;i++) {
				srcy=i*bmpwidth;
				for(j=0;j<width;j++) {
					prev=j*bmpwidth/width;
					next=(j+1)*bmpwidth/width;
					rsum=gsum=bsum=0;
					srcptr=&data[srcy+prev];
					for(k=prev;k<next;k++) {
						rsum+=(*srcptr & 0x000000ff);
						gsum+=(*srcptr & 0x0000ff00)>>8;
						bsum+=(*srcptr & 0x00ff0000)>>16;
						srcptr++;
					}
					*dstptr=
						(rsum/(next-prev)) |
						((gsum/(next-prev))<<8) |
						((bsum/(next-prev))<<16);
					dstptr++;
				}
			}
		} else {
			/*同じ大きさなので単純にコピー*/
			dsty=bmpwidth*bmpheight;
			srcptr=&data[0];
			dstptr=&data2[0];
			for(i=0;i<dsty;i++) {
				*dstptr=*srcptr;
				dstptr++;
				srcptr++;
			}
		}
		/*データの並べ替え*/
		dstptr=&data3[0];
		for(i=0;i<width;i++) {
			srcptr=&data2[i];
			for(j=0;j<bmpheight;j++) {
				*dstptr=*srcptr;
				dstptr++;
				srcptr+=width;
			}
		}
		/*縦方向のリサイズ*/
		if(height>bmpheight) {
			/*拡大なので補間*/
			dstptr=&data4[0];
			for(i=0;i<width;i++) {
				srcptr=&data3[i*bmpheight];
				for(j=0;j<height;j++) {
					prev=j*bmpheight/height;
					next=prev+1;
					if(next>=bmpheight)next=bmpheight-1;
					hokan=((j<<8)*bmpheight/height)&0xff;
					*dstptr=((
						(srcptr[prev] & 0x00ff00ff)*(256-hokan)+
						(srcptr[next] & 0x00ff00ff)*hokan
						)>>8) & 0x00ff00ff;
					*dstptr+=((
						(srcptr[prev] & 0x0000ff00)*(256-hokan)+
						(srcptr[next] & 0x0000ff00)*hokan
						)>>8) & 0x0000ff00;
					dstptr++;
				}
			}
		} else if(height<bmpheight) {
			/*縮小なので平均を取る*/
			dstptr=&data4[0];
			for(i=0;i<width;i++) {
				srcy=i*bmpheight;
				for(j=0;j<height;j++) {
					prev=j*bmpheight/height;
					next=(j+1)*bmpheight/height;
					rsum=gsum=bsum=0;
					srcptr=&data3[srcy+prev];
					for(k=prev;k<next;k++) {
						rsum+=(*srcptr & 0x000000ff);
						gsum+=(*srcptr & 0x0000ff00)>>8;
						bsum+=(*srcptr & 0x00ff0000)>>16;
						srcptr++;
					}
					*dstptr=
						(rsum/(next-prev)) |
						((gsum/(next-prev))<<8) |
						((bsum/(next-prev))<<16);
					dstptr++;
				}
			}
		} else {
			/*同じ大きさなので単純にコピー*/
			dsty=width*height;
			srcptr=&data3[0];
			dstptr=&data4[0];
			for(i=0;i<dsty;i++) {
				*dstptr=*srcptr;
				dstptr++;
				srcptr++;
			}
		}
	} else {
		/*補間無効モード*/
		dstptr=&data4[0];
		for(i=0;i<width;i++) {
			for(j=0;j<height;j++) {
				int x=i*bmpwidth/width;
				int y=j*bmpheight/height;
				*dstptr=data[y*bmpwidth+x];
				dstptr++;
			}
		}
	}
	/*bmpデータに変換*/
	bmp2=bmpMake(width,height);
	if(bmp2!=NULL) {
		rgbptr=bmp2->data;
		for(i=0;i<height;i++) {
			srcptr=&data4[i];
			for(j=0;j<width;j++) {
				rgbptr->r=(*srcptr & 0x000000ff);
				rgbptr->g=(*srcptr & 0x0000ff00)>>8;
				rgbptr->b=(*srcptr & 0x00ff0000)>>16;
				rgbptr++;
				srcptr+=height;
			}
		}
	}
	/*メモリ開放*/
	HeapFree(GetProcessHeap(),0,data);
	HeapFree(GetProcessHeap(),0,data2);
	HeapFree(GetProcessHeap(),0,data3);
	HeapFree(GetProcessHeap(),0,data4);
	/*データを返す*/
	return bmp2;
}
測定結果です。
「新形式」が今回のコード、「旧形式」は前に投稿した配列のコードです。

コード:

拡大 旧方式 補間有効:8596ms
拡大 旧方式 補間無効:3994ms
拡大 新方式 補間有効:7207ms
拡大 新方式 補間無効:4493ms
縮小 旧方式 補間有効:3244ms
縮小 旧方式 補間無効:1217ms
縮小 新方式 補間有効:3323ms
縮小 新方式 補間無効:1217ms
等倍 旧方式 補間有効:4742ms
等倍 旧方式 補間無効:5055ms
等倍 新方式 補間有効:7129ms
等倍 新方式 補間無効:5944ms
ISLe さんが書きました:内部的には常にリニアにA8R8B8G8形式でピクセル情報を持ち、入出力時にBMPファイル形式に変換するような設計にすると描画する度にフォーマット変換する必要が無くなります。
縮小が遅くなっているのはほとんどがフォーマット変換に掛かっている時間かと思います。
このライブラリ上で画像を簡単に扱えるようにしておきたいため、そのような設計の変更は見送らせていただきます。
ISLe さんが書きました:あと、これも繰り返しになりますが、描画したピクセル情報をキャッシュして同じピクセルを何度もサンプリングしないように工夫するのも効果的かと思います。
よくわからないのですが、具体的にどのように実装するのでしょうか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

ISLe
記事: 2650
登録日時: 15年前
連絡を取る:

Re: 画像の拡大と縮小について

#9

投稿記事 by ISLe » 14年前

みけCAT さんが書きました:
ISLe さんが書きました:内部的には常にリニアにA8R8B8G8形式でピクセル情報を持ち、入出力時にBMPファイル形式に変換するような設計にすると描画する度にフォーマット変換する必要が無くなります。
縮小が遅くなっているのはほとんどがフォーマット変換に掛かっている時間かと思います。
このライブラリ上で画像を簡単に扱えるようにしておきたいため、そのような設計の変更は見送らせていただきます。
内部フォーマットを固定すると描画ルーチンがひとつで済むので高速化とシンプルさを両立できると思います。
過去にソフトウェア3Dレンダリングエンジンを作ったときはピクセルフォーマット固定が最も効果的でしたが。
設計を変更できないなら高速化はあきらめるしか無いと思います。
みけCAT さんが書きました:
ISLe さんが書きました:あと、これも繰り返しになりますが、描画したピクセル情報をキャッシュして同じピクセルを何度もサンプリングしないように工夫するのも効果的かと思います。
よくわからないのですが、具体的にどのように実装するのでしょうか?
リニアにアクセスできればサンプリング位置のインデックスの計算もピクセルの平均化も起点の一回だけ計算してあとは差分を更新していくだけで進めることができます。
ピクセル情報の差分の更新にはキャッシュが必要です。
ただしピクセル情報にリニアにアクセスできることが高速化の必須条件ですので設計を変更しないならこの手も使えません。

アバター
みけCAT
記事: 6734
登録日時: 15年前
住所: 千葉県
連絡を取る:

Re: 画像の拡大と縮小について

#10

投稿記事 by みけCAT » 14年前

うーん、難しいですね。
とりあえず解決にします。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

閉鎖

“C言語何でも質問掲示板” へ戻る