鱼C论坛

 找回密码
 立即注册
查看: 1706|回复: 1

[作品展示] 以图搜图(粗略图搜原图)(本机)

[复制链接]
发表于 2022-9-2 09:40:46 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
  1. from tkinter import *
  2. import cv2
  3. from webbrowser import open as webopen
  4. import numpy as np
  5. from threading import Thread
  6. from multiprocessing.dummy import Pool
  7. from os import listdir, mkdir, remove, startfile, getcwd
  8. from os.path import isdir, isfile, join, exists
  9. from tkinter import filedialog
  10. from windnd import hook_dropfiles
  11. from PIL import Image, ImageTk
  12. from tkinter.messagebox import showinfo, askyesno
  13. from shutil import copy
  14. from skimage import io
  15. from pickle import dump, load
  16. from time import time

  17. class APP:

  18.     def __init__(self):
  19.         self.root = Tk()
  20.         self.root.geometry('450x430')
  21.         self.root.title('星猫搜图~')
  22.         self.imgnum = 0     #用于判断文件夹内图片个数
  23.         self.if_change = 0   #判断图片数量是否变化
  24.         self.file = ''
  25.         self.hashdic = {}      
  26.         self.img11 = 0
  27.         self.num111 = 0
  28.         self.badimg = 0
  29.         self.badimglist = []
  30.         self.msg1 = StringVar(value=r'C:\Users\陈东豪\Desktop')
  31.         self.if_top123 = 0
  32.         self.filelist = []
  33.         self.initimg = []
  34.         self.pk_file = 'hashֵ.pk'
  35.         if not isfile(self.pk_file):
  36.             with open(self.pk_file, 'wb') as (f):
  37.                 dump(self.hashdic, f)
  38.         with open(self.pk_file, 'rb') as (b):
  39.             self.hashdic = load(b)

  40.     def wait(self):
  41.         """等候时打开页面,显示加载过程"""
  42.         self.wait1 = Label((self.root), text='请稍等...')

  43.     def main(self):
  44.         Label((self.root), text='img文件路径:').grid(row=0, column=0)
  45.         self.hostVar = StringVar(value=r'C:\Users\Administrator\Desktop')
  46.         self.numVar = StringVar(self.root, str(len(self.filelist)) + '张')
  47.         self.filename = Entry((self.root), textvariable=(self.hostVar))
  48.         self.filename.grid(row=0, column=1)
  49.         
  50.         self.bt = Button((self.root), text='确定', command=self.change_lujing)
  51.         self.bt.grid(row=0, column=2)
  52.         self.lb4 = Label((self.root), textvariable=(self.numVar))
  53.         self.lb4.grid(row=0, column=3)
  54.         self.bt2 = Button((self.root), text='打开目标img文件夹', command=(self.open_lujing))
  55.         self.bt2.grid(row=1, column=0)
  56.         self.bt4 = Button((self.root), text='此次查找详情', command=lambda:self.show_init(self.initimg))
  57.         self.bt4.grid(row=1, column=1)
  58.         self.bt5 = Button((self.root), text='打开星猫图片', command=(self.open_files))
  59.         self.bt5.grid(row=1, column=3)
  60.         self.bt6 = Button((self.root), text='查看损坏img', command=(self.see_badimg))
  61.         self.bt6.grid(row=1, column=2)
  62.         self.balloon = Label(self.root)

  63.         self.bt.bind('<Button-3>', self.chose_dir)
  64.         self.bt.bind('<Enter>', lambda event: self.Balloon_show(event, msg='单击鼠标右键打开文件选择框!', fg='blue'))
  65.         self.bt5.bind('<Enter>', lambda event: self.Balloon_show(event, msg='双击鼠标右键打开百度搜图\n[url]https://graph.baidu.com/pcpage/index?tpl_from=pc'[/url], fg='blue'))
  66.         self.root.bind('<Leave>', self.Balloon_destroy)
  67.         self.root.bind('<Double-Button-3>', self.baidu)
  68.         hook_dropfiles(self.root, func=self.dragged_files)   #当拖入文件时触发
  69.         self.change_lujing()
  70.         self.display_num()
  71.         self.root.mainloop()
  72.         
  73.         
  74.         
  75.     def show_init(self,filename):
  76.         '''展示目标img信息'''
  77.         file_msg = '\n'.join((item for item in filename))
  78.         showinfo(title='提示', message=('总共有' + str(len(filename)) + '张相似图片!\n相似度为' +\
  79.             str(100 - self.min_) + '\n' + file_msg + '\n损坏图片:' + str(self.badimg) + \
  80.                 '张\n共耗时:' + str(self.t) + 's'))
  81.         
  82.     def Balloon_destroy(self, event):
  83.         """隐藏气泡&#702;"""
  84.         try:
  85.             self.balloon.place_forget()
  86.         except:
  87.             pass

  88.     def Balloon_show(self, event, msg, fg=None, bg=None, font=('微软雅黑', 10, 'bold')):
  89.         try:
  90.             self.balloon.place_forget()
  91.         except:
  92.             pass
  93.         else:
  94.             self.balloon['fg'] = fg
  95.             self.balloon['bg'] = bg
  96.             self.balloon['text'] = msg
  97.             self.balloon['font'] = font
  98.             self.balloon.place(x=35, y=190)

  99.     def baidu(self, event):
  100.         """百度搜图"""
  101.         webopen('https://graph.baidu.com/pcpage/index?tpl_from=pc')

  102.     def chose_dir(self, event):
  103.         file = filedialog.askopenfilename()
  104.         if file != '':
  105.             file1 = file.rsplit('/', 1)[0]
  106.             self.hostVar.set(file1)
  107.             self.change_lujing()

  108.     def open_lujing(self, *args):
  109.         """打开目标img文件夹"""
  110.         if len(self.initimg) == 0:
  111.             showinfo(title='提示', message='无目标img!')
  112.         else:
  113.             files = []
  114.             for each in self.initimg:
  115.                 files.append(each.rsplit('\\', 1)[0])
  116.             else:
  117.                 files = list(set(files))
  118.                 for each in files:
  119.                     startfile(each)

  120.     def open_files(self, *args):
  121.         """打开文件夹"""
  122.         fname = getcwd() + '//星猫图片'
  123.         startfile(fname)


  124.     def see_badimg(self, *args):
  125.         """损坏badimg"""
  126.         if len(self.badimglist) == 0:
  127.             showinfo(title='提示', message='没有badimg!')
  128.         else:
  129.             msg = '\n'.join(self.badimglist)
  130.             showinfo('badimg路径', msg)
  131.             if askyesno('警告', '是否删除损坏imgs?'):
  132.                 for each in self.badimglist:
  133.                     try:
  134.                         remove(each)
  135.                         self.badimglist.remove(each)
  136.                     except Exception as e:
  137.                         showinfo(title='提示', message=e)


  138.     def display_num(self):
  139.         """提示图片数量, 判断图片个数是否不再改变"""

  140.         num = len(self.filelist)
  141.         self.numVar.set(str(num) + '张')
  142.         if self.imgnum!=num:
  143.             self.imgnum = num
  144.             self.if_change = 1
  145.         else:
  146.             if self.if_change:
  147.                 #如果变化停止则开始计算hash值
  148.                 self.if_change = 0
  149.                 self.pool_map(self.compute_hash,self.filelist)
  150.                 # showinfo('提示','开始计算图片hash值啦!')
  151.         self.root.after(1000, self.display_num)

  152.     def change_lujing(self):
  153.         """更改搜图路径"""

  154.         self.file = self.filename.get()
  155.         if isdir(self.file):
  156.             pass
  157.         else:
  158.             lujing = getcwd()
  159.             self.hostVar.set(lujing)
  160.             self.file = self.filename.get()
  161.         self.filelist = []
  162.         self.thread_it(self.getFileList, self.file, self.filelist)

  163.     def pool_map(self, func, *args):
  164.         """多进程"""
  165.         pool = Pool()
  166.         pool.map_async(func, args)
  167.         pool.close()
  168.         pool.join()

  169.     def getFileList(self, dir, imglist, ext=['jpg', 'png', 'jpeg', 'bmp', 'webp']):
  170.         """
  171.         获取文件夹及其子文件夹中图片列表
  172.         输入 dir:文件夹根目录
  173.         输入 ext: 扩展名
  174.         返回: 文件路径列表
  175.         """
  176.         newDir = dir
  177.         if isfile(dir):
  178.             if dir.rsplit('.', 1)[(-1)] in ext:
  179.                 imglist.append(dir)

  180.         elif isdir(dir):
  181.             try:
  182.                 for s in listdir(dir):
  183.                     newDir = join(dir, s)
  184.                     self.getFileList(newDir, imglist, ext)

  185.             except Exception as e:
  186.                     print(s,'\n',e)

  187.         return imglist


  188.     #打开指定的图片,缩放到指定尺寸
  189.     def get_img(self,filename,width=445,height=365):
  190.         try:
  191.             im = Image.open(filename).resize((width,height))
  192.             # 引用:添加一个Label,用来存储图片。使用PanedWindow也行。
  193.             panel = Label(master=self.root)
  194.             panel.photo = ImageTk.PhotoImage(im)  # 将原本的变量photo改为panel.photo
  195.             return panel.photo
  196.         except:
  197.             return 0

  198.     def compute_hash(self,imgname):
  199.         '''专门用于计算大规模计算hash值'''
  200.         if imgname not in self.hashdic:
  201.             img2_ = self.cv_imread(imgname)
  202.             hash2 = self.aHash(img2_, imgname)
  203.             if hash2 != 0:
  204.                 self.hashdic[str(imgname)] = hash2

  205.                
  206.     def dragged_files(self, files):
  207.         """拖拽图片"""
  208.         sum_msg2 = [item.decode('gbk') for item in files]
  209.         for sum_msg in sum_msg2:
  210.             ext1 = sum_msg.rsplit('.', 1)[(-1)]
  211.             try:
  212.                 self.img.destroy()
  213.             except:
  214.                 pass
  215.         
  216.             if ext1 in ('jpg', 'png', 'jpeg', 'bmp', 'ico', 'webp'):
  217.                 if ext1 == 'ico':
  218.                     showinfo('提示', '暂不支持ico格式图片搜索!')
  219.                 else:
  220.                     image1 = self.get_img(sum_msg)
  221.                     if image1 != 0:
  222.                         self.img = Label((self.root), image=image1)
  223.                         self.img.grid(row=2, columnspan=4)
  224.                         self.imgdict = {}   #用来保存图片相似度
  225.                         self.thread_it(self.Compare2, sum_msg)
  226.                     else:
  227.                         showinfo('提示',sum_msg+'\n该图片有问题!')
  228.                         
  229.             else:
  230.                 showinfo(title='提示', message=('暂不支持当前格式搜索!(' + str(ext1) + ')'))

  231.     def Compare2(self, img):
  232.         '''开始搜索相似图片ing'''
  233.         t1 = time()
  234.         img1 = self.cv_imread(img)
  235.         self.hash1 = self.aHash(img1,img)
  236.         if self.hash1 == 0:
  237.             showinfo('提示', '此图片损坏,请更换其他图片!')
  238.         else:
  239.             # self.pool_map(self.compare, self.filelist)
  240.             #使用多进程加速

  241.             pool = Pool()
  242.             pool.map_async(self.compare, self.filelist)
  243.             pool.close()
  244.             pool.join()

  245.             try:

  246.                 self.min_ = min(self.imgdict.values())
  247.                 min_key = [i for i in self.imgdict.keys() if self.imgdict[i] == self.min_]
  248.                 self.initimg = min_key
  249.                 if not exists('星猫图片'):
  250.                     mkdir('星猫图片')
  251.                 for each_img in min_key:
  252.                     try:
  253.                         copy(each_img, './/星猫图片')
  254.                     except:
  255.                         pass

  256.                 if self.min_ == 0:
  257.                     file_img = []
  258.                     file_img.append(min_key[0])
  259.                     self.show_img1(file_img)
  260.                 else:
  261.                     self.show_img1(min_key)
  262.                 with open(self.pk_file, 'wb') as (f):
  263.                     dump(self.hashdic, f)
  264.                 t2 = time()
  265.                 self.t = t2 - t1

  266.             except:
  267.                 showinfo('提示', '无相关图片!')

  268.     def show_msg(self, title1='提示', msg='呱呱呱!', ms=0, fontbig=20):
  269.         """显示消息"""
  270.         if msg != self.msg1:
  271.             self.msg1.set(msg)
  272.         if not self.if_top123:
  273.             self.top123 = Toplevel()
  274.             self.top123.title(title1)
  275.             self.top123.protocol('WM_DELETE_WINDOW', self.destroytop)
  276.             msg1 = Label((self.top123), textvariable=(self.msg1), font=('微软雅黑', fontbig, 'bold'))
  277.             msg1.pack()
  278.         if ms != 0:
  279.             self.top123.after(ms, self.destroytop)

  280.     def destroytop(self):
  281.         """关闭窗口"""
  282.         self.top123.destroy()
  283.         self.if_top123 = 0

  284.     def show_img1(self, filename=[], ms=4500, num=0):
  285.         """可切换显示多张照片"""
  286.         try:
  287.             if not self.img11:
  288.                 self.top11 = Toplevel()
  289.                 self.top11.title('星猫图片浏览~')
  290.                 self.top11.geometry('640x400')
  291.                 self.top11.protocol('WM_DELETE_WINDOW', lambda : self.closeimg(filename))
  292.                 self.img11 = 1
  293.                 self.img2 = Label((self.top11), image=(self.get_img(filename[num], 640, 400)))
  294.                 self.img2.pack()
  295.             else:
  296.                 self.img2.destroy()
  297.                 self.img2 = Label((self.top11), image=(self.get_img(filename[num], 640, 400)))
  298.                 self.img2.pack()
  299.             num += 1
  300.             self.top11.after(3000, self.show_img1, filename, ms, num)
  301.         except:
  302.             self.closeimg(filename)

  303.     def closeimg(self, filename):
  304.         """窗口关闭时触发"""
  305.         self.img11 = 0
  306.         self.top11.destroy()

  307.     def thread_it(self, func, *args):
  308.         """多线程"""
  309.         t = Thread(target=func, args=args)
  310.         t.setDaemon(True)
  311.         t.start()

  312.     #差值感知算法
  313.     def aHash(self,img,imgname,if_shishi=0):
  314.         '''img:图片矩阵,imgname:图片名字,if_shishi是否'''
  315.         try:
  316.             #缩放8*9
  317.             img1=cv2.resize(img,(9,8),interpolation=cv2.INTER_CUBIC)
  318.             #转换灰度图
  319.             gray=cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)
  320.             hash_str=''
  321.             #每行前一个像素大于后一个像素为1,相反为0,生成哈希
  322.             for i in range(8):
  323.                 for j in range(8):
  324.                     if   gray[i,j]>gray[i,j+1]:
  325.                         hash_str=hash_str+'1'
  326.                     else:
  327.                         hash_str=hash_str+'0'
  328.             return hash_str
  329.         except:
  330.             '''如果打开图片失败,就重新保存试一试'''
  331.             if not if_shishi:
  332.                 '''如果是第一次失败,则重试'''
  333.                 try:
  334.                     image = io.imread(imgname)
  335.                     image = cv2.cvtColor(image, cv2.COLOR_RGBA2BGRA)
  336.                     cv2.imencode(imgname.rsplit('.',1)[-1],image)[1].tofile(imgname)
  337.                     return self.aHash(image,imgname,1)
  338.                 except:
  339.                     print(imgname,'打开失败!')
  340.                     self.badimg+=1
  341.                     return 0
  342.             else:
  343.                 '''表示获取hash失败!'''
  344.                 return 0

  345.     def cmpHash(self, hash1, hash2):
  346.         n = 0
  347.         if len(hash1) != len(hash2):
  348.             return -1
  349.         for i in range(len(hash1)):
  350.             if hash1[i] != hash2[i]:
  351.                 n = n + 1
  352.         return n

  353.     def cv_imread(self, filePath):
  354.         cv_img = cv2.imdecode(np.fromfile(filePath, dtype=(np.uint8)), -1)
  355.         return cv_img

  356.     def compare(self, img2):
  357.         '''比较图片相似度并保存相似度n1'''
  358.         imgname = img2
  359.         try:
  360.             if str(imgname) in self.imgdict:
  361.                 pass
  362.             elif str(imgname) not in self.hashdic:
  363.                 img2_ = self.cv_imread(img2)
  364.                 hash2 = self.aHash(img2_, imgname=img2)
  365.                 if hash2 != 0:
  366.                     self.hashdic[str(imgname)] = hash2
  367.                     n1 = self.cmpHash(self.hash1, self.hashdic[str(imgname)])
  368.                     if n1 != (-1):
  369.                         self.imgdict[str(imgname)] = n1
  370.                     elif n1 == 0:
  371.                         self.num111+=1
  372.             else:
  373.                 n1 = self.cmpHash(self.hash1, self.hashdic[str(imgname)])
  374.                 if n1 != (-1):
  375.                     self.imgdict[str(imgname)] = n1
  376.                 elif n1 == 0:
  377.                     self.num111+=1
  378.             # print(n1)
  379.         except Exception as e:
  380.                 print(e)

  381. if __name__ == '__main__':
  382.         a = APP()
  383.         a.main()
复制代码

代码运行效果

代码运行效果

评分

参与人数 3荣誉 +5 鱼币 +4 贡献 +5 收起 理由
Hello. + 1 + 1 鱼C有你更精彩^_^
逃兵 + 2 + 2 + 2 鱼C有你更精彩^_^
小甲鱼 + 2 + 2 + 2 鱼C有你更精彩^_^

查看全部评分

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2022-9-2 09:42:05 | 显示全部楼层
本帖最后由 陈东豪 于 2022-9-2 10:01 编辑

很多代码不错的
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-4-27 03:40

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表