Win7/10 でGetPixel を使用しないで画面RGB 情報を取得するテクニックを紹介
中井技術工房 Win7/10 で画面 RGB 情報を高速取得する方法 May.10,2019 改正

トップ> Win7 での推奨使用法 > ここ

現象−−GetPixel が異常に遅い

Win7,10で GetPixel API が異常に遅いらしく、当方の関係ソフトもフリーズするというクレームが届きました。
調べてみると、どうも
GetPixel にとんでもない時間がかかる ことがわかりました。
画像をブロックで取得するとフリーズのような現象を起こしていました。

Microsoft は Win7 に移行するときに高速化のために GetPixel を犠牲にしたようです。
動作はしますが、極端に応答が悪くなりました。

対策−−ブロック転送テクを使おう

そこで、画像印刷処理で使っていた BilBlt によるブロック転送技術を逆用し、画面からメモリ内に Bitmap イメージでブロック転送してから、RGB 情報にばらして取り出す方法をコーディングしました。
当方のソフトに組み込んでみたところ、これで十分実用的であることがわかりました。

インターネットで調べてもこれで悩んでいる人が多いようですのでソースを公開しておきます。
当方のソフトから直接切り出しましたのと、< が html 文で正しく理解してくれず、わざとスペースをいれたりして、多少みにくいところもあるかと思います。
でもまあコンバートは難しくないでしょう。
Win10 でも同じ関数を使っていますが、なんら問題を生じていませんので有効です。

VC サンプルソース

[ 関数仕様 ]

長方形ゾーンを指定し、ドット数*3バイトのRGB バッファを用意して呼び出すと RGB 順、べたづめでバッファに格納して返します。
メモリ上に互換ビットマップを確保し、そこに画面から指定ゾーンのビットマップ情報を一括転送後、RGB 順に並べ替えています。
なお、画面の表示タイプ(ドットあたりビット数)による場合分けが必要です。注意ください。

[ ソース ]


