您好,
会员登录 快速注册
退出 ( 条未读消息)
关于本站 意见反馈 首页

公告:小宅博客网可以开发票了,需要发票的,去群里找群主哈!!
全部文章分类
  • 人工智能 >

  • 编程语言 >

  • WPF系列 >

  • ASP.NET系列 >

  • Linux >

  • 数据库 >

  • 嵌入式 >

  • WEB技术 >

  • PLC系列 >

  • 微服务与框架 >

  • 小宅DIY >

  • 学习资料 >

OpenCv基础 ANN车牌识别 yolov5车牌识别 指针式仪表识别 ROS系列 YOLO Halcon Detectron2 昇腾AI ChatGPT在线体验 英伟达JETSON ChatGLM ChatTTS FunASR 地平线 ByteTrack 魔搭社区 LangChain
C C# C++ Python Java Go
WPF
ASP.NET小功能 GPS定位系统-MVC GPS定位系统-VUE ASP.NET WebRTC
Linux Linux内核 Shell MakeFile
MySql SqlServer Oracle
STM8 STM32 51单片机
VUE入门 HTML JavaScript CSS layui镜像网站 ElementUi中文官网 element-plus 图标
三菱 欧姆龙 西门子 施耐德 松下 台达
IOTSharp IOTGateway ABP FRAMEWORK Docker
亚克力音响 编程仙途:智驭万法
面试题与技巧 Python入门技能树 微软C#教程
首页 编程之美 工具下载 全国就业 流量地图 文心一言
OpenCv基础
内容介绍与资料分享 C# OpenCv环境搭建 C++ OpenCv环境搭建 Python OpenCv环境搭建 Java OpenCv环境搭建 OpenCv组件结构解析 OpenCv命名规范 OpenCv基本专业术语与方法 OpenCV 常用函数与构造体详细说明 创建画布 打开一张图片 利用imwrite生成透明png图像 图像打开、混合显示和输出 图像腐蚀 blur图像模糊(均值滤波) sobel边缘检测 canny边缘检测 Scharr滤波器 程序性能检测及优化 视频播放 摄像头录像与播放 双摄像头操作与图像相似度检测 颜色空间转换与物体追踪 彩色目标追踪 光流法运动目标检测 OpenCV中的稠密光流 背景减除 点追踪 人脸识别 支持向量机之SVM引导 支持向量机之处理线性不可分数据 ROI矩形截取 鼠标绘制矩形 用OpenCV进行基本绘图 绘图函数(python中文显示) 把鼠标当画笔 用滑动条做调色板 图像的基础操作 图像上的算术运算 多通道图像混合 图像的亮度、对比度调整 XML和YAML文件的写入 XML和YAML文件的读取 卷积操作 三种线性滤波 两种非线性滤波 7种图像处理形态学(1) 7种图像处理形态学(2) 漫水填充 图像缩放与图像金字塔 二值化基本阈值操作 图像阈值 Laplacian图像变换(拉普拉斯算子) 霍夫变换HoughLines边缘检测与线性矢量 霍夫变换HoughLinesP边缘检测与线性矢量 霍夫变换HoughCircles边缘检测与线性矢量 LSD快速直线检测 几何变换 remap重映射 remap实现多种重映射 仿射变换综合示例 直方图均衡化 CLAHE有限对比适应性直方图均衡化 draw最大的轮廓 轮廓的性质 点到多边形的最短距离 形状匹配 椭圆拟合与直线拟合 基础轮廓查找 查找并绘制轮廓综合示例 凸缺陷/凸包检测 凸包检测 创建包围轮廓的矩形边界 创建包围轮廓的圆形边 创建包围轮廓的矩形和圆形边界框 查找和绘制图片轮廓矩 分水岭算法 图像修补 H-S二维直方图的绘制/2D直方图 一维直方图的绘制 RGB三色直方图的绘制 直方图对比 使用掩膜绘制直方图 直方图反向投影 模板匹配 多对象模板匹配 cornerHarris角点检测 cornerHarris角点检测综合示例 Shi-Tomasi角点检测 亚像素级角点检测 角点检测的FAST算法(FAST特征检测器) 颜色识别 warpPerspective透视变换 SURF/SIFT特征点检测 SURF/SIFT特征描述 使用FLANN进行特征点匹配 FLANN结合SURF/SIFT进行关键点的描述和匹配 寻找已知物体(一) 寻找已知物体(二) 目标查找与跟踪 - Meanshift与CamShift BRIEF描述符 ORB ORB算法描述与匹配 LUT 图像灰度调整 离散傅里叶变换 双目摄像头与图像拼接 环境亮度检测 stitching 全景拼接 Maze-Solver迷宫解密 使用Haar分类器之面部检测 使用Haar分类器之行人检测 OpenCv Haar/LBP/HOG分类器-人脸识别 斑点检测 使用GrabCut算法进行交互式前景提取 对极几何 摄像机标定 姿势估计 立体图像中的深度地图 OpenCv中的KNN KNN手写数字识别 ​SVM手写数字识别(使用SVM进行手写数据OCR) 英文字母的OCR 预测手写数字(预测不准) K值聚类(一) K值聚类(二) 计算摄影学-图像去噪 高动态范围成像(HDRI或HDR) MSER区域检测 二维码、条形码识别 删除图像中的水印 OpenCv深度学习dnn Kinect-深度相机 OpenCv常用数学算法 360度旋转直线绘制 向量延长线上的像素扫描 Tools工具包-窗体分配
寻找已知物体(一)
目标查找与跟踪 - Meanshift与CamShift
激萌の小宅 小宅博客网 OpenCv基础

文章作者:激萌の小宅

促销:¥0

价格:¥0

配送方式: 购买后立即生效(如购买异常,请联系站长)
付款之后一定要等待自动跳转结束,否则购买可能会失败
  • 0 天

    有效期

  • 0

    总销量

  • 3

    累计评价

寻找已知物体(二)

视频讲解如下:


在本章节中给大家演示如何利用SURF和SIFT来进行关键物体的查找,并将查找到的物体标记出来。主要函数:SURF和SIFT。

本章节的C++同样还是采用毛星云的代码框架,不过做了些许修改。


当前系列所有demo下载地址:

