画像のコンボリュート法によるぼかし処理

解説

 コンボリュート法によるぼかし処理。"コンボリュート"なんて聞くと意味不明だが処理自体 は非常に簡単。あるピクセルの周り9ピクセルの平均を新しい色値にしている。

 ここでは下のような3行3列の処理パラメータのテーブルを用意してぼかし処理を実行してい る。このパラメータの値を変えればぼかし以外にもシャープ処理やエッジ検出処理などさまざま な画像処理を行える。
111
111
111

 ぼかし自体も色々な方法で行える。例えば以下のようなパラメータでもOK。左のパラメー タは上下左右のピクセルのみの平均を新しい色値に、右のパラメータは周囲のピクセルに2 倍の重みを置いた平均を新しい色値として採用するぼかし処理になる。
010
101
010
 
222
212
222


実行例(上から文章中で紹介したパラメータ順での実行結果)





ソースコード

 GDI+を利用した画像処理テストのスケルトンの 画像処理部分を以下のように変更する。



		////////////////////////////
		//画像処理
		//
		UINT		x;
		UINT		y;
		UINT		nPos;
		RGBTRIPLE*	pTmp;
		Gdiplus::BitmapData		bitmapData;

		pImage->LockBits(&Gdiplus::Rect(0,0,pImage->GetWidth(),pImage->GetHeight())
			,Gdiplus::ImageLockModeRead | Gdiplus::ImageLockModeWrite,PixelFormat24bppRGB,&bitmapData);

		pTmp = (RGBTRIPLE*)bitmapData.Scan0;

		int		pnOperate[9];

		//ぼかし処理パラメータ1
		pnOperate[0] = 1;
		pnOperate[1] = 1;
		pnOperate[2] = 1;
		pnOperate[3] = 1;
		pnOperate[4] = 1;
		pnOperate[5] = 1;
		pnOperate[6] = 1;
		pnOperate[7] = 1;
		pnOperate[8] = 1;

		////ぼかし処理パラメータ2
		//pnOperate[0] = 0;
		//pnOperate[1] = 1;
		//pnOperate[2] = 0;
		//pnOperate[3] = 1;
		//pnOperate[4] = 0;
		//pnOperate[5] = 1;
		//pnOperate[6] = 0;
		//pnOperate[7] = 1;
		//pnOperate[8] = 0;

		////ぼかし処理パラメータ3
		//pnOperate[0] = 2;
		//pnOperate[1] = 2;
		//pnOperate[2] = 2;
		//pnOperate[3] = 2;
		//pnOperate[4] = 1;
		//pnOperate[5] = 2;
		//pnOperate[6] = 2;
		//pnOperate[7] = 2;
		//pnOperate[8] = 2;

		{
			int			i;
			long		pnRefPos[9];
			int			nTmpR;
			int			nTmpG;
			int			nTmpB;
			int			nDiv;
			int			nStride;
			int			nPixelCount;
			RGBTRIPLE*	prgbTmp;

			nDiv = 0;
			for(i = 0; i < 9; i++)
				nDiv += pnOperate[i];
			if(nDiv < 0)
				nDiv *= -1;

			nStride = bitmapData.Stride / sizeof(RGBTRIPLE);
			nPixelCount = nStride * pImage->GetHeight();

			prgbTmp = new RGBTRIPLE[nPixelCount];
			for(y = 1; y < pImage->GetHeight() - 1; y++)
			{
				nPos = y * nStride;

				for(x = 1; x < pImage->GetWidth() - 1; x++, nPos++)
				{
					//あるピクセルの上下左右のピクセルオフセット計算
					//いちいち計算するのは効率が悪いがソースコードは見やすい?
					pnRefPos[0] = nPos - nStride - 1;
					pnRefPos[1] = pnRefPos[0] + 1;
					pnRefPos[2] = pnRefPos[0] + 2;
					pnRefPos[3] = nPos - 1;
					pnRefPos[4] = nPos;
					pnRefPos[5] = nPos + 1;
					pnRefPos[6] = nPos + nStride - 1;
					pnRefPos[7] = pnRefPos[6] + 1;
					pnRefPos[8] = pnRefPos[6] + 2;

					//テーブルの値を使って計算
					nTmpR = 0;
					nTmpG = 0;
					nTmpB = 0;
					for(i = 0; i < 9; i++)
					{
						if(pnRefPos[i] < 0 || pnRefPos[i] >= nPixelCount)
							continue;

						nTmpR += pTmp[pnRefPos[i]].rgbtRed	* pnOperate[i];
						nTmpG += pTmp[pnRefPos[i]].rgbtGreen* pnOperate[i];
						nTmpB += pTmp[pnRefPos[i]].rgbtBlue	* pnOperate[i];
					}
					
					//平均値を求めるために除算
					if(nDiv != 1 && nDiv != 0)
					{
						nTmpR /= nDiv;
						nTmpG /= nDiv;
						nTmpB /= nDiv;
					}

					//色ビット数を超えないように制限
					nTmpR = (nTmpR < 0) ? 0 : nTmpR;
					nTmpR = (nTmpR > 255) ? 255 : nTmpR;
					nTmpG = (nTmpG < 0) ? 0 : nTmpG;
					nTmpG = (nTmpG > 255) ? 255 : nTmpG;
					nTmpB = (nTmpB < 0) ? 0 : nTmpB;
					nTmpB = (nTmpB > 255) ? 255 : nTmpB;

					prgbTmp[nPos].rgbtBlue	= (BYTE)nTmpB;
					prgbTmp[nPos].rgbtGreen	= (BYTE)nTmpG;
					prgbTmp[nPos].rgbtRed	= (BYTE)nTmpR;
				}
			}

			memcpy(pTmp,prgbTmp,sizeof(RGBTRIPLE) * nPixelCount);
			delete	prgbTmp;
		}
		pImage->UnlockBits(&bitmapData);

※ソースコード中に既知のバグというかミスコーディングがあります。nStrideを用意 していますがこれが諸悪の原因です。画像にどんな変化が加わるかを見るだけのテスト用途のため修正 はしていません。



カテゴリー「VC++ TIPS」 のエントリー