Accord.NET を利用したシンプルな画像判定プログラム - C#

Accord.NET を利用してシンプルな画像判定プログラムを作成します。
補足
ML.NETで実装した同様のプログラムはこちらの記事を参照してください。

プログラム

UI

Windows Formアプリケーションを作成し、下図のフォームを作成します。

コード

下記のコードを作成します。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Accord;
using Accord.Imaging;
using Accord.MachineLearning;
using Accord.MachineLearning.VectorMachines;
using Accord.MachineLearning.VectorMachines.Learning;

namespace SimpleImageFilpApp
{
  public partial class FormMain : Form
  {
    const int CodeWordCount = 32; 
    private BagOfVisualWords bagofVW;
    private MulticlassSupportVectorMachine msvm;
    private List<ImageItem> ItemList;

    public FormMain()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      ItemList = new List<ImageItem>();

      ImageItem ii;
      ii = new ImageItem();
      ii.FileName = "img\\windows-10.jpg";
      ii.bmp = new Bitmap(ii.FileName);
      ii.Classification = 0;
      ItemList.Add(ii);

      ii = new ImageItem();
      ii.FileName = "img\\windows-8.jpg";
      ii.bmp = new Bitmap(ii.FileName);
      ii.Classification = 1;
      ItemList.Add(ii);

      ii = new ImageItem();
      ii.FileName = "img\\windows-7.jpg";
      ii.bmp = new Bitmap(ii.FileName);
      ii.Classification = 2;
      ItemList.Add(ii);

      ii = new ImageItem();
      ii.FileName = "img\\windows-vista.jpg";
      ii.bmp = new Bitmap(ii.FileName);
      ii.Classification = 3;
      ItemList.Add(ii);
      
      BinarySplit binarySplit = new BinarySplit(CodeWordCount);
      bagofVW = new BagOfVisualWords(binarySplit);

      Bitmap[] trainImages = new Bitmap[] { ItemList[0].bmp, ItemList[1].bmp, ItemList[2].bmp, ItemList[3].bmp};
      bagofVW.Compute(trainImages);

      for (int i = 0; i < ItemList.Count; i++) {
        ItemList[i].codeWord = bagofVW.GetFeatureVector(ItemList[i].bmp);

        for (int j = 0; j < ItemList[i].codeWord.Length; j++) {
          textBox1.Text += Convert.ToString(ItemList[i].codeWord[j]) + ", ";
        }
        textBox1.Text += "\r\n\r\n";
      }
    }

    private void button2_Click(object sender, EventArgs e)
    {
      int classes = 4;
      var kernel = new Accord.Statistics.Kernels.ChiSquare();

      msvm = new MulticlassSupportVectorMachine(0, kernel, classes);

      double[][] input = { ItemList[0].codeWord, ItemList[1].codeWord, ItemList[2].codeWord, ItemList[3].codeWord };
      int[] output = { ItemList[0].Classification, ItemList[1].Classification, ItemList[2].Classification, ItemList[3].Classification };

      MulticlassSupportVectorLearning teacher = new MulticlassSupportVectorLearning(msvm, input, output);
      teacher.Algorithm = func;
      teacher.Run();
      textBox1.Text = "Complete\r\n";

    }

    private void button3_Click(object sender, EventArgs e)
    {
      if (openFileDialog1.ShowDialog() == DialogResult.OK) {
        Bitmap bmp = new Bitmap(openFileDialog1.FileName);
        double[] codeword = bagofVW.GetFeatureVector(bmp);
        int classResult = msvm.Compute(codeword);
        textBox1.Text = "Result:"+Convert.ToString(classResult) +"\r\n";
      }
    }

    public ISupportVectorMachineLearning func(
      KernelSupportVectorMachine machine,
      double[][] inputs,
      int[] outputs,
      int class1,
      int class2) {
      SequentialMinimalOptimization smo = new SequentialMinimalOptimization(machine, inputs, outputs);
      smo.UseComplexityHeuristic = true;
      return smo;
    }
  }
}

学習データについて

今回、画像認識に使う学習データは下図の4つの画像を利用します。(Windows の壁紙です。)