https://github.com/GaoRenBao/OpenCv4-Demo

不同编程语言对应的OpenCv版本以及开发环境信息如下: 

语言

OpenCv版本

IDE

C#

OpenCvSharp4.4.8.0.20230708

Visual Studio 2022

C++

OpenCv-4.5.5-vc14_vc15

Visual Studio 2022

Python

OpenCv-Python (4.6.0.66)

PyCharm Community Edition 2022.1.3


首先呢,我们需要准备两张测试图片, 这里不推荐使用毛星云的那两张测试图片哈,毛星云的测试图片效果不是很好,所以需要对图片做些裁剪,准备的图片如下,一张是需要查找的图片,另一张是目标图片。

11.jpg

2.jpg


考虑到只有C#版本有SURF和SIFT两个查找,博主这里直接演示C#的效果。

首先是SURF的效果:

SURF.jpg


然后使SIFT的效果:

SIFT.jpg


在目前的基础上,博主做了个利用视频进行检测的程序,效果如下。感兴趣的童鞋可以自己尝试修改测试一下。目前该效果不提供源码。

6379308487622719934650070 (1).gif


两个版本效果相差好像也不是很大。不过SIFT形成的点集好像更多。

C#版本代码如下:

using OpenCvSharp;
using System.Collections.Generic;

namespace demo
{
    internal class Program
    {
        static void Main(string[] args)
        {
            SURF();
            SIFT();
            Cv2.WaitKey();
        }

        public static Point2d Point2fToPoint2d(Point2f pf)
        {
            return new Point2d(((int)pf.X), ((int)pf.Y));
        }

        /// <summary>
        /// SURF
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void SURF()
        {
            // 载入源图片并显示
            Mat srcImage1 = Cv2.ImRead("../../../images/book_box.jpg");
            Mat srcImage2 = Cv2.ImRead("../../../images/book2.jpg");

            // 定义一个特征检测类对象
            KeyPoint[] keypoints_object, keypoints_scene;
            Mat descriptors_object = new Mat();
            Mat descriptors_scene = new Mat();
            var MySurf = OpenCvSharp.XFeatures2D.SURF.Create(400);

            // 方法1:计算描述符(特征向量),将Detect和Compute操作分开
            keypoints_object = MySurf.Detect(srcImage1);
            keypoints_scene = MySurf.Detect(srcImage2);
            MySurf.Compute(srcImage1, ref keypoints_object, descriptors_object);
            MySurf.Compute(srcImage2, ref keypoints_scene, descriptors_scene);

            // 方法2:计算描述符(特征向量),将Detect和Compute操作合并
            //MySurf.DetectAndCompute(srcImage1, null, out keypoints_object, descriptors_object);
            //MySurf.DetectAndCompute(srcImage2, null, out keypoints_scene, descriptors_scene);

            // 创建基于FLANN的描述符匹配对象
            FlannBasedMatcher matcher = new FlannBasedMatcher();
            DMatch[] matches = matcher.Match(descriptors_object, descriptors_scene);
            double max_dist = 0; double min_dist = 100;//最小距离和最大距离

            // 计算出关键点之间距离的最大值和最小值
            for (int i = 0; i < descriptors_object.Rows; i++)
            {
                double dist = matches[i].Distance;
                if (dist < min_dist) min_dist = dist;
                if (dist > max_dist) max_dist = dist;
            }

            System.Diagnostics.Debug.WriteLine($">Max dist 最大距离 : {max_dist}");
            System.Diagnostics.Debug.WriteLine($">Min dist 最小距离 : {min_dist}");

            // 存下匹配距离小于3*min_dist的点对
            List<DMatch> good_matches = new List<DMatch>();
            for (int i = 0; i < descriptors_object.Rows; i++)
            {
                if (matches[i].Distance < 3 * min_dist)
                {
                    good_matches.Add(matches[i]);
                }
            }

            // 绘制出匹配到的关键点
            Mat img_matches = new Mat();
            Cv2.DrawMatches(srcImage1, keypoints_object, srcImage2, keypoints_scene, good_matches, img_matches);

            //定义两个局部变量
            List<Point2f> obj = new List<Point2f>();
            List<Point2f> scene = new List<Point2f>();

            //从匹配成功的匹配对中获取关键点
            for (int i = 0; i < good_matches.Count; i++)
            {
                obj.Add(keypoints_object[good_matches[i].QueryIdx].Pt);
                scene.Add(keypoints_scene[good_matches[i].TrainIdx].Pt);
            }

            //计算透视变换 
            List<Point2d> objPts = obj.ConvertAll(Point2fToPoint2d);
            List<Point2d> scenePts = scene.ConvertAll(Point2fToPoint2d);
            Mat H = Cv2.FindHomography(objPts, scenePts, HomographyMethods.Ransac);

            //从待测图片中获取角点
            List<Point2f> obj_corners = new List<Point2f>();
            obj_corners.Add(new Point(0, 0));
            obj_corners.Add(new Point(srcImage1.Cols, 0));
            obj_corners.Add(new Point(srcImage1.Cols, srcImage1.Rows));
            obj_corners.Add(new Point(0, srcImage1.Rows));

            //进行透视变换
            Point2f[] scene_corners = Cv2.PerspectiveTransform(obj_corners, H);

            //绘制出角点之间的直线
            Point2f P0 = scene_corners[0] + new Point2f(srcImage1.Cols, 0);
            Point2f P1 = scene_corners[1] + new Point2f(srcImage1.Cols, 0);
            Point2f P2 = scene_corners[2] + new Point2f(srcImage1.Cols, 0);
            Point2f P3 = scene_corners[3] + new Point2f(srcImage1.Cols, 0);

            Cv2.Line(img_matches, new Point(P0.X, P0.Y), new Point(P1.X, P1.Y), new Scalar(255, 0, 123), 4);
            Cv2.Line(img_matches, new Point(P1.X, P1.Y), new Point(P2.X, P2.Y), new Scalar(255, 0, 123), 4);
            Cv2.Line(img_matches, new Point(P2.X, P2.Y), new Point(P3.X, P3.Y), new Scalar(255, 0, 123), 4);
            Cv2.Line(img_matches, new Point(P3.X, P3.Y), new Point(P0.X, P0.Y), new Scalar(255, 0, 123), 4);

            //显示最终结果
            Cv2.ImShow("SURF效果图", img_matches);
            // Cv2.ImWrite("SURF.jpg", img_matches);
        }

