OpenCV-针对不同分辨率的匹配操作

项目要求

有一个需要,在UI自动化中,我们需要匹配某个元素在app中的位置,如何获取该元素的位置呢?一般可以通过Automation ID或者XPath,但是,有些控件或者元素,它无法通过这种方法定位,所以,我们把问题抽象成在一张图片中,框出目标元素的位置。

OpenCV模板匹配

模板匹配的工作方式

模板匹配的工作方式跟直方图的反向投影基本一样,大致过程是这样的:通过在输入图像上滑动图像块对实际的图像块和输入图像进行匹配。
假设我们有一张100x100的输入图像,有一张10x10的模板图像,查找的过程是这样的:
(1)从输入图像的左上角(0,0)开始,切割一块(0,0)至(10,10)的临时图像;
(2)用临时图像和模板图像进行对比,对比结果记为c;
(3)对比结果c,就是结果图像(0,0)处的像素值;
(4)切割输入图像从(0,1)至(10,11)的临时图像,对比,并记录到结果图像;
(5)重复(1)~(4)步直到输入图像的右下角。
大家可以看到,直方图反向投影对比的是直方图,而模板匹配对比的是图像的像素值;模板匹配比直方图反向投影速度要快一些,但是我个人认为直方图反向投影的鲁棒性会更好。

模板匹配的匹配方式

在OpenCv和EmguCv中支持以下6种对比方式:
CV_TM_SQDIFF 平方差匹配法:该方法采用平方差来进行匹配;最好的匹配值为0;匹配越差,匹配值越大。
CV_TM_CCORR 相关匹配法:该方法采用乘法操作;数值越大表明匹配程度越好。
CV_TM_CCOEFF 相关系数匹配法:1表示完美的匹配;-1表示最差的匹配。
CV_TM_SQDIFF_NORMED 归一化平方差匹配法
CV_TM_CCORR_NORMED 归一化相关匹配法
CV_TM_CCOEFF_NORMED 归一化相关系数匹配法

根据我的测试结果来看,上述几种匹配方式需要的计算时间比较接近(跟《学习OpenCV》书上说的不同),我们可以选择一个能适应场景的匹配方式。

模板匹配存在的问题

1.对于分辨率不同的图片,它无法正常匹配。在实际的任务中,我们的模板图片可能发生分辨率的改变。
2.对于些许形变的图片,它也无法正常匹配。
3.截图内容无法匹配。

原图:原图
传统匹配结果:
test

关于自适应屏幕显示:

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

using namespace cv;
using namespace std;
void Show_Picture() {
	Mat src = imread("C:/Users/wenhaofu/Desktop/Multi-Scale-Template-Matching-master/test3.png");

	if (src.empty()) {
		printf("could not load image...\n");
		return;
	}
	namedWindow("input", WINDOW_NORMAL);
	imshow("input", src);

	waitKey(0);
	return;
}
int main(int artc, char** argv) {
	Show_Picture();
	return 0;
}

但是为了完整得显示图片,多次尝试发现,flag参数为WINDOW_NORMAL时才可以在手动调整窗口的条件下显示完整的图片。

解决方法

方法1:直方图+自适应模板匹配

#include<opencv2/highgui/highgui_c.h>
#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
#include "histogram.h"
#include "GrayMatching.h"
struct Best_Rect
{
	Point best_point;
	Mat best_mat;
};

using namespace std;
using namespace cv;