Class : 0 とします。

Class : 1 とします。

Class : 2 とします。

Class : 3 とします。

解説

button1

Button1のClickイベントでは、画像の準備と特徴量の算出をします。特徴量は"Bag-of-Visual Words"(BoVW)/Bag-of-Features という特徴表現を利用します。
下記コードでは、学習用画像ファイルの準備と画像とそれぞれの画像がどのクラスに属するかの情報を設定しています。情報はクラス型(ImageItem)のListで保持しています。
      ItemList = new List<ImageItem>();

      ImageItem ii;
      ii = new ImageItem();
      ii.FileName = "img\\windows-10.jpg";
      ii.bmp = new Bitmap(ii.FileName);
      ii.Classification = 0;
      ItemList.Add(ii);

      ii = new ImageItem();
      ii.FileName = "img\\windows-8.jpg";
      ii.bmp = new Bitmap(ii.FileName);
      ii.Classification = 1;
      ItemList.Add(ii);

      ii = new ImageItem();
      ii.FileName = "img\\windows-7.jpg";
      ii.bmp = new Bitmap(ii.FileName);
      ii.Classification = 2;
      ItemList.Add(ii);

      ii = new ImageItem();
      ii.FileName = "img\\windows-vista.jpg";
      ii.bmp = new Bitmap(ii.FileName);
      ii.Classification = 3;
      ItemList.Add(ii);


続いて下記コードで"Bag-of-Visual Words"算出クラスを準備します。クラスタリングアルゴリズムとして今回はBinarySplitを利用します。特徴量の長さは32としています。
      BinarySplit binarySplit = new BinarySplit(CodeWordCount);
      bagofVW = new BagOfVisualWords(binarySplit);

今回のコードでは、BinarySplitを利用して、"Bag-of-Visual Words"オブジェクトを作成しましたが、BinarySplit以外にも下記の方式が利用できます。
BinarySplit
KMeansk-means法 (k平均法)
MeanShiftMean shift法
GaussianMixtureModelMixture model によるクラスタリング

下記コードで"Bag-of-Visual Words"にBitmap配列の初期値を与えて初期化(Bag of Words modelを計算)します。
      Bitmap[] trainImages = new Bitmap[] { ItemList[0].bmp, ItemList[1].bmp, ItemList[2].bmp, ItemList[3].bmp};
      bagofVW.Compute(trainImages);

"Bag-of-Visual Words"のクラスBagOfVisualWordsのGetFeatureVector()メソッドを呼び出して特徴量のベクトルを取得します。取得したベクトルは保存するとともにテキストボックスに表示します。
      for (int i = 0; i < ItemList.Count; i++) {
        ItemList[i].codeWord = bagofVW.GetFeatureVector(ItemList[i].bmp);

        for (int j = 0; j < ItemList[i].codeWord.Length; j++) {
          textBox1.Text += Convert.ToString(ItemList[i].codeWord[j]) + ", ";
        }
        textBox1.Text += "\r\n\r\n";
      }

button2

button1のClickイベントで算出したBag-of-Visual Wordsの特徴量(特徴量ベクトル)をサポートベクターマシン(SVM)に入力する処理です。

下記コードにより、サポートベクターマシンのクラスを作成します。MulticlassSupportVectorMachineクラスを作成します。入力数は不定なので、コンストラクタの第一引数は0を与えます。サポートベクターマシンのカーネル(カーネル関数)は、ChiSquare(カイ二乗検定)を利用します。今回判定するクラスは4つのため、サポートベクタマシンのクラスには4を与えます。
     int classes = 4;
      var kernel = new Accord.Statistics.Kernels.ChiSquare();

      msvm = new MulticlassSupportVectorMachine(0, kernel, classes);

サポートベクターマシンへの入力と出力を準備します。入力は画像の"Bag-of-Visual Words"の特徴ベクトルになります。出力はあらかじめ決めておいたクラス番号とします。
      double[][] input = { ItemList[0].codeWord, ItemList[1].codeWord, ItemList[2].codeWord, ItemList[3].codeWord };
      int[] output = { ItemList[0].Classification, ItemList[1].Classification, ItemList[2].Classification, ItemList[3].Classification };

