python实现图像检索的三种(直方图/OpenCV/哈希法)


Posted in Python onAugust 08, 2019

简介:

本文介绍了图像检索的三种实现方式,均用python完成,其中前两种基于直方图比较,哈希法基于像素分布。
检索方式是:提前导入图片库作为检索范围,给出待检索的图片,将其与图片库中的图片进行比较,得出所有相似度后进行排序,从而检索结果为相似度由高到低的图片。由于工程中还包含Qt界面类、触发函数等其他部分,在该文档中只给出关键函数的代码。

开发系统:MacOS
实现方式:Qt + Python

方法一:自定义的直方图比较算法

a) 基本思路

遍历图片像素点,提取R\G\B值并进行对应的计数,得到原始直方图,但由于0-255的范围太大,因此每一个像素值的统计量均偏小,因此分别将R\G\B的256个像素值映射到0-31共32个像素值上,将像素值范围由256*3缩小到32*3。记录像素值采用的数据结构为一维数组,第1到32个值为R的像素直方图,第33到第64个值为G的像素统计,第65到96个值为B的像素统计。得到直方图后,计算待检索图的直方图和图片库中图像的直方图之间的相似性。

b) 具体实现

用到的函数:

  • split_Img()
  • calc_Hist(img)
  • calc_Similar(h1,h2)
  • calc_Similar_Split(h1,h2)

遍历图片的像素点以计算直方图:calc_Hist(img)

尝试了两种方式,第一种是对图像遍历时逐个调用getpixel()来获取R,G,B的值,但发现这种方式的速度太慢。第二种采用的是内存读取,利用load()函数一次性读取图像的像素值,然后对像素值进行遍历,该方法的速度比逐个提取更快。

#统计直方图,用load()载入图片的像素pix,再分别读取每个像素点的R\G\B值进行统计(分别为0-255)
#将256个颜色值的统计情况投影到32个,返回R\G\B投影后的统计值数组,共32*3=96个元素
def calc_Hist(img):
  '''
  #120张图片,4.43s
  w,h = img.size
  pix = img.load() #载入图片,pix存的是像素
  calcR = [0 for i in range(0,32)]
  calcG = [0 for i in range(0,32)]
  calcB = [0 for i in range(0,32)]
  for i in range(0,w):
    for j in range(0,h):
      (r,g,b) = pix[i,j]
      #print (r,g,b)
      calcR[r/8] += 1
      calcG[g/8] += 1
      calcB[b/8] += 1
  calcG.extend(calcB)
  calcR.extend(calcG)

  return calcR
  '''
  #120张图,3.49s

  w,h = img.size
  pix = img.load() #载入图片,pix存的是像素
  calcR = [0 for i in range(0,256)]
  calcG = [0 for i in range(0,256)]
  calcB = [0 for i in range(0,256)]
  for i in range(0,w):
    for j in range(0,h):
      (r,g,b) = pix[i,j]
      #print (r,g,b)
      calcR[r] += 1
      calcG[g] += 1
      calcB[b] += 1
  calcG.extend(calcB)
  calcR.extend(calcG) #256*3

  #calc存放最终结果,32*3
  calc = [0 for i in range(0,96)]
  step = 0 #calc的下标,0~95
  start = 0 #每次统计的开始位置
  while step < 96:
    for i in range(start,start+8): #8个值为1组,统计值相加,eg:色彩值为0~7的统计值全部转换为色彩值为0的统计值
      calc[step] += calcR[i]
    start = start+8
    step += 1
  #print calc 
  return calc

直方图比较 calc_Similar(h1,h2)

采用的公式是: 

python实现图像检索的三种(直方图/OpenCV/哈希法)

其中N为颜色级数,Sim越靠近1则两幅图像的相似度越高。

c) 问题和改进

简单实现直方图比较后,检索的结果并不好,和预期相比误差较大。分析原因,直方图比较主要依靠整幅图像的色彩统计来进行比较,而对像素的位置并没有很好的记录,因此会造成误判。

同时增加calc_Similar_Split(h1,h2)函数,加入分块比较的部分,计算方法是:对每个小块调用calc_Similar(h1,h2),累加计算结果,最后除以16取平均值。

测试发现效果显著提升,基于颜色相似的同时保留了形状信息。

函数如下:

