画像のヒストグラムを表示させる(その2)

ヒストグラムプログラム

前回の続きです。 OpenCVでのヒストグラムの使用法について解説していきます。

前回までは、CustomViewの四角形をクリックしたら、画像を選択・表示できるところまでやりました。

次にすることは、画像のヒストグラムを調べて、CustomViewに表示することです。 その方法を説明します。

変数を導入

はじめに、View.hに変数を追加します。

@interface View : NSView{
@public
bool flag;
int histSize;
CvHistogram* hist;
}
@end

まず、flagは、ヒストグラムを取得した際にtrueになり、そうでなければfalseとなります。

ヒストグラムを表示可能かの判定フラグです。 histSizeはヒストグラムのサイズ(256)です。

そして、histがヒストグラムの構造体です。

次に、起動時にflagをfalse、histSizeを256に設定します。View.mを以下のように変更します。

- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
flag=false;
histSize=256;
}

return self;
}

ヒストグラムを取得する

では、選択した際にヒストグラムも取得するようにします。

以下のようにView.mにコードを追加してください。

-(void)mouseUp:(NSEvent *)theEvent
{
    NSOpenPanel *openPanel=[NSOpenPanel openPanel];//許容ファイルの種類を設定
    NSArray *allowedFileTypes=[NSArray arrayWithObjects:@"png",@"PNG",nil];
    [openPanel setAllowedFileTypes:allowedFileTypes];//ダイアローグを開く
    NSInteger pressButton=[openPanel runModal];
    if(pressButton ==NSOKButton)
    {
        //OKボタンが押された場合
        NSURL * filePath=[openPanel URL];  //パスを取得
        NSString* fileName=[filePath path];//形式を変換
        const char* path=[fileName UTF8String];//形式を変換
        //画像を読み込む
        IplImage* image;
        IplImage* gray;
        image=cvLoadImage(path,CV_LOAD_IMAGE_ANYCOLOR);
        if(image!=NULL){
            //読み込めたら
            //大きさや色の深さを設定
            gray=cvCreateImage(cvGetSize(image),IPL_DEPTH_8U,1);
            //グレーへ変換
            cvCvtColor(image,gray,CV_BGR2GRAY);
            cvShowImage("window",gray);//画像を表示

            //追加************************
            float range[]={0,255};
            float* ranges[]={range};
            hist=cvCreateHist(1,&histSize,CV_HIST_ARRAY,ranges,1);
            cvCalcHist(&gray,hist,0,NULL);
            flag=true;
            [self setNeedsDisplay:YES];
            //ここまで*********************

            cvReleaseImage(&image);     //メモリの開放
            cvReleaseImage(&gray);
        }
    }

}

ここで、「追加***」から「ここまで***」が追加した部分です。

cvCreateHistでヒストグラムを作成しています。

histSizeがヒストグラムの階級数0-255です。

rangesはヒストグラムを保存するための配列です。

rangesでメモリを確保しているというわけです。

そして、cvCalcHistで、画像のヒストグラムの計算をしています。

ヒストグラムを取得したのでflagをtrueにしています。 [self setNeedsDisplay:YES]で画面の最描写を行います。

ヒストグラムを表示する

では、ヒストグラムを表示するプログラムをかきます。

- (void)drawRect:(NSRect)dirtyRect
{
// Drawing code here.
[[NSColor colorWithDeviceRed:1.0 green:1.0 blue:1.0 alpha:1.0] set];
NSRect rect = NSMakeRect(0, 0, 440, 320);//設定
[NSBezierPath fillRect:rect];
[[NSColor colorWithDeviceRed:0.0 green:0.0 blue:0.0 alpha:1.0] set];
NSFrameRect(rect);
//追加*******
if(flag){
for(int i=0;i<histSize;i++){
[NSBezierPath strokeLineFromPoint:NSMakePoint(i,0) toPoint:NSMakePoint(i,(int)cvGetReal1D(hist->bins,i))];
}
}
//ここまで****
}

cvGetReal1Dでhistのヒストグラムを読み取り、線で描写しています。 これで実行すると以下のようになります。

ヒストグラムの値が大きすぎて枠を超えています。 これを修正するために正規化を行います。

ヒストグラムの正規化

修正するため、以下のように書き換えましょう

- (void)drawRect:(NSRect)dirtyRect
{
// Drawing code here.
[[NSColor colorWithDeviceRed:1.0 green:1.0 blue:1.0 alpha:1.0] set];
NSRect rect = NSMakeRect(0, 0, 440, 320);//設定
[NSBezierPath fillRect:rect];
[[NSColor colorWithDeviceRed:0.0 green:0.0 blue:0.0 alpha:1.0] set];
NSFrameRect(rect);
if(flag){
//追加******
float max;
//ヒストグラムの最大値を取得
cvGetMinMaxHistValue(hist,NULL,&max,NULL,NULL);
//正規化する
cvConvertScale(hist->bins,hist->bins,(double)320/max,0);
//ここまで****
for(int i=0;i<histSize;i++){
[NSBezierPath strokeLineFromPoint:NSMakePoint(i,0) toPoint:NSMakePoint(i,(int)cvGetReal1D(hist->bins,i))];
}
}
}

まず、cvGetMinMaxHistValueで最大値maxを取得します。 そして、cvConvertScaleで正規化しています。 これで実行すれば以下のように表示されます。

お疲れ様です。これで解説を終わります。

著者:安井 真人(やすい まさと)