浅谈OpenCV的多对象匹配透明图像的实现,以及如何匹配半透明控件

引子

  1. OpenCV提供的templateMatch只负责将(相关性等)计算出来,并不会直接提供目标的对应坐标,一般来说我们直接遍历最高的相关度,就可以得到匹配度最高的坐标。但是这样一般只能得到一个坐标。
  2. 在实际操作中,我们可能需要匹配一个不规则的图像,把这个不规则的图像放进矩形Mat里,会出现很多不应该参与匹配的地方参与结果的计算,导致识别率下降。
  3. 有时候面对半透明控件,其后的背景完全不一样,传统的匹配方法直接歇菜了,怎么办?

解决方法

1. 解决多对象匹配的问题

通过templateMatch算法,可以得到目标与原图像中等大子图像对应归一化的相关系数,这个归一化的相关系数可以看作是对于的概率(其实不是这样),可以设定一个阈值,把大于这个阈值的坐标都筛选出来。但是这样在一个成功匹配的坐标附近也会存在许多相关性稍小的坐标也大于这个阈值,我们无法区分这些坐标对于的图像是原来的图像还是其他的图像,这样就把这个问题转化为了怎么把这些副产物给去除。有cv经验的应该很快会想到[nms算法](非极大值抑制(NMS)算法讲解|理论+代码 - 知乎 (zhihu.com))。想了解的同学可以点进去看看。下面就只提供代码实现。

2. 解决不规则图像匹配问题

OpenCV的templateMatch中提供了一个可选的参数mask,这个mask是和目标等大的一张图,可以是U8C1也可以是FP32,其中U8C1对于每个点的含义是为0则放弃匹配该点,非0就会匹配,FP32是会将这个点像素在计算相关性时赋予对于的权重。要求比较简单,只需要不匹配不规则图像中的空白部分就好了,可以在mask中把这里涂黑,要匹配的地方涂白就好了(绿幕抠像?)。

3. 解决半透明控件的匹配问题

对于半透明控件,某个坐标对应的像素值就是会随着背景变化而变化的。templateMatch这种通过计算字节上相似度的算法会因为背景变化而导致整个图像的像素发生整体性的大规模变化而受到影响。但是即便整个图像的像素发生变化,寻找目标颜色与坐标的相对关系是基本不变的(目标具有某种特征,这也就是人为什么可以对这种控件进行识别)。可以用特征匹配的方法,利用这个特性对透明控件进行匹配。

需要注意的是部分算法来自于nonfree的xfeature,使用时请注意避免纠纷,当然也需要使用者手动打开这个编译开关,相关代码Fork自OpenCV: Features2D + Homography to find a known object

最终代码实现

libmatch.h
#ifdef LIBMATCH_EXPORTS
#define LIBMATCH_API extern "C" __declspec(dllexport)
struct objectEx
{cv::Rect_<float> rect;float prob;
};struct objectEx2
{cv::Point2f dots[4];
};static void qsort_descent_inplace(std::vector<objectEx>& objects)
{if (objects.empty())return;std::sort(objects.begin(), objects.end(), [](const objectEx& a, const objectEx& b) {return a.prob > b.prob; });
}static inline float intersection_area(const objectEx& a, const objectEx& b)
{cv::Rect_<float> inter = a.rect & b.rect;return inter.area();
}static void nms_sorted_bboxes(const std::vector<objectEx>& faceobjects, std::vector<int>& picked, float nms_threshold)
{picked.clear();const int n = faceobjects.size();std::vector<float> areas(n);for (int i = 0; i < n; i++){areas[i] = faceobjects[i].rect.area();}for (int i = 0; i < n; i++){const objectEx& a = faceobjects[i];int keep = 1;for (int j = 0; j < (int)picked.size(); j++){const objectEx& b = faceobjects[picked[j]];// intersection over unionfloat inter_area = intersection_area(a, b);float union_area = areas[i] + areas[picked[j]] - inter_area;// float IoU = inter_area / union_areaif (inter_area / union_area > nms_threshold)keep = 0;}if (keep)picked.push_back(i);}
}const int version = 230622;#else
#define LIBMATCH_API extern "C" __declspec(dllimport)
struct objectEx
{struct Rect{float x, y, width, height;} rect;float prob;
};
struct objectEx2
{struct{float x, y;}dots[4];
};#endifLIBMATCH_API int match_get_version();LIBMATCH_API size_t match_scan(uint8_t* src_img_data,const size_t src_img_size,uint8_t* target_img_data,const size_t target_img_size,const float prob_threshold,const float nms_threshold,objectEx* RetObejectArr,const size_t maxRetCount,const uint32_t MaskColor //Just For BGR,if high 2bit isn`t zero,mask will be disabled
);LIBMATCH_API bool match_feat(uint8_t* src_img_data,const size_t src_img_size,uint8_t* target_img_data,const size_t target_img_size,objectEx2 &result
);
libmatch.cpp
// libmatch.cpp : 定义 DLL 的导出函数。
//#include "pch.h"
#include "framework.h"
#include "libmatch.h"LIBMATCH_API int match_get_version()
{return version;
}LIBMATCH_API size_t match_scan(uint8_t* src_img_data,const size_t src_img_size,uint8_t* target_img_data,const size_t target_img_size,const float prob_threshold,const float nms_threshold,objectEx* RetObejectArr,const size_t maxRetCount,const uint32_t MaskColor //Just For BGR,if high 2bit isn`t zero,mask will be disabled
)
{//Read and Process img Startcv::_InputArray src_img_arr(src_img_data, src_img_size);cv::Mat src_mat = cv::imdecode(src_img_arr, cv::IMREAD_GRAYSCALE);if (src_mat.empty()){std::cout << "[Match] Err Can`t Read src_img" << std::endl;return -1;}cv::_InputArray target_img_arr(target_img_data, target_img_size);cv::Mat target_mat = cv::imdecode(target_img_arr, cv::IMREAD_GRAYSCALE);if (target_mat.empty()){std::cout << "[Match] Err Can`t Read target_img" << std::endl;return -1;}if (target_mat.cols > src_mat.cols || target_mat.rows > src_mat.rows){std::cout << "[Match]ERR Target is too large" << std::endl;return false;}//Read Over//Template Match Startcv::Mat result(src_mat.cols - target_mat.cols + 1, src_mat.rows - target_mat.rows + 1, CV_32FC1);if ((MaskColor & 0xff000000) != 0){cv::matchTemplate(src_mat, target_mat, result, cv::TM_CCOEFF_NORMED);}else{cv::Mat temp_target_mat = cv::imdecode(target_img_arr, cv::IMREAD_COLOR);cv::Mat maks_mat = cv::Mat::zeros(target_mat.rows, target_mat.cols, CV_8U);//Replace MaskColorfor (int i = 0; i < temp_target_mat.rows; i++)for (int j = 0; j < temp_target_mat.cols; j++) {cv::Vec3b temp_color=temp_target_mat.at<cv::Vec3b>(cv::Point(j, i));if (((temp_color[0] << 16) | (temp_color[1] << 8) | temp_color[2]) != MaskColor) {
//                    std::cout << ((temp_color[0] << 16) | (temp_color[1] << 8) | temp_color[2]) << std::endl;maks_mat.at<uint8_t>(cv::Point(j, i)) = 255;}}
//      cv::imshow("result", maks_mat);
//      cv::waitKey();cv::matchTemplate(src_mat, target_mat, result, cv::TM_CCOEFF_NORMED, maks_mat);}//Template Match Over//BackEnd Processstd::vector <objectEx> proposals;for (int i = 0; i < result.rows; ++i)for (int j = 0; j < result.cols; ++j){if (result.at<float>(cv::Point(j, i)) >= prob_threshold){objectEx buf;buf.prob = result.at<float>(cv::Point(j, i));buf.rect.x = j;buf.rect.y = i;buf.rect.height = target_mat.rows;buf.rect.width = target_mat.cols;proposals.push_back(buf);}}std::vector<int> picked;qsort_descent_inplace(proposals);nms_sorted_bboxes(proposals, picked, nms_threshold);std::vector <objectEx> objects;for (auto x : picked)objects.emplace_back(proposals[x]);//BackEnd Overmemcpy(RetObejectArr, objects.data(), sizeof(objectEx) * std::min(objects.size(), maxRetCount));return objects.size();
}LIBMATCH_API bool match_feat(uint8_t* src_img_data,const size_t src_img_size,uint8_t* target_img_data,const size_t target_img_size,objectEx2 &result
) 
{//Read and Process img Startcv::_InputArray src_img_arr(src_img_data, src_img_size);cv::Mat src_mat = cv::imdecode(src_img_arr, cv::IMREAD_GRAYSCALE);if (src_mat.empty()){std::cout << "[Match] Err Can`t Read src_img" << std::endl;return false;}cv::_InputArray target_img_arr(target_img_data, target_img_size);cv::Mat target_mat = cv::imdecode(target_img_arr, cv::IMREAD_GRAYSCALE);if (target_mat.empty()){std::cout << "[Match] Err Can`t Read target_img" << std::endl;return false;}//Read Over//-- Step 1: Detect the keypoints using SURF Detector, compute the descriptorsint minHessian = 400;cv::Ptr<cv::xfeatures2d::SURF> detector = cv::xfeatures2d::SURF::create(minHessian);std::vector<cv::KeyPoint> keypoints_object, keypoints_scene;cv::Mat descriptors_object, descriptors_scene;detector->detectAndCompute(target_mat, cv::noArray(), keypoints_object, descriptors_object);detector->detectAndCompute(src_mat,cv::noArray(), keypoints_scene, descriptors_scene);//-- Step 2: Matching descriptor vectors with a FLANN based matcher// Since SURF is a floating-point descriptor NORM_L2 is usedcv::Ptr<cv::DescriptorMatcher> matcher = cv::DescriptorMatcher::create(cv::DescriptorMatcher::FLANNBASED);std::vector< std::vector<cv::DMatch> > knn_matches;matcher->knnMatch(descriptors_object, descriptors_scene, knn_matches, 2);//-- Filter matches using the Lowe's ratio testconst float ratio_thresh = 0.75f;std::vector<cv::DMatch> good_matches;for (size_t i = 0; i < knn_matches.size(); i++){if (knn_matches[i][0].distance < ratio_thresh * knn_matches[i][1].distance){good_matches.push_back(knn_matches[i][0]);}}/*OpenCV(4.7.0) D:\opencv-4.7.0\modules\calib3d\src\fundam.cpp:385. error:.(-28:Unknown error code -28) The input arrays should have at least 4corresponding point sets to calculate Homography in function'cv:findHomography'*/if (good_matches.size() < 4)return false;//-- Draw matches//Mat img_matches;//drawMatches(img_object, keypoints_object, img_scene, keypoints_scene, good_matches, img_matches, Scalar::all(-1),//  Scalar::all(-1), std::vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);//-- Localize the objectstd::vector<cv::Point2f> obj;std::vector<cv::Point2f> scene;for (size_t i = 0; i < good_matches.size(); i++){//-- Get the keypoints from the good matchesobj.push_back(keypoints_object[good_matches[i].queryIdx].pt);scene.push_back(keypoints_scene[good_matches[i].trainIdx].pt);}cv::Mat H = findHomography(obj, scene, cv::RANSAC);//-- Get the corners from the image_1 ( the object to be "detected" )std::vector<cv::Point2f> obj_corners(4);obj_corners[0] = cv::Point2f(0, 0);obj_corners[1] = cv::Point2f((float)target_mat.cols, 0);obj_corners[2] = cv::Point2f((float)target_mat.cols, (float)target_mat.rows);obj_corners[3] = cv::Point2f(0, (float)target_mat.rows);std::vector<cv::Point2f> buf_corners(4);cv::perspectiveTransform(obj_corners, buf_corners, H);memcpy(result.dots, buf_corners.data(), buf_corners.size() * sizeof(cv::Point2f));return true;
}

实现效果

多对象匹配+不规则匹配

​编辑

半透明控件匹配

​编辑

后记

紧张而刺激的高考在本月落下了帷幕,结束了长达12年的通识教育,笔者终于能够潜下心来研究这些东西背后的数学原理。由于笔者的能力有限,本文存在不严谨的部分,希望读者可以谅解。

经验之谈:特征匹配不要出现过量的重复元素

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/371841.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Hadoop-12-Hive 基本介绍 下载安装配置 MariaDB安装 3台云服务Hadoop集群 架构图 对比SQL HQL

章节内容 上一节我们完成了&#xff1a; Reduce JOIN 的介绍Reduce JOIN 的具体实现DriverMapperReducer运行测试 背景介绍 这里是三台公网云服务器&#xff0c;每台 2C4G&#xff0c;搭建一个Hadoop的学习环境&#xff0c;供我学习。 之前已经在 VM 虚拟机上搭建过一次&am…

Atom CMS v2.0 SQL 注入漏洞(CVE-2022-25488)

前言 CVE-2022-25488 是一个发现于 Telesquare SDT-CW3B1 设备中的命令注入漏洞。这一漏洞可以被未经认证的远程攻击者利用&#xff0c;通过特殊构造的 HTTP 请求在设备上执行任意命令。以下是关于该漏洞的详细信息&#xff1a; 漏洞详细信息 漏洞编号: CVE-2022-25488影响范…

记录一次ffmpeg手动编译出现的问题

前言部分 使用环境: ubuntu 22.04 最近手动编译了一次的ffmpeg&#xff08;参考博客ffmpeg学习&#xff1a;ubuntu下编译ffmpeg(全网最懒的编译脚本)&#xff09;&#xff0c;但是过程出现了一些问题&#xff0c;因此在此记录一下&#xff0c;若有疑问&#xff0c;欢迎讨论~。 …

【MotionCap】pycharm 远程在wsl2 ubuntu20.04中root的miniconda3环境

pycharm wsl2 链接到pycharmsbin 都能看到内容,/root 下内容赋予了zhangbin 所有,pycharm还是看不到/root 下内容。sudo 安装了miniconda3 引发了这些问题 由于是在 root 用户安装的miniconda3 所以安装路径在/root/miniconda3 里 这导致了环境也是root用户的,会触发告警 WA…

【MySQL】MySQL锁冲突排障纪要

【MySQL】MySQL锁冲突排障纪要 开篇词&#xff1a;干货篇&#xff1a;1.查看当前innodb status,里面包含事务,锁占用情况2.查看mysql进程状态3.查看innodb事务&#xff0c;锁&#xff0c;锁等待情况4.定位持有锁的线程信息 总结篇&#xff1a;一、锁冲突的原因二、锁冲突的表现…

BigDecimal(double)和BigDecimal(String)有什么区别?BigDecimal如何精确计数?

BigDecimal(double)和BigDecimal(String)的区别 double是不精确的&#xff0c;所以使用一个不精确的数字来创建BigDecimal&#xff0c;得到的数字也是不精确的。如0.1这个数字&#xff0c;double只能表示他的近似值。所以&#xff0c;当我们使用new BigDecimal(0.1)创建一个Bi…

一.2.(4)放大电路静态工作点的稳定;

1.Rb对Q点及Au的影响 输入特性曲线&#xff1a;Rb减少&#xff0c;IBQ&#xff0c;UBEQ增大 输出特性曲线&#xff1a;ICQ增大&#xff0c;UCEQ减少 AUUO/Ui分子减少&#xff0c;分母增大&#xff0c;但由于分子带负号&#xff0c;所以|Au|减少 2.Rc对Q点及Au的影响 输入特性曲…

Google账号输入用户名和密码后提醒要到手机通知点是,还要点击数字,但是我手机收不到

有一些朋友换了一个新的电脑后手机登录谷歌账号时&#xff0c;用户名和密码都正确输入以后&#xff0c;第三步弹出一个提示&#xff0c;要在手机上的通知栏点击是&#xff0c;并且点击手机上相应的数字才能继续登录。 但是自己的手机上下拉通知栏却没有来自谷歌的通知&#xf…

Qt(MSVC)下报“语法错误缺少“}““语法错误缺少“常数“ 的解决办法

1.现象 目前我在工程中试图使用QHttpServer时&#xff0c;一编译&#xff0c;就报了一堆奇奇怪怪的错误&#xff1a; D:\Qt\httpServer\Qt5.15.2\include\QtHttpServer\qhttpserverrequest.h:75: error: C2143: 语法错误: 缺少“}”(在“(”的前面) D:\Qt\httpServer\Qt5.15.…

0/1背包问题总结

文章目录 &#x1f347;什么是0/1背包问题&#xff1f;&#x1f348;例题&#x1f349;1.分割等和子集&#x1f349;2.目标和&#x1f349;3.最后一块石头的重量Ⅱ &#x1f34a;总结 博客主页&#xff1a;lyyyyrics &#x1f347;什么是0/1背包问题&#xff1f; 0/1背包问题是…

实现ubuntu的任务计划反弹shell

1.实验目的 使用Ubuntu定时任务反弹shell 2实验环境 ubuntu&#xff1a;ip地址&#xff1a;192.168.80.133 kali&#xff1a;ip地址&#xff1a;192.168.80.134 3.编写crontab计划任务 在ubuntu的系统中使用crontab -e命令编写计划任务 作用&#xff1a;是将一个交互式的…

【基于R语言群体遗传学】-12-超显性与次显性

欢迎先看前面的博客&#xff0c;再继续进行后面的内容&#xff1a; 群体遗传学_tRNA做科研的博客-CSDN博客 当杂合子的适应度超出纯合子的范围时&#xff0c;二倍体能够展现出更多令人着迷的选择实例。这种形式的一种是杂合子优势&#xff0c;或称为“超显性”&#xff0c;其…

MySQL中的数据类型

这里写目录标题 数值类型整数类型浮点数类型定点数类型 日期和时间类型字符串类型定长字符串变长字符串文本类型二进制类型 枚举和集合类型选择标准示例 SET FOREIGN_KEY_CHECKS0; DROP TABLE IF EXISTS sys_user; CREATE TABLE sys_user (user_id bigint NOT NULL AUTO_INCREM…

轻松转换!两款AI工具让word秒变ppt!

想把Word文档一键生成PPT&#xff0c;过去有一个很常见的做法&#xff1a;先在Word文档中设置标题样式&#xff0c;通过标题样式来分隔每一部分&#xff0c;之后导出为PPT&#xff0c;就能得到一份PPT的雏形&#xff0c;但这种方法无法对PPT自动进行美化&#xff0c;即得到的只…

现货黄金技术出现这一信号赶紧止损!

很多现货黄金投资者都并不知道&#xff0c;移动平均线除了可以用于寻找进场的机会&#xff0c;还可以用来设置止损&#xff0c;让自己在交易中更好地进行防守。其实移动平均线止损&#xff0c;是常用的技术止损方法之一&#xff0c;本文将和大家分享怎样利用均线设置止损点&…

Java | Leetcode Java题解之第217题存在重复元素

题目&#xff1a; 题解&#xff1a; class Solution {public boolean containsDuplicate(int[] nums) {Set<Integer> set new HashSet<Integer>();for (int x : nums) {if (!set.add(x)) {return true;}}return false;} }

VSCode远程连接Linux服务器

VSCode远程连接Linux服务器 一、下载VSCode二、远程连接Linux服务器2.1 安装插件2.2 连接linux服务器 我用的Linux服务器(腾讯云服务器&#xff0c;如果是虚拟机需要手动去配置ssh)&#xff0c;操作系统是ubuntu 20.04&#xff08;系统如果不一样&#xff0c;可以重装系统&…

2024/7/7总结

Servlet WebServlet("/demo2") public class servlet_demo2 extends HttpServlet {/*** 就绪/服务方法&#xff08;处理请求数据&#xff09;* 系统方法&#xff0c;服务器自动调用* 当有请求到达servlet时&#xff0c;就会调用该方法* 方法可以被多次调用* param r…

iOS多target时怎么对InfoPlist进行国际化

由于不同target要显示不同的App名称、不同的权限提示语&#xff0c;国际化InfoPlist文件必须创建名称为InfoPlist.strings的文件&#xff0c;那么多个target时怎么进行国际化呢&#xff1f;步骤如下&#xff1a; 一、首先我们在项目根目录创建不同的文件夹对应多个不同的targe…

多方SQL计算场景下,如何达成双方共识,确认多方计算作业的安全性

安全多方计算在SQL场景下的限制 随着MPC、隐私计算等概念的流行&#xff0c; 诸多政府机构、金融企业开始考虑参与到多方计算的场景中&#xff0c; 扩展数据的应用价值。 以下面这个场景为例&#xff0c; 银行可能希望获取水电局和税务局的数据&#xff0c;来综合计算得到各…