これで良いのか?と言う不安もありますが、備忘録的な感じで。
最初はこう書いてみました。
using (var mat = image.ToMat())
using (var gray = mat.CvtColor(ColorConversionCodes.RGB2GRAY))
using (var bin = gray.Threshold(mythreshold, 255, ThresholdTypes.Binary))
{
var contours = bin.FindContoursAsMat(RetrievalModes.List, ContourApproximationModes.ApproxSimple);
var candidatre = contours
.Select(c =>
{
var outputMat = new MatOfPoint();
Cv2.ApproxPolyDP(c, outputMat, 0.01 * c.ArcLength(true), true);
//MEMO : 角のPointコレクションと面積をペアで返します。
return new Tuple<Point[], double>(outputMat.ToArray(), Cv2.ContourArea(c.ToArray()));
})
//MEMO : 面積で区切ってゴミを除去してます。
.Where(c => c.Item2 < myMaxArea && c.Item2 > myMinArea);
return candidatre.Select(x => x.Item1).ToArray();
}
これでMat.DrawContour
すると若干ずれ(2ピクセル程度)が出てしまってました。
ApproxPolyDP
で近似してるせいかな?と思いますが深く追ってません。
取り合えずoutputMat
をMatOfPoint2f
にしたら勝手に精度上げてくれないかな?と期待したのですが、そうすると数値がおかしな値が返ってきてしまいました。
恐らく内部的に整数しか扱えないものと思われます。(これもソースは追ってません)
そもそも位置がずれてしまっているので別の手段を使った方がよさそうです。
調べてみるとこんなページが有りました。
このリンク先ではサブピクセル精度のコーナー検出について触れられています。
・・・そういえばCornerSubPix
なんてありましたね・・・。
と言うことで修正です。
サブピクセル精度
using (var mat = image.ToMat())
using (var gray = mat.CvtColor(ColorConversionCodes.RGB2GRAY))
using (var bin = gray.Threshold(mythreshold, 255, ThresholdTypes.Binary))
{
var contours = bin.FindContoursAsMat(RetrievalModes.List, ContourApproximationModes.ApproxSimple);
var candidatre = contours
.Select(c =>
{
var outputMat = new MatOfPoint();
Cv2.ApproxPolyDP(c, outputMat, 0.01 * c.ArcLength(true), true);
var criteria = new TermCriteria(CriteriaType.Eps | CriteriaType.MaxIter, 100, 0.001);
var corners = Cv2.CornerSubPix(gray, outputMat.Select(x => new Point2f(x.X, x.Y)).ToArray(), new Size(5, 5), new Size(-1, -1), criteria);
//MEMO : 角のPointコレクションと面積をペアで返します。
return new Tuple<Point2f[], double>(corners, Cv2.ContourArea(c.ToArray()));
})
//MEMO : 面積で区切ってゴミを除去してます。
.Where(c => c.Item2 < myMaxArea && c.Item2 > myMinArea);
return candidatre.Select(x => x.Item1).ToArray();
}
今回のケースではこれで結構いい感じに追従できてます。