        /// <summary>
        /// SIFT
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void SIFT()
        {
            // 载入源图片并显示
            Mat srcImage1 = Cv2.ImRead("../../../images/book_box.jpg");
            Mat srcImage2 = Cv2.ImRead("../../../images/book2.jpg");

            // 定义一个特征检测类对象
            KeyPoint[] keypoints_object, keypoints_scene;
            Mat descriptors_object = new Mat();
            Mat descriptors_scene = new Mat();
            var MySift = OpenCvSharp.Features2D.SIFT.Create(400);

            // 方法1:计算描述符(特征向量),将Detect和Compute操作分开
            keypoints_object = MySift.Detect(srcImage1);
            keypoints_scene = MySift.Detect(srcImage2);
            MySift.Compute(srcImage1, ref keypoints_object, descriptors_object);
            MySift.Compute(srcImage2, ref keypoints_scene, descriptors_scene);

            // 方法2:计算描述符(特征向量),将Detect和Compute操作合并
            //MySift.DetectAndCompute(srcImage1, null, out keypoints_object, descriptors_object);
            //MySift.DetectAndCompute(srcImage2, null, out keypoints_scene, descriptors_scene);

            // 创建基于FLANN的描述符匹配对象
            FlannBasedMatcher matcher = new FlannBasedMatcher();
            DMatch[] matches = matcher.Match(descriptors_object, descriptors_scene);
            double max_dist = 0; double min_dist = 100;//最小距离和最大距离

            // 计算出关键点之间距离的最大值和最小值
            for (int i = 0; i < descriptors_object.Rows; i++)
            {
                double dist = matches[i].Distance;
                if (dist < min_dist) min_dist = dist;
                if (dist > max_dist) max_dist = dist;
            }

            System.Diagnostics.Debug.WriteLine($">Max dist 最大距离 : {max_dist}");
            System.Diagnostics.Debug.WriteLine($">Min dist 最小距离 : {min_dist}");

            // 存下匹配距离小于3*min_dist的点对
            List<DMatch> good_matches = new List<DMatch>();
            for (int i = 0; i < descriptors_object.Rows; i++)
            {
                if (matches[i].Distance < 3 * min_dist)
                {
                    good_matches.Add(matches[i]);
                }
            }

            // 绘制出匹配到的关键点
            Mat img_matches = new Mat();
            Cv2.DrawMatches(srcImage1, keypoints_object, srcImage2, keypoints_scene, good_matches, img_matches);

            //定义两个局部变量
            List<Point2f> obj = new List<Point2f>();
            List<Point2f> scene = new List<Point2f>();

            //从匹配成功的匹配对中获取关键点
            for (int i = 0; i < good_matches.Count; i++)
            {
                obj.Add(keypoints_object[good_matches[i].QueryIdx].Pt);
                scene.Add(keypoints_scene[good_matches[i].TrainIdx].Pt);
            }

            //计算透视变换 
            List<Point2d> objPts = obj.ConvertAll(Point2fToPoint2d);
            List<Point2d> scenePts = scene.ConvertAll(Point2fToPoint2d);
            Mat H = Cv2.FindHomography(objPts, scenePts, HomographyMethods.Ransac);

            //从待测图片中获取角点
            List<Point2f> obj_corners = new List<Point2f>();
            obj_corners.Add(new Point(0, 0));
            obj_corners.Add(new Point(srcImage1.Cols, 0));
            obj_corners.Add(new Point(srcImage1.Cols, srcImage1.Rows));
            obj_corners.Add(new Point(0, srcImage1.Rows));

            //进行透视变换
            Point2f[] scene_corners = Cv2.PerspectiveTransform(obj_corners, H);

            //绘制出角点之间的直线
            Point2f P0 = scene_corners[0] + new Point2f(srcImage1.Cols, 0);
            Point2f P1 = scene_corners[1] + new Point2f(srcImage1.Cols, 0);
            Point2f P2 = scene_corners[2] + new Point2f(srcImage1.Cols, 0);
            Point2f P3 = scene_corners[3] + new Point2f(srcImage1.Cols, 0);

            Cv2.Line(img_matches, new Point(P0.X, P0.Y), new Point(P1.X, P1.Y), new Scalar(255, 0, 123), 4);
            Cv2.Line(img_matches, new Point(P1.X, P1.Y), new Point(P2.X, P2.Y), new Scalar(255, 0, 123), 4);
            Cv2.Line(img_matches, new Point(P2.X, P2.Y), new Point(P3.X, P3.Y), new Scalar(255, 0, 123), 4);
            Cv2.Line(img_matches, new Point(P3.X, P3.Y), new Point(P0.X, P0.Y), new Scalar(255, 0, 123), 4);

            //显示最终结果
            Cv2.ImShow("SIFT效果图", img_matches);
            // Cv2.ImWrite("SIFT.jpg", img_matches);
        }
    }
}


C++版本代码如下:

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d.hpp>
#include <opencv2/features2d/features2d.hpp>

using namespace cv;
using namespace std;

