C++ 视觉开发 六.特征值匹配

news2024/11/26 19:35:53

 以图片识别匹配的案例来分析特征值检测与匹配方法。

目录

一.感知哈希算法(Perceptual Hash Algorithm)

二.特征值检测步骤

1.减小尺寸

2.简化色彩

3.计算像素点均值

4.构造感知哈希位信息

5.构造一维感知哈希值

三.实现程序

1.感知哈希值计算函数

2.计算距离函数

3.计算图像库内所有图像的哈希值

4.比较距离,输出结果

5.完整程序


一.感知哈希算法(Perceptual Hash Algorithm)

哈希值是数据的指纹,是数据的独一无二的特征值。任何微小的差异都会导致两个数据的哈希值完全不同。与传统哈希值的不同之处在于,感知哈希值可以对不同数据对应的哈希值进行比较,进而可以判断两个数据之间的相似性。也就是说,借助感知哈希能够实现对数据的比较。
一般来说,相似的图像即使在尺度、纵横比不同及颜色(对比度、亮度等)存在微小差异的情况下,仍然会具有相似的感知哈希值。这个属性为使用感知哈希值进行图像检索提供了理论基础。

流程图 

由图可知,基本流程是先提取所有图像的特征值(感知哈希值),然后比较检索图像和图像库中所有图像的特征值,和检索图像差值最小的图像库中的图像就是检索结果。针对图中的利用检索图像寻找相似图像,图像库第2行第1幅图像的特征值489与检索图像的特征值462的差为27,是所有的差值中最小的,因此该图像就是检索结果。

从上述分析可以看出,检索的关键点在于找到特征值,并计算距离。这涉及的图像处理领域中的三个关键问题:
(1)提取哪些特征:图像有很多特征,要找到有用的特征,这是关键一步。
(2)如何量化特征:简单来说就是用数字来表示特征,让特征变为可计算的。
(3)如何计算距离:有很多种不同的计算距离的方式,从中选择一种合适的即可,本章将使用汉明距离来衡量距离。

二.特征值检测步骤

1.减小尺寸

将图像减小至8像素x8像素大小,总计64像素。减小尺寸的作用在于去除图像内的高频和细节信息,仅保留图像中最重要的结构、亮度等信息。尺寸减小后的图像看起来是非常模糊的。该步骤不需要考虑纵横比等问题,无论原始图像如何,一律处理为8像素x8像素大小。这样的处理,能够让算法适用于各种尺度的图像。
需要注意的是,该方法虽然简单,但是在学习深度学习方法后会发现,该方法与深度学习方法提取图像特征的基本思路是一致的。当然,本章的特征提取相对比较粗糙,而深度学习方法提取的特征更具有抽象性。

#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;

int main() {
	cv::Mat img = cv::imread("lena.bmp");
	if (img.empty())
	{
		cerr<<"error"<<endl;
		return -1;
	}
	cv::Size size(8, 8);
	cv::Mat rst;
	cv::resize(img, rst, size);
	std::cout << "img.shape = (" << img.rows << ", " << img.cols << ", " << img.channels() << ")" << std::endl;
	std::cout << "rst.shape = (" << rst.rows << ", " << rst.cols << ", " << rst.channels() << ")" << std::endl;
	cv::imshow("img", img);
	cv::imshow("rst", rst);
	cv::waitKey();
	cv::destroyAllWindows();

	return 0;
}

2.简化色彩

色彩空间转换到灰度空间

    cv::Mat gray;
	cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);

3.计算像素点均值

计算图像中64个像素点的均值M。

 double m = cv::mean(img)[0];

对于灰度图像,只需使用 [0] 即可。

4.构造感知哈希位信息

依次将每个像素点的像素值与均值M进行比较。像素值大于或等于均值M的像素点记为1;否则,记为0。

    cv::Mat r = img > m;

    // 打印特征值矩阵
    std::cout << "特征值:\n" << r << std::endl;

注意,在python中可以使用astype(int)将逻辑值转换为整数值,C++中的 cv::Mat 没有直接的 astype(int) 方法,打印布尔矩阵时会显示 0255。如果需要将布尔矩阵转换为整数矩阵(0 和 1),可以使用以下代码: 

cv::Mat r_int;
r.convertTo(r_int, CV_8U, 1.0 / 255.0);
std::cout << "特征值:\n" << r_int << std::endl;

5.构造一维感知哈希值

