目标效果:近似于淘宝上——拍照搜索,检索商品的效果,在传统算法领域,也有一些优秀的算法能粗略的实现该效果,本文便基于传统算法中的颜色空间匹配法来实现,代码较为简略,主要代码为UI界面的框架。
附:没忍心往下接着改善,原因有二:1.代码实现效果太差,所选方法在绝大部分的图片检索要求上效果不好,例如:图片大小不一样的时候,对比空间出现偏差,效果很差;图片较大时对比较慢,算法时间复杂度较高,图片较小时信息不足,匹配成功点数不够. 2.代码结构没控制好,中间写的太粗糙,不想改动。。。。
先上效果图:
再次点击目标检索,获得第二相似的图片:
# 定义函数导入图片库,调用了全局变量PATH,path_file;fig_init返回值img
def photo_gal():
# 定义检索函数,调用了全局变量list_distance,path_file,control;
def retrieve():
# 定义函数导入待检索图片,调用了全局变量list_distance,path_file,control;fig_init返回值img
#引用函数 dis_sim()
def not_retrieved():
# 定义相似距离列表函数,调用全局变量PATH,path_file,调用函数
# 返回值为相似距离列表.
# 写的比较粗糙,这个函数稍作修改后可以省略,主要进程在子函数里.
def dis_sim():
第一个参数为待检索图片,第二个参数为预匹配图片文件路径的list
def dis_cal(im1, lis_im):
第一个参数为N,第二个参数为数字list,返回值为序列第N大的值的索引
# 将一个序列依次最大的N-1个值变为最小值,再找最大值.
# 附:num超出数组大小会取最小值,num小于1会取最大值.
def maxN(num, lis):
第一个参数为int型控制变量,第二个参数为路径列表,第三个参数为相似距离序列.
# 返回int型控制变量,赋值给全局变量以实现控制操作.
def plt_ctrl(num, paths, lis):
def ui_init():
def fig_init(ui):
import tkinter as tk
import tkinter.font as tkFont
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
import numpy as np
import math
import os
import PIL.ImageTk as ImageTk
import PIL.Image as Image
import glob
import time
import random
import cv2
import re
# 定义图片库路径
PATH = b'C:\\Users\\Lenovo\\jing\\photos'
if not os.path.exists(PATH):
os.makedirs(PATH)
# 获得文件路径下的所有*png和*jpg文件的路径list
path_file = glob.glob(PATH+b'\\*.png')+glob.glob(PATH+b'\\*.jpg')
list_distance = []
# 定义全局变量,控制检索出的图片索引.
control = 0
filepath = ''
# 定义函数导入图片库,调用了全局变量PATH,path_file;fig_init返回值img
def photo_gal():
plt.subplot(1, 2, 1)
plt.rcParams['font.sans-serif'] = 'SimHei'
plt.title('图片库')
plt.axis('off')
for i in range(0, 5):
if i < 5:
image = Image.open(path_file[i])
image = np.array(image)
plt.imshow(image)
img.draw()
time.sleep(0.3)
i = i+1
if i == 5:
image = Image.open(
path_file[random.randint(0, len(path_file)-1)])
image = np.array(image)
plt.imshow(image)
img.draw()
# 定义检索函数,调用了全局变量list_distance,path_file,control;
def retrieve():
global list_distance, path_file, control
# for i in range(control,len(path_file)):
control = plt_ctrl(control, path_file, list_distance)
return 0
# 定义函数导入待检索图片,调用了全局变量list_distance;fig_init返回值img
def not_retrieved():
global list_distance, filepath, control
control = 0
filepath = tk.filedialog.askopenfilename()
if(os.path.exists(filepath)):
plt.subplot(1, 2, 2)
image = Image.open(filepath)
image = np.array(image)
plt.imshow(image)
plt.axis('off')
img.draw()
list_distance = dis_sim().copy()
# 定义界面初始化函数,返回值为界面变量ui
def ui_init():
ui = tk.Tk()
ui.resizable(False, False) # 取小最大化的按键
ui.title("exp5:传统算法实现图像目标检索")
ui.geometry("1000x650+0+0") # 设置界面大小
# 设置按键及点击事件
button1 = tk.Button(ui, text='导入图片库', command=photo_gal)
button1.place(relx=0.12, rely=0.1, width=120, height=60)
button2 = tk.Button(ui, text='待检索图片', command=not_retrieved)
button2.place(relx=0.42, rely=0.1, width=120, height=60)
button3 = tk.Button(ui, text='目标检索', command=retrieve)
button3.place(relx=0.72, rely=0.1, width=120, height=60)
return ui
# 定义画布初始化函数,调用了ui_init返回值ui,返回值为画面变量img1
def fig_init(ui):
matplotlib.use('TkAgg')
# 设置画布1显示图片
fig1 = plt.figure(num=1)
img1 = FigureCanvasTkAgg(fig1, master=ui)
img1.draw()
img1.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH)
img1._tkcanvas.place(relx=0.02, rely=0.25, width=900, height=460)
toolbars = NavigationToolbar2Tk(img1, ui)
toolbars.update()
# 将图片间的空白间隙尽可能缩小
fig1.subplots_adjust(top=1, left=0, right=1, bottom=0, wspace=0.04)
return img1
# 定义相似距离列表函数,调用全局变量PATH,path_file,调用函数
# 返回值为相似距离列表.
# 写的比较粗糙,这个函数稍作修改后可以省略,主要进程在子函数里.
def dis_sim():
global filepath, path_file
m1 = cv2.imread(filepath, 0)
result_list = dis_cal(m1, path_file)
print(result_list)
return result_list
# 定义相似距离函数,第一个参数为待检索图片,第二个参数为预匹配图片文件路径的list
def dis_cal(im1, lis_im):
resultlist = []
for num in range(0, len(lis_im)):
path_str = str(lis_im[num], encoding="utf8")
path_str = re.sub(r'\\', '/', path_str)
im2 = cv2.imread(path_str)
success = []
sift = cv2.xfeatures2d.SIFT_create()
kp1, des1 = sift.detectAndCompute(im1, None)
kp2, des2 = sift.detectAndCompute(im2, None)
FLANN_INDEX_KDTREE = 0
indexParams = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
searchParams = dict(checks=50)
flann = cv2.FlannBasedMatcher(indexParams, searchParams)
matches = flann.knnMatch(des1, des2, k=2)
# 绘制一个空白图片
matchesMask = [[0, 0] for i in range(len(matches))]
# 绘制图像
for i, (m, n) in enumerate(matches):
if m.distance < 0.7*n.distance:
matchesMask[i] = [1, 0]
success.append(m)
# 图像参数
drawParams = dict(matchColor=(0, 255, 0), singlePointColor=(
255, 0, 0), matchesMask=matchesMask, flags=0)
# 最终结果
resultImage = cv2.drawMatchesKnn(
im1, kp1, im2, kp2, matches, None, **drawParams)
# 检测是否匹配
if len(success) > 20:
if(len(success) > 100):
print('完全匹配;相似点数:'+str(len(success)))
else:
print('些许匹配;相似点数'+str(len(success)))
'''# 设置显示窗口
cv2.namedWindow('img', 0)
cv2.resizeWindow('img', 840, 480)
cv2.imshow('img', resultImage)
cv2.waitKey(0)
cv2.destroyAllWindows()
return num'''
resultlist.append(len(success))
else:
print('不匹配;相似点数'+str(len(success)))
resultlist.append(len(success))
num = num+1
return resultlist
# 定义返回序列第N大的值的函数,第一个参数为N,第二个参数为数字list,返回值为序列第N大的值的索引
# 将一个序列依次最大的N-1个值变为最小值,再找最大值.
# 附:num超出数组大小会取最小值,num小于1会取最大值.
def maxN(num, lis):
tmp = lis.copy() # 直接相等,后续的赋值也会受到影响.
if (num-1) > 0 and num > 0:
for i in range(0, num-1):
tmp[tmp.index(max(tmp))] = min(tmp)
i = i+1
return tmp.index(max(tmp)), max(tmp)
# 定义在参数控制control下的作图函数,第一个参数为int型控制变量,第二个参数为路径列表,第三个参数为相似距离序列.
# 返回int型控制变量,赋值给全局变量以实现控制操作.
def plt_ctrl(num, paths, lis):
if num < len(paths):
index, success_max = maxN(num+1, lis)
plt.subplot(1, 2, 1)
plt.axis('off')
plt.rcParams['font.sans-serif'] = 'SimHei'
plt.title('相似点数 '+'Num:'+str(success_max))
image = Image.open(paths[index])
image = np.array(image)
plt.imshow(image)
img.draw()
num = num+1
if(num == len(paths)):
num = 0
return num
ui = ui_init()
img = fig_init(ui)
ui.mainloop()
共41张图片,代码对数据集的要求 :
1.代码支持了png和jpg格式,其他格式因为借用了多种库的不同方法,多少会出现一些不兼容,可能会报错。
2.图片数据最好不要太大,否则代码运行会很慢。
w(image)
img.draw()
num = num+1
if(num == len(paths)):
num = 0
return num
ui = ui_init()
img = fig_init(ui)
ui.mainloop()
#### 附图片库展示
[外链图片转存中...(img-IQXkK4WN-1600179108008)]
共41张图片,代码对数据集的要求 :
1.代码支持了png和jpg格式,其他格式因为借用了多种库的不同方法,多少会出现一些不兼容,可能会报错。
2.图片数据最好不要太大,否则代码运行会很慢。