int main()
{
	//【1】载入原始图片
	Mat srcImage1 = imread("../images/book_box.jpg");
	Mat srcImage2 = imread("../images/book2.jpg");
	if (!srcImage1.data || !srcImage2.data)
	{
		printf("读取图片错误,请确定目录下是否有imread函数指定的图片存在~! \n"); return false;
	}

	// 使用SIFT算子检测关键点
	Ptr<SiftFeatureDetector> detector = SiftFeatureDetector::create(400);

	// vector模板类,存放任意类型的动态数组
	vector<KeyPoint> keypoints_object, keypoints_scene;
	Mat descriptors_object, descriptors_scene;

	// 调用detect函数检测出SURF特征关键点,保存在vector容器中
	detector->detect(srcImage1, keypoints_object);
	detector->detect(srcImage2, keypoints_scene);
	// 计算描述符(特征向量)
	detector->compute(srcImage1, keypoints_object, descriptors_object);
	detector->compute(srcImage2, keypoints_scene, descriptors_scene);

	// 使用FLANN匹配算子进行匹配
	FlannBasedMatcher matcher;
	vector<DMatch> matches;
	matcher.match(descriptors_object, descriptors_scene, matches);
	double max_dist = 0; double min_dist = 100;//最小距离和最大距离

	// 计算出关键点之间距离的最大值和最小值
	for (int i = 0; i < descriptors_object.rows; i++)
	{
		double dist = matches[i].distance;
		if (dist < min_dist) min_dist = dist;
		if (dist > max_dist) max_dist = dist;
	}

	printf(">Max dist 最大距离 : %f \n", max_dist);
	printf(">Min dist 最小距离 : %f \n", min_dist);

	// 存下匹配距离小于3*min_dist的点对
	std::vector< DMatch > good_matches;
	for (int i = 0; i < descriptors_object.rows; i++)
	{
		if (matches[i].distance < 3 * min_dist)
		{
			good_matches.push_back(matches[i]);
		}
	}

	// 绘制出匹配到的关键点
	Mat img_matches;
	drawMatches(srcImage1, keypoints_object, srcImage2, keypoints_scene,
		good_matches, img_matches, Scalar::all(-1), Scalar::all(-1),
		vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);

	// 定义两个局部变量
	vector<Point2f> obj;
	vector<Point2f> scene;

	//从匹配成功的匹配对中获取关键点
	for (unsigned int i = 0; i < good_matches.size(); i++)
	{
		obj.push_back(keypoints_object[good_matches[i].queryIdx].pt);
		scene.push_back(keypoints_scene[good_matches[i].trainIdx].pt);
	}

	Mat H = findHomography(obj, scene, RANSAC);//计算透视变换 

	//从待测图片中获取角点
	vector<Point2f> obj_corners(4);
	obj_corners[0] = Point(0, 0);
	obj_corners[1] = Point(srcImage1.cols, 0);
	obj_corners[2] = Point(srcImage1.cols, srcImage1.rows);
	obj_corners[3] = Point(0, srcImage1.rows);
	vector<Point2f> scene_corners(4);

	//进行透视变换
	perspectiveTransform(obj_corners, scene_corners, H);

	//绘制出角点之间的直线
	line(img_matches, scene_corners[0] + Point2f(static_cast<float>(srcImage1.cols), 0), scene_corners[1] + Point2f(static_cast<float>(srcImage1.cols), 0), Scalar(255, 0, 123), 4);
	line(img_matches, scene_corners[1] + Point2f(static_cast<float>(srcImage1.cols), 0), scene_corners[2] + Point2f(static_cast<float>(srcImage1.cols), 0), Scalar(255, 0, 123), 4);
	line(img_matches, scene_corners[2] + Point2f(static_cast<float>(srcImage1.cols), 0), scene_corners[3] + Point2f(static_cast<float>(srcImage1.cols), 0), Scalar(255, 0, 123), 4);
	line(img_matches, scene_corners[3] + Point2f(static_cast<float>(srcImage1.cols), 0), scene_corners[0] + Point2f(static_cast<float>(srcImage1.cols), 0), Scalar(255, 0, 123), 4);

	//显示最终结果
	imshow("效果图", img_matches);

	waitKey(0);
	return 0;
}


Python版本代码如下:

import os
import cv2
import numpy as np

# 读取图片和缩放图片
srcImage1 = cv2.imread('../images/book_box.jpg')
srcImage2 = cv2.imread('../images/book2.jpg')

# 创建SIFT
sift = cv2.SIFT_create(400)
# 计算特征点和描述点
(keypoints_object, descriptors_object) = sift.detectAndCompute(srcImage1, None)
(keypoints_scene, descriptors_scene) = sift.detectAndCompute(srcImage2, None)

# 使用FLANN匹配算子进行匹配
# matcher = cv2.BFMatcher() #建立匹配关系
# matches=matcher.match(descriptors_object,descriptors_scene) #匹配描述子

matcher = cv2.FlannBasedMatcher()
matches = matcher.match(descriptors_object, descriptors_scene)

# 最小距离和最大距离
max_dist = 0
min_dist = 100
for i in range(descriptors_object.shape[1]):
    dist = matches[i].distance
    if dist < min_dist:
        min_dist = dist
    if dist > max_dist:
        max_dist = dist

print(">Max dist 最大距离 :", max_dist)
print(">Min dist 最小距离 :", min_dist)

good_matches = []
for i in range(descriptors_object.shape[1]):
    if matches[i].distance < 3 * min_dist:
        good_matches.append(matches[i])

# 绘制出匹配到的关键点
img_matches = cv2.drawMatches(srcImage1, keypoints_object, srcImage2, keypoints_scene, good_matches, None)

