[C#]OpenCVで類似画像検索を行う


OpenCVと.Net Framework を使用して類似画像検索を行ってみます。
.Net Frameworkを使用してOpenCVを使う時にはラッパーを使用します。
今回はOpenCVSharpを使用します。
1.)準備
まず、OpenCVSharpを以下のサイトからダウンロードします。
http://code.google.com/p/opencvsharp/
001.jpg

左側のDownload欄の「OpenCvSharp-2.4-x86-20120801.zip」を選択します。
次にOpenCV(http://opencv.org/)をダウンロードします。
OPenCVSharpのバージョンにあわせてVer.2.4.0をダウンロードする必要があります。
Ver.2.4.0のダウンロードは以下からできます。
http://sourceforge.net/projects/opencvlibrary/files/opencv-win/2.4.0/OpenCV-2.4.0.exe/download
2.)プロジェクトの準備
 ソリューションエクスプローラの右クリックメニュー「参照の追加」を選択します。
 
002.jpg

 「browse」を選択しファイル選択ダイアログを開きます。
 
 OpenCvSharp-2.4-x86-20120801.zipに含まれている。「OpenCvSharp.dll」を選択します。
 
3.)OpenCVの実装
 OpenCVSharpの名前空間は「OpenCvSharp」です。ソースコードの先頭に以下を追加します。
 

using OpenCvSharp;

4.)ヒストグラムを使用した類似検索
 ヒストグラムを使用した類似性を検出するコードはOpenCVのサンプルコードがありますので、それを参考に組み立てます。
 該当のサンプルコードは以下です。
 
 ヒストグラム間の距離 cvCompareHist
 http://opencv.jp/sample/histogram.html#hist_distance
 
 このサンプルコードをOpenCVSharpを使用して書き直すと以下のようになります。

static void Main(string[] args)
{
int i, sch = 0;
float [] range_0 = { 0, 256 };
float[][] ranges = { range_0 };
double tmp, dist = 0;
IplImage src_img1, src_img2;
IplImage [] dst_img1 = new IplImage[4];
IplImage [] dst_img2 = new IplImage[4];
CvHistogram[] hist1 = new CvHistogram[4];
CvHistogram hist2;
// (1)二枚の画像を読み込む.チャンネル数が等しくない場合は,終了
if (args.Count() < 2) {
Console.WriteLine("Usage : TestHistgram <file> <folder>");
return;
}
//templateフォルダ以下にテンプレート画像を入れておく。
IEnumerable<string> tempFiles = Directory.EnumerateFiles(args[1], "*.jpg", SearchOption.TopDirectoryOnly);
Dictionary<string, double> NCCPair = new Dictionary<string, double>();
//(1)二枚の画像を読み込む.チャンネル数が等しくない場合は,終了
src_img1 = IplImage.FromFile(args[0], LoadMode.AnyDepth | LoadMode.AnyColor);
// (2)入力画像のチャンネル数分の画像領域を確保
sch = src_img1.NChannels;
for (i = 0; i < sch; i++)
{
dst_img1[i] = Cv.CreateImage(Cv.Size(src_img1.Width, src_img1.Height), src_img1.Depth, 1);
}
// (3)ヒストグラム構造体を確保
int[] nHisSize = new int[1];
nHisSize[0] = 256;
hist1[0] = Cv.CreateHist(nHisSize, HistogramFormat.Array, ranges, true);
// (4)入力画像がマルチチャンネルの場合,画像をチャンネル毎に分割
if (sch == 1)
{
Cv.Copy(src_img1, dst_img1[0]);
}
else
{
Cv.Split(src_img1, dst_img1[0], dst_img1[1], dst_img1[2], dst_img1[3]);
}
for (i = 0; i < sch; i++)
{
Cv.CalcHist(dst_img1[i], hist1[i], false);
Cv.NormalizeHist(hist1[i], 10000);
if (i < 3)
{
Cv.CopyHist(hist1[i], ref hist1[i + 1]);
}
}
Cv.ReleaseImage(src_img1);
foreach (string file in tempFiles)
{
try
{
dist = 0.0;
src_img2 = IplImage.FromFile(file, LoadMode.AnyDepth | LoadMode.AnyColor);
// (2)入力画像のチャンネル数分の画像領域を確保
//sch = src_img1.NChannels;
for (i = 0; i < sch; i++)
{
dst_img2[i] = Cv.CreateImage(Cv.Size(src_img2.Width, src_img2.Height), src_img2.Depth, 1);
}
// (3)ヒストグラム構造体を確保
nHisSize[0] = 256;
hist2 = Cv.CreateHist(nHisSize, HistogramFormat.Array, ranges, true);
// (4)入力画像がマルチチャンネルの場合,画像をチャンネル毎に分割
if (sch == 1)
{
Cv.Copy(src_img2, dst_img2[0]);
}
else
{
Cv.Split(src_img2, dst_img2[0], dst_img2[1], dst_img2[2], dst_img2[3]);
}
// (5)ヒストグラムを計算,正規化して,距離を求める
for (i = 0; i < sch; i++)
{
Cv.CalcHist(dst_img2[i], hist2, false);
Cv.NormalizeHist(hist2, 10000);
tmp = Cv.CompareHist(hist1[i], hist2, HistogramComparison.Bhattacharyya);
dist += tmp * tmp;
}
dist = Math.Sqrt(dist);
// (6)求めた距離を文字として画像に描画
Console.WriteLine("{0} => Distance={1:F3}", file, dist);
Cv.ReleaseHist(hist2);
Cv.ReleaseImage(src_img2);
}
catch (OpenCVException ex)
{
Console.WriteLine("Error : " + ex.Message);
}
}
Cv.ReleaseHist(hist1[0]);
Cv.ReleaseHist(hist1[1]);
Cv.ReleaseHist(hist1[2]);
Cv.ReleaseHist(hist1[3]);
}

5.)実行
実行にはOpenCVに含まれている以下のファイルが必要です。
<OpenCVのファイル>

  • opencv_core240.dll

  • opencv_highgui240.dll

  • opencv_imgproc240.dll


<OpenCVSharpのファイル>

  • tbb.dll

  • OpenCvSharp.dll


このプログラムを以下のように実行します。

SampleCode priga.jp_S02938.jpg .\data

「.\data」フォルダには以下の画像が含まれています。
・priga.jp_P03113.jpg
dat01.jpg

・priga.jp_S02938.jpg
dat02.jpg

・priga.jp_S02976.jpg
dat03.jpg

・priga.jp_S03027.jpg
dat04.jpg

・priga.jp_S04303.jpg
dat05.jpg

実行結果は以下のように出力されます。

priga.jp_P03113.jpg => Distance=1.077
priga.jp_S02938.jpg => Distance=0.000 (同じ画像)
priga.jp_S02976.jpg => Distance=0.844
priga.jp_S03027.jpg => Distance=0.810
priga.jp_S04303.jpg => Distance=0.749

Distanceの値で類似性がわかります。私の実験では0.21以下で類似画像が判別できるようでした。





0 件のコメント :

コメントを投稿