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) 具体实现
用到的函数:
遍历图片的像素点以计算直方图: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 |
|
其中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) |
|
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) |
|
#获取排序时的关键值(即汉明距离) 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 |
|