OpenCV實現圖像搜索引擎(Image Search Engine)

Cocktail_py 2022-01-07 07:59:18 阅读数:413

opencv 搜索引擎 搜索 索引 引擎
一.原理
1. 圖像搜索原理
圖像搜索算法基本可以分為如下步驟:
提取圖像特征。如采用SIFT、指紋算法函數、哈希函數、bundling features算法等。當然如知乎中所言,也可以針對特定的圖像集群采用特定的模式設計算法,從而提高匹配的精度。如已知所有圖像的中間部分在顏色空間或構圖上有顯著的區別,就可以加强對中間部分的分析,從而更加高效地提取圖像特征。
圖像特征的存儲。一般將圖像特征量化為數據存放於索引錶中,並存儲在外部存儲介質中,搜索圖片時僅搜索索引錶中的圖像特征,按匹配程度從高到低查找類似圖像。對於圖像尺寸分辯率不同的情况可以采用降低采樣或歸一化方法。
相似度匹配。如存儲的是特征向量,則比較特征向量之間的加權後的平方距離。如存儲的是散列碼,則比較Hamming距離。初篩後,還可以進一步篩選最佳圖像集。
2. 圖片搜索引擎算法及框架設計
基本步驟
采用顏色空間特征提取器和構圖空間特征提取器提取圖像特征。
圖像索引錶構建驅動程序生成待搜索圖像庫的圖像特征索引錶。
圖像搜索引擎驅動程序執行搜索命令,生成原圖圖像特征並傳入圖片搜索匹配器。
圖片搜索匹配內核執行搜索匹配任務。返回前limit個最佳匹配圖像。

color_descriptor.py

# -*- coding: utf-8 -*-
# @Time : 2021/10/9 9:44
# @Author : 
import cv2
import numpy
""" 顏色空間特征提取器ColorDescriptor 類成員bins。記錄HSV色彩空間生成的色相、飽和度及明度分布直方圖的最佳bins分配。bins分配過多則可能導致程序效率低下,匹配難度和匹配要求過分苛嚴;bins分配過少則會導致匹配精度不足,不能錶證圖像特征。 成員函數describe(self, image)。將圖像從BGR色彩空間轉為HSV色彩空間(此處應注意OpenCV讀入圖像的色彩空間為BGR而非RGB)。生成左上、右上、左下、右下、中心部分的掩模。中心部分掩模的形狀為橢圓形。這樣能够有效區分中心部分和邊緣部分,從而在getHistogram()方法中對不同部比特的色彩特征做加權處理。 """
class ColorDescriptor:
__slot__ = ["bins"]
def __init__(self, bins):
self.bins = bins
def getHistogram(self, image, mask, isCenter):
""" :param image: :param mask: :param isCenter: :return: """
# get histogram
imageHistogram = cv2.calcHist([image], [0, 1, 2], mask, self.bins, [0, 180, 0, 256, 0, 256])
# normalize
imageHistogram = cv2.normalize(imageHistogram, imageHistogram).flatten()
if isCenter:
weight = 5.0
for index in range(len(imageHistogram)):
imageHistogram[index] *= weight
return imageHistogram
def describe(self, image):
image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
features = []
# get dimension and center
height, width = image.shape[0], image.shape[1]
centerX, centerY = int(width * 0.5), int(height * 0.5)
# initialize mask dimension
segments = [(0, centerX, 0, centerY), (0, centerX, centerY, height), (centerX, width, 0, centerY),
(centerX, width, centerY, height)]
# initialize center part
axesX, axesY = int(width * 0.75) / 2, int(height * 0.75) / 2
ellipseMask = numpy.zeros([height, width], dtype="uint8")
# img, center, axes, angle, startAngle, endAngle, color, thickness=None, lineType=None, shift=None
cv2.ellipse(ellipseMask, (centerX, centerY), (int(axesX), int(axesY)), 0, 0, 360, 255, -1)
# initialize corner part
for startX, endX, startY, endY in segments:
cornerMask = numpy.zeros([height, width], dtype="uint8")
cv2.rectangle(cornerMask, (startX, startY), (endX, endY), 255, -1)
cornerMask = cv2.subtract(cornerMask, ellipseMask)
# get histogram of corner part
imageHistogram = self.getHistogram(image, cornerMask, False)
features.append(imageHistogram)
# get histogram of center part
imageHistogram = self.getHistogram(image, ellipseMask, True)
features.append(imageHistogram)
# return
return features

index.py