#该函数用于统一图片大小为256*256,并且分割为16个块,返回值是16个局部图像句柄的数组
def split_Img(img, size = (64,64)):
  img = img.resize((256,256)).convert('RGB')
  w,h = img.size
  sw,sh = size
  return [img.crop((i,j,i+sw,j+sh)).copy() for i in xrange(0,w,sw) for j in xrange(0,h,sh)]

#计算两个直方图之间的相似度,h1和h2为直方图,zip表示同步遍历
def calc_Similar(h1,h2):
  return sum(1 - (0 if g==s else float(abs(g-s))/max(g,s)) for g,s in zip(h1,h2)) / len(h1)

方法二:openCV库的直方图比较算法实现

openCV开源库已经集成了直方图提取、直方图均衡化以及直方图比较的功能,调用方便。为了进一步了解直方图比较的各类实现方法,利用openCV重新进行了实验。

a) 基本思路

对图片库中每个图片提取直方图并均衡化,然后调用cv库函数进行直方图比较,结果进行排序,并显示。

b) 具体实现

首先调用cv2.imread()读取图像,然后调用cv2.calcHist()计算直方图,cv2.normalize()均衡化后进入比较阶段,调用cv2.compareHist(),比较待检索图和图片库图像之间的直方图差异,然后调用DisplayTotalPics()进行显示。

关键代码如下:

results = {} #记录结果
reverse = True #correlation/intersection方法reverse为true,另外两种为false

imgCV = cv2.imread(self.testImg.encode('utf-8'))
#self.testImg为待匹配图片
testHist = cv2.calcHist([imgCV],[0,1,2],None,[8,8,8],[0,256,0,256,0,256])
#提取直方图
testHist = cv2.normalize(testHist,testHist,0,255,cv2.NORM_MINMAX).flatten()
#均衡化

#计算self.testImg和其他图片的直方图差异,INTERSECTION方法效果比较好
for (k, hist) in self.index_cv.items(): 
#self.index_cv保存的是图片库中图片的直方图信息
  d = cv2.compareHist(testHist,hist, cv2.cv.CV_COMP_INTERSECT)
  results[k] = d
  #对结果排序,以v即上面的d作为关键字
  results = sorted([(v, k) for (k, v) in results.items()], reverse = reverse) 
  end = time.time()
  print 'OpenCV Time:'
  print end-start     
self.DisplayTotalPics(results)

c) 问题与解决

openCV中的compareHist函数中提供了4中比较方法:
1.相关系数标准(method=CV_COMP_CORREL) 值越大,相关度越高,最大值1,最小值0
2.卡方系数标准(method=CV_COMP_CHISQR) 值越小,相关度越高,无上限,最小值0
3.相交系数标准(method=CV_COMP_INTERSECT)值大,相关度越高,最大9.455319,最小0
4.巴氏系数的标准(method=CV_COMP_BHATTACHARYYA) 值小,相关度越高,最大值1,最小值0

测试后选择的是method = cv2.cv.CV_COMP_INTERSECT

另外,该方法的速度很快,完全基于图像的色彩分布,但在一些情况下精度并不高。

方法三:平均哈希值比较算法实现

用到的函数:getKey(),getCode(),cmpCode()

a) 基本思路

平均哈希值的比较算法是基于像素分布的,比较对象是灰度图的图像指纹。图像指纹的计算通过比较每个图的像素值和平均像素值来计算,然后计算图像指纹之间的汉明距离,排序后得到相似图像。

b) 具体实现

具体方法是:计算进行灰度处理后图片的所有像素点的平均值,然后遍历灰度图片每一个像素,如果大于平均值记录为1,否则为0,这一步通过定义函数getCode(img)完成。接着计算编码之间的汉明距离,即一组二进制数据变为另一组数据所需的步骤数,汉明距离越小,说明图像指纹的相似度越高。计算汉明距离可以通过简单的遍历和计数来完成,函数为compCode(code1,code2),其中code1和code2为getCode得到的图像指纹。

关键函数代码如下:

#获取排序时的关键值(即汉明距离)    
def getKey(x): 
  return int(x[1])

#由灰度图得到2值“指纹”,从而计算汉明距离
def getCode(img):
  w,h = img.size
  pixel = []
  for i in range(0,w):
    for j in range(0,h):
      pixel_value = img.getpixel((i,j))
      pixel.append(pixel_value) #加入pixel数组
  avg = sum(pixel)/len(pixel) #计算像素平均值

  cp = [] #二值数组
  for px in pixel:
    if px > avg:
      cp.append(1)
    else:
      cp.append(0)
  return cp

