2025年3月27日 星期四 甲辰(龙)年 月廿六 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > Python

基于Python实现图像去重

时间:08-03来源:作者:点击数:37

python图像去重(imagededup)

github:https://github.com/idealo/imagededup

安装库

  • pip install imagededup
  • 示例代码
  • from imagededup.methods import PHash
  • phasher = PHash()
  • # 生成图像目录中所有图像的二值hash编码
  • encodings = phasher.encode_images(image_dir='/tmp/close_eyes_jt/jingtiao_eyes_img') # 图像路径
  • # 对已编码图像寻找重复图像
  • d_1 = phasher.find_duplicates(encoding_map=encodings)
  • # 给定一幅图像,显示与其重复的图像
  • from imagededup.utils import plot_duplicates
  • plot_duplicates(image_dir='path/to/image/directory',
  • duplicate_map=d_1,
  • filename='ukbench00120.jpg')
  • repeat_img = [] # 重复图片列表
  • is_img = [] # 不重复图片列表
  • for k, v in d_1.items():
  • if not v:
  • is_img.append(k)
  • elif k not in repeat_img:
  • is_img.append(k)
  • repeat_img.extend(v)
  • else:
  • repeat_img.extend(v)
  • print(len(is_img))

项目中应用实例

  • """
  • 图片去重
  • """
  • import os
  • from imagededup.methods import PHash
  • def process_file(img_path):
  • """
  • 处理图片去重
  • :return:
  • """
  • try:
  • phasher = PHash()
  • # 生成图像目录中所有图像的二值hash编码
  • encodings = phasher.encode_images(image_dir=img_path)
  • # print(encodings)
  • # 对已编码图像寻找重复图像
  • duplicates = phasher.find_duplicates(encoding_map=encodings)
  • # print(duplicates)
  • only_img = [] # 唯一图片
  • like_img = [] # 相似图片
  • for img, img_list in duplicates.items():
  • if ".png" in img:
  • continue
  • if img not in only_img and img not in like_img:
  • only_img.append(img)
  • like_img.extend(img_list)
  • # 删除文件
  • for like in like_img:
  • like_src = os.path.join(img_path, like)
  • png_src = like_src[:-4] + ".png"
  • if os.path.exists(like_src):
  • os.remove(like_src)
  • if os.path.exists(png_src):
  • os.remove(png_src)
  • except Exception as e:
  • print(e)
  • if __name__ == "__main__":
  • img_path = "/tmp/t3/"
  • num = 0
  • for root, dirs, files in os.walk(img_path):
  • for dir in dirs:
  • file_dir_path = os.path.join(root, dir)
  • process_file(file_dir_path)
  • num += 1
  • print("处理文件夹个数:{}".format(num))

python实现图像去重(哈希算法、lshashbox、imagehash)

为了防止冗余的图片占用存储空间,我们常常需要进行图片去重操作。Python具有丰富的图像处理库,因此Python图像去重也成为了一种流行的操作方法。

使用哈希算法进行图像去重

哈希算法是一种非常常用的去重算法,通过对图片进行哈希计算,得到一个指纹,再通过比较指纹的方式找到相似的图片。

Python中有很多图像哈希算法的实现,比如dhash、aHash等,这里我们以dhash算法为例:

  • import cv2
  • import numpy as np
  • def dhash(image, hashSize=8):
  • # 将图像缩放为(hashSize+1,hashSize)大小,这是为了比较相邻的像素
  • resized = cv2.resize(image, (hashSize + 1, hashSize))
  • # 将图像转换为灰度图
  • gray = cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY)
  • # 计算横向相邻像素的差值
  • diff = gray[:, 1:] > gray[:, :-1]
  • # 将二进制数值转换为十进制数值
  • return sum([2 ** i for (i, v) in enumerate(diff.flatten()) if v])

计算两张图片的hash值,若hash值相同,则认为它们是相似的图片。

  • def compareHash(hash1, hash2, hashLength):
  • # 计算不同位数的数目
  • return bin(hash1 ^ hash2)[2:].zfill(hashLength).count("1")
使用局部敏感哈希算法进行图像去重

局部敏感哈希算法(LSH)可以更加精确地比较两张图片的相似度,以达到更好的去重效果。

在Python中,我们可以使用LSHBOX库来进行图像的局部敏感哈希操作。

首先需要安装LSHBOX库:

  • pip install lshashbox