# 当匹配项大于4时
if len(good_matches) >= 4:
    # 查找单应性矩阵
    # 转换为n行的元素,每一行一个元素,并且这个元素由两个值组成
    obj = np.float32([keypoints_object[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)
    scene = np.float32([keypoints_scene[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)

    # 获取单应性矩阵
    H, _ = cv2.findHomography(obj, scene, cv2.RANSAC)

    # 要搜索的图的四个角点
    h, w = srcImage1.shape[0:2]
    obj_corners = np.float32([[0, 0], [w, 0], [w, h], [0, h]]).reshape(-1, 1, 2)
    scene_corners = cv2.perspectiveTransform(obj_corners, H)

    scene_corners[0][0][0] += w
    scene_corners[1][0][0] += w
    scene_corners[2][0][0] += w
    scene_corners[3][0][0] += w

    # 绘制多边形
    cv2.polylines(img_matches, pts=[np.int32(scene_corners)], isClosed=True, color=(255, 0, 123), thickness=4)

cv2.imshow("img_matches", img_matches)
cv2.waitKey(0)


补充:

下面这段代码是 OpenCV-Python-Tutorial-中文版.pdf (P218)中的实现,效果和毛星云的一样,这里放出来,给大伙参考下。

运行效果:

Figure_1.png

源码如下:

import numpy as np
import cv2
from matplotlib import pyplot as plt

MIN_MATCH_COUNT = 10
img1 = cv2.imread('../images/book_box.jpg', 0)  # queryImage
img2 = cv2.imread('../images/book2.jpg', 0)  # trainImage

# Initiate SIFT detector
sift = cv2.xfeatures2d.SIFT_create()
# find the keypoints and descriptors with SIFT
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)

FLANN_INDEX_KDTREE = 0
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(des1, des2, k=2)

# store all the good matches as per Lowe's ratio test.
good = []
for m, n in matches:
    if m.distance < 0.7 * n.distance:
        good.append(m)
'''
现在我们设置只有存在10个以上匹配时才去查找目标( MIN_ MATCH_ COUNT=10 ),否则显示警告消息:“现在匹配不足!"
如果找到了足够的匹配,我们要提取两幅图像中匹配点的坐标。
把它们传入到函数中计算透视变换。
-旦我们找到3x3的变换矩阵,就可以使用它将查询图像的四个顶点(四个角)变换到目标图像中去了。
然后再绘制出来。
'''

if len(good) > MIN_MATCH_COUNT:
    # 获取关 点的坐标
    src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)
    dst_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)

    # 第三个参数 Method used to computed a homography matrix. The following methods are possible: #0 - a regular method using all the points
    # CV_RANSAC - RANSAC-based robust method
    # CV_LMEDS - Least-Median robust method
    # 第四个参数取值范围在 1 到 10  绝一个点对的 值。原图像的点经 变换后点与目标图像上对应点的 差 #    差就 为是 outlier
    #  回值中 M 为变换矩 。
    M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
    matchesMask = mask.ravel().tolist()
    # 获得原图像的高和宽
    h, w = img1.shape
    # 使用得到的变换矩 对原图像的四个   变换 获得在目标图像上对应的坐标
    pts = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2)
    dst = cv2.perspectiveTransform(pts, M)
    # 原图像为灰度图
    img2 = cv2.polylines(img2, [np.int32(dst)], True, 255, 3, cv2.LINE_AA)
else:
    print("Not enough matches are found - %d/%d" % (len(good), MIN_MATCH_COUNT))
    matchesMask = None

# 最后我再绘制 inliers 如果能成功的找到目标图像的话  或者匹配的关  点 如果失败。
draw_params = dict(matchColor=(0, 255, 0),  # draw matches in green color
                   singlePointColor=None,
                   matchesMask=matchesMask,  # draw only inliers
                   flags=2)

img3 = cv2.drawMatches(img1, kp1, img2, kp2, good, None, **draw_params)

plt.imshow(img3, 'gray'), plt.show()
# 复杂图像中被找到的目标图像被标记成白色

    

寻找已知物体(一)
目标查找与跟踪 - Meanshift与CamShift

友情链接: CSDN激萌の小宅 95知识库 自考题库 罗分明个人网络博客 精益编程leanboot

小宅博客  www.bilibili996.com All Rights Reserved. 备案号: 闽ICP备2024034575号

网站经营许可证  福建省福州市 Copyright©2021-2025 版权所有

小宅博客
首页 智能家居 地图定位
公告:小宅博客网可以开发票了,需要发票的,去群里找群主哈!!

文章作者:激萌の小宅

促销:¥0

价格:¥0

配送方式: 购买后立即生效(如购买异常,请联系站长)
付款之后一定要等待自动跳转结束,否则购买可能会失败
  • 0 天

    有效期

  • 0

    总销量

  • 3

    累计评价

寻找已知物体(二)

视频讲解如下:


在本章节中给大家演示如何利用SURF和SIFT来进行关键物体的查找,并将查找到的物体标记出来。主要函数:SURF和SIFT。

本章节的C++同样还是采用毛星云的代码框架,不过做了些许修改。


当前系列所有demo下载地址:

https://github.com/GaoRenBao/OpenCv4-Demo

不同编程语言对应的OpenCv版本以及开发环境信息如下: 

语言

OpenCv版本

IDE

C#

OpenCvSharp4.4.8.0.20230708

Visual Studio 2022

C++

OpenCv-4.5.5-vc14_vc15

Visual Studio 2022

Python

OpenCv-Python (4.6.0.66)

PyCharm Community Edition 2022.1.3


首先呢,我们需要准备两张测试图片, 这里不推荐使用毛星云的那两张测试图片哈,毛星云的测试图片效果不是很好,所以需要对图片做些裁剪,准备的图片如下,一张是需要查找的图片,另一张是目标图片。

11.jpg

2.jpg


考虑到只有C#版本有SURF和SIFT两个查找,博主这里直接演示C#的效果。

首先是SURF的效果:

SURF.jpg


然后使SIFT的效果:

SIFT.jpg


在目前的基础上,博主做了个利用视频进行检测的程序,效果如下。感兴趣的童鞋可以自己尝试修改测试一下。目前该效果不提供源码。

6379308487622719934650070 (1).gif


两个版本效果相差好像也不是很大。不过SIFT形成的点集好像更多。

C#版本代码如下:

using OpenCvSharp;
using System.Collections.Generic;

namespace demo
{
    internal class Program
    {
        static void Main(string[] args)
        {
            SURF();
            SIFT();
            Cv2.WaitKey();
        }

        public static Point2d Point2fToPoint2d(Point2f pf)
        {
            return new Point2d(((int)pf.X), ((int)pf.Y));
        }

