形状比較 (Huモーメント)

```C#
private (Mat, Point[][], HierarchyIndex[]) FindContours(string filepath)
{
    var src = new Mat(filepath, ImreadModes.Grayscale);

    Cv2.Threshold(src, src, thresh: 87, maxval: 255, ThresholdTypes.BinaryInv);

    Cv2.FindContours(src, out Point[][] contours, out HierarchyIndex[] hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxSimple);

    Mat edge = new Mat();
    Cv2.Canny(src, edge, threshold1: 96, threshold2: 96);

    return (edge, contours, hierarchy);
}

private void btnRun_Click(object sender, EventArgs e)
{
    Point[][] contours1;
    HierarchyIndex[] hierarchy1;

    Mat pattern;
    (pattern, contours1, hierarchy1) = FindContours(@".\pattern_nut.png");

    Cv2.ImShow("pattern", pattern);

    Point[][] contours2;
    HierarchyIndex[] hierarchy2;

    Mat data;
    (data, contours2, hierarchy2) = FindContours(@".\data.png");

    var output = new Mat();
    Cv2.CvtColor(data, output, ColorConversionCodes.GRAY2BGR);

    Cv2.ImShow("data", data);

    // ノイズ除去
    for (int i = 0; i < contours2.GetLength(0); ++i)
    {
        var area = Cv2.ContourArea(contours2[i].ToArray());

        if (area < 100)
        {
            var rect = Cv2.BoundingRect(contours2[i]);
            rect.Inflate(width: 1, height: 1);
            output.Rectangle(rect, Scalar.Black, thickness: -1);
        }
    }

    for (int i = 0; i < contours2.GetLength(0); ++i)
    {
        var area = Cv2.ContourArea(contours2[i].ToArray());

        if (area < 100)
        {
            continue;
        }

        var rect = Cv2.BoundingRect(contours2[i]);
        output.Rectangle(rect, Scalar.GreenYellow);

        var moments = Cv2.Moments(contours2[i]);
        Point centroid = new Point();
        centroid.X = (int)(moments.M10 / moments.M00);
        centroid.Y = (int)(moments.M01 / moments.M00);
        output.Circle(centroid, radius: 3, Scalar.Yellow, thickness: -1);

        double ratio = Cv2.MatchShapes(data[rect], pattern, ShapeMatchModes.I1);
        output.PutText(ratio.ToString("F3"), rect.TopLeft, HersheyFonts.HersheySimplex, fontScale: 0.8, (ratio < 0.005) ? Scalar.White : Scalar.Red);

        Cv2.MinEnclosingCircle(contours2[i], out Point2f center, out float fadius);
        var diff = center - centroid;
        double distance = Math.Sqrt(diff.X * diff.X + diff.Y * diff.Y);
        output.PutText(distance.ToString("F1"), centroid + new Point(10, 0), HersheyFonts.HersheySimplex, fontScale: 0.8, Scalar.Yellow, thickness: 2);
    }

    Cv2.ImShow("output", output);
}
```
![7394c6479293d836cc457ac5306cbcd0[1].png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/662099/5c90068a-b56b-a84b-d46a-4c29d1cd1b5a.png)
![343e5c0d1c2180e2f6beef37b26fbad9[3].png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/662099/c3a8dfbe-3a4b-8904-5235-9ff8a440dc27.png)
![cf732495f3cc1aa373a4d6be2497e53b[1].png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/662099/7d632719-eff4-eceb-1933-8d14457e93b8.png)
![2fe1da1f2dffeed36b29452765cfb8f9[1].png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/662099/006e3453-afbf-c505-fab1-f57cb7531758.png)
![bb030c5cb11803102cfed1fc84143d7a[1].png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/662099/fc7cac54-3f9a-0ddb-0208-fc5aa5000d4e.png)