#计算两个编码之间的汉明距离
def compCode(code1,code2):
  num = 0
  for index in range(0,len(code1)):
    if code1[index] != code2[index]:
      num+=1
  #print num
  #print '\n'
  return num

c) 问题与优化

我们发现在数据量大时,该方法的检索速度较慢,因此我们将图像指纹也作为图像的属性存在self.hashCode中,在importFolder时计算好,避免后续操作中的冗余重复计算。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python自动重试HTTP连接装饰器
Apr 28 Python
Python多进程multiprocessing用法实例分析
Aug 18 Python
Python基于多线程实现抓取数据存入数据库的方法
Jun 22 Python
Python3爬虫教程之利用Python实现发送天气预报邮件
Dec 16 Python
树莓派使用USB摄像头和motion实现监控
Jun 22 Python
Django框架中间件定义与使用方法案例分析
Nov 28 Python
python读写Excel表格的实例代码(简单实用)
Dec 19 Python
pytorch查看torch.Tensor和model是否在CUDA上的实例
Jan 03 Python
基于Python3.6中的OpenCV实现图片色彩空间的转换
Feb 03 Python
Python hashlib和hmac模块使用方法解析
Dec 08 Python
Python字符串的15个基本操作(小结)
Feb 03 Python
Python的Tqdm模块实现进度条配置
Feb 24 Python
python批量修改ssh密码的实现
Aug 08 #Python
更新pip3与pyttsx3文字语音转换的实现方法
Aug 08 #Python
Python使用百度翻译开发平台实现英文翻译为中文功能示例
Aug 08 #Python
python按行读取文件并找出其中指定字符串
Aug 08 #Python
python实现PID算法及测试的例子
Aug 08 #Python
python开头的coding设置方法
Aug 08 #Python
pycharm 安装JPype的教程
Aug 08 #Python
You might like
php include加载文件两种方式效率比较
2010/08/08 PHP
从手册去理解分析PHP session机制
2011/07/17 PHP
跨浏览器PHP下载文件名中的中文乱码问题解决方法
2015/03/05 PHP
详解PHP归并排序的实现
2016/10/18 PHP
解决laravel 5.1报错:No supported encrypter found的办法
2017/06/07 PHP
php提交表单时保留多个空格及换行的文本样式的方法
2017/06/20 PHP
使用PHPExcel导出Excel表
2018/09/08 PHP
Laravel 手动开关 Eloquent 修改器的操作方法
2019/12/30 PHP
RR vs IO BO3 第二场2.13
2021/03/10 DOTA
一种JavaScript的设计模式
2006/11/22 Javascript
dojo 之基础篇
2007/03/24 Javascript
javascript事件模型代码
2007/07/01 Javascript
js模拟弹出效果代码修正版
2008/08/07 Javascript
仿微博字符限制效果实现代码
2012/04/20 Javascript
Jquery实现带动画效果的经典二级导航菜单
2013/03/22 Javascript
漂亮! js实现颜色渐变效果
2016/08/12 Javascript
jQuery弹出遮罩层效果完整示例
2016/09/13 Javascript
JavaScript实现Fly Bird小游戏
2016/12/15 Javascript
js获取指定时间的前几秒
2017/04/05 Javascript
深入浅析javascript继承体系
2017/10/23 Javascript
JavaScript实现重力下落与弹性效果的方法分析
2017/12/20 Javascript
JavaScript定时器常见用法实例分析
2019/11/15 Javascript
微信小程序登录时如何获取input框中的内容
2019/12/04 Javascript
node.js事件轮询机制原理知识点
2019/12/22 Javascript
使用Node.js实现base64和png文件相互转换的方法
2020/03/11 Javascript
Python实现保证只能运行一个脚本实例
2015/06/24 Python
windows 10下安装搭建django1.10.3和Apache2.4的方法
2017/04/05 Python
200行python代码实现2048游戏
2019/07/17 Python
Django 模型类(models.py)的定义详解
2019/07/19 Python
python 利用openpyxl读取Excel表格中指定的行或列教程
2021/02/06 Python
static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数与普通函数有什么区别?
2015/02/22 面试题
会计大学生职业生涯规划书范文
2014/01/13 职场文书
幼儿园春游活动方案
2014/01/19 职场文书
小学生操行评语大全
2014/04/22 职场文书
稽核岗位职责范本
2015/04/13 职场文书
关于迟到的检讨书
2015/05/06 职场文书