OpenCV関係(VB2005)


4.サンプル4(ヒストグラム)

VB(VB2005)によるOpenCV使用のサンプルプログラムの4番目です。
全ソースは下記からダウンロードできます。
OpenCVHistogram.lzh


このサンプルプログラムは、OpenCV付属サンプルの「demhist.exe」をVB2005で作り直したものになります。
読み込んだイメージのヒストグラムを取得/表示し、ブライトネスとコントラストを調整できます。

基本的な動きはOpenCVサンプルと同じですが、HighGUIを使わずにWindowsのコントロールを使っています。

HighGUIを使わずにWindowsのコントロールを使っている点を除いては、
基本的にOpenCVのサンプルと同じです。

ただし、API呼び出しの関係でアンマネージ領域の操作部分で何箇所かコードが膨れているところはあります。
またヒストグラムの表示などはデータの取得以外はVB用のコードに最適化(?)しています。

メインフォームのコードは下記になります。

Option Explicit On
Option Strict On

Imports System.Runtime.InteropServices
Imports Histogram.cxTypes
Imports Histogram.cvTypes
Imports Histogram.Cv
Imports Histogram.CxCore
Imports Histogram.HighGUI


Public Class Frm_Image

    '----------------------------------------------------------
    '定数/定義

    Private IMAGEFILENAME As String = "\samples\c\baboon.jpg"

    '----------------------------------------------------------
    '内部データ

    Private mSrcImg As New IplImageWrapper          '補正前イメージ
    Private mDstImg As New IplImageWrapper          '補正後イメージ

    Private mHistPtr As IntPtr                      'CvHistogramのポインタ
    Private mHistSize As Integer = 64               'ヒストグラム個数
    'Private mHistSizePtr As IntPtr                  'ヒストグラム個数のポインタ
    Private mRange0() As Single = {0, 256}          'ヒストグラムの値範囲
    Private mRange0Ptr As IntPtr                    '値範囲のポインタ
    Private mRangeTBLPtr As IntPtr                  '値範囲テーブルのポインタ

    Private mLUTPtr As IntPtr
    Private mLUTMatPtr As IntPtr

    Private WithEvents mHistForm As Frm_Hist        'ヒストグラム表示フォーム

    '----------------------------------------------------------
    '----------------------------------------------------------

    'フォームロード時
    Private Sub Frm_Image_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        '初期処理
        'mHistSizePtr = Marshal.AllocHGlobal(Marshal.SizeOf(GetType(Integer)))               'アンマネージ領域確保
        'Marshal.WriteInt32(mHistSizePtr, 0, mHistSize)          'アンマネージ領域に転送
        mRange0Ptr = Marshal.AllocHGlobal(Marshal.SizeOf(GetType(Single)) * mRange0.Length) 'アンマネージ領域確保
        Marshal.Copy(mRange0, 0, mRange0Ptr, mRange0.Length)    'アンマネージ領域に転送
        mRangeTBLPtr = Marshal.AllocHGlobal(Marshal.SizeOf(GetType(IntPtr)) * 1)            'アンマネージ領域確保
        Marshal.WriteIntPtr(mRangeTBLPtr, 0, mRange0Ptr)        'アンマネージ領域に転送

        'ヒストグラムの準備
        mHistPtr = cvCreateHist(1, mHistSize, CV_HIST_ARRAY, mRangeTBLPtr, 1)   'ヒストグラムの生成
        mLUTPtr = Marshal.AllocHGlobal(256)                     'アンマネージ領域確保
        mLUTMatPtr = cvCreateMatHeader(1, 256, CV_8UC1)         '変換用行列のヘッダ作成
        cvSetData(mLUTMatPtr, mLUTPtr, 0)                       'ヘッダとデータの連結

        'ヒストグラム表示用フォームの準備
        mHistForm = New Frm_Hist
        mHistForm.Show()

        'イメージ読み込み
        LoadImage(OpenCVUtility.GetDLLPath() + IMAGEFILENAME)

    End Sub

    'フォームが閉じられるとき
    Private Sub Frm_Image_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing

        '後始末
        mSrcImg.Release()
        mDstImg.Release()
        cvReleaseHist(mHistPtr)
        Marshal.FreeHGlobal(mRange0Ptr)
        Marshal.FreeHGlobal(mRangeTBLPtr)
        Marshal.FreeHGlobal(mLUTPtr)
        cvReleaseMat(mLUTMatPtr)

    End Sub

    '----------------------------------------------------------

    'イメージの読み込み
    Private Sub LoadImage(ByVal Filename As String)

        '解放
        mSrcImg.Release()
        mDstImg.Release()

        'クリア
        Scr_Brightness.Value = 0
        Scr_Contrast.Value = 0

        '読み込み
        mSrcImg.Ptr = cvLoadImage(Filename, 0)
        If mSrcImg.Ptr = IntPtr.Zero Then
            MsgBox("イメージを読み込めません。- " + Filename)
            Exit Sub
        End If

        'ウィンドウサイズ変更
        Pic_Image.Size = mSrcImg.Size
        Dim sz As Size = Pic_Image.Size
        sz.Height += Pnl_Control.Height
        Me.ClientSize = sz

        '出力イメージ作成
        mDstImg.Ptr = cvCloneImage(mSrcImg.Ptr)

        'イメージ表示
        mDstImg.Update()
        Pic_Image.Image = mDstImg.Bitmap
        Pic_Image.Refresh()

        'ヒストグラム表示
        UpdateHistogram()

    End Sub

    '----------------------------------------------------------

    'ブライトネス変更
    Private Sub Scr_Brightness_Scroll(ByVal sender As System.Object, ByVal e As System.Windows.Forms.ScrollEventArgs) Handles Scr_Brightness.Scroll

        '値表示
        Lbl_Brightness.Text = Scr_Brightness.Value.ToString

        'イメージ更新
        UpdateImage()

    End Sub

    'コントラスト変更
    Private Sub Scr_Contrast_Scroll(ByVal sender As System.Object, ByVal e As System.Windows.Forms.ScrollEventArgs) Handles Scr_Contrast.Scroll

        '値表示
        Lbl_Contrast.Text = Scr_Contrast.Value.ToString

        'イメージ更新
        UpdateImage()

    End Sub

    '----------------------------------------------------------

    'ヒストグラム表示フォームのサイズ変更時
    Private Sub mHistForm_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles mHistForm.Resize

        'ヒストグラム表示
        UpdateHistogram()

    End Sub

    '----------------------------------------------------------

    'イメージ更新
    Private Sub UpdateImage()

        'イメージが読み込まれているか?
        If mSrcImg.Ptr = IntPtr.Zero Then Exit Sub '読み込まれてない

        '変換用テーブルの係数計算
        Dim brightness As Integer = Scr_Brightness.Value
        Dim contrast As Integer = Scr_Contrast.Value
        Dim delta As Double
        Dim a As Double, b As Double
        If contrast > 0 Then
            delta = 127.0 * contrast / 100
            a = 255.0 / (255 - delta * 2)
            b = a * (brightness - delta)
        Else
            delta = -128.0 * contrast / 100
            a = (256.0 - delta * 2) / 255.0
            b = a * brightness + delta
        End If

        '変換用テーブルの更新
        For i As Integer = 0 To 255
            Dim v As Integer = CInt(a * i + b)
            If v < 0 Then v = 0
            If v > 255 Then v = 255
            Marshal.WriteByte(mLUTPtr, i, CByte(v))
        Next

        '変換
        cvLUT(mSrcImg.Ptr, mDstImg.Ptr, mLUTMatPtr)

        '表示
        mDstImg.Update()
        Pic_Image.Image = mDstImg.Bitmap
        Pic_Image.Refresh()

        'ヒストグラム表示
        UpdateHistogram()

    End Sub

    'ヒストグラム表示の更新
    Private Sub UpdateHistogram()

        '出力イメージがあるか?
        If mDstImg.Ptr = IntPtr.Zero Then Exit Sub '出力イメージがない

        '表示範囲取得
        Dim sx As Integer = mHistForm.Pic_Hist.ClientSize.Width
        Dim sy As Integer = mHistForm.Pic_Hist.ClientSize.Height
        If (sx = 0) Or (sy = 0) Then Exit Sub '表示できない

        'ビットマップ作成
        mHistForm.Pic_Hist.Image = New Bitmap(sx, sy)

        '描画開始
        Dim g As Graphics = Graphics.FromImage(mHistForm.Pic_Hist.Image)
        g.Clear(Color.White)

        'ヒストグラムの取得
        Dim DstImgPtrPtr As IntPtr 'イメージへのポインタのポインタ
        DstImgPtrPtr = Marshal.AllocHGlobal(Marshal.SizeOf(GetType(IntPtr)))    'アンマネージ領域確保
        Marshal.WriteIntPtr(DstImgPtrPtr, 0, mDstImg.Ptr)   'アンマネージ領域に書込み
        cvCalcArrHist(DstImgPtrPtr, mHistPtr, 0, IntPtr.Zero)
        Marshal.FreeHGlobal(DstImgPtrPtr)
        'ハッキリいってこの辺は美しくない

        'スケーリング(各要素を0〜syの値にする)
        Dim histdata As CvHistogram
        histdata = PtrToStructure(Of CvHistogram)(mHistPtr) 'CvHistogramの内容をマネージ領域にコピーする
        Dim minval As Single, maxval As Single
        cvGetMinMaxHistValue(mHistPtr, minval, maxval, IntPtr.Zero, IntPtr.Zero)
        If maxval = 0 Then maxval = Integer.MaxValue
        cvConvertScale(histdata.bins, histdata.bins, sy / maxval, 0)

        'グラフ描画
        Dim bin_w As Integer = CInt(sx / mHistSize)
        For i As Integer = 0 To mHistSize - 1
            Dim v As Integer = CInt(cvGetReal1D(histdata.bins, i))
            g.FillRectangle(Brushes.Black, bin_w * i, sy - v, bin_w, v)
        Next

        '描画終了
        g.Dispose()
        g = Nothing
        mHistForm.Pic_Hist.Refresh()

    End Sub


End Class


cvCreateHist()の第4引数はfloat型配列のポインタテーブルのポインタになる訳ですが、
スマートなマーシャリングのやり方を知らないので(^^;;、
アンマネージ領域にテーブルを作ってそのアドレスを渡しています。

またcvScale()はcvConvertScale()、cvCalcHist()はcvCalcArrHist()など
OpenCVサンプル内では別名で定義されている関数は元の関数を呼び出すようにしています。


上に戻る