【OpenCV】ガンマ補正

OpenCVにはcvGammaのようなガンマ補正の関数は無いのですが、ルックアップテーブルを使った輝度値変換の関数(cvLUT)はあるので、これを使ってガンマ補正を行いたいと思います。

 

以下、ガンマ補正のサンプルプログラムです。OpenCV2.2を用いて、作成しています。

#include "stdafx.h"

//プロジェクトのプロパティ⇒C/C++⇒全般 の追加のインクルードディレクトリに
// 『C:\OpenCV2.2\include』を追加のこと
#include "opencv2\\opencv.hpp"
 
#ifdef _DEBUG
    //Debugモードの場合
    #pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_core220d.lib")
    #pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_imgproc220d.lib")
    #pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_highgui220d.lib")
#else
    //Releaseモードの場合
    #pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_core220.lib")
    #pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_imgproc220.lib")
    #pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_highgui220.lib")
#endif
//---------------------------------------------------------------  
//【関数名 】:cv_Gamma  
//【処理概要】:ガンマ補正  
//【引数  】:src        = 入力画像  
//      :dst        = 出力画像  
//      :gamma   = ガンマ補正値  
//【戻り値 】:なし  
//【備考  】:モノクロ/カラー対応可  
//      :カラーの場合はRGB全て同じガンマ補正値  
//---------------------------------------------------------------   
void cv_Gamma(IplImage* src, IplImage* dst, double gamma){  

	int i;  

	uchar LUT[256];  
              
	//ガンマ補正テーブルの作成  
	for (i = 0; i < 256; i++)
	{  
		LUT[i] = (int)(pow((double)i / 255.0, 1.0 / gamma) * 255.0);  
	}  

	//CvMatへ変換  
	CvMat lut_mat = cvMat(1, 256, CV_8UC1, LUT);  

	//ルックアップテーブル変換  
	cvLUT(src, dst, &lut_mat);  
}

int _tmain(int argc, _TCHAR* argv[])
{
	//画像データの読込
	IplImage* src = cvLoadImage("Girl.bmp", CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR);
	if (src == NULL){
		return 0;
	}
 
	//表示ウィンドウの作成
	cvNamedWindow("src");
	cvNamedWindow("dst");
 
	//処理後画像データの確保
	IplImage* dst = cvCreateImage(cvGetSize(src), src->depth, src->nChannels);
 
	//ガンマ補正
	cv_Gamma(src, dst, 2.0);
 
	//画像の表示
	cvShowImage ("src", src);
	cvShowImage ("dst", dst);
 
	//キー入力待ち
	cvWaitKey (0);
 
	//全てのウィンドウの削除
	cvDestroyAllWindows();
 
	//画像データの解放
	cvReleaseImage(&src);
	cvReleaseImage(&dst);

	return 0;
}

サンプルプログラムのダウンロードはこちらより。

gamma-correction-opencv-sample.zip

(OpenCV2.2対応。Visual Studio 2010 C++ Expressにより作成)

 

実行例

 

OpenCV2.2の使用方法および、ガンマ補正については下記のページを参照下さい。

 

OpenCVへ戻る

 

ガンマ補正(ルックアップテーブルの例)

ルックアップテーブルLookup Table【略LUT】)は、ある値の答えが必ず1つの値となる場合、あらかじめ答えを計算しておき、配列に格納しておくことで、毎回の計算をすることなく、 配列(テーブル)を参照することで効率的に処理を行う手法です。

 

画像処理では、コントラスト調整やガンマ補正などによく用いられます。

 

例えば、ガンマ補正の場合、ガンマ補正値をγ、補正前の輝度値をsrc
補正後の輝度値をdstとすると、補正式は

 

 

となりますが、この計算を全ての画素に対して処理を行うのは非常に非効率です。
そこで、例えばLUTという配列に、ガンマ補正の計算結果を以下のように格納しておきます。

 

src 0 1 2 3 ・・・ 252 253 254 255
dst 0 16 23 28 ・・・ 253 254 254 255

 

 

あとは、各画素に対して、

 

dst = LUT[src]

 

という変換をすればよいだけなので、高速に処理を行うことができます。
こうする事でルックアップテーブルを用いない場合は、画素数分のガンマ補正計算をしないといけないのが、ルックアップテールを用いる事で、この例の場合では、たかだか256回の計算だけで済みます。

 

ガンマ補正例

  • ガンマ補正前

 

  • ルックアップテーブル

 

  • ガンマ補正後

 

他にも二値化処理や、輝度値を0~255の範囲内に収めるための if 文の代わりなどにも応用できる場合があるので、処理を高速にしたい場合は、このルックアップテーブルが使えるかどうか?検討してみるのも良いと思います。

 

画像処理アルゴリズムへ戻る

 

【C++/CLI】配列の確保(Arrayクラス)

配列の確保は

 

array<型>^変数名 = gcnew array<型>(要素数);

 

のように行います。

 

多次元の場合は

 

array<型, 次数>^変数名 = gcnew array<型, 次数>(0次の要素数, 1次の要素数, 2次の要素数・・・);

 

コード例は以下の通り

 

///////////////////////////////////////////////
// 配列の初期化
///////////////////////////////////////////////
array^a = gcnew array {0, 1, 2, 3, 4};
// a[0] = 0
// a[1] = 1
// a[2] = 2
// a[3] = 3
// a[4] = 4

///////////////////////////////////////////////
// 配列の確保
///////////////////////////////////////////////
array^b = gcnew array(6);
int Len = b->Length;	// 配列の全個数

for (int i = 0; i < Len; i++)
{
	b[i] = i * 2;
}
// b[0] = 0
// b[1] = 2
// b[2] = 4
// b[3] = 6
// b[4] = 8
// b[5] = 10

///////////////////////////////////////////////
// 二次元配列の確保
///////////////////////////////////////////////
array<int, 2>^c = gcnew array<int, 2>(2, 3);

int iLen = c->GetLength(1);	// 1次の配列の個数  (= 3)
int jLen = c->GetLength(0);	// 0次の配列の個数 (= 2)

for (int j = 0; j < jLen; j++)
{
	for (int i = 0; i < iLen; i++)
	{
		c[j, i] = j * 10 + i;
	}
}
//c[0, 0] = 0	c[0, 1] = 1		c[0, 2] = 2
//c[1, 0] = 10	c[1, 1] = 11	c[1, 2] = 12

///////////////////////////////////////////////
// ポインタで二次元配列を参照し一次元配列へ代入
///////////////////////////////////////////////
pin_ptrp = &c[0, 0];
array^d = gcnew array(6);

for (int i = 0; i < iLen * jLen; i++){
	d[i] = p[i];
}
// d[0] = 0
// d[1] = 1
// d[2] = 2
// d[3] = 10
// d[4] = 11
// d[5] = 12

ここで、多次元配列の場合、メモリに格納されている順番は右側の添え字から順番に格納されているので、ご注意下さい。
ポインタで参照している部分を参考にして下さい。

 

良く使うメソッド、プロパティは以下の通り

メソッド

  • GetLength(dimension)
    指定した次元(dimension)の要素数を取得します。
  • Resize(array, newSize)
    配列(array)のサイズをnewSizeに変更し、各要素は新しい配列へコピーされます。

 

プロパティ

  • Length
    全ての次元内の要素数を取得します。