//================================================================
// モニタの指定ゾーンのRGBデータを取得
// 時間はかからないはずなので途中経過表示はしない
// 0  - 正常
// -1 - 画像取得失敗
//================================================================
int CNoSrch::MonitRGBGet(
	unsigned char *pRGB,	// 上流で dx*dy*3 バイトの領域を確保のこと
				// RGB 順
	int xsd,		// 左端
	int ysd,		// 上端
	int dxd,		// 幅
	int dyd)		// 高さ
{
	int x,y;

	// モニタ全域をカバーする DC を作る
    CDC dcMonit;
	   dcMonit.CreateDC("DISPLAY",NULL,NULL,NULL);

	// メモリ上に互換性のある DC を作る
	CDC dcMem;
		dcMem.CreateCompatibleDC(&dcMonit);

	// モニタDC に指定サイズのコンパチビットマップを作る
	CBitmap GBmp;
	BITMAP	bm;

	if(FALSE==GBmp.CreateCompatibleBitmap(
				&dcMonit,
				dxd,
				dyd) )
	{
		MessageBox("Compatible Bitmap 作成に失敗しました","確認",0);
		return -1;
	}
	// このビットマップオブジェクトでバッファを確保する
	GBmp.GetObject(sizeof(BITMAP),&bm);
	
	// メモリ上の互換DCを選択する
	CBitmap* pOldBmp=dcMem.SelectObject(&GBmp);

	//--------------------------------------------------------------------
	// メモリDC へ指定ゾーンのモニタ画像を一括転送する
	if(!dcMem.BitBlt(
			// 転送先
		0,		// xs
		0,		// ys
		dxd,		// Δx
		dyd,		// Δy
			// 転送元
		&dcMonit,	// DC
		xsd,		// xs
		ysd,		// ys
		SRCCOPY)) 
	{
		MessageBox("Bitmap 転送に失敗しました","確認",0);
		dcMem.SelectObject(pOldBmp);
		return -1;
	}

	//----------------------------------------------------------------------
	int LineBytes;
	int jpad;	// パッディングバイト数 24ビットのときのみ必要 
			// 横1列を偶数バイトにするため
	int NBbuf;	// バッファサイズ
	int NBGet;
	unsigned char *pbmpc;
	unsigned char Hi,Lo;
	int kk;
	unsigned char R,G,B;
	int Sr=0;	// 格納バイトシリアル
	// ビットあたりのピクセル数
	int crtBitPxl = dcMonit.GetDeviceCaps(BITSPIXEL);

	switch(crtBitPxl)
	{
	case 32:	// 32ビット=4バイトタイプ
		// 変換用バッファを確保する
		NBbuf=dxd*4*dyd;	// 必ず偶数になる
		pbmpc=new unsigned char [NBbuf];

		// ビットマップパターンをCBitmap から変換用バッファへ転送する
		// 指定バイト数のデータをバッファへ転送
		NBGet=GBmp.GetBitmapBits(NBbuf,&pbmpc[0]);

		kk=0;
		for(y=0; y < dyd; y++)
		{
		for(x=0; x< dxd; x++)
			{
			B=pbmpc[kk++];	// B
			G=pbmpc[kk++];	// G
			R=pbmpc[kk++];	// R
			kk++;				// 4バイト目はダミー
			  
			pRGB[Sr++]=R;			// RGBバッファへ格納
			pRGB[Sr++]=G;
			pRGB[Sr++]=B;
			}	// for x 
			// パッディング不要
		}	// for y
		break;
	case 24:	// 24ビット=3バイトタイプ
				// 24 ビットはパッディングが必要かも
				// 横ドット数*3を偶数にする必要がある
		LineBytes=dxd*3;
			if(LineBytes % 2 !=0)
			{
			LineBytes++;
			jpad=1;
			}
			else jpad=0;

		NBbuf=LineBytes*dyd;
		pbmpc=new unsigned char [NBbuf];

		// ビットマップパターンをCBitmap から変換用バッファへ転送する
		// 指定バイト数のデータをバッファへ転送
		NBGet=GBmp.GetBitmapBits(NBbuf,&pbmpc[0]);

		kk=0;
		for(y=0; y < dyd; y++)
		{
		for(x=0; x < dxd; x++)
			{
			B=pbmpc[kk++];	// B
			G=pbmpc[kk++];	// G
			R=pbmpc[kk++];	// R
				  
			pRGB[Sr++]=R;			// RGBバッファへ格納
			pRGB[Sr++]=G;
			pRGB[Sr++]=B;
			}	// for x 

			if(jpad!=0) kk++;	// バッディング かならず1バイトだけ
		}	// for y
		break;
	case 16:	// 16ビット=2バイトタイプ
		LineBytes=dxd*2;
		NBbuf=LineBytes*dyd;	// 必ず偶数になる
		// 変換用バッファを確保
		pbmpc=new unsigned char [NBbuf];
		
		// ビットマップパターンをCBitmap から変換用バッファへ転送する
		// 指定バイト数のデータをバッファへ転送
		NBGet=GBmp.GetBitmapBits(NBbuf,&pbmpc[0]);

		kk=0;
		for(y=0; y< dyd; y++)
		{
		for(x=0; x< dxd; x++)
			{	// 2バイトを取り出す
			Lo=pbmpc[kk++];
			Hi=pbmpc[kk++];

			R=Hi & 0xf8;	// 上位バイトの上位5ビットがR 下位3ビットをクリアしておく
							// 上位バイトの下位3ビットと下位バイトの上位3ビットがG
			G=( Hi << 5 ) | ( ( Lo & 0xe0 ) >> 3 );
			B= Lo << 3;		// 下位バイトの下位5ビットがB 上位ヅメで0が下位3ビットに入る	

			pRGB[Sr++]=R;			// RGBバッファへ格納
			pRGB[Sr++]=G;
			pRGB[Sr++]=B;
			}	// for x 
			// パッディング不要
		}	// for y
		break;
	default:
		MessageBox("モニタに 32,24,16 ビットタイプ以外が指定されました","確認",0);
		dcMem.SelectObject(pOldBmp);
		return -1;
	}
	
		// バッファはもう不要
	delete [] pbmpc;
	dcMem.SelectObject(pOldBmp);

	return 0;		// 正常
}


トップ > Win7でのソフトの推奨使用法 > 頁トップ