将上一步得到的64个1或0组合在一起,得到当前图像的一维感知哈希值表。

 cv::Mat r1 = r_int.reshape(1, 1);

需要说明的是,64个像素值的组合顺序并不重要,但需针对所有图像采用相同的顺序组合。通常情况采用从左到右从上到下的顺序拼接所有特征值。经过上述处理得到的感知哈希值可以看作图像的指纹。在图像被缩放或纵横比发生变化时,它的感知哈希值不会改变。通常情况下,调整图像的亮度或对比度,甚至改变图像的颜色都不会显著改变感知哈希值。重要的是,这种提取感知哈希值的方法的速度非常快。
综上所述,感知哈希值提取过程示意图如图所示:

按照上述方式分别提取检索图像图像库内图像的感知哈希值。
依次将检索图像的感知哈希值与图像库内图像的感知哈希值进行比较,距离最小的图像就是检索结果。
比较时,可以采用汉明距离来衡量不同图像之间的距离。具体为,将两幅图像的感知哈希值不同的位个数作为二者的距离。距离为0,表示二者可能是非常相似的图像(或同一幅图像的变体);距离较小,表示二者之间可能有一些不同,但它们十分相似;距离较大,表示二者的差距较大;距离非常大,表示二者可能是完全不同的两幅图像。 

三.实现程序

1.感知哈希值计算函数

为了方便,将上述特征值检测步骤封装成函数:注意返回类型为cv::Mat

cv::Mat getHash(cv::Mat& img) {
    // 缩放图像到 8x8
    cv::Size size(8, 8);
    cv::Mat rst;
    cv::resize(img, rst, size);
    cout << "Resized image:\n" << rst << endl;

    // 转换为灰度图像
    cv::Mat gray;
    cv::cvtColor(rst, gray, cv::COLOR_BGR2GRAY);
    cout << "Grayscale image:\n" << gray << endl;

    // 计算图像的平均值
    cv::Scalar mean_value = cv::mean(gray);
    double m = mean_value[0];
    cout << "Mean value: " << m << endl;

    // 进行比较,并转换为整数矩阵(0和1)
    cv::Mat r = (gray > m);
    cv::Mat r_int;
    r.convertTo(r_int, CV_8U, 1.0 / 255.0);
    cout << "Binary image:\n" << r_int << endl;

    // 转换为一维数组
    cv::Mat r1 = r_int.reshape(1, 1); // 展平为1行
    return r1;
}

2.计算距离函数

比较感知哈希值时,可以采用汉明距离来衡量二者的距离,具体为将两个感知哈希值位值不同的位个数作为二者的距离。假设感知哈希值test为“1011”:
感知哈希值x1值为“1010”,test1与x1只有第4位上的值不同,因此二者的距离为1。

可以借助OpenCV中的按位异或运算函数cv::bitwise xor()来计算两个感知哈希值之间的距离在进行按位异或运算时,若运算数相同则返回0,若运算数不同则返回1。
按位异或运算是将两个数值按二进制位逐位进行异或运算。

将上述运算结果逐位相加求和,得到的值就是两个感知哈希值的汉明距离。

例如:cv::bitwise xor(“1011”,“1010”,r)的返回值为“0001”,将“0001”逐位相加求和“0+0+0+1=1”。因此,“1011”和“1010”的距离为1。

int hanming(cv::Mat& h1, cv::Mat& h2) {
    cv::Mat r;
    cv::bitwise_xor(h1, h2, r);
    int h = cv::sum(r)[0];
    return h;
}

3.计算图像库内所有图像的哈希值

为了高效读取文件,利用cv::glob函数设计findImages函数来实现读取指定文件夹下所有图像文件。

void findImages(vector<string>& images, const vector<string>& exts, const string& folder) {
    for (const auto& ext : exts) {
        vector<cv::String> files;
        glob(folder + "/*." + ext, files, false);
        images.insert(images.end(), files.begin(), files.end());
    }
}

在主函数中调用并计算其哈希值:

    // 计算检索图像的哈希值
    cv::Mat h = getHash(img);
    cout << "检索图像的感知哈希值为:\n" << h << endl;

    // 获取图像文件列表
    vector<string> images;
    vector<string> exts = { "jpg", "jpeg", "JPG", "JPEG", "gif", "GIF", "png", "PNG", 
                             "bmp", "BMP" };
    findImages(images, exts, "image");

    vector<pair<string, cv::Mat>> seq;

    // 读取图像并计算哈希值
    for (const auto& f : images) {
        cv::Mat I = cv::imread(f, cv::IMREAD_COLOR);
        if (I.empty()) {
            cerr << "无法读取图像文件: " << f << endl;
            continue;
        }
        seq.push_back(make_pair(f, getHash(I)));
    }

