opencv 如何用SIFT找到图像中所有匹配的对象

ijxebb2r  于 2023-05-07  发布在  其他
关注(0)|答案(1)|浏览(143)

我有一张钻石卡的图片,还有一张一颗钻石的小图片,我在试着找出大图片里所有的钻石
下面是图片:

下面是实验代码:

using System.Collections.Generic;
using System;
using OpenCvSharp;
using OpenCvSharp.Features2D;
using System.Linq;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var src = new Mat("8_diamonds.png");
            var templ = new Mat("diamonds_template.png");
            var dst = new Mat();
            SiftDetector(src, templ, dst, 0, 3, 0.04, 10, 0.6, 1.75);
            Cv2.ImShow("dst", dst);
            Cv2.ImShow("src", src);
            Cv2.ImShow("templ", templ);
            Cv2.WaitKey();
        }

        private static Scalar[] _Scalars = new[]
        {
            new Scalar(255, 0, 0),
            new Scalar(0, 255, 0),
            new Scalar(0, 0, 255),
            new Scalar(255, 255, 0),
            new Scalar(0, 255, 255),
            new Scalar(255, 0, 255),
        };

        public static void SiftDetector(Mat src, Mat templ, Mat dst,
            int nFeatures = 0,
            int nOctaveLayers = 3,
            double contrastThreshold = 0.04,
            double edgeThreshold = 10,
            double sigma = 1.6,
            double ratio_thresh = 0.75)
        {
            var detector = SIFT.Create(nFeatures, nOctaveLayers, contrastThreshold, edgeThreshold, sigma);
            var descriptors_templ = new Mat();
            var descriptors_src = new Mat();
            detector.DetectAndCompute(templ, null, out var keypoints_templ, descriptors_templ);
            detector.DetectAndCompute(src, null, out var keypoints_src, descriptors_src);

            var matcher = new FlannBasedMatcher();

            src.CopyTo(dst);
            if (dst.Channels() == 1)
            {
                Cv2.CvtColor(dst, dst, ColorConversionCodes.GRAY2BGR);
            }

            for (int j = 0; j < 5; j++)
            {
                var knn_matches = matcher.KnnMatch(descriptors_templ, descriptors_src, 2);

                //-- Filter matches using the Lowe's ratio test            
                var good_matches = new List<DMatch>();
                for (var i = 0; i < knn_matches.Length; i++)
                {
                    if (knn_matches[i][0].Distance < ratio_thresh * knn_matches[i][1].Distance)
                    {
                        good_matches.Add(knn_matches[i][0]);
                    }
                }

                if (good_matches.Count < 4)
                {
                    break;
                }

                var dstPts = new List<Point2d>();
                var srcPts = new List<Point2d>();
                for (var i = 0; i < good_matches.Count; i++)
                {
                    dstPts.Add(keypoints_templ[good_matches[i].QueryIdx].Pt.ToPoint2d());
                    srcPts.Add(keypoints_src[good_matches[i].TrainIdx].Pt.ToPoint2d());
                }

                Mat H = Cv2.FindHomography(dstPts, srcPts, HomographyMethods.Ransac);

                if (H.Cols != 0)
                {
                    var obj_corners = new Point2d[4];
                    obj_corners[0] = new Point2d(0, 0);
                    obj_corners[1] = new Point2d(templ.Cols, 0);
                    obj_corners[2] = new Point2d(templ.Cols, templ.Rows);
                    obj_corners[3] = new Point2d(0, templ.Rows);
                    var scene_corners = Cv2.PerspectiveTransform(obj_corners, H);

                    var drawingPoints = scene_corners.Select(p => (Point)p).ToArray();
                    Cv2.Polylines(dst, new[] { drawingPoints }, true, Scalar.Lime, 4);
                }

                foreach (var match in srcPts)
                {
                    dst.Circle(match.ToPoint(), 5, _Scalars[j % _Scalars.Length], 2);
                }

                var toRemove = good_matches.Select(p => p.TrainIdx).Distinct().ToList();

                foreach (var row in toRemove)
                {
                    descriptors_src.Row(row).SetTo(new Scalar(float.MaxValue, float.MaxValue, float.MaxValue));
                }
            }
        }
    }

    static class Extentions
    {
        public static Point2d ToPoint2d(this Point2f point2F)
        {
            return new Point2d(point2F.X, point2F.Y);
        }
    }
}

在这段代码中,我搜索匹配项,在找到的对象周围进行绘制,然后删除匹配项并再次搜索。
结果是:

问题是我如何找到所有的钻石而不仅仅是第一个?
SIFT不是必需的,SURF或任何类似算法也是可能的。
如果能用Python或**C++**回答,也将不胜感激。

0dxa2lsx

0dxa2lsx1#

你提出的问题的基本解决方案是在10-15行:

import cv2
import numpy as np

card = cv2.imread('rIQyp.png')
card_gray = cv2.cvtColor(card, cv2.COLOR_BGR2GRAY)
template = cv2.imread('zzBUd.png', cv2.IMREAD_GRAYSCALE)
w, h = template.shape[::-1]

cv2.namedWindow("card", cv2.WINDOW_NORMAL)
res = cv2.matchTemplate(card_gray,template,cv2.TM_CCOEFF_NORMED)
threshold = 0.8
loc = np.where( res >= threshold)

for pt in zip(*loc[::-1]):
    cv2.rectangle(card, pt, (pt[0] + w, pt[1] + h), (0,0,255), 2)
    
while cv2.getWindowProperty('card', 0) >= 0:
    cv2.waitKey(10)
    cv2.imshow("card", card)

它之所以有效,是因为这个问题在简而言之就是算法的名称:你需要在一个给定的图像中找到区域(“匹配”),这些区域应该在欧几里德变换中与一组标准图像(“模板”)相匹配。即模板匹配。
模板匹配有很多种,主要处理所提到的变换和照明的问题。如果你出于任何原因不想深入其中,你总是可以自己处理这些问题:例如,在卡片的情况下,你可以应用霍夫变换来获得所有的卡片,即使是部分被遮挡的卡片,将它们变换成适当的矩形块,给出你已经知道的比例,并进行你可以完全自己编写的朴素模板匹配,并且你可以精确地获得在一般情况下存在的所有匹配。
这就是我在真实的应用中实际要做的,比如从相机馈送中计数卡片。如果像这样的问题变得过于随意-只需训练一个基本的CNN,现在也是10-30行代码。

相关问题