反向投影法是用已知图像的某些特征来突出其它图像中此类特征的一种方法,基于直方图。

  主要步骤如下:

  1、统计已知图像某个特征的色度直方图,通常用色度-饱和度(Hue-Saturation)来统计二维直方图,并把直方图表示为概率的形式。

  2、选取测试图像,对于图像中的每一个像素,查看它的色度在已统计直方图上的概率,用这个概率来重建整张图像。

  3、最后把重建出的图像归一化,会发现用于统计直方图的特征被突出出来。

  举OpenCV的例子,已知图像如下:

  我们称之为模型。选取图中的手作为要统计的特征,它的mask如下:

  于是可以统计得到手的Hue-Saturation直方图和联合概率分布(看做相互独立)的热度图:

  测试图片是另一张手的图像:

  然后执行第三步:

  可以发现手被突出出来了,这是因为,相同物体在不同的图像中的色值通常是相近的,因此可以通过这种方式来突出它。如果把上图当做mask对原测试图进行遮罩,还可以把手的图片单独提取出来:

  Python代码如下:

import cv2
import numpy as np
import matplotlib.pyplot as plt
  
#获取mask、model、test图像数据,并进行预处理
mask = plt.imread('mask.jpg')[:,:,0] 
mask = ((mask<120)*255).astype(np.uint8) 
n = np.sum(mask>100)
model = cv2.cvtColor(plt.imread('model.jpg'),cv2.COLOR_RGB2HSV)#把RGB颜色空间转换为HSV
test = cv2.cvtColor(plt.imread('test.jpg'),cv2.COLOR_RGB2HSV) 

#根据色度和饱和度(明度不考虑),统计model颜色信息,并绘制直方图和它们联合分布的热度图
hist_size = 50  #直方图划分的区间数量
hist_h = cv2.calcHist([model],[0],mask,[hist_size],[0,256])/n
hist_s = cv2.calcHist([model],[1],mask,[hist_size],[0,256])/n 
hot_values = np.matmul(hist_s, hist_h.T) 

fig = plt.figure()
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122)
ax1.bar(range(len(hist_h)),hist_h[:,0])
ax1.bar(range(len(hist_s)),hist_s[:,0])  
hot = ax2.imshow(hot_values)
plt.colorbar(hot)
plt.show()
  
#使用热度图重建test图像
projection = np.zeros([test.shape[0],test.shape[1]])
for i in range(projection.shape[0]):
  for j in range(projection.shape[1]):
    h_pos = (test[i,j,0]/256*hist_size).astype(np.uint8)
    s_pos = (test[i,j,1]/256*hist_size).astype(np.uint8) 
    projection[i,j] = hist_h[h_pos] * hist_s[s_pos]  
 
projection = projection / np.max(projection)
plt.imshow(projection,cmap='gray') 
plt.show()

#用反向投影法重建的图像当做mask提取原图
recons = cv2.cvtColor(test,cv2.COLOR_HSV2RGB)*(projection[:,:,np.newaxis] > 0.00005)
plt.imshow(recons) 
plt.show()

  我们还可以用RGB来统计,建立RGB的三维联合分布,然后对测试图像进行重建,效果如下:

 

  可以看出突出效果并没有H-S模型来得好。代码如下:

import cv2
import numpy as np
import matplotlib.pyplot as plt
  
mask = plt.imread('mask.jpg')[:,:,0] 
mask = ((mask<120)*255).astype(np.uint8) 
n = np.sum(mask>100)
model = plt.imread('model.jpg')
test = plt.imread('test.jpg') 
hist_size = 50  #直方图划分的区间数量
hist_r = cv2.calcHist([model],[0],mask,[hist_size],[0,256])[:,0]/n
hist_g = cv2.calcHist([model],[1],mask,[hist_size],[0,256])[:,0]/n
hist_b = cv2.calcHist([model],[2],mask,[hist_size],[0,256])[:,0]/n
  
plt.bar(range(len(hist_r)),hist_r)
plt.bar(range(len(hist_g)),hist_g)
plt.bar(range(len(hist_b)),hist_b)
plt.show()

projection = np.zeros([test.shape[0],test.shape[1]])
for i in range(projection.shape[0]):
  for j in range(projection.shape[1]):
    r_pos = (test[i,j,0]/256*hist_size).astype(np.uint8)
    g_pos = (test[i,j,1]/256*hist_size).astype(np.uint8)
    b_pos = (test[i,j,2]/256*hist_size).astype(np.uint8)
    projection[i,j] = hist_r[r_pos] * hist_g[g_pos] * hist_b[b_pos]
 
projection = projection / np.max(projection)
plt.imshow(projection,cmap='gray') 
plt.show()

   参考自:https://docs.opencv.org/master/da/d7f/tutorial_back_projection.html