int match_method = CV_TM_SQDIFF_NORMED;
void template_match_demo(Mat& test1, Mat& test2);
void histogram(string& s1,string& s2);
//C:/Users/wenhaofu/Desktop/Multi-Scale-Template-Matching-master/test1.png
//C:/Users/wenhaofu/Desktop/Multi-Scale-Template-Matching-master/button.png
int main(int argc, char* argv)
{
	string s1 = "C:/Users/wenhaofu/Desktop/Multi-Scale-Template-Matching-master/test3.png";
	string s2 = "C:/Users/wenhaofu/Desktop/Multi-Scale-Template-Matching-master/button.png";
	//histogram(s1,s2);
	clock_t start_time = clock();
	graymatching(s1, s2);
	clock_t end_time = clock();
	cout << (double)(end_time - start_time) / CLOCKS_PER_SEC <<"s" << endl;
	return 0;
}
void histogram(string& s1, string& s2) {
	Mat test, temp;
	test = imread(s1);//待检测图像
	temp = imread(s2);//模板图像
	if (test.empty() || temp.empty())
	{
		cout << "could not load image...\n" << endl;
		return;
	}
	template_match_demo(test, temp);
	waitKey(0);
	return;
}
void template_match_demo(Mat& test1, Mat& temp)
{
	map<double, Best_Rect> record;
	for (int i = 0; i < 15; i++) {
		Mat test = test1.clone();
		int new_rows = test1.rows - i * 0.05 * test1.rows;
		int new_cols = test1.cols - i * 0.05 * test1.cols;
		resize(test, test, Size(new_cols, new_rows));

		int result_rows = test.rows - temp.rows + 1;
		int result_cols = test.cols - temp.cols + 1;
		Mat result(result_rows, result_cols, CV_32FC1);

		//模板匹配
		matchTemplate(test, temp, result, match_method);

		//归一化
		normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());

		//寻找模板匹配的最大最小匹配值
		Point minLoc;
		Point maxLoc;
		Point match_loc;
		double min_value, max_value;
		minMaxLoc(result, &min_value, &max_value, &minLoc, &maxLoc, Mat());
		if (match_method == CV_TM_SQDIFF || match_method == CV_TM_SQDIFF_NORMED)
		{
			match_loc = minLoc;
		}
		else
		{
			match_loc = maxLoc;
		}
		Rect rect(match_loc.x, match_loc.y, temp.cols, temp.rows);
		Mat result_img = test.clone();
		Mat result_img_roi = result_img(rect);
		double his_temp = histogram(temp, result_img_roi);
		Best_Rect best_rect = { match_loc,test };
		record.insert(make_pair(his_temp, best_rect));
	}
	//将待检测图中与模板匹配出框起来
	auto it = record.rbegin();
	/*for (it; it != record.end(); it++) {
		cout << it->first << endl;
	}*/
	rectangle(it->second.best_mat, Rect(it->second.best_point.x, it->second.best_point.y, temp.cols, temp.rows), Scalar(0, 0, 255), 2, CV_AA);
	namedWindow("result", WINDOW_NORMAL);
	imshow("result", it->second.best_mat);
	return;
}


histogram.h

//直方图比较
#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
#include<opencv2/highgui/highgui_c.h>
using namespace cv;
using namespace std;

double histogram(Mat &test1,Mat &test2){
	Mat hsvtest1, hsvtest2;
	if (!test1.data || !test2.data)
	{
		printf("could not load image...\n");
		return -1;
	}
	//步骤一:从RGB空间转换到HSV空间
	cvtColor(test1, hsvtest1, CV_BGR2HSV);
	cvtColor(test2, hsvtest2, CV_BGR2HSV);

	//步骤二:计算直方图与归一化
	int h_bins = 50;
	int s_bins = 60;
	int histsize[] = { h_bins,s_bins };
	//hue varies from 0 to 179,saturation from 0 to 255
	float h_ranges[] = { 0,180 };
	float s_ranges[] = { 0,256 };
	const float* histRanges[] = { h_ranges,s_ranges };
	//use the 0-th and 1-st channels
	int channels[] = { 0,1 };
	MatND hist_test1;
	MatND hist_test2;
	//计算直方图
	calcHist(&hsvtest1, 1, channels, Mat(), hist_test1, 2, histsize, histRanges, true, false);
	calcHist(&hsvtest2, 1, channels, Mat(), hist_test2, 2, histsize, histRanges, true, false);

	//归一化
	normalize(hist_test1, hist_test1, 0, 1, NORM_MINMAX, -1, Mat());
	normalize(hist_test2, hist_test2, 0, 1, NORM_MINMAX, -1, Mat());

	//步骤三:比较直方图,并返回值
	double basetest2 = compareHist(hist_test1, hist_test2, CV_COMP_CORREL);
	return basetest2;
}

大致思路:
给定的模板,它已经是分辨率比较低的了,我们只能改变截图,根据,每次缩放截图,我们把结果用map存起来,map的key为直方图相似度,value为Point和Rect(该点坐标和对比的图),通过,map自动排序。

结果

result

方法二:SIFT

#!/usr/bin/env python

# Python 2/3 compatibility
from __future__ import print_function

import numpy as np
import cv2

def init_feature():
    # SIFT匹配
    detector = cv2.SIFT_create(700)
    # BRISK匹配
    #cv2.BRISK_create()
    # 归一化
    norm = cv2.NORM_L2
    #cv2.NORM_L2
    # 特征值全匹配
    matcher = cv2.BFMatcher(norm)
    return detector, matcher

