//---------------------------------------------------------------------
//概要		:ヒストグラム
//File Name	:histogram.cpp
//Library	:OpenCV for MS-Windows 1.0
//---------------------------------------------------------------------

#include <stdio.h>
#include <cv.h>
#include <highgui.h>

#define DIMENSIONS	1				//	ヒストグラムの次元数
#define UNIFORM	1					//	一様性に関するフラグ
#define	ACCUMULATE	0				//	計算フラグ
#define SCALE_SHIFT	0				//	スケーリングした入力配列の要素に加える値

#define HISTOGRAM_WIDTH	512			//	ヒストグラムを描く画像の横幅
#define HISTOGRAM_HEIGHT	100		//	ヒストグラムを描く画像の縦幅

#define TRACKBAR_MAX_VALUE	200		//	トラックバーの最大値

#define LINE_THICKNESS	-1			//	線の太さ
#define	LINE_TYPE	8				//	線の種類
#define SHIFT	0					//	座標の小数点以下の桁を表すビット数

CvMat* lookUpTableMatrix;			//	濃度対応行列
unsigned char lookUpTable[256];		//	濃度対応表
CvHistogram *histogram;				//	ヒストグラム

int histogramSize = 128;			//	ヒストグラムに描画される縦棒の数
int trackbarBrightness = 100;		//	トラックバーでの輝度の表示位置の初期値
int trackbarContrast = 100;			//	トラックバーでのコントラストの表示位置の初期値

float range_0[] = { 0, 256 };		//	ヒストグラムの範囲
float* ranges[] = { range_0 };		//	ヒストグラム各次元の範囲を示す配列のポインタ

IplImage *grayImage = NULL;			//	グレースケール画像用IplImage
IplImage *destinationImage = NULL;	//	結果出力用IplImage
IplImage *histogramImage = NULL;	//	ヒストグラム画像用IplImage

char windowNameDestination[] = "Destination";	//	処理結果を表示するウィンドウの名前
char windowNameHistogram[] = "Histogram";		//	ヒストグラムを表示するウィンドウの名前

//
//	トラックバーの値が変わった時に呼ばれる関数
//
//	引数:
//		pos : トラックバーの値
//
void changeBrightnessAndContrast( int pos ) {
	
    int brightness = trackbarBrightness - TRACKBAR_MAX_VALUE / 2;	//	輝度値
    int contrast = trackbarContrast - TRACKBAR_MAX_VALUE / 2;		//	コントラスト
    int bin_w;														//	ヒストグラムの縦棒の横幅
    float max_value = 0;											//	ヒストグラムの最大値

	//	濃度対応表を生成する
    if ( contrast > 0 ) {
        double delta = 127.0 * contrast / 100.0;
        double a = 255.0 / ( 255.0 - delta * 2 );
        double b = a * ( brightness - delta );
        for (int i = 0; i < 256; i++ ){
			//	変換後の階調を求める
            int v = cvRound( a * i + b );
			if( v < 0 ){
                v = 0;
			}
			if( v > 255 ){
                v = 255;
			}
            lookUpTable[i] = ( unsigned char )v;
        }
    } else {
        double delta = -128.0 * contrast / 100.0;
        double a = (256.0 - delta * 2.0) / 255.0;
        double b = a * brightness + delta;
        for(int i = 0; i < 256; i++ ){
            int v = cvRound( a * i + b);
			if( v < 0 ){
                v = 0;
			}
			if( v > 255 ){
                v = 255;
			}
            lookUpTable[i] = ( unsigned char )v;
        }
    }

	//	濃度対応行列を用いた濃度階調変換を行う
	cvLUT( grayImage, destinationImage, lookUpTableMatrix );

	//	画像を表示する
	cvShowImage( windowNameDestination, destinationImage );

	//	画像のヒストグラムを計算する
    cvCalcHist( &destinationImage, histogram, ACCUMULATE, NULL );
		
	//	ヒストグラム値の最大値を得る
    cvGetMinMaxHistValue( histogram, NULL, &max_value, NULL, NULL );
    
	//	ヒストグラムを最大値によって正規化する
	cvConvertScale( histogram->bins, histogram->bins, 
		( ( double )histogramImage->height ) / max_value, SCALE_SHIFT );

	//	ヒストグラム画像を白で初期化する
	cvSet( histogramImage, cvScalarAll( 255 ), NULL );

	//	ヒストグラムの縦棒の横幅を計算する
    bin_w = cvRound( ( double )histogramImage->width / histogramSize );

	//	ヒストグラムの縦棒を描画する
	for ( int i = 0; i < histogramSize; i++ ) {
        cvRectangle(
			histogramImage,
			cvPoint( i * bin_w, histogramImage->height ),
			cvPoint( ( i + 1 ) * bin_w,histogramImage->height - cvRound( cvGetReal1D( histogram->bins, i) ) ),
			cvScalarAll( 0 ),
			LINE_THICKNESS,
			LINE_TYPE,
			SHIFT
		);
	}
  	//	画像を表示する
    cvShowImage( windowNameHistogram, histogramImage );
}

