图像目标检索:基于Opencv的颜色空间匹配法

图像目标检索:基于Opencv的颜色空间匹配法

目标效果:近似于淘宝上——拍照搜索,检索商品的效果,在传统算法领域,也有一些优秀的算法能粗略的实现该效果,本文便基于传统算法中的颜色空间匹配法来实现,代码较为简略,主要代码为UI界面的框架。

附:没忍心往下接着改善,原因有二:1.代码实现效果太差,所选方法在绝大部分的图片检索要求上效果不好,例如:图片大小不一样的时候,对比空间出现偏差,效果很差;图片较大时对比较慢,算法时间复杂度较高,图片较小时信息不足,匹配成功点数不够. 2.代码结构没控制好,中间写的太粗糙,不想改动。。。。

效果展示

先上效果图:

图像目标检索:基于Opencv的颜色空间匹配法_第1张图片

再次点击目标检索,获得第二相似的图片:

图像目标检索:基于Opencv的颜色空间匹配法_第2张图片

程序结构

初始化UI界面
初始化画布
读取界面信息
导入图片库
待检索图片
展示库中数张图片
对比图片库图片
获得匹配成功的点数列表
目标检索
搜索相似图片

自定义函数

# 定义函数导入图片库

# 定义函数导入图片库,调用了全局变量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大的值的函数

第一个参数为N,第二个参数为数字list,返回值为序列第N大的值的索引

# 将一个序列依次最大的N-1个值变为最小值,再找最大值.

# 附:num超出数组大小会取最小值,num小于1会取最大值.

def maxN(num, lis):
# 定义在参数控制control下的作图函数

第一个参数为int型控制变量,第二个参数为路径列表,第三个参数为相似距离序列.

# 返回int型控制变量,赋值给全局变量以实现控制操作.

def plt_ctrl(num, paths, lis):
# 定义界面初始化函数,返回值为界面变量ui
def ui_init():
# 定义画布初始化函数,调用了ui_init返回值ui,返回值为画面变量img1
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()

附图片库展示

图像目标检索:基于Opencv的颜色空间匹配法_第3张图片

共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.图片数据最好不要太大,否则代码运行会很慢。

你可能感兴趣的:(python)