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