# -*- coding: utf-8 -*-
# @Time : 2021/10/9 9:45
# @Author : 
import color_descriptor
import structure_descriptor
import glob
import argparse
import cv2
""" 圖像索引錶構建驅動index.py。 引入color_descriptor和structure_descriptor。用於解析圖片庫圖像,獲得色彩空間特征向量和構圖空間特征向量。 用argparse設置命令行參數。參數包括圖片庫路徑、色彩空間特征索引錶路徑、構圖空間特征索引錶路徑。 用glob獲得圖片庫路徑。 生成索引錶文本並寫入csv文件。 可采用如下命令行形式啟動驅動程序: python index.py --dataset dataset --colorindex color_index.csv --structure structure_index.csv """
searchArgParser = argparse.ArgumentParser()
searchArgParser.add_argument("-d", "--dataset", required=True,
help="Path to the directory that contains the images to be indexed")
searchArgParser.add_argument("-c", "--colorindex", required=True,
help="Path to where the computed color index will be stored")
searchArgParser.add_argument("-s", "--structureindex", required=True,
help="Path to where the computed structure index will be stored")
arguments = vars(searchArgParser.parse_args())
idealBins = (8, 12, 3)
colorDesriptor = color_descriptor.ColorDescriptor(idealBins)
output = open(arguments["colorindex"], "w")
for imagePath in glob.glob(arguments["dataset"] + "/*.jpg"):
imageName = imagePath[imagePath.rfind("/") + 1:]
image = cv2.imread(imagePath)
features = colorDesriptor.describe(image)
# write features to file
features = [str(feature).replace("\n", "") for feature in features]
output.write("%s,%s\n" % (imageName, ",".join(features)))
# close index file
output.close()
idealDimension = (16, 16)
structureDescriptor = structure_descriptor.StructureDescriptor(idealDimension)
output = open(arguments["structureindex"], "w")
for imagePath in glob.glob("dataset" + "/*.jpg"):
imageName = imagePath[imagePath.rfind("/") + 1:]
image = cv2.imread(imagePath)
structures = structureDescriptor.describe(image)
# write structures to file
structures = [str(structure).replace("\n", "") for structure in structures]
output.write("%s,%s\n" % (imageName, ",".join(structures)))
# close index file
output.close()

searchEngine.py

# -*- coding: utf-8 -*-
# @Time : 2021/10/9 9:45
# @Author : 
import color_descriptor
import structure_descriptor
import searcher
import argparse
import cv2
""" 圖像搜索引擎驅動searchEngine.py。 引入color_descriptor和structure_descriptor。用於解析待匹配(搜索)的圖像,獲得色彩空間特征向量和構圖空間特征向量。 用argparse設置命令行參數。參數包括圖片庫路徑、色彩空間特征索引錶路徑、構圖空間特征索引錶路徑、待搜索圖片路徑。 生成索引錶文本並寫入csv文件。 可采用如下命令行形式啟動驅動程序: python searchEngine.py -c color_index.csv -s structure_index.csv -r dataset -q query/pyramid.jpg dataset為圖片庫路徑。color_index.csv為色彩空間特征索引錶路徑。structure_index.csv為構圖空間特征索引錶路徑,query/pyramid.jpg為待搜索圖片路徑。 """
searchArgParser = argparse.ArgumentParser()
searchArgParser.add_argument("-c", "--colorindex", required = True, help = "Path to where the computed color index will be stored")
searchArgParser.add_argument("-s", "--structureindex", required = True, help = "Path to where the computed structure index will be stored")
searchArgParser.add_argument("-q", "--query", required = True, help = "Path to the query image")
searchArgParser.add_argument("-r", "--resultpath", required = True, help = "Path to the result path")
searchArguments = vars(searchArgParser.parse_args())
idealBins = (8, 12, 3)
idealDimension = (16, 16)
colorDescriptor = color_descriptor.ColorDescriptor(idealBins)
structureDescriptor = structure_descriptor.StructureDescriptor(idealDimension)
queryImage = cv2.imread(searchArguments["query"])
colorIndexPath = searchArguments["colorindex"]
structureIndexPath = searchArguments["structureindex"]
resultPath = searchArguments["resultpath"]
queryFeatures = colorDescriptor.describe(queryImage)
queryStructures = structureDescriptor.describe(queryImage)
imageSearcher = searcher.Searcher(colorIndexPath, structureIndexPath)
searchResults = imageSearcher.search(queryFeatures, queryStructures)
print(searchResults)
for imageName, score in searchResults:
queryResult = cv2.imread( imageName)
cv2.imshow("Result Score: " + str(int(score)) + " (lower is better)", queryResult)
cv2.imshow("Query", queryImage)
cv2.waitKey(0)

searcher.py