4.比较距离,输出结果

    // 计算检索图像与图像库内所有图像距离,将最小距离作为检索结果
    vector<pair<int, string>> distance;
    for (const auto& x : seq) {
        distance.push_back(make_pair(hamming(h, x.second), x.first)); // 每次添加(距离值,图像名称)
    }

    // 排序,把距离最小的放在最前面
    sort(distance.begin(), distance.end());

    // 打印最相似的三个图像
    cout << "最相似的图像:\n";
    for (int i = 0; i < 3 && i < distance.size(); ++i) {
        cout << distance[i].second << " with distance: " << distance[i].first << endl;
    }

    // 显示最相似的三个图像
    cv::Mat r1 = cv::imread(distance[0].second);
    cv::Mat r2 = cv::imread(distance[1].second);
    cv::Mat r3 = cv::imread(distance[2].second);

    // 使用 OpenCV 显示结果图像
    cv::imshow("Original", img);
    cv::imshow("Most similar 1", r1);
    cv::imshow("Most similar 2", r2);
    cv::imshow("Most similar 3", r3);
    cv::waitKey(0);
    cv::destroyAllWindows();

5.完整程序

#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

using namespace std;
using namespace cv;

// 提取感知哈希值函数
cv::Mat getHash(cv::Mat& img) {
    // 缩放图像到 8x8
    cv::Size size(8, 8);
    cv::Mat rst;
    cv::resize(img, rst, size);

    // 转换为灰度图像
    cv::Mat gray;
    cv::cvtColor(rst, gray, cv::COLOR_BGR2GRAY);

    // 计算图像的平均值
    cv::Scalar mean_value = cv::mean(gray);
    double m = mean_value[0];

    // 进行比较,并转换为整数矩阵(0和1)
    cv::Mat r = (gray > m);
    cv::Mat r_int;
    r.convertTo(r_int, CV_8U, 1.0 / 255.0);

    // 转换为一维数组
    cv::Mat r1 = r_int.reshape(1, 1); // 展平为1行
    return r1;
}

// 计算汉明距离函数
int hamming(const cv::Mat& h1, const cv::Mat& h2) {
    cv::Mat r;
    cv::bitwise_xor(h1, h2, r);
    int h = cv::sum(r)[0];
    return h;
}

// 查找指定文件夹中的图像文件
void findImages(vector<string>& images, const vector<string>& exts, const string& folder) {
    for (const auto& ext : exts) {
        vector<cv::String> files;
        glob(folder + "/*." + ext, files, false);
        images.insert(images.end(), files.begin(), files.end());
    }
}