def filter_matches(kp1, kp2, matches, ratio = 0.75):
    mkp1, mkp2 = [], []
    for m in matches:
        if len(m) == 2 and m[0].distance < m[1].distance * ratio:
            m = m[0]
            mkp1.append( kp1[m.queryIdx] )
            mkp2.append( kp2[m.trainIdx] )
    p1 = np.float32([kp.pt for kp in mkp1])
    p2 = np.float32([kp.pt for kp in mkp2])
    kp_pairs = zip(mkp1, mkp2)
    return p1, p2, kp_pairs

def explore_match(win, img1, img2, kp_pairs, status = None, H = None):
    h1, w1 = img1.shape[:2]
    h2, w2 = img2.shape[:2]
    vis = np.zeros((max(h1, h2), w1+w2), np.uint8)
    vis[:h1, :w1] = img1
    vis[:h2, w1:w1+w2] = img2
    vis = cv2.cvtColor(vis, cv2.COLOR_GRAY2BGR)

    if H is not None:
        corners = np.float32([[0, 0], [w1, 0], [w1, h1], [0, h1]])
        corners = np.int32( cv2.perspectiveTransform(corners.reshape(1, -1, 2), H).reshape(-1, 2) + (w1, 0) )
        cv2.polylines(vis, [corners], True, (0, 0, 255))

    cv2.imshow(win, vis)
    return vis

if __name__ == '__main__':

    img1 = cv2.imread('button.png', 0)
    img2 = cv2.imread('test1.png', 0)
    smaller_img1 = cv2.resize(img1, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA)
    smaller_img2 = cv2.resize(img2, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA)
    detector, matcher = init_feature()

    kp1, desc1 = detector.detectAndCompute(smaller_img1, None)
    kp2, desc2 = detector.detectAndCompute(smaller_img2, None)

    raw_matches = matcher.knnMatch(desc1, trainDescriptors = desc2, k = 2)
    p1, p2, kp_pairs = filter_matches(kp1, kp2, raw_matches)
    if len(p1) >= 4:
        H, status = cv2.findHomography(p1, p2, cv2.RANSAC, 5.0)
        print('%d / %d  inliers/matched' % (np.sum(status), len(status)))
        vis = explore_match('find_obj', smaller_img1, smaller_img2, kp_pairs, status, H)
        cv2.waitKey()
        cv2.destroyAllWindows()
    else:
        print('%d matches found, not enough for homography estimation' % len(p1))

通过,缩放和SIFT。其实际效果,有时候并不好。

效果

1
2

方法三:灰度匹配+模板匹配

GrayMatching.h

#include <opencv2/opencv.hpp>
#include <iostream>
#include <imgproc/types_c.h>
#include <map>
using namespace cv;
using namespace std;

extern int match_method;
struct Best_Mat
{
    Rect best_rect;
    Mat best_mat;
};
//相似度匹配算法之灰度值方差匹配法:
double get_variance(Mat& a, Mat& b)
{
    if (a.rows != b.rows || a.cols != b.cols || a.channels() != b.channels())
    {
        printf("not the same size!\n");
        return 0;
    }

    //处理图像相似度
    //1.求出每一行到灰度值均值,加入容器,作为特征值;
    //2.求出灰度值总平均值与每行平均值的方差;
    //3.行行比较与模版方差的接近程度


    vector<double> variance_a;
    vector<double> variance_b;
    double var_a = 0;
    double var_b = 0;
    double sum_a = 0;
    double sum_b = 0;
    double mean_a;
    double mean_b;
    double sum_variance = 0.0;
    //将每行灰度值均值存入容器
    for (int i = 0; i < a.rows; i++) {
        mean_a = 0;
        mean_b = 0;
        for (int j = 0; j < a.cols; j++) {
            mean_a += a.at<uchar>(i, j);
            mean_b += b.at<uchar>(i, j);
        }
        mean_a /= (double)(a.rows * a.cols);
        mean_b /= (double)(a.rows * a.cols);
        sum_a += mean_a;
        sum_b += mean_b;
        variance_a.push_back(mean_a);
        variance_b.push_back(mean_b);
    }
    //全图灰度值均值
    mean_a = sum_a / (double)variance_a.size();
    mean_b = sum_b / (double)variance_b.size();
    //灰度值方差之差累加
    for (int i = 0; i < variance_a.size(); i++) {
        var_a = (variance_a[i] - mean_a) * (variance_a[i] - mean_a);
        var_b = (variance_b[i] - mean_b) * (variance_b[i] - mean_b);
        sum_variance += abs(var_a - var_b);
    }

    return sum_variance;
}