        /// <summary>
        /// SURF
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void SURF()
        {
            // 载入源图片并显示
            Mat srcImage1 = Cv2.ImRead("../../../images/book_box.jpg");
            Mat srcImage2 = Cv2.ImRead("../../../images/book2.jpg");

            // 定义一个特征检测类对象
            KeyPoint[] keypoints_object, keypoints_scene;
            Mat descriptors_object = new Mat();
            Mat descriptors_scene = new Mat();
            var MySurf = OpenCvSharp.XFeatures2D.SURF.Create(400);

            // 方法1:计算描述符(特征向量),将Detect和Compute操作分开
            keypoints_object = MySurf.Detect(srcImage1);
            keypoints_scene = MySurf.Detect(srcImage2);
            MySurf.Compute(srcImage1, ref keypoints_object, descriptors_object);
            MySurf.Compute(srcImage2, ref keypoints_scene, descriptors_scene);

            // 方法2:计算描述符(特征向量),将Detect和Compute操作合并
            //MySurf.DetectAndCompute(srcImage1, null, out keypoints_object, descriptors_object);
            //MySurf.DetectAndCompute(srcImage2, null, out keypoints_scene, descriptors_scene);

            // 创建基于FLANN的描述符匹配对象
            FlannBasedMatcher matcher = new FlannBasedMatcher();
            DMatch[] matches = matcher.Match(descriptors_object, descriptors_scene);
            double max_dist = 0; double min_dist = 100;//最小距离和最大距离

            // 计算出关键点之间距离的最大值和最小值
            for (int i = 0; i < descriptors_object.Rows; i++)
            {
                double dist = matches[i].Distance;
                if (dist < min_dist) min_dist = dist;
                if (dist > max_dist) max_dist = dist;
            }

            System.Diagnostics.Debug.WriteLine($">Max dist 最大距离 : {max_dist}");
            System.Diagnostics.Debug.WriteLine($">Min dist 最小距离 : {min_dist}");

            // 存下匹配距离小于3*min_dist的点对
            List<DMatch> good_matches = new List<DMatch>();
            for (int i = 0; i < descriptors_object.Rows; i++)
            {
                if (matches[i].Distance < 3 * min_dist)
                {
                    good_matches.Add(matches[i]);
                }
            }

            // 绘制出匹配到的关键点
            Mat img_matches = new Mat();
            Cv2.DrawMatches(srcImage1, keypoints_object, srcImage2, keypoints_scene, good_matches, img_matches);

            //定义两个局部变量
            List<Point2f> obj = new List<Point2f>();
            List<Point2f> scene = new List<Point2f>();

            //从匹配成功的匹配对中获取关键点
            for (int i = 0; i < good_matches.Count; i++)
            {
                obj.Add(keypoints_object[good_matches[i].QueryIdx].Pt);
                scene.Add(keypoints_scene[good_matches[i].TrainIdx].Pt);
            }

            //计算透视变换 
            List<Point2d> objPts = obj.ConvertAll(Point2fToPoint2d);
            List<Point2d> scenePts = scene.ConvertAll(Point2fToPoint2d);
            Mat H = Cv2.FindHomography(objPts, scenePts, HomographyMethods.Ransac);

            //从待测图片中获取角点
            List<Point2f> obj_corners = new List<Point2f>();
            obj_corners.Add(new Point(0, 0));
            obj_corners.Add(new Point(srcImage1.Cols, 0));
            obj_corners.Add(new Point(srcImage1.Cols, srcImage1.Rows));
            obj_corners.Add(new Point(0, srcImage1.Rows));

            //进行透视变换
            Point2f[] scene_corners = Cv2.PerspectiveTransform(obj_corners, H);

            //绘制出角点之间的直线
            Point2f P0 = scene_corners[0] + new Point2f(srcImage1.Cols, 0);
            Point2f P1 = scene_corners[1] + new Point2f(srcImage1.Cols, 0);
            Point2f P2 = scene_corners[2] + new Point2f(srcImage1.Cols, 0);
            Point2f P3 = scene_corners[3] + new Point2f(srcImage1.Cols, 0);

            Cv2.Line(img_matches, new Point(P0.X, P0.Y), new Point(P1.X, P1.Y), new Scalar(255, 0, 123), 4);
            Cv2.Line(img_matches, new Point(P1.X, P1.Y), new Point(P2.X, P2.Y), new Scalar(255, 0, 123), 4);
            Cv2.Line(img_matches, new Point(P2.X, P2.Y), new Point(P3.X, P3.Y), new Scalar(255, 0, 123), 4);
            Cv2.Line(img_matches, new Point(P3.X, P3.Y), new Point(P0.X, P0.Y), new Scalar(255, 0, 123), 4);

            //显示最终结果
            Cv2.ImShow("SURF效果图", img_matches);
            // Cv2.ImWrite("SURF.jpg", img_matches);
        }