int main( int argc, char **argv ){
	char windowNameGray[] = "Gray";					//	グレースケール画像を表示するウィンドウの名前
	char trackbarNameBrightness[] = "Brightness";	//	輝度を変更するトラックバーの名前
	char trackbarNameContrast[] = "Contrast";		//	コントラストを変更するトラックバーの名前

	//	画像を読み込む
    IplImage *sourceImage = cvLoadImage( "image/baboon.jpg", CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR );

	if( sourceImage == NULL ){
		//	画像が見つからなかった場合
		printf( "画像が見つかりません\n" );
		return -1;
	}

	//	画像を生成する
	grayImage = cvCreateImage( cvGetSize( sourceImage ), IPL_DEPTH_8U, 1 );
	destinationImage = cvCreateImage( cvGetSize( sourceImage ), IPL_DEPTH_8U, 1 );
	histogramImage = cvCreateImage( cvSize( HISTOGRAM_WIDTH,HISTOGRAM_HEIGHT ), IPL_DEPTH_8U, 1 );
	
	//	BGRからグレースケールに変換する
	cvCvtColor( sourceImage, grayImage, CV_BGR2GRAY );

	//	画像をコピーする
	destinationImage = cvCloneImage( grayImage );

	//	ヒストグラムを生成する
	histogram = cvCreateHist( DIMENSIONS, &histogramSize, CV_HIST_ARRAY, ranges, UNIFORM );

	//	行列を生成する
	lookUpTableMatrix = cvCreateMatHeader( 1, 256, CV_8UC1 );

	//	濃度対応行列に濃度対応表をセットする
	cvSetData( lookUpTableMatrix, lookUpTable, NULL );

	//	ウィンドウを生成する
	cvNamedWindow( windowNameGray, CV_WINDOW_AUTOSIZE );
	cvNamedWindow( windowNameDestination, CV_WINDOW_AUTOSIZE );
	cvNamedWindow( windowNameHistogram, CV_WINDOW_AUTOSIZE );

	//	画像を表示する
    cvShowImage( windowNameGray, grayImage );

	//	トラックバーを生成する
	cvCreateTrackbar( trackbarNameBrightness, windowNameDestination, &trackbarBrightness, 
		TRACKBAR_MAX_VALUE, changeBrightnessAndContrast );
    cvCreateTrackbar( trackbarNameContrast, windowNameDestination, &trackbarContrast, 
		TRACKBAR_MAX_VALUE, changeBrightnessAndContrast );

	changeBrightnessAndContrast( 0 );

	//	キー入力を待つ
	cvWaitKey( 0 );

	//	メモリを解放する
	cvReleaseImage( &sourceImage );
	cvReleaseImage( &grayImage );
	cvReleaseImage( &destinationImage );
	cvReleaseImage( &histogramImage );
	//	ウィンドウを破棄する
	cvDestroyWindow( windowNameGray );
	cvDestroyWindow( windowNameDestination );
	cvDestroyWindow( windowNameHistogram );

	return 0;
}