void graymatching(string& s1,string& s2)
{
    //加载图像
    Mat org = imread(s1);
    Mat my_template = imread(s2);
    if (org.empty() || my_template.empty())
    {
        cout << "could not load image...\n" << endl;
        return;
    }
    cvtColor(org, org, CV_RGB2GRAY);
    cvtColor(my_template, my_template, CV_RGB2GRAY);
  
    map<double, Best_Mat> arr;
    //循环缩放,当前模版为最大尺寸,每次循环缩小5%,循环10次
    for (int index = 0; index < 10; index++)
    {
        //获得缩放后的模版
        Mat temp_template = org.clone();
        int new_rows = org.rows - index * 0.05 * org.rows;
        int new_cols = org.cols - index * 0.05 * org.cols;
        resize(temp_template, temp_template, Size(new_cols, new_rows));

        //模版匹配
        Mat result;
        result.create(temp_template.dims, temp_template.size, temp_template.type());
        matchTemplate(temp_template, my_template, result, 0);
        //获取模版匹配得到的rect
        Point minPoint;
        Point maxPoint;
        double minVal;
        double maxVal;
        minMaxLoc(result, &minVal, &maxVal, &minPoint, &maxPoint);
        Rect rect(minPoint.x, minPoint.y, my_template.cols, my_template.rows);
       
        //获取匹配部分的roi图像
        Mat result_img = temp_template.clone();
        Mat result_img_roi = result_img(rect);
        //相似度比较部分:
        //比较相似度的算法很多,各有所长,这里用的是一个灰度值方差的相似度比较
        //variance_diff表示灰度值方差,方差越小,相似度越高;
        double variance_diff = get_variance(result_img_roi, my_template);
        Best_Mat best_mat = { rect ,temp_template };
        arr.insert(make_pair(variance_diff, best_mat));
    }
    auto it = arr.begin();
    rectangle(it->second.best_mat, it->second.best_rect, Scalar(255, 0, 0), 3);
    namedWindow("result", WINDOW_NORMAL);
    imshow("result", it->second.best_mat);
    waitKey(0);
    return;
}

结果和结论

gary
button

on
off

test1

2
1
直方图8.186s,灰度匹配2.212s,此方法速度快。而且,匹配精度高。

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OpenCV-Python提供了一系列用于图像处理和计算机视觉任务的功能,其中包括图像匹配图像匹配是指在一幅图像中寻找与目标图像相似的区域。通过OpenCV-Python,你可以使用模板匹配技术来实现图像匹配。 模板匹配是一种基于像素值相似度的匹配方法,它通过在输入图像中滑动一个模板图像,并计算模板与滑动窗口区域的相似度来找到最佳匹配位置。在OpenCV-Python中,你可以使用cv2.matchTemplate()函数来实现模板匹配。 下面是一个简单的图像匹配的例子代码: ```python import cv2 import numpy as np # 读取输入图像和目标图像 input_img = cv2.imread('input_img.jpg') target_img = cv2.imread('target_img.jpg') # 将输入图像和目标图像转换为灰度图像 input_gray = cv2.cvtColor(input_img, cv2.COLOR_BGR2GRAY) target_gray = cv2.cvtColor(target_img, cv2.COLOR_BGR2GRAY) # 执行模板匹配 result = cv2.matchTemplate(input_gray, target_gray, cv2.TM_CCOEFF_NORMED) # 获取最佳匹配位置 min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) top_left = max_loc # 获取目标图像的宽度和高度 target_width, target_height = target_gray.shape[::-1] # 绘制矩形框标记目标图像 cv2.rectangle(input_img, top_left, (top_left + target_width, top_left + target_height), (0, 255, 0), 2) # 显示结果图像 cv2.imshow('Result', input_img) cv2.waitKey(0) cv2.destroyAllWindows() ``` 在这个例子中,我们首先读取了输入图像和目标图像,并将它们转换为灰度图像。然后,我们使用cv2.matchTemplate()函数执行模板匹配,并通过获取最佳匹配位置来找到目标图像在输入图像中的位置。最后,我们使用cv2.rectangle()函数在输入图像上绘制一个矩形框来标记目标图像的位置,并显示结果图像。 请注意,这只是一个简单的图像匹配示例,你可以根据自己的需求进行更复杂的图像匹配操作,例如使用不同匹配方法、应用阈值、使用多个目标图像等。同时,确保你已经安装了OpenCV-Python库,并准备好了输入图像和目标图像以供匹配使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值