反向投影通俗的讲就是拿一张小图片在一张大图片中找出和小图片相似的区域。
直方图反向投影有两种,一种是基于块的,一种是基于像素的。
一、基于块的直方图反向投影。
先看下直方图反向投影矩阵的计算方法:
假设原图灰度图像矩阵如下:
img = [1 2 3 4
5 6 7 7
9 8 0 1
5 6 7 6]
将灰度值划分为四个如下区间:[0,2] [3,5] [6,7] [8,10]也就是划分为四个BIN,那么很容易算出来 四个区间包含个数是多少,在区间[0,2]的有4个数,[3,5]区间的有4个数,[6,7]区间的有6个数,[8,10]区间的有2个数,即hist = 4 4 6 2
接下来计算反向投影矩阵:原图中坐标为(0,0)的灰度值为1,1位于区间[0,2]中,区间[0,2]对应直方图值为4,所以反向投影矩阵中坐标为(0,0)的值就替换为4,重复上述方法,将原图像灰度值全替换为四个区间的个数值,得到以下反向投影矩阵:
back_projection = [4 4 4 4
4 6 6 6
2 2 4 4
4 6 6 6]
从上述数据可以看出,在反向投影中,如在大图中遇到与小图相似区域,相似区域像素值就会被加亮显示,从而找出全部相似区域,
基于块的反向投影,是指从大图左上角开始将大图切割成一个个和小图一样大小的块,并计算出每个块的直方图,然后基于小图直方图的BIN值去与被切割成的块直方图对比,有落在BIN值区域的则加亮,无落在BIN值区域的则置0或者降低像素值。比如,上述小图的矩阵为img,大图切割后其中一个块矩阵像素为:[12 15 11 45 65 34 53 16 2 47 89 15 51 43 15 68],那么这个块反向投影后数据就变为[0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0],很显然这个块大部为黑色。
块的直方图反向投影基于函数cv2.calcBackProject()函数实现,代码如下(其中img为最上面的大图,img_roi对应上述的小图):
import cv2
import numpy as np
img = cv2.imread(r'D:\python\opencv\pic\calchist_for_clothes.PNG')
hsv_target = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
img_roi = cv2.imread(r'D:\python\opencv\pic\clothes_for_test.PNG')
hsv_roi = cv2.cvtColor(img_roi,cv2.COLOR_BGR2HSV)
roihist = cv2.calcHist([hsv_roi],[0,1],None,[180,256],[0,180,0,256])
'''
归一化:原始图像,结果图像,映射到结果图像中的最小值,最大值,归一化类型
cv2.NORM_MINMAX对数组的所有进行转化,使它们线性映射到最小值和最大值之间
归一化之后的直方图便于显示,归一化之后就成了0到255之间的数了。
'''
cv2.normalize(roihist,roihist,0,256,cv2.NORM_MINMAX)
dst = cv2.calcBackProject([hsv_target],[0,1],roihist,[0,180,0,256],1)
disc = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5))
dst = cv2.filter2D(dst,-1,disc)
ret,thresh = cv2.threshold(dst,50,255,0)
thresh = cv2.merge((thresh,thresh,thresh))
res = cv2.bitwise_and(img,thresh)
res = np.hstack((img,thresh,res))
cv2.imshow('img',res)
cv2.waitKey()
cv2.destroyAllWindows()
二、基于像素的直方图反向投影。
基于像素的直方图反向投影涉及到两个图像的除尘运算,在opencv_python中两个图像矩阵的除法计算方法如下:
基于像素的直方图反向投影原理如下:
1、 计算出小图与大图颜色直方图(cv2.calcHist())分别记任M和I,用print打印输出你会发现小图(模板图)直方图矩阵绝大部分为数据为0,只有一小部分的数值,说明模板图的H值和S值集中在一小区域,大图(目标图)则有大批数值。
import cv2
import numpy as np
np.set_printoptions(threshold=np.inf)#设置输出为全显示。
roi = cv2.imread('D:\python\opencv\pic\clothes_for_test.PNG')
hsv = cv2.cvtColor(roi,cv2.COLOR_BGR2HSV)
target = cv2.imread('D:\python\opencv\pic\calchist_for_clothes.PNG')
hsvt = cv2.cvtColor(target,cv2.COLOR_BGR2HSV)
M = cv2.calcHist([hsv],[0, 1], None, [180, 256], [0, 180, 0, 256] )
I = cv2.calcHist([hsvt],[0, 1], None, [180, 256], [0, 180, 0, 256] )
R = M/(I+1)
2、对两直方图矩阵做除法R = M/I。从上面数据可知,小图(模板图)数据集中在一小片区域,那么要在大图(目标图)中找出与之相似的区域,那么大图中该区域数值也应该要落在与小图对应的H值及s值区域,作除法则将其他非相似区域的值置0(也就是黑色)(这里R=M/I因为I矩阵中也可能包含0,所以在运行时会报错RuntimeWarning: invalid value encountered in true_divide,即除数为0警告,这里的除法只是为了找出相似区域,所以可以将R=M/I改成R=M/(I+1))
3、作除法后,非相似区域为0,相似区域则为一个比值,如下图所示:
4、目标区域找到之后,我们就要知道大图(目标图像)哪些点是落在这些区域的,那么我们就用split()方法分离出大图(目标图像)的H矩阵和S矩阵,再用R[h.ravel(),s.ravel()](ravel()方法是将原矩阵一维化)找出这些点,最后用reshape()重建图像,就实现了反向投影,当然为了让图像能正常显示,还需要对图像进行归一化处理cv2.normalize()。完整代码如下:
import cv2
import numpy as np
np.set_printoptions(threshold=np.inf)#设置输出为全显示。
roi = cv2.imread('D:\python\opencv\pic\clothes_for_test.PNG')
hsv = cv2.cvtColor(roi,cv2.COLOR_BGR2HSV)
target = cv2.imread('D:\python\opencv\pic\calchist_for_clothes.PNG')
hsvt = cv2.cvtColor(target,cv2.COLOR_BGR2HSV)
M = cv2.calcHist([hsv],[0, 1], None, [180, 256], [0, 180, 0, 256] )
I = cv2.calcHist([hsvt],[0, 1], None, [180, 256], [0, 180, 0, 256] )
R = M/(I+1)
cv2.imshow('R',R)
h,s,v = cv2.split(hsvt) #获取H轴S轴矩阵。
B = R[h.ravel(),s.ravel()] #找出图像H轴、S轴对应的值
B = np.minimum(B,1)
B = B.reshape(hsvt.shape[:2])#以目标图像大小为模板重建图像。
disc = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5))
cv2.filter2D(B,-1,disc,B) #对图像进行卷积运算。
B = np.uint8(B) #将图像数据类型转换为uint8
cv2.normalize(B,B,0,255,cv2.NORM_MINMAX) #归一化处理。
ret,thresh = cv2.threshold(B,50,255,0)
res = cv2.bitwise_and(target,target,mask = thresh)
cv2.imshow('nice',res)
cv2.imshow('img',target)
res = np.vstack((target,cv2.merge((B,B,B)),res))
cv2.imshow("result",res)
cv2.waitKey(0)
cv2.destroyAllWindows()
通过上述效果图对比可知,基于块的直方图反向投影在效果上要比基于像素的直方图反向投影效果好,其原因在于基于块的直方图反向投影用的对比后用数据取代原值,可非直接置0.
上述如有错误请指出,谢谢。
参考:
【opencv学习笔记】027之直方图反向投影 – calcBackProject函数详解_水亦心的博客-CSDN博客
opencv python 直方图反向投影_python opencv 直方图反向投影的方法_宗智敏的博客-CSDN博客
Original: https://blog.csdn.net/weixin_66412464/article/details/126863607
Author: weixin_66412464
Title: opencv_python直方图反向投影原理解析
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/702213/
转载文章受原作者版权保护。转载请注明原作者出处!