图片批量去重---(均值哈希、插值哈希、感知哈希、三/单通道直方图)
一、整体步骤
本脚本中,关键步骤包括以下步骤:
1、图片加载:
脚本会遍历指定的图片目录,将所有图片加载到内存中。
2、图像预处理:
比较之前,通常需要对图片进行预处理,如调整大小、灰度化或直方图均衡化,以消除 颜色、尺寸等因素的影响。
3、相似度计算:
图像相似度的衡量有很多种方法,如像素级别的差异(均方误差)、结构相似度指数 (SSIM)、归一化互信息(NMI)或者哈希算法(如PCA-SIFT、BRIEF等)。OpenCV提供了部 分功能来实现这些算法,例如`cv2.compareHist`可以用于直方图比较。
4、阈值设置:
根据实际需求,设定一个相似度阈值,低于这个阈值的图片被认为是重复的。该阈值可 能需要通过实验调整以达到最佳效果。
5、比较与去重:
脚本会比较每一对图片的相似度,如果超过阈值,则认为这两张图片是重复的,移动到 指定目录。这里可能使用一种数据结构(如字典或集合)来记录已检查过的图片,避免不必 要的比较。
二、原理解析
2.1 均值哈希
1.缩放:图片缩放为8*8,保留结构,除去细节。
2.灰度化:转换为灰度图。
3.求平均值:计算灰度图所有像素的平均值。
4.比较:像素值大于平均值记作1,相反记作0,总共64位。
5.生成hash:将上述步骤生成的1和0按顺序组合起来既是图片的指纹(hash)。
6.对比指纹:将两幅图的指纹对比,计算汉明距离,即两个64位的hash值有多少位是不一样 的,不相同位数越少,图片越相似。
2.2 插值哈希
-
缩小图像:将输入图像调整为10x10像素(可自己设置),以便进行后续的差值计算。
-
灰度化:将彩色图像转换为灰度图像。
-
计算差异值:比较相邻像素的灰度值,如果左边的像素比右边的更亮,则记录为1,否则为0。每行10个像素通过左右像素的两两比较,会产生10个不同的差异值,一共10行,则会产生100个差异值。
-
生成哈希值:将64位的二进制值按每4个字符为1组,转换成16进制,生成一个长度为16的字符串。
-
哈希值比较:通过比较两个图像的哈希值的汉明距离(Hamming Distance),评估图像的相似度,距离越小表示图像越相似。
2.3 感知哈希
均值哈希算法过于严格,不够精确,更适合搜索缩略图,为了获得更精确的结果可以选择感知哈希 算法,它采用的是DCT(离散余弦变换)来降低频率的方法。
1. 缩小图片:32 * 32是一个较好的大小,这样方便DCT计算
2. 转化为灰度图:把缩放后的图片转化为灰度图。
3. 计算DCT: DCT把图片分离成分率的集合
4.缩小DCT:DCT计算后的矩阵是32 * 32,保留左上角的8 * 8,这些代表图片的最低频率。
5.计算平均值:计算缩小DCT后的所有像素点的平均值。
6.进一步减小DCT:大于平均值记录为1,反之记录为0.
7.得到信息指纹:组合64个信息位,顺序随意保持一致性。
8.最后比对两张图片的指纹,获得汉明距离即可。
2.4 直方图对比
直方图距离通过比较图像的灰度直方图来衡量相似性,直方图相似度值越大,图像越相似。
最后,附上整体代码,只需替换图片路径、选择使用哪种方法、调整阈值即可使用:
import cv2
import numpy as np
import os
import shutil
from PIL import Image# 均值哈希算法
def aHash(img,shape=(10,10)):# 缩放为10*10img = cv2.resize(img, shape)# 转换为灰度图gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# s为像素和初值为0,hash_str为hash值初值为''s = 0hash_str = ''# 遍历累加求像素和for i in range(shape[0]):for j in range(shape[1]):s = s + gray[i, j]# 求平均灰度avg = s / 100# 灰度大于平均值为1相反为0生成图片的hash值for i in range(shape[0]):for j in range(shape[1]):if gray[i, j] > avg:hash_str = hash_str + '1'else:hash_str = hash_str + '0'return hash_str# 差值哈希算法
def dHash(img,shape=(10,10)):# 缩放10*11img = cv2.resize(img, (shape[0]+1, shape[1]))# 转换灰度图gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)hash_str = ''# 每行前一个像素大于后一个像素为1,相反为0,生成哈希for i in range(shape[0]):for j in range(shape[1]):if gray[i, j] > gray[i, j + 1]:hash_str = hash_str + '1'else:hash_str = hash_str + '0'return hash_str# 感知哈希算法(pHash)
def pHash(img,shape=(10,10)):# 缩放32*32img = cv2.resize(img, (32, 32)) # , interpolation=cv2.INTER_CUBIC# 转换为灰度图gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 将灰度图转为浮点型,再进行dct变换dct = cv2.dct(np.float32(gray))# opencv实现的掩码操作dct_roi = dct[0:10, 0:10]hash = []avreage = np.mean(dct_roi)for i in range(dct_roi.shape[0]):for j in range(dct_roi.shape[1]):if dct_roi[i, j] > avreage:hash.append(1)else:hash.append(0)return hash# 通过得到RGB每个通道的直方图来计算相似度
def classify_hist_with_split(image1, image2, size=(256, 256)):# 将图像resize后,分离为RGB三个通道,再计算每个通道的相似值image1 = cv2.resize(image1, size)image2 = cv2.resize(image2, size)sub_image1 = cv2.split(image1)sub_image2 = cv2.split(image2)sub_data = 0for im1, im2 in zip(sub_image1, sub_image2):sub_data += calculate(im1, im2)sub_data = sub_data / 3return sub_data# 计算单通道的直方图的相似值
def calculate(image1, image2):hist1 = cv2.calcHist([image1], [0], None, [256], [0.0, 255.0])hist2 = cv2.calcHist([image2], [0], None, [256], [0.0, 255.0])# 计算直方图的重合度degree = 0for i in range(len(hist1)):if hist1[i] != hist2[i]:degree = degree + (1 - abs(hist1[i] - hist2[i]) / max(hist1[i], hist2[i]))else:degree = degree + 1degree = degree / len(hist1)return degree# Hash值对比
def cmpHash(hash1, hash2,shape=(10,10)):n = 0# hash长度不同则返回-1代表传参出错if len(hash1)!=len(hash2):return -1# 遍历判断for i in range(len(hash1)):# 相等则n计数+1,n最终为相似度if hash1[i] == hash2[i]:n = n + 1return n/(shape[0]*shape[1])if __name__ == '__main__':load_path = r'images_full' # 要去重的文件夹save_path = r'img_dir_repeat' # 空文件夹,用于存储检测到的重复的照片os.makedirs(save_path, exist_ok=True)# 获取图片列表 file_map,字典{文件路径filename : 文件大小image_size}file_map = {}image_size = 0# 遍历filePath下的文件、文件夹(包括子目录)for parent, dirnames, filenames in os.walk(load_path):# for dirname in dirnames:# print('parent is %s, dirname is %s' % (parent, dirname))for filename in filenames:image_size = os.path.getsize(os.path.join(parent, filename))file_map.setdefault(os.path.join(parent, filename), image_size)# 获取的图片列表按 文件大小image_size 排序file_map = sorted(file_map.items(), key=lambda d: d[1], reverse=False)file_list = []for filename, image_size in file_map:file_list.append(filename)# 取出重复的图片file_repeat = []for currIndex, _ in enumerate(file_list):dir_image1 = cv2.imread(file_list[currIndex])dir_image2 = cv2.imread(file_list[currIndex + 1])hash1 = aHash(dir_image1) # 此处可替换不同的方法 hash2 = aHash(dir_image2) # 此处可替换不同的方法 result = cmpHash(hash1, hash2)if (result >= 0.7): # 阈值设置0.7,可以自己调节file_repeat.append(file_list[currIndex + 1])print("\n相同的图片:", file_list[currIndex], file_list[currIndex + 1])else:print('\n不同的图片:', file_list[currIndex], file_list[currIndex + 1])currIndex += 1if currIndex >= len(file_list) - 1:break# 将重复的图片移动到新的文件夹,实现对原文件夹降重for image in file_repeat:shutil.move(image, save_path)print("正在移除重复照片:", image)
相关文章:
图片批量去重---(均值哈希、插值哈希、感知哈希、三/单通道直方图)
一、整体步骤 本脚本中,关键步骤包括以下步骤: 1、图片加载: 脚本会遍历指定的图片目录,将所有图片加载到内存中。 2、图像预处理: 比较之前,通常需要对图片进行预处理,如调整大小、灰度化或直方…...
Linux:(3)
一:Linux和Linux互传(压缩包) scp:Linux scp 命令用于 Linux 之间复制文件和目录。 scp 是 secure copy 的缩写, scp 是 linux 系统下基于 ssh 登陆进行安全的远程文件拷贝命令。 scp 是加密的,rcp 是不加密的,scp 是…...
vscode设置自动换行
vscode设置自动换行 方法 方法 点击文件->首选项->设置。搜索word wrap -> 选择 on 。 搜索Word Wrap,并把选项改为on。...
Instagram 隐私设置全面解析:如何保护你的个人数据?
Instagram 隐私设置全面解析:如何保护你的个人数据? 在这个数字化时代,社交媒体平台如 Instagram 已成为我们日常生活的一部分。然而,随着个人信息泄露和隐私侵犯事件的频发,保护个人数据变得尤为重要。本文将全面解析…...
Activiti 5 + Spring Boot全流程开发指南
目录 一、环境搭建(Spring Boot 2.x) 1.1 依赖配置 1.2 配置文件 二、流程定义与部署 2.1 创建BPMN文件(leave.bpmn) 2.2 流程部署服务 三、流程操作核心实现 3.1 启动流程实例 3.2 查询待办任务 四、审批流程处理 4.1 …...
spring结合mybatis多租户实现单库分表
实现单库分表 思路:student表数据量大,所以将其进行分表处理。一共有三个分表,分别是student0,student1,student2,在新增数据的时候,根据请求头中的meta-tenant参数决定数据存在哪张表表。 数…...
面向对象编程(OOP)基础:Java入门指南
引言 随着计算机技术的发展,软件的应用越来越复杂,单个程序的功能也逐渐增多。为了提高代码的复用性和可维护性,Java语言引入了**面向对象编程(Object-Oriented Programming, OOP)**这一设计理念。 OOP是一种设计程序…...
day7作业
编写一个如下场景: 有一个英雄Hero类,私有成员,攻击(Atx),防御(Defense),速度(Speed),生命值(Blood),以及所有的set get 方…...
图像处理之图像边缘检测算法
目录 1 图像边缘检测算法简介 2 Sobel边缘检测 3 经典的Canny边缘检测算法 4 演示Demo 4.1 开发环境 4.2 功能介绍 4.3 下载地址 参考 1 图像边缘检测算法简介 图像边缘检测是计算机视觉和图像处理中的基本问题,主要目的是提取图像中明暗变化明显的边缘细节…...
第二十五 :搭建 pinia 环境
第一步:npm install pinia 第二步:操作src/main.ts import { createApp } from vue import App from ./App.vue /* 引入createPinia,用于创建pinia */ import { createPinia } from pinia /* 创建pinia */ const pinia createPinia(…...
学习Java数组操作:从基础到高级技巧详解
在Java编程中,数组是一种非常基础且常用的非 primitives 数据结构,它用于存储一组相同类型的值。无论是数据处理、遍历还是其他操作,数组都是一个不可或缺的工具。本文将从数组的基本概念开始,逐步介绍常用的操作方法,…...
算法题(79):两个数组的交集
审题: 本题需要我们查找两个给定数组的无重复数据交集,并以数组的形式返回 思路: 方法一:set 之前我们学习过unordered_set的使用,但是unordered_set是无序的,而这里我们的比对算法需要有序数据,…...
TFChat:腾讯大模型知识引擎+飞书机器人实现AI智能助手
效果 TFChat项目地址 https://github.com/fish2018/TFChat 腾讯大模型知识引擎用的是DeepSeek R1,项目为sanic和redis实现,利用httpx异步处理流式响应,同时使用buffer来避免频繁调用飞书接口更新卡片的网络耗时。为了进一步减少网络IO消耗&…...
Linux红帽:RHCSA认证知识讲解(四)修改远程配置文件,取消root禁用,便于使用root身份远程
Linux红帽:RHCSA认证知识讲解(四)修改远程配置文件,取消root禁用,便于使用root身份远程 前言一、远程连接的用途和原因二、通过 ssh 远程登陆系统三、默认限制及解决方案(一)非常规方法一&#…...
验证码介绍及生成与验证(HTML + JavaScript实现)
验证码介绍及生成与验证(HTML JavaScript实现) 验证码 验证码(全自动区分计算机和人类的图灵测试,CAPTCHA ,Completely Automated Public Turing test to tell Computers and Humans A…...
文心一言AI创意画
介绍 文心一言是百度推出的新一代知识增强大语言模型,属于文心大模型家族的新成员。它能够与人对话互动、回答问题、协助创作,高效便捷地帮助人们获取信息、知识和灵感。 特点 文心一言基于数万亿数据和数千亿知识进行融合学习,采用预训…...
WebRTC解析
一、WebRTC 协议概述 WebRTC(Web Real-Time Communication)是由 Google 发起并成为 W3C 标准的实时音视频通信技术,核心特点: 零插件:浏览器原生支持端到端加密(SRTP DTLS)P2P 优先架构&…...
升级Office软件后,Windows 系统右键里没有新建Word、Excel、PowerPoint文件的解决办法
我办公用的电脑,Office 2013 已经用了好多年,最近突发奇想给升级到了 Ofiice 2024。升级过程还蛮顺利的,但是安装完成后,发现点右键里没有新建Word、Excel、PowerPoint,开始菜单里 Word、Excel、PowerPoint 使用都正常…...
车载DoIP诊断框架 --- 连接 DoIP ECU/车辆的故障排除
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 简单,单纯,喜欢独处,独来独往,不易合同频过着接地气的生活,除了生存温饱问题之外,没有什么过多的欲望,表面看起来很高冷,内心热情,如果你身…...
洛谷每日1题-------Day4__陶陶摘苹果
# P1046 [NOIP 2005 普及组] 陶陶摘苹果 ## 题目描述 陶陶家的院子里有一棵苹果树,每到秋天树上就会结出 $10$ 个苹果。苹果成熟的时候,陶陶就会跑去摘苹果。陶陶有个 $30$ 厘米高的板凳,当她不能直接用手摘到苹果的时候,就会踩…...
使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
