Visual Basic でOpenCV(29) - フーリエ変換・逆変換

Visual BasicOpenCVを用い、画像に対する離散フーリエ変換、および逆変換を行う例を紹介します。

逆離散フーリエ変換

先の例を拡張し、DFT したデータをIDFT して元の画像に戻ることを確認します。画像に対してDFT を実施した結果を可視化し、その結果をIDFT して元の画像に戻ることを示します。これらを3 つのフォームで表示します。

Form1.vb

ユーザーインターフェースは少し変わります。以降にソースリスト一部を示します。

    :
Public Class Form1
    Private ReadOnly ttl As String = "sample"
    Private ReadOnly mFormDft As Form2 = Nothing
    Private ReadOnly mFormIdft As Form2 = Nothing
    Private ccvfunc As CCvFunc = Nothing

    Public Sub New()
        MyBase.New
        InitializeComponent()

        :

        mFormDft = New Form2 With {.Text = "DFT"}
        mFormIdft = New Form2 With {.Text = "IDFT"}
        ccvfunc = New CCvFunc()
    End Sub

    :
    Private Sub ToolMenuEffect_Click(sender As Object, e As EventArgs) _
                                                Handles ToolMenuEffect.Click
        Try
            If PBox.Image Is Nothing Then Return         ' 読み込んでいるか

            Cursor = Cursors.WaitCursor

            Dim result = ccvfunc.DoCvFunction()
            mFormDft.DoCvShow(ccvfunc, result.Item1)
            mFormIdft.DoCvShow(ccvfunc, result.Item2)

        Catch ex As Exception
            MessageBox.Show(ex.Message)
        Finally
            Cursor = Cursors.Default
        End Try
    End Sub
    :

本プログラムは、フォームが3つ必要です。Form1は、これまでと同じです。Form2をフーリエ変換パワースペクトル、DFT)と逆フーリエ変換(IDFT)の表示に用います。このため、2つのForm2インスタンスを生成し、かつタイトルの表示を変更します。そして、DoCvFunctionメソッドは、2つのBitmapオブジェクトを返しますので、それぞれを生成した2つのフォームに表示します。

CCvFunc.vb

以降に、実際にIDFT を行うCCv の派生クラスCCvFunc を示します。

    :
Public Function DoCvFunction() As (Bitmap, Bitmap)
    Dim dft As Mat = mat2Dft(mSrc)          ' image to DFT
    Dim dft8u As Mat = dft2dispMat(dft)     ' DFT to display image
    Dim dispDft As Mat = swapDft(dft8u)     ' swap: 1 <-> 4, 2 <-> 3

    Dim dispIdft As Mat = dft2idft8u(dft, mSrc)
    Return (OpenCvSharp.Extensions.BitmapConverter.ToBitmap(dispDft),
            OpenCvSharp.Extensions.BitmapConverter.ToBitmap(dispIdft))
End Function

Private Function mat2Dft(src As Mat) As Mat
    :

Private Function dft2dispMat(complex As Mat) As Mat
    :

Private Function swapDft(src As Mat) As Mat
    :

Private Function dft2idft8u(dft As Mat, src As Mat) As Mat
    Dim temp As New Mat()
    Cv2.Idft(dft, temp)             ' IDFT

    ' 複素画像の実部と虚部を2枚の画像に分離する。
    Dim readImage = New Mat(1) {}
    Cv2.Split(temp, readImage)      ' (0)-> Real, (1)->imaginary

    ' 実部について正規化を行う。入力画像のサイズはDFT用に
    ' 拡大されているので、原画像の同サイズにROIを設定して縮小する。
    Dim idftRoi As Mat = New Mat(readImage(0),
                                 New Rect(0, 0, src.Cols, src.Rows))
    Dim idft As New Mat()
    Cv2.Normalize(idftRoi, idft, 0, 1, NormTypes.MinMax)
    idft.ConvertTo(idft, MatType.CV_8UC1, 255.0, 0)

    Return idft
End Function

mat2Dft メソッドで入力画像をDFT するのは前のプログラムと同じです。
DoCvFunctionメソッドは、DFTとIDFTを行い、2つのBitmapオブジェクトをタプルで戻します。
dft2idft8u メソッドは、入力画像をDFT したMat オブジェクトへIDFT 処理を行い、その結果のMat オブジェクトを返します。引数にDFT したMat オブジェクト(dft)と入力画像を格納したMat オブジェクト(src)を受け取ります。
Cv2.Idft メソッドでIDFT 処理を行います。次に、Cv2.Split メソッドでIDFT の結果をMat 配列であるreadImage へ分離します。この配列の (0) に実数部が、(1) に虚数部が格納されます。
一般的にDFT したMat オブジェクトのみでIDFT は可能と思うでしょうが、本プログラムはDFT を行う際に高速に処理できるようにMat オブジェクトのサイズを調整します。このため、何も考えずにIDFT を行うと、入力画像と異なったサイズのMat オブジェクトを返してしまいます。これを避けるために、入力画像も引数で受け取ります。この入力画像はサイズしか使用しません。このため、Mat オブジェクトではなくサイズを受け取っても良いでしょう。このサイズを利用し、IDFT 処理後の実数部にROI を設定し、これをidftRoi とします。
次に、Cv2.Normalize メソッドで0.0 ~ 1.0 へ正規化します。最後に、この正規化したMat オブジェクトをCV_8U へ変更します。その際に0.0 ~ 1.0 を0 ~ 255 へスケーリングします。このMat オブジェクトを呼び出し元へ戻します。

実行

以降に実行例を示します。左から原画像、DFT で得られたパワースペクトル、そしてIDFTで戻した画像を示します。

次に、この現画像にノイズを乗せ、オリジナル画像に比べ高周波成分を付加したものを示します。

ノイズも正確に元に戻っています。
ノイズを乗せたため、高周波成分が増えているのも観察できます。