MulticlassSupportVectorLearningクラスを作成します。コンストラクタの第一引数に、サポートベクタマシーンのオブジェクト、第二引数に入力、第三引数に出力を与えます。
MulticlassSupportVectorLearning のRun()メソッドを実行すると、学習計算が始まります。計算が終了した時点で、テキストボックスに"Complete"のメッセージを表示します。
      MulticlassSupportVectorLearning teacher = new MulticlassSupportVectorLearning(msvm, input, output);
      teacher.Algorithm = func;
      teacher.Run();
      textBox1.Text = "Complete\r\n";

MulticlassSupportVectorLearning の Algorithmプロパティに渡すデリゲートは以下となります。
    public ISupportVectorMachineLearning func(
      KernelSupportVectorMachine machine,
      double[][] inputs,
      int[] outputs,
      int class1,
      int class2) {
      SequentialMinimalOptimization smo = new SequentialMinimalOptimization(machine, inputs, outputs);
      smo.UseComplexityHeuristic = true;
      return smo;
    }

button3

button3では判定画像を読み込み、どの画像に近いか判定をする処理です。

OpenFileDialogのShowDialog()メソッドを呼び出し、ファイルを開くダイアログを表示します。ダイアログで画像を開くと、その画像のBitmapオブジェクトを作成します。Bitmapオブジェクトから、"Bag-of-Visual Words"のクラスBagOfVisualWordsのGetFeatureVector()メソッドを呼び出して特徴量のベクトルを取得します。取得した特徴量ベクトルをサポートベクターマシンで判定します。サポートベクターマシンの判定にはMulticlassSupportVectorMachineクラスのComputeメソッドを呼び出します。戻り値はサポートベクターマシンのOutputで設定した値のいずれかになります。
サポートベクターマシンの結果をテキストボックスに表示します。
      if (openFileDialog1.ShowDialog() == DialogResult.OK) {
        Bitmap bmp = new Bitmap(openFileDialog1.FileName);
        double[] codeword = bagofVW.GetFeatureVector(bmp);
        int classResult = msvm.Compute(codeword);
        textBox1.Text = "Result:"+Convert.ToString(classResult) +"\r\n";
      }

実行結果

プロジェクトを実行します。下図のウィンドウが表示されます。


[button1]をクリックします。画像の特徴量の計算が実行され、特徴量のCodeWordがテキストボックスに表示されます。


続いて[button2]をクリックします。特徴量のCodeWordから学習(クラスタリング計算)が実行されます。完了すると"Complete"のメッセージがテキストボックスに表示されます。


[button3]をクリックします。ファイルを開くダイアログが表示されます。判定に使う画像を選択します。

判定に利用する画像

今回、判定に利用する画像は以下を利用します。

こちらは元の画像の画像の大きさを50%に縮小した画像です。


こちらは元の画像の画像の一部を抜き出した画像です。



画像を開きます。まずはWindows 7の壁紙の50%縮小画像を開きます。


"Result:2"が表示されました。Class:2はWindows 7の壁紙なので当たっています。


[button3]をクリックします。続いてWindows 8の壁紙の50%縮小画像を開きます。


"Result:1"が表示されました。Class:1はWindows 8の壁紙なので当たっています。


[button3]をクリックします。続いてWindows 10の壁紙の50%縮小画像を開きます。


"Result:0"が表示されました。Class:0はWindows 10の壁紙なので当たっています。


Windows Vistaの壁紙の50%縮小画像を開きます。


"Result:3"が表示されました。Class:3はWindows Vistaの壁紙なので当たっています。


続いて、一部分を抜き出した画像で判定してみます。Windows 8の壁紙の一部を切り取った画像を開きます。


"Result:1"が表示されました。Class:1はWindows 8の壁紙なので、こちらも正解です。


Windows 7の壁紙の一部を切り取った画像を開きます。


"Result:2"が表示されました。正解です。


Accord.NET を利用して、シンプルな画像判定プログラムの作成ができました。
著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
掲載日: 2016-03-23
iPentec all rights reserverd.