然后进行如下操作:

  • from LSHBOX import LSHBOX
  • import cv2
  • import numpy as np
  • #定义一个hash函数
  • def onBit(img, i, j):
  • if img[i, j] > 128:
  • return 1
  • else:
  • return 0
  • #获取图像的哈希值
  • def getHash(img):
  • img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  • img = cv2.resize(img, (8, 8))
  • tolist = img.tolist()
  • hashstr = ""
  • for i in range(8):
  • for j in range(8):
  • hashstr += str(onBit(tolist, i, j))
  • return hashstr
  • # 读取图片并计算哈希值
  • img1 = cv2.imread("img1.jpg")
  • hash1 = getHash(img1)
  • img2 = cv2.imread("img2.jpg")
  • hash2 = getHash(img2)
  • # 加载LSHBOX
  • lshbox = LSHBOX(16, 4)
  • lshbox.index([hash1, hash2])
  • # 查询相邻的图像
  • similar_hash = lshbox.query(hash1, num_results=2, distance_func='hamming')
  • print(similar_hash)
使用感知哈希算法进行图像去重

感知哈希算法(pHash)是一种比较复杂的哈希算法,它考虑了图片的颜色、纹理、边缘等因素,因此可以更加精确地比较两张图片的相似度。

在Python中,我们可以使用ImageHASH库来进行感知哈希操作。

首先需要安装ImageHASH库:

  • pip install imagehash

然后进行如下操作:

  • import cv2
  • from PIL import Image
  • import imagehash
  • #读取图像并计算哈希值
  • img1 = cv2.imread("img1.jpg")
  • img1 = Image.fromarray(cv2.cvtColor(img1, cv2.COLOR_BGR2RGB))
  • hash1 = imagehash.phash(img1)
  • img2 = cv2.imread("img2.jpg")
  • img2 = Image.fromarray(cv2.cvtColor(img2, cv2.COLOR_BGR2RGB))
  • hash2 = imagehash.phash(img2)
  • #比较哈希值
  • print(hash1 - hash2)
总结

本文介绍了Python图像去重的三种方法,分别是哈希算法、局部敏感哈希算法和感知哈希算法。在实际的应用场景中,可以根据具体情况选择合适的方法来进行图像去重操作。


图片数据清洗,图片去重,去掉模糊图片,去掉结构性相似的图片