# -*- coding: utf-8 -*-
# @Time : 2021/10/9 9:45
# @Author : 
import numpy
import csv
import re
""" 圖片搜索匹配內核Searcher。 類成員colorIndexPath和structureIndexPath。記錄色彩空間特征索引錶路徑和結構特征索引錶路徑。 成員函數solveColorDistance(self, features, queryFeatures, eps = 1e-5)。求features和queryFeatures特征向量的二範數。eps是為了避免除零錯誤。 成員函數solveStructureDistance(self, structures, queryStructures, eps = 1e-5)。同樣是求特征向量的二範數。eps是為了避免除零錯誤。需作統一化處理,color和structure特征向量距離相對比例適中,不可過分偏頗。 成員函數searchByColor(self, queryFeatures)。使用csv模塊的reader方法讀入索引錶數據。采用re的split方法解析數據格式。用字典searchResults存儲query圖像與庫中圖像的距離,鍵為圖庫內圖像名imageName,值為距離distance。 成員函數transformRawQuery(self, rawQueryStructures)。將未處理的query圖像矩陣轉為用於匹配的特征向量形式。 成員函數searchByStructure(self, rawQueryStructures)。類似4。 成員函數search(self, queryFeatures, rawQueryStructures, limit = 3)。將searchByColor方法和searchByStructure的結果匯總,獲得總匹配分值,分值越低代錶綜合距離越小,匹配程度越高。返回前limit個最佳匹配圖像。 """
class Searcher:
__slot__ = ["colorIndexPath", "structureIndexPath"]
def __init__(self, colorIndexPath, structureIndexPath):
self.colorIndexPath, self.structureIndexPath = colorIndexPath, structureIndexPath
def solveColorDistance(self, features, queryFeatures, eps=1e-5):
distance = 0.5 * numpy.sum([((a - b) ** 2) / (a + b + eps) for a, b in zip(features, queryFeatures)])
return distance
def solveStructureDistance(self, structures, queryStructures, eps=1e-5):
distance = 0
normalizeRatio = 5e3
for index in range(len(queryStructures)):
for subIndex in range(len(queryStructures[index])):
a = structures[index][subIndex]
b = queryStructures[index][subIndex]
distance += (a - b) ** 2 / (a + b + eps)
return distance / normalizeRatio
def searchByColor(self, queryFeatures):
searchResults = {
}
with open(self.colorIndexPath) as indexFile:
reader = csv.reader(indexFile)
for line in reader:
features = []
for feature in line[1:]:
feature = feature.replace("[", "").replace("]", "")
findStartPosition = 0
feature = re.split("\s+", feature)
rmlist = []
for index, strValue in enumerate(feature):
if strValue == "":
rmlist.append(index)
for _ in range(len(rmlist)):
currentIndex = rmlist[-1]
rmlist.pop()
del feature[currentIndex]
feature = [float(eachValue) for eachValue in feature]
features.append(feature)
distance = self.solveColorDistance(features, queryFeatures)
searchResults[line[0]] = distance
indexFile.close()
# print "feature", sorted(searchResults.iteritems(), key = lambda item: item[1], reverse = False)
return searchResults
def transformRawQuery(self, rawQueryStructures):
queryStructures = []
for substructure in rawQueryStructures:
structure = []
for line in substructure:
for tripleColor in line:
structure.append(float(tripleColor))
queryStructures.append(structure)
return queryStructures
def searchByStructure(self, rawQueryStructures):
searchResults = {
}
queryStructures = self.transformRawQuery(rawQueryStructures)
with open(self.structureIndexPath) as indexFile:
reader = csv.reader(indexFile)
for line in reader:
structures = []
for structure in line[1:]:
structure = structure.replace("[", "").replace("]", "")
structure = re.split("\s+", structure)
if structure[0] == "":
structure = structure[1:]
structure = [float(eachValue) for eachValue in structure]
structures.append(structure)
distance = self.solveStructureDistance(structures, queryStructures)
searchResults[line[0]] = distance
indexFile.close()
# print "structure", sorted(searchResults.iteritems(), key = lambda item: item[1], reverse = False)
return searchResults
def search(self, queryFeatures, rawQueryStructures, limit=3):
featureResults = self.searchByColor(queryFeatures)
structureResults = self.searchByStructure(rawQueryStructures)
results = {
}
for key, value in featureResults.items():
results[key] = value + structureResults[key]
results = sorted(results.items(), key=lambda item: item[1], reverse=False)
return results[: limit]

structure_descriptor.py

# -*- coding: utf-8 -*-
# @Time : 2021/10/9 9:45
# @Author : 
import cv2
""" 構圖空間特征提取器StructureDescriptor。 類成員dimension。將所有圖片歸一化(降低采樣)為dimension所規定的尺寸。由此才能够用於統一的匹配和構圖空間特征的生成。 成員函數describe(self, image)。將圖像從BGR色彩空間轉為HSV色彩空間(此處應注意OpenCV讀入圖像的色彩空間為BGR而非RGB)。返回HSV色彩空間的矩陣,等待在搜索引擎核心中的下一步處理。 """
class StructureDescriptor:
__slot__ = ["dimension"]
def __init__(self, dimension):
self.dimension = dimension
def describe(self, image):
image = cv2.resize(image, self.dimension, interpolation=cv2.INTER_CUBIC)
# image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
return image
二.執行
# 構建索引
python index.py --dataset dataset --colorindex color_index.csv --structure structure_index.csv
# 搜圖
python searchEngine.py -c color_index.csv -s structure_index.csv -r dataset -q query/pyramid.jpg

在這裏插入圖片描述
在這裏插入圖片描述

參考:https://blog.csdn.net/coderhuhy/article/details/46575667

版权声明:本文为[Cocktail_py]所创,转载请带上原文链接,感谢。 https://gsmany.com/2022/01/202201070759178475.html