OpenCvSharp を用いて特定の色を検出するサンプルコードです。

元ネタはこれ【OpenCV】色領域の抽出

 やってることは元ネタのサイトと一緒ですが、 C++ API を用いて(そこそこ)エレガントに書き直しました。LUT を生成する部分で OpenCvSharp Wiki に TypeSpecificMat (faster) と書いてある方法でピクセルにアクセスしているので、 unsafe じゃないやり方では最も高速なんじゃないかと思います。それから、Mat ではなく IplImage を使いたい人(C++ API を使わない人)向けに、元ネタのコードを OpenCvSharp で使えるようそのまま書き換えたものも載せておきました。

 以下は画像から肌色領域を抽出するコードです。そのまま動かすには、実行ファイルと同じフォルダに lena.jpg という名前のイメージファイルが存在する必要があります。

C++ API を使った方


using OpenCvSharp;
using OpenCvSharp.CPlusPlus;
using System;
namespace OpenCvSharpSample
{
class Program
{
static void Main(string[] args)
{
// ファイル読み込み
var mat = new Mat("lena.jpg");
// 肌色領域を抽出
var skinMat = ColorExtraction(mat, ColorConversion.BgrToHsv, 0, 10, 80, 255, 0, 255);
Cv2.ImShow("SrcMat", mat);
Cv2.ImShow("SkinColor", skinMat);
Cv2.WaitKey(0);
}
// OpenCvSharp3 では ColorConversionCodes
public static Mat ColorExtraction(Mat srcMat, ColorConversion code,
int ch1Lower, int ch1Upper,
int ch2Lower, int ch2Upper,
int ch3Lower, int ch3Upper)
{
if (srcMat == null)
throw new ArgumentNullException("srcMat");
var colorMat = srcMat.CvtColor(code);
var lut = new Mat(256, 1, MatType.CV_8UC3);
var lower = new int[3] { ch1Lower, ch2Lower, ch3Lower };
var upper = new int[3] { ch1Upper, ch2Upper, ch3Upper };
// cv::Mat_<cv::Vec3b>
var mat3 = new MatOfByte3(lut);
var indexer = mat3.GetIndexer();
for (int i = 0; i < 256; i++)
{
var color = indexer[i];
byte temp;
for (int k = 0; k < 3; k++)
{
if (lower[k] <= upper[k])
{
if ((lower[k] <= i) && (i <= upper[k]))
{
temp = 255;
}
else
{
temp = 0;
}
}
else
{
if ((i <= upper[k]) || (lower[k] <= i))
{
temp = 255;
}
else
{
temp = 0;
}
}
color[k] = temp;
}
indexer[i] = color;
}
Cv2.LUT(colorMat, lut, colorMat);
var channelMat = colorMat.Split();
var maskMat = new Mat();
Cv2.BitwiseAnd(channelMat[0], channelMat[1], maskMat);
Cv2.BitwiseAnd(maskMat, channelMat[2], maskMat);
srcMat.CopyTo(maskMat, maskMat);
return maskMat;
}
}
}

C++ API を使わない方


using OpenCvSharp;
using System;
namespace OpenCvSharpSample
{
class Program
{
static void Main(string[] args)
{
// ファイル読み込み
var srcImage = new IplImage("lena.jpg");
var dstImage = new IplImage(srcImage.Size,srcImage.Depth,srcImage.NChannels);
// 肌色領域を抽出
ColorExtraction(srcImage,dstImage, ColorConversion.BgrToHsv, 0, 10, 80, 255, 0, 255);
Cv.ShowImage("SrcMat", srcImage);
Cv.ShowImage("SkinColor", dstImage);
Cv.WaitKey(0);
}
public static void ColorExtraction(IplImage srcImage, IplImage dstImage, ColorConversion code,
int ch1Lower, int ch1Upper,
int ch2Lower, int ch2Upper,
int ch3Lower, int ch3Upper)
{
if (srcImage == null)
throw new ArgumentNullException("srcImage");
else if (dstImage == null)
throw new ArgumentNullException("dstImage");
IplImage colorImage;
IplImage ch1Image, ch2Image, ch3Image;
IplImage maskImage;
int i, k;
int[] lower = new int[3];
int[] upper = new int[3];
int[] val = new int[3];
CvMat lut;
colorImage = Cv.CreateImage(Cv.GetSize(srcImage), srcImage.Depth, srcImage.NChannels);
Cv.CvtColor(srcImage, colorImage, code);
lut = Cv.CreateMat(256, 1, MatrixType.U8C3);
lower[0] = ch1Lower;
lower[1] = ch2Lower;
lower[2] = ch3Lower;
upper[0] = ch1Upper;
upper[1] = ch2Upper;
upper[2] = ch3Upper;
for (i = 0; i < 256; i++)
{
for (k = 0; k < 3; k++)
{
if (lower[k] <= upper[k])
{
if ((lower[k] <= i) && (i <= upper[k]))
{
val[k] = 255;
}
else
{
val[k] = 0;
}
}
else
{
if ((i <= upper[k]) || (lower[k] <= i))
{
val[k] = 255;
}
else
{
val[k] = 0;
}
}
}
Cv.Set1D(lut, i, new CvScalar(val[0], val[1], val[2]));
}
Cv.LUT(colorImage, colorImage, lut);
Cv.ReleaseMat(lut);
ch1Image = Cv.CreateImage(Cv.GetSize(colorImage), colorImage.Depth, 1);
ch2Image = Cv.CreateImage(Cv.GetSize(colorImage), colorImage.Depth, 1);
ch3Image = Cv.CreateImage(Cv.GetSize(colorImage), colorImage.Depth, 1);
Cv.Split(colorImage, ch1Image, ch2Image, ch3Image, null);
maskImage = Cv.CreateImage(Cv.GetSize(colorImage), colorImage.Depth, 1);
Cv.And(ch1Image, ch2Image, maskImage);
Cv.And(maskImage, ch3Image, maskImage);
Cv.Zero(dstImage);
Cv.Copy(srcImage, dstImage, maskImage);
Cv.ReleaseImage(colorImage);
Cv.ReleaseImage(ch1Image);
Cv.ReleaseImage(ch2Image);
Cv.ReleaseImage(ch3Image);
Cv.ReleaseImage(maskImage);
}
}
}