OpenCvSharp を用いて特定の色を検出するサンプルコードです。
元ネタはこれ【OpenCV】色領域の抽出
やってることは元ネタのサイトと一緒ですが、 C++ API を用いて(そこそこ)エレガントに書き直しました。LUT を生成する部分で OpenCvSharp Wiki に TypeSpecificMat (faster) と書いてある方法でピクセルにアクセスしているので、 unsafe じゃないやり方では最も高速なんじゃないかと思います。それから、Mat ではなく IplImage を使いたい人(C++ API を使わない人)向けに、元ネタのコードを OpenCvSharp で使えるようそのまま書き換えたものも載せておきました。
以下は画像から肌色領域を抽出するコードです。そのまま動かすには、実行ファイルと同じフォルダに lena.jpg という名前のイメージファイルが存在する必要があります。
C++ API を使った方
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 を使わない方
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |