C++调用opencv使用透明遮罩进行模板匹配定位——以梦幻西游鼠标为例

发布时间 2023-12-17 23:38:50作者: 爆米LiuChen

程序简介

项目调用C++的opencv模块进行模板匹配,即在一张源图上找到对应模板图最相似的位置,网上大多数使用matchTemplate方法并没有使用到mask遮罩(也可以叫掩膜),而在现实情况中不规则的模板更为常见,而模板加遮罩则可以实现不规则模板,本文以梦幻西游鼠标为例,展示了用遮罩和不使用遮罩的差异。

程序输入:需要定位模板的原图、模板图、遮罩图

程序输出:模板图在原图中的位置

程序/数据集下载

点击进入下载地址

本文章只发布于博客园CSDN爆米算法,被抄袭后可能排版错乱或下载失效,作者:爆米LiuChen

代码分析

载入图片

#include <opencv2/opencv.hpp>
#include <string> 

cv::Mat sourceMat = cv::imread("2号鼠标测试.png");
cv::Mat templateMat = cv::imread("2号鼠标模板.png");
cv::Mat maskMat = cv::imread("2号鼠标遮罩.png", -1);

源图,目标是找到对话框里那个手型鼠标的位置

source

模板图 即直接从其他源图中截取得到的鼠标图,没有做任何处理

template

掩膜图,即用PS扣下的带透明通道的图,只留下了鼠标主体

mask

处理mask遮罩,先读取mask的透明通道,如果同道值为0,则将mask像素值置0,随后转为灰度图,然后二值化,最后将二值图叠加成3通道的图,因为每个通道都可以有自己的遮罩,可以自行处理,但本文是3个通道用一样的遮罩

int maskWidth = maskMat.cols;
int maskHeight = maskMat.rows;

// 根据透明通道修改像素值 透明通道值为0 则像素值置0
for (int y = 0; y < maskHeight; ++y) {
    for (int x = 0; x < maskWidth; ++x) {
        // 获取像素值,注意通道顺序是BGR(A)而不是RGBA
        cv::Vec4b pixel = maskMat.at<cv::Vec4b>(y, x);
        if (pixel[3] == 0) {
            maskMat.at<cv::Vec4b>(y, x) = cv::Vec4b(0, 0, 0, 0);
        }

    }
}
// 转换为3通道图片 因为每个通道有单独的遮罩
cv::cvtColor(maskMat, maskMat, cv::COLOR_BGRA2BGR);
cv::cvtColor(maskMat, maskMat, cv::COLOR_BGR2GRAY);
cv::threshold(maskMat, maskMat, 1, 255, cv::THRESH_BINARY);
std::vector<cv::Mat> channels = { maskMat, maskMat, maskMat };
cv::merge(channels, maskMat);

调用matchTemplate定位模板,这里使用mask和不使用mask进行对比,可以看出不使用mask很容易误判位置,定位到了右下角类似木桩的位置,使用mask后精准定位到了鼠标

    cv::Mat result;
    //cv::matchTemplate(sourceMat, templateMat, result, cv::TM_SQDIFF_NORMED);//不用mask的对比
    cv::matchTemplate(sourceMat, templateMat, result, cv::TM_SQDIFF_NORMED,maskMat);//mask的对比
    double minVal, maxVal;
    cv::Point minLoc, maxLoc;
    cv::minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc);
    cv::rectangle(sourceMat, minLoc, cv::Point(minLoc.x + maskWidth, minLoc.y + maskHeight), cv::Scalar(0, 0, 255), 3);
    cv::resize(sourceMat, sourceMat, cv::Size(800, 600));

	cv::imshow("", sourceMat);
	cv::waitKey(0);
	cv::destroyAllWindows();

withoutmask

withmask