        /// <summary>
        /// SIFT
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void SIFT()
        {
            // 载入源图片并显示
            Mat srcImage1 = Cv2.ImRead("../../../images/book_box.jpg");
            Mat srcImage2 = Cv2.ImRead("../../../images/book2.jpg");

            // 定义一个特征检测类对象
            KeyPoint[] keypoints_object, keypoints_scene;
            Mat descriptors_object = new Mat();
            Mat descriptors_scene = new Mat();
            var MySift = OpenCvSharp.Features2D.SIFT.Create(400);

            // 方法1:计算描述符(特征向量),将Detect和Compute操作分开
            keypoints_object = MySift.Detect(srcImage1);
            keypoints_scene = MySift.Detect(srcImage2);
            MySift.Compute(srcImage1, ref keypoints_object, descriptors_object);
            MySift.Compute(srcImage2, ref keypoints_scene, descriptors_scene);

            // 方法2:计算描述符(特征向量),将Detect和Compute操作合并
            //MySift.DetectAndCompute(srcImage1, null, out keypoints_object, descriptors_object);
            //MySift.DetectAndCompute(srcImage2, null, out keypoints_scene, descriptors_scene);

            // 创建基于FLANN的描述符匹配对象
            FlannBasedMatcher matcher = new FlannBasedMatcher();
            DMatch[] matches = matcher.Match(descriptors_object, descriptors_scene);
            double max_dist = 0; double min_dist = 100;//最小距离和最大距离

            // 计算出关键点之间距离的最大值和最小值
            for (int i = 0; i < descriptors_object.Rows; i++)
            {
                double dist = matches[i].Distance;
                if (dist < min_dist) min_dist = dist;
                if (dist > max_dist) max_dist = dist;
            }

            System.Diagnostics.Debug.WriteLine($">Max dist 最大距离 : {max_dist}");
            System.Diagnostics.Debug.WriteLine($">Min dist 最小距离 : {min_dist}");

            // 存下匹配距离小于3*min_dist的点对
            List<DMatch> good_matches = new List<DMatch>();
            for (int i = 0; i < descriptors_object.Rows; i++)
            {
                if (matches[i].Distance < 3 * min_dist)
                {
                    good_matches.Add(matches[i]);
                }
            }

            // 绘制出匹配到的关键点
            Mat img_matches = new Mat();
            Cv2.DrawMatches(srcImage1, keypoints_object, srcImage2, keypoints_scene, good_matches, img_matches);

            //定义两个局部变量
            List<Point2f> obj = new List<Point2f>();
            List<Point2f> scene = new List<Point2f>();

            //从匹配成功的匹配对中获取关键点
            for (int i = 0; i < good_matches.Count; i++)
            {
                obj.Add(keypoints_object[good_matches[i].QueryIdx].Pt);
                scene.Add(keypoints_scene[good_matches[i].TrainIdx].Pt);
            }

            //计算透视变换 
            List<Point2d> objPts = obj.ConvertAll(Point2fToPoint2d);
            List<Point2d> scenePts = scene.ConvertAll(Point2fToPoint2d);
            Mat H = Cv2.FindHomography(objPts, scenePts, HomographyMethods.Ransac);

            //从待测图片中获取角点
            List<Point2f> obj_corners = new List<Point2f>();
            obj_corners.Add(new Point(0, 0));
            obj_corners.Add(new Point(srcImage1.Cols, 0));
            obj_corners.Add(new Point(srcImage1.Cols, srcImage1.Rows));
            obj_corners.Add(new Point(0, srcImage1.Rows));

            //进行透视变换
            Point2f[] scene_corners = Cv2.PerspectiveTransform(obj_corners, H);

            //绘制出角点之间的直线
            Point2f P0 = scene_corners[0] + new Point2f(srcImage1.Cols, 0);
            Point2f P1 = scene_corners[1] + new Point2f(srcImage1.Cols, 0);
            Point2f P2 = scene_corners[2] + new Point2f(srcImage1.Cols, 0);
            Point2f P3 = scene_corners[3] + new Point2f(srcImage1.Cols, 0);

            Cv2.Line(img_matches, new Point(P0.X, P0.Y), new Point(P1.X, P1.Y), new Scalar(255, 0, 123), 4);
            Cv2.Line(img_matches, new Point(P1.X, P1.Y), new Point(P2.X, P2.Y), new Scalar(255, 0, 123), 4);
            Cv2.Line(img_matches, new Point(P2.X, P2.Y), new Point(P3.X, P3.Y), new Scalar(255, 0, 123), 4);
            Cv2.Line(img_matches, new Point(P3.X, P3.Y), new Point(P0.X, P0.Y), new Scalar(255, 0, 123), 4);

            //显示最终结果
            Cv2.ImShow("SIFT效果图", img_matches);
            // Cv2.ImWrite("SIFT.jpg", img_matches);
        }
    }
}


C++版本代码如下:

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d.hpp>
#include <opencv2/features2d/features2d.hpp>

using namespace cv;
using namespace std;

int main()
{
	//【1】载入原始图片
	Mat srcImage1 = imread("../images/book_box.jpg");
	Mat srcImage2 = imread("../images/book2.jpg");
	if (!srcImage1.data || !srcImage2.data)
	{
		printf("读取图片错误,请确定目录下是否有imread函数指定的图片存在~! \n"); return false;
	}

	// 使用SIFT算子检测关键点
	Ptr<SiftFeatureDetector> detector = SiftFeatureDetector::create(400);

	// vector模板类,存放任意类型的动态数组
	vector<KeyPoint> keypoints_object, keypoints_scene;
	Mat descriptors_object, descriptors_scene;

	// 调用detect函数检测出SURF特征关键点,保存在vector容器中
	detector->detect(srcImage1, keypoints_object);
	detector->detect(srcImage2, keypoints_scene);
	// 计算描述符(特征向量)
	detector->compute(srcImage1, keypoints_object, descriptors_object);
	detector->compute(srcImage2, keypoints_scene, descriptors_scene);

	// 使用FLANN匹配算子进行匹配
	FlannBasedMatcher matcher;
	vector<DMatch> matches;
	matcher.match(descriptors_object, descriptors_scene, matches);
	double max_dist = 0; double min_dist = 100;//最小距离和最大距离

	// 计算出关键点之间距离的最大值和最小值
	for (int i = 0; i < descriptors_object.rows; i++)
	{
		double dist = matches[i].distance;
		if (dist < min_dist) min_dist = dist;
		if (dist > max_dist) max_dist = dist;
	}

	printf(">Max dist 最大距离 : %f \n", max_dist);
	printf(">Min dist 最小距离 : %f \n", min_dist);

	// 存下匹配距离小于3*min_dist的点对
	std::vector< DMatch > good_matches;
	for (int i = 0; i < descriptors_object.rows; i++)
	{
		if (matches[i].distance < 3 * min_dist)
		{
			good_matches.push_back(matches[i]);
		}
	}

	// 绘制出匹配到的关键点
	Mat img_matches;
	drawMatches(srcImage1, keypoints_object, srcImage2, keypoints_scene,
		good_matches, img_matches, Scalar::all(-1), Scalar::all(-1),
		vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);

	// 定义两个局部变量
	vector<Point2f> obj;
	vector<Point2f> scene;

	//从匹配成功的匹配对中获取关键点
	for (unsigned int i = 0; i < good_matches.size(); i++)
	{
		obj.push_back(keypoints_object[good_matches[i].queryIdx].pt);
		scene.push_back(keypoints_scene[good_matches[i].trainIdx].pt);
	}

	Mat H = findHomography(obj, scene, RANSAC);//计算透视变换 

	//从待测图片中获取角点
	vector<Point2f> obj_corners(4);
	obj_corners[0] = Point(0, 0);
	obj_corners[1] = Point(srcImage1.cols, 0);
	obj_corners[2] = Point(srcImage1.cols, srcImage1.rows);
	obj_corners[3] = Point(0, srcImage1.rows);
	vector<Point2f> scene_corners(4);

	//进行透视变换
	perspectiveTransform(obj_corners, scene_corners, H);

	//绘制出角点之间的直线
	line(img_matches, scene_corners[0] + Point2f(static_cast<float>(srcImage1.cols), 0), scene_corners[1] + Point2f(static_cast<float>(srcImage1.cols), 0), Scalar(255, 0, 123), 4);
	line(img_matches, scene_corners[1] + Point2f(static_cast<float>(srcImage1.cols), 0), scene_corners[2] + Point2f(static_cast<float>(srcImage1.cols), 0), Scalar(255, 0, 123), 4);
	line(img_matches, scene_corners[2] + Point2f(static_cast<float>(srcImage1.cols), 0), scene_corners[3] + Point2f(static_cast<float>(srcImage1.cols), 0), Scalar(255, 0, 123), 4);
	line(img_matches, scene_corners[3] + Point2f(static_cast<float>(srcImage1.cols), 0), scene_corners[0] + Point2f(static_cast<float>(srcImage1.cols), 0), Scalar(255, 0, 123), 4);

	//显示最终结果
	imshow("效果图", img_matches);

	waitKey(0);
	return 0;
}