1、python代码去掉完全相同的图片, 重复的图片移动到另一文件夹保存

  • import shutil
  • import numpy as np
  • from PIL import Image
  • import os
  • def 比较图片大小(dir_image1, dir_image2):
  • with open(dir_image1, "rb") as f1:
  • size1 = len(f1.read())
  • with open(dir_image2, "rb") as f2:
  • size2 = len(f2.read())
  • if (size1 == size2):
  • result = "大小相同"
  • else:
  • result = "大小不同"
  • return result
  • def 比较图片尺寸(dir_image1, dir_image2):
  • image1 = Image.open(dir_image1)
  • image2 = Image.open(dir_image2)
  • if (image1.size == image2.size):
  • result = "尺寸相同"
  • else:
  • result = "尺寸不同"
  • return result
  • def 比较图片内容(dir_image1, dir_image2):
  • image1 = np.array(Image.open(dir_image1))
  • image2 = np.array(Image.open(dir_image2))
  • if (np.array_equal(image1, image2)):
  • result = "内容相同"
  • else:
  • result = "内容不同"
  • return result
  • def 比较两张图片是否相同(dir_image1, dir_image2):
  • # 比较两张图片是否相同
  • # 第一步:比较大小是否相同
  • # 第二步:比较长和宽是否相同
  • # 第三步:比较每个像素是否相同
  • # 如果前一步不相同,则两张图片必不相同
  • result = "两张图不同"
  • 大小 = 比较图片大小(dir_image1, dir_image2)
  • if (大小 == "大小相同"):
  • 尺寸 = 比较图片尺寸(dir_image1, dir_image2)
  • if (尺寸 == "尺寸相同"):
  • 内容 = 比较图片内容(dir_image1, dir_image2)
  • if (内容 == "内容相同"):
  • result = "两张图相同"
  • return result
  • if __name__ == '__main__':
  • load_path = r'D:\data\imgs_dir' # 要去重的文件夹
  • save_path = r'D:\data\imgs_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:
  • # print('parent is %s, filename is %s' % (parent, filename))
  • # print('the full name of the file is %s' % os.path.join(parent, filename))
  • 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 = file_list[currIndex]
  • dir_image2 = file_list[currIndex + 1]
  • result = 比较两张图片是否相同(dir_image1, dir_image2)
  • if (result == "两张图相同"):
  • 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 += 1
  • if currIndex >= len(file_list) - 1:
  • break
  • # 将重复的图片移动到新的文件夹,实现对原文件夹降重
  • for image in file_repeat:
  • shutil.move(image, save_path)
  • print("正在移除重复照片:", image)
  1. python代码去掉模糊图片
  • import os
  • import cv2
  • import shutil
  • class item: # (图片, 图片清晰度) 结构体
  • def __init__(self):
  • self.name = '' # 图片名称
  • self.val = 10 # 图片清晰度 也就是 getImageVar(img)
  • #利用拉普拉斯 利用拉普拉斯算子计算图片的二阶导数,反映图片的边缘信息,同样事物的图片,清晰度高的,相对应的经过拉普拉斯算子滤波后的图片的方差也就越大
  • def getImageVar(imgPath):
  • image = cv2.imread(imgPath)
  • img2gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
  • imageVar = cv2.Laplacian(img2gray, cv2.CV_64F).var()
  • return imageVar
  • if __name__ == "__main__":
  • src_img_dir = r"D:\data\img_dir"
  • move_img_dir = r"D:\data\img_dir_blur"
  • os.makedirs(move_img_dir, exist_ok=True)
  • img_files = os.listdir(src_img_dir)
  • print("len(img_files): ", len(img_files))
  • a_list = []
  • for i in range(len(img_files)):
  • img_file_path = os.path.join(src_img_dir, img_files[i])
  • imageVar = getImageVar(img_file_path)
  • # print(imageVar)
  • a = item()
  • a.val = imageVar
  • a.name = img_files[i]
  • a_list.append(a)
  • print("len(a_list): ", len(a_list))
  • a_list.sort(key=lambda ita: ita.val, reverse=False) # 对 (图片, 图片清晰度) 结构体 列表 按照 图片清晰度排序, 模糊的放在列表头部, 清晰的放在列表尾部
  • count = 0
  • for i in range(int(len(a_list)*0.1)): # 移除最模糊的 %10 的图片
  • print(a_list[i].name, a_list[i].val)
  • src_path = os.path.join(src_img_dir, a_list[i].name)
  • dest_path = os.path.join(move_img_dir, a_list[i].name)
  • shutil.move(src_path, dest_path)
  • count += 1
  • # break
  • print("count: ", count)
  1. python代码,设置阈值,去掉结构性相似的图片。后续还要从每组结构性相似的图片,手动筛选一张图片放回原文件夹
  • # coding: utf-8
  • import os
  • import cv2
  • # from skimage.measure import compare_ssim
  • # from skimage.metrics import _structural_similarity
  • from skimage.metrics import structural_similarity as ssim
  • import shutil
  • # def delete(filename1):
  • # os.remove(filename1)
  • def list_all_files(root):
  • files = []
  • list = os.listdir(root)
  • # os.listdir()方法:返回指定文件夹包含的文件或子文件夹名字的列表。该列表顺序以字母排序
  • for i in range(len(list)):
  • element = os.path.join(root, list[i])
  • # 需要先使用python路径拼接os.path.join()函数,将os.listdir()返回的名称拼接成文件或目录的绝对路径再传入os.path.isdir()和os.path.isfile().
  • if os.path.isdir(element): # os.path.isdir()用于判断某一对象(需提供绝对路径)是否为目录
  • # temp_dir = os.path.split(element)[-1]
  • # os.path.split分割文件名与路径,分割为data_dir和此路径下的文件名,[-1]表示只取data_dir下的文件名
  • files.append(list_all_files(element))
  • elif os.path.isfile(element):
  • files.append(element)
  • # print('2',files)
  • return files
  • def ssim_compare(img_files):
  • imgs_n = []
  • count = 0
  • # thresh_lis = [0.9, 0.8, 0.7, 0.6, 0.55, 0.5, 0.45, 0.4, 0.35]
  • # for thresh in thresh_lis:
  • for currIndex, _ in enumerate(img_files):
  • if not os.path.exists(img_files[currIndex]):
  • print('not exist', img_files[currIndex])
  • break
  • img = cv2.imread(img_files[currIndex])
  • img1 = cv2.imread(img_files[currIndex + 1])
  • # 进行结构性相似度判断
  • # ssim_value = _structural_similarity.structural_similarity(img,img1,multichannel=True)
  • ssim_value = ssim(img, img1, multichannel=True)
  • thresh = 0.9
  • if ssim_value > thresh:
  • # 基数
  • count += 1
  • imgs_n.append(img_files[currIndex + 1])
  • imgs_n.append(img_files[currIndex])
  • print('big_ssim:', img_files[currIndex], img_files[currIndex + 1], ssim_value)
  • # 避免数组越界
  • if currIndex + 1 >= len(img_files) - 1:
  • break
  • save_dir = r"D:\data\img_dir_sim_"+str(thresh)
  • os.makedirs(save_dir, exist_ok=True)
  • for file in list(set(imgs_n)): # 去掉重复的路径,再遍历 剪切
  • shutil.move(file, os.path.join(save_dir, os.path.basename(file)))
  • return count
  • if __name__ == '__main__':
  • path = r'D:\data\img_dir'
  • all_files = list_all_files(path) # 返回包含完整路径的所有图片名的列表
  • print('len: ', len(all_files))
  • count = ssim_compare(all_files)
  • print(count)
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