int main() {
    // 读取检索图像
    cv::Mat img = cv::imread("apple.jpg");
    if (img.empty()) {
        std::cerr << "无法读取图像文件: apple.jpg" << std::endl;
        return -1;
    }

    // 计算检索图像的哈希值
    cv::Mat h = getHash(img);
    cout << "检索图像的感知哈希值为:\n" << h << endl;

    // 获取图像文件列表
    vector<string> images;
    vector<string> exts = { "jpg", "jpeg", "JPG", "JPEG", "gif", "GIF", "png", "PNG", "bmp", "BMP" };
    findImages(images, exts, "image");

    vector<pair<string, cv::Mat>> seq;

    // 读取图像并计算哈希值
    for (const auto& f : images) {
        cv::Mat I = cv::imread(f, cv::IMREAD_COLOR);
        if (I.empty()) {
            cerr << "无法读取图像文件: " << f << endl;
            continue;
        }
        seq.push_back(make_pair(f, getHash(I)));
    }

    // 计算检索图像与图像库内所有图像距离,将最小距离作为检索结果
    vector<pair<int, string>> distance;
    for (const auto& x : seq) {
        distance.push_back(make_pair(hamming(h, x.second), x.first)); // 每次添加(距离值,图像名称)
    }

    // 排序,把距离最小的放在最前面
    sort(distance.begin(), distance.end());

    // 打印最相似的三个图像
    cout << "最相似的图像:\n";
    for (int i = 0; i < 3 && i < distance.size(); ++i) {
        cout << distance[i].second << " with distance: " << distance[i].first << endl;
    }

    // 显示最相似的三个图像
    cv::Mat r1 = cv::imread(distance[0].second);
    cv::Mat r2 = cv::imread(distance[1].second);
    cv::Mat r3 = cv::imread(distance[2].second);

    // 使用 OpenCV 显示结果图像
    cv::imshow("Original", img);
    cv::imshow("Most similar 1", r1);
    cv::imshow("Most similar 2", r2);
    cv::imshow("Most similar 3", r3);
    cv::waitKey(0);
    cv::destroyAllWindows();

    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Transformer和Mamba强强结合!最新混合架构全面开源,推理速度狂飙8倍

最近发现&#xff0c;将Mamba和Transformer模块混合使用&#xff0c;效果会比单独使用好很多&#xff0c;这是因为该方法结合了Mamba的长序列处理能力和Transformer的建模能力&#xff0c;可以显著提升计算效率和模型性能。 典型案例如大名鼎鼎的Jamba&#xff1a;Jamba利用Tr…

自定义流程表单开发优势体现在什么地方?

提质、增效、降本&#xff0c;应该是很多职场办公需要实现的发展目标。那么&#xff0c;应用什么样的软件平台可以实现&#xff1f;低代码技术平台、自定义流程表单开发是目前流行于职场行业中的软件产品&#xff0c;可视化操作界面、够灵活、易维护等优势特点明显&#xff0c;…

考到PMP证书后 如何获得PDU?

目前仅续证一次&#xff0c;也是在威班PMP考试后免费积攒的。其实获取PMP的渠道很多&#xff0c;网上也有很多售卖的&#xff0c;积攒起来也挺容易&#xff0c;不过在续证的时候千万不要找不明渠道来源的人去搞&#xff0c;不靠谱。续证期有三年的&#xff0c;三年时间积攒60PD…

理解机器学习中的潜在空间(Understanding Latent Space in Machine Learning)

1、什么是潜在空间&#xff1f; If I have to describe latent space in one sentence, it simply means a representation of compressed data. 如果我必须用一句话来描述潜在空间&#xff0c;它只是意味着压缩数据的表示。 想象一个像上面所示的手写数字&#xff08;0-9&…

天猫超市卡怎么用

猫超卡是在天猫超市里面消费用的卡 但是我们现在买东西都喜欢货比三家&#xff0c;肯定是哪家划算在哪买&#xff0c;要是淘宝其他店铺或京东卖的更便宜&#xff0c;猫超卡自然就用不上了 这种情况的话&#xff0c;还不如直接把猫超卡的余额提出来&#xff0c;买东西也不受限…

RPMBUILD从入门到放弃

前言 为什么会产生这篇文章&#xff1f;促使我写出这片文章的背景是&#xff1a;前几周出差至客户现场部署服务,由于客户是zf,底层物理机都是浪潮品牌,且内网涉密。由于没有提前告知此情况,我误以为还是之前的安装方式一套流程就能部署完成&#xff0c;但没想到的是中间件的安…

Pandas 入门 15 题

Pandas 入门 15 题 1. 相关知识点1.1 修改DataFrame列名1.2 获取行列数1.3 显示前n行1.4 条件数据选取值1.5 创建新列1.6 删去重复的行1.7 删除空值的数据1.9 修改列名1.10 修改数据类型1.11 填充缺失值1.12 数据上下合并1.13 pivot_table透视表的使用1.14 melt透视表的使用1.1…

精准调整:数控切割机导轨的水平与垂直度校准!

滚柱导轨因其具有高承载、高精度、高稳定性和长寿命等特点&#xff0c;被广泛应用在重型设备、精密设备、自动化生产线、航空航天和半导体设备等领域。尤其是在数控切割机中的应用&#xff0c;最为广泛。 对于数控切割机来说&#xff0c;滚柱导轨的调整非常重要&#xff0c;是数…

在 Baklib Experience 中实现混合 CMS 架构

“还记得 CMS 主要用于在网页上布局内容吗&#xff1f;当时&#xff0c;这满足了网站管理需求。然而&#xff0c;行业正在发生变化&#xff0c;数字体验平台 Baklib Digital Content Experience 正在引领潮流。继续阅读以了解如何以及详细了解可用于确保全渠道成功的两个原则。…

[go-zero] 简单微服务调用

文章目录 1.注意事项2.服务划分及创建2.1 用户微服务2.2 订单微服务 3.启动服务3.1 etcd 服务启动3.2 微服务启动3.3 测试访问 1.注意事项 go-zero微服务的注册中心默认使用的是Etcd。 本小节将以一个订单服务调用用户服务来简单演示一下&#xff0c;其实订单服务是api服务&a…

【二】Ubuntu24虚拟机在Mac OS的VMware Fusion下无法联网问题

文章目录 1.环境背景2. 需求背景3. 解决方法3.1 在mac的终端查看虚拟机NAT网络3.2 查看unbuntu节点2的网络配置3.3 问题定位与解决3.3.1 检查是否有冲突3.3.2 冲突解决方法 4. 总结4.1 NAT 网关的原理4.2 VMware Fusion 的 NAT 模式4.3 为什么网关冲突会引起问题4.4 理解配置冲…

龙迅#LT8642UXE适用于四路HDMI转两路HDMI切换应用功能,分辨率高达4K60HZ!

1. 概述 LT8642UXE HD-DVI2.0/1.4 交换机具有符合 HD-DVI2.0/1.4 规范的 4&#xff1a;2 交换机、最大 6Gbps 高速数据速率、自适应均衡接收输入和预加重 TX 输出&#xff0c;以支持长电缆应用。 LT8642UXE HD-DVI2.0/1.4 开关自动检测电缆损耗&#xff0c;并自适应优化均衡设置…

web Worker学习笔记 | 浏览器切换标签,定时器失效的解决办法

文章目录 web Workerweb Worker介绍 - 多线程解决方案浏览器多进程架构 web workers 的使用关闭worker引用其他js文件 浏览器切换标签&#xff0c;定时器失效的解决办法窗口可见性 API解决定时器失效的方案 web Worker web Worker介绍 - 多线程解决方案 Web Workers 是Html5提…

《中国品牌网》揭秘格行品牌崛起之路:如何从混乱市场中杀出重围,领跑未来?

在随身WiFi行业乱象丛生的背景下&#xff0c;格行品牌凭借其独特的经营理念和长期主义的精神&#xff0c;逐渐崭露头角&#xff0c;成为行业的领跑者。近日&#xff0c;《中国品牌网》记者专访了格行品牌的创始人刘永先先生&#xff0c;就他的经营理念、市场策略以及未来展望进…

TCP的pop网络模式

TCP的pop网络模式 1、tcp连接的状态有以下11种 CLOSED&#xff1a;关闭状态LISTEN&#xff1a;服务端状态&#xff0c;等待客户端发起连接请求SYN_SENT&#xff1a;客户端已发送同步连接请求&#xff0c;等待服务端相应SYN_RECEIVED&#xff1a;服务器收到客户端的SYN请请求&…

sql注入问题批量处理

问题&#xff1a;SQL注入修改&#xff0c;历史代码全是${};无法修改的比如表名&#xff0c;列名&#xff0c;动态排序之类的不改&#xff0c;其他的都要修改完成 背景&#xff1a;新公司第一个任务就是SQL注入的修改&#xff0c;历史sql全部都是${},一个个调整不太合适只能批量…

JDBC 学习笔记+代码整理

Tip Idea自带可视界面&#x1f449;MySQL 图形化界面-CSDN博客 Idea2022无add Framework support选项&#x1f449;最新版IDEA:Add web Framework Support步骤/构建JavaWeb项目步骤_idea add framework support-CSDN博客 基本步骤 1.加载驱动包Driver 2.建立与数据库的连接C…

[TensorFlow-Lite][深度学习]【快速简介-1】

前言&#xff1a; 很多场景下面我们需要需要把我们的深度学习模型部署到Android,IOS 手机上面. Google 通过TensorFlow Lite 提供了对应的解决方案. 目录&#xff1a; 端侧部署优点 硬件支持 性能 应用案例 一 端侧部署优点 1; 很多场景下面&#xff1a; 无网络,数据无法…

维护和管理LDAP之OpenDJ

目录 基本介绍 服务专有名词 安装 命令行工具 密码管理 重置管理员密码 管理服务器进程 管理索引 如何搜索 管理索引 管理目录数据 测试数据 导出数据 导入数据 LDIF文件数据查看和比较 数据存储-Backends 配置连接 开启 HTTP/HTTPS连接 使用 REST访问 -open…

如何学习和提升SQL

资料来源于腾讯技术直播&#xff0c;只作为学习记录&#xff0c;如有侵权&#xff0c;请联系作者进行删除