Python版本代码如下:

import os
import cv2
import numpy as np

# 读取图片和缩放图片
srcImage1 = cv2.imread('../images/book_box.jpg')
srcImage2 = cv2.imread('../images/book2.jpg')

# 创建SIFT
sift = cv2.SIFT_create(400)
# 计算特征点和描述点
(keypoints_object, descriptors_object) = sift.detectAndCompute(srcImage1, None)
(keypoints_scene, descriptors_scene) = sift.detectAndCompute(srcImage2, None)

# 使用FLANN匹配算子进行匹配
# matcher = cv2.BFMatcher() #建立匹配关系
# matches=matcher.match(descriptors_object,descriptors_scene) #匹配描述子

matcher = cv2.FlannBasedMatcher()
matches = matcher.match(descriptors_object, descriptors_scene)

# 最小距离和最大距离
max_dist = 0
min_dist = 100
for i in range(descriptors_object.shape[1]):
    dist = matches[i].distance
    if dist < min_dist:
        min_dist = dist
    if dist > max_dist:
        max_dist = dist

print(">Max dist 最大距离 :", max_dist)
print(">Min dist 最小距离 :", min_dist)

good_matches = []
for i in range(descriptors_object.shape[1]):
    if matches[i].distance < 3 * min_dist:
        good_matches.append(matches[i])

# 绘制出匹配到的关键点
img_matches = cv2.drawMatches(srcImage1, keypoints_object, srcImage2, keypoints_scene, good_matches, None)

# 当匹配项大于4时
if len(good_matches) >= 4:
    # 查找单应性矩阵
    # 转换为n行的元素,每一行一个元素,并且这个元素由两个值组成
    obj = np.float32([keypoints_object[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)
    scene = np.float32([keypoints_scene[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)

    # 获取单应性矩阵
    H, _ = cv2.findHomography(obj, scene, cv2.RANSAC)

    # 要搜索的图的四个角点
    h, w = srcImage1.shape[0:2]
    obj_corners = np.float32([[0, 0], [w, 0], [w, h], [0, h]]).reshape(-1, 1, 2)
    scene_corners = cv2.perspectiveTransform(obj_corners, H)

    scene_corners[0][0][0] += w
    scene_corners[1][0][0] += w
    scene_corners[2][0][0] += w
    scene_corners[3][0][0] += w

    # 绘制多边形
    cv2.polylines(img_matches, pts=[np.int32(scene_corners)], isClosed=True, color=(255, 0, 123), thickness=4)

cv2.imshow("img_matches", img_matches)
cv2.waitKey(0)


补充:

下面这段代码是 OpenCV-Python-Tutorial-中文版.pdf (P218)中的实现,效果和毛星云的一样,这里放出来,给大伙参考下。

运行效果:

Figure_1.png

源码如下:

import numpy as np
import cv2
from matplotlib import pyplot as plt

MIN_MATCH_COUNT = 10
img1 = cv2.imread('../images/book_box.jpg', 0)  # queryImage
img2 = cv2.imread('../images/book2.jpg', 0)  # trainImage

# Initiate SIFT detector
sift = cv2.xfeatures2d.SIFT_create()
# find the keypoints and descriptors with SIFT
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)

FLANN_INDEX_KDTREE = 0
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(des1, des2, k=2)

# store all the good matches as per Lowe's ratio test.
good = []
for m, n in matches:
    if m.distance < 0.7 * n.distance:
        good.append(m)
'''
现在我们设置只有存在10个以上匹配时才去查找目标( MIN_ MATCH_ COUNT=10 ),否则显示警告消息:“现在匹配不足!"
如果找到了足够的匹配,我们要提取两幅图像中匹配点的坐标。
把它们传入到函数中计算透视变换。
-旦我们找到3x3的变换矩阵,就可以使用它将查询图像的四个顶点(四个角)变换到目标图像中去了。
然后再绘制出来。
'''

if len(good) > MIN_MATCH_COUNT:
    # 获取关 点的坐标
    src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)
    dst_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)

    # 第三个参数 Method used to computed a homography matrix. The following methods are possible: #0 - a regular method using all the points
    # CV_RANSAC - RANSAC-based robust method
    # CV_LMEDS - Least-Median robust method
    # 第四个参数取值范围在 1 到 10  绝一个点对的 值。原图像的点经 变换后点与目标图像上对应点的 差 #    差就 为是 outlier
    #  回值中 M 为变换矩 。
    M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
    matchesMask = mask.ravel().tolist()
    # 获得原图像的高和宽
    h, w = img1.shape
    # 使用得到的变换矩 对原图像的四个   变换 获得在目标图像上对应的坐标
    pts = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2)
    dst = cv2.perspectiveTransform(pts, M)
    # 原图像为灰度图
    img2 = cv2.polylines(img2, [np.int32(dst)], True, 255, 3, cv2.LINE_AA)
else:
    print("Not enough matches are found - %d/%d" % (len(good), MIN_MATCH_COUNT))
    matchesMask = None

# 最后我再绘制 inliers 如果能成功的找到目标图像的话  或者匹配的关  点 如果失败。
draw_params = dict(matchColor=(0, 255, 0),  # draw matches in green color
                   singlePointColor=None,
                   matchesMask=matchesMask,  # draw only inliers
                   flags=2)

img3 = cv2.drawMatches(img1, kp1, img2, kp2, good, None, **draw_params)

plt.imshow(img3, 'gray'), plt.show()
# 复杂图像中被找到的目标图像被标记成白色