0
点赞
收藏
分享

微信扫一扫

Python计算机视觉基础实验3-显著性检测(HC&FC)

一、实验基础

图像显著性检测

图像的显著性是指对于给定一副真实世界中的场景图像,人们会自动地识别出感兴趣区域,忽略掉不感兴趣的区域,即将注意力集中在图像中某些显著的部分区域。

图像的注意预测,也称视觉显著性检测,指通过智能算法模拟人的视觉系统特点,预测人类的视觉凝视点(就是全神贯注的看一件物品的一个点)和眼动(进行视野搜索的要素),提取图像中的显著性区域(即人类感兴趣的区域)。


Lab颜色空间

Lab颜色空间(Lab color space)是颜色-对立空间,维度L表示亮度,a和b表示颜色对立维度,其常用来描述人眼可见的所有颜色的最完备的色彩空间,它更接近人类对真实场景中色彩的“感觉”。

Lab中的明度通道(L)专门负责整张图的明暗度,简单的说就是整幅图的黑白版。a通道和b通道只负责颜色的多少,而且a通道和b通道的颜色没有亮度。


基于直方图对比度的显著性检测

基于直方图对比度(Histogram-based Contrast, HC)的显著性检测是清华大学程明明等人2011年提出的一种基一种基于颜色全局直方图的颜色对比度算法,像素点与其它像素之间的颜色特征差异度越大,则显著性越高。

  • 给定图像D,将其从RGB空间转换到Lab空间,每个像索点的特征为I = [L,a,b]
  • 计算像素点(x,y)的显著性值
  • 显著值计算公式
  • 由于该算法中相同的颜色具有相同的显著值,所以可以采用颜色统计直方图对其进行归类后再运算。
  • 颜色归类
  • 显著性值计算

图像量化(直方图优化加速)步骤:

1、量化颜色通道。找出图像中一共有多少种颜色以及对应的像素总数。

2、按照像素总数从大到小排序,并同时记录相应颜色。

3、找出像素数目覆盖图像不小于95%的高频颜色,以及其他的不高于5%的颜色种类,假设高频颜色共有maxNum种。

4、把低频颜色的像素归类到与它lab颜色距离相距最近的高频颜色中。


频率调谐(Frequency-tuned, FT)算法

FT算法从频率角度分析图像。图像在频率域可以分成低频部分和高频部分。低频部分反映了图像的整体信息,如物体的轮廓,基本的组成区域。高频部分反映了图像的细节信息,如物体的纹理。显著性区域检测用到的更多的是低频部分的信息。在实际进行计算时,FT方法一般使用窗口5*5的高斯平滑来实现对最高频的舍去。


算法步骤:

  1. 输入图像进行高斯滤波(一般使用5*5的模板),去除高频信息
  2. 将原图与滤波后的图像分别从RGB颜色空间转换到Lab颜色空间
  3. 在原图上计算Lab空间颜色向量平均值 Iu = [Lu, au, bu]
  4. 计算三个通道均值与高斯滤波后图像的欧氏距离之和,公式如下图所示

Python计算机视觉基础实验3-显著性检测(HC&FC)_Lab颜色空间

算法结构图:

Python计算机视觉基础实验3-显著性检测(HC&FC)_FT算法_02


二、实验要求

对提供的 im1、im2、im3 三张图按照HC、FT两种算法进行图像的显著度检测。

Python计算机视觉基础实验3-显著性检测(HC&FC)_Lab颜色空间_03

Python计算机视觉基础实验3-显著性检测(HC&FC)_Lab颜色空间_04

Python计算机视觉基础实验3-显著性检测(HC&FC)_Lab颜色空间_05


三、实验源码与结果

导入包

import cv2
import numpy as np
import matplotlib.pyplot as plt
# 距离计算模块,
from scipy.spatial.distance import pdist, squareform

HC算法显著性检测

'''直方图优化加速'''
def Quantize(img3f,ratio=0.95,colorNums=(12,12,12)):
    '''量化三通道图像img3f的颜色,三通道的量化阶数为colorNums
        ratio指定最后保留的颜色总数的一定比例数量的颜色
    '''
    
    '''量化颜色通道。找出图像中一共有多少种颜色以及对应的像素总数'''
    clrTmp = clrTmp = [colorNum-0.0001 for colorNum in colorNums]   # clrTmp = [12-0.0001, 12-0.0001, 12-0.0001]
    # 指定每个通道分量最多存储多少种的像素
    w = [colorNums[1] * colorNums[2], colorNums[2], 1]  # 如 w = [12*12,12,1],最后得到的像素种类数量为 w[0]*[w1]*w[2]
    # 拆分图像的三个通道的分量(图)
    img3f_0,img3f_1,img3f_2 = cv2.split(img3f)
    # 各个通道的分量图的像素值(范围:0.0~1.0) * 量化阶数-->强制转为int32类型-->乘以w[i]
    # “强制转为int32类型”是量化颜色的关键,这操作大大减少了颜色数量(量化后每个不同的值代表不同的颜色)
    idx_img3f_0 = (img3f_0 * clrTmp[0] ).astype(np.int32)* w[0]
    idx_img3f_1 = (img3f_1 * clrTmp[1] ).astype(np.int32)* w[1]
    idx_img3f_2 = (img3f_2 * clrTmp[2] ).astype(np.int32)* w[2]
    # 处理后三个通道分量相加,得到idx1i为颜色量化后的“像素”矩阵,每个不同的"像素值"表示不同的颜色,默认最多为12*12*12种
    idx1i = idx_img3f_0 + idx_img3f_1 + idx_img3f_2
    # 统计像素出现频数
    # np.bincount(arr) 函数:统计 区间[0,arr数组中最大数字]内所有数值 出现的个数(即每种颜色的总数),返回array数组结果
    # b = [1,3,2,4,3],a = np.bincount(b),得到a:[0 1 1 2 1]
    bincount_pallet = np.bincount(idx1i.reshape(1,-1)[0])   # idx1i.reshape(1,-1)[0]把 idx1i展开成一维的数组
    
    '''按照像素总数从大到小排序,并同时记录相应颜色(这里先从小到大排序)'''
    # 按照像素总数从小到大排序,并同时记录相应颜色
    sort_pallet = np.sort(bincount_pallet)  # 从大到小的像素总数
    argsort_pallet = np.argsort(bincount_pallet)  # sort_pallet对应的颜色(原索引)
    # 合并两个数组:颜色数量,颜色值
    numpy_pallet = np.vstack((sort_pallet, argsort_pallet))
    # 筛选掉总数为0的颜色
    # print('numpy_pallet.shape(before selected): ',numpy_pallet.shape)
    numpy_pallet = numpy_pallet[:, np.nonzero(sort_pallet)]  # np.nonzero(a)函数返回的是目标数组a中非零元素的索引
    # print('numpy_pallet.shape(after selected): ',numpy_pallet.shape)
    # 获取筛选后的结果,若总数大于0的颜色值共有351种,则得到的num的形状为(2, 351)
    # num[0],num[1]分别存放所有总数不为0的颜色(数值)出现的总数(从小到大排序)以及对应的颜色值(数值)
    num = np.swapaxes(numpy_pallet, 0, 1)[0] # np.swapaxes实现数组的维度交换,比如(2, 1, 351) 交换维度后变为(1, 2, 351) 
    # maxNum 分别为高频颜色种类数
    len_num = maxNum = len(num[0])  # len(num[0])为筛选总数为0的颜色后的颜色种类数
    
    '''找出像素数目覆盖图像不小于95%的高频颜色,以及其他的不高于5%的颜色种类,假设高频颜色共有maxNum种'''
    height,width = img3f.shape[:2]   
    maxDropNum = int(np.round(height * width * (1 - ratio))) # 设置删除最大元素阈值
    sum_pallet = np.add.accumulate(num[0])  # np.add.accumulate()实现累加,每一个位置的元素和前面的所有元素加起来求和,结果返回numpy数组
    # 这里调用 np.argwhere( ) 获取 sum_pallet中元素值>=maxDropNum的元素的索引
    arg_sum_pallett = np.argwhere(sum_pallet >= maxDropNum)[0][0] # 前95%的颜色值数量
    print('像素数目覆盖图像不小于95%的颜色值数量: ',arg_sum_pallett)
    minNum = maxNum - arg_sum_pallett # 后5%的颜色值数量
    
    num_values = num[1][::-1] # 所有高频次颜色值的索引(频次从高到低的颜色值的索引)
    minNum = 256 if minNum > 256 else minNum
    if minNum <= 10:
        minNum = 10 if len(num) > 10 else len(num)
    
    '''把低频颜色的像素归类到与它lab颜色距离相距最近的高频颜色中'''
    # 获取由高频颜色组成的三通道图color3i
    color3i_init0 = (num_values / w[0]).astype(np.int32)
    color3i_init1 = (num_values % w[0]/w[1]).astype(np.int32)
    color3i_init2 = (num_values % w[1]).astype(np.int32)
    color3i = np.array([color3i_init0,color3i_init1,color3i_init2]).T
    zero2maxNum = color3i[:minNum] # 5%的颜色值数量部分
    maxNum2len_Num = color3i[minNum:] # 95%的颜色值数量部分
    temp_matrix = np.zeros((len_num-minNum,minNum),dtype=np.int32)
    for i,single in enumerate(maxNum2len_Num): #分别求:95%的颜色值与5%的颜色值的距离
        temp_matrix[i] = np.sum(np.square(single-zero2maxNum),axis=1)
    arg_min = np.argmin(temp_matrix, axis=1) # np.argmin(a, axis=指定值)求的就是a在指定的第axis维度(轴)上的最小值对应的位置索引
    replaceable_colors = num_values[arg_min] # 通过索引获取5%的颜色值中距离95%的颜色值最近的颜色值
    pallet = dict(zip(num_values[:minNum], range(minNum)))
    for num_value,index_dist in zip(num_values[minNum:],replaceable_colors):
        pallet[num_value] = pallet[index_dist]
    
    idx1i_reshape = idx1i.copy().reshape(1,-1)[0]
    idx1i_0 = np.zeros(height * width, dtype=np.int32)
    for i, v in enumerate(idx1i_reshape):
        idx1i_0[i] = pallet[v]
        
    idx1i = idx1i_0.reshape((height,width))
    color3f = np.zeros((1, minNum, 3), np.float32)
    colorNum = np.zeros((1, minNum), np.int32)
    np.add.at(color3f[0], idx1i, img3f)  # 在color3f[0]指定的位置idx1i上 加上指定的值img3f
    np.add.at(colorNum[0], idx1i, 1)   #在colorNum[0]指定的位置idx1i上 加上指定的值1
    colorNum_reshape = colorNum.reshape(color3f.shape[1],1)
    color3f[0] /= colorNum_reshape
    # 第一个返回结果为量化后的直方图的binN(即直方图的“条形”的数量,比常规的256小了许多)
    print('量化后直方图的“条形”数量:',color3f.shape[1])
    return color3f.shape[1],idx1i,color3f,colorNum

def GetHC(img_float,delta=0.25):
    binN, idx1i, binColor3f, colorNums1i = Quantize(img_float)                 # 颜色量化
    binColor3f = cv2.cvtColor(binColor3f, cv2.COLOR_BGR2Lab)                 # 颜色空间:BGR2Lab
    weight1f = np.zeros(colorNums1i.shape, np.float32)
    cv2.normalize(colorNums1i.astype(np.float32), weight1f, 1, 0, cv2.NORM_L1) # 相邻色彩相关权重

    binColor3f_reshape = binColor3f.reshape(-1, 3)[:binN]
    similar_dist = squareform(pdist(binColor3f_reshape))
    similar_dist_sort = np.sort(similar_dist)
    similar_dist_argsort = np.argsort(similar_dist)

    weight1f = np.tile(weight1f, (binN, 1))
    color_weight_dist = np.sum(np.multiply(weight1f, similar_dist), axis=1)    # 颜色距离的权重分配

    colorSal = np.zeros((1, binN), np.float64)
    if colorSal.shape[1] < 2:
        return
    tmpNum = int(np.round(binN * delta))                                       # tmpNum 占比0.25的变化的颜色值数量
    n = tmpNum if tmpNum > 2 else 2

    similar_nVal = similar_dist_sort[:, :n]
    totalDist_similar = np.sum(similar_nVal, axis=1)
    every_Dist = np.tile(totalDist_similar[:, np.newaxis], (1, n)) - similar_nVal

    idx = similar_dist_argsort[:, :n]
    val_n = np.take(color_weight_dist,idx)              # 获取占比前0.25的颜色权重距离,np.take()沿轴从数组中获取元素

    valCrnt = np.sum(val_n[:, :n] * every_Dist, axis=1)
    newSal_img = valCrnt / (totalDist_similar * n)
    cv2.normalize(newSal_img, newSal_img, 0, 1, cv2.NORM_MINMAX)              # 归一化
    salHC_img = np.take(newSal_img,idx1i)
    cv2.GaussianBlur(salHC_img, (3, 3), 0, salHC_img)
    cv2.normalize(salHC_img, salHC_img, 0, 1, cv2.NORM_MINMAX)
    return salHC_img

# plt显示多个图像
def plt_shows(titles_l, imgs_l):
    for t,im in zip(titles_l,imgs_l):
        plt.figure()
        plt.title(t)
        plt.imshow(im,cmap='gray')
    plt.show()

def get_img3_float(img_file):
    img3_int = cv2.imread(img_file)  # BGR三通道图
    img3_float = img3_int.astype(np.float32)   # 数组img3_int的副本,强制转换为指定的类型(float32)
    img3_float = img3_float / 255.0  # 0~255.0的像素值 映射 到0.0~1.0区间范围
    return img3_float

im1_3f = get_img3_float('images_dir/im1.jpg')
im2_3f = get_img3_float('images_dir/im2.jpg')
im3_3f = get_img3_float('images_dir/im3.jpg')

sal1 = GetHC(im1_3f)
sal1 = (sal1*255).astype(np.int32) # 0.0~1.0区间范围的像素值 映射 到 0~255的范围并转换类型
plt_shows(['im1','HC im1'],[im1_3f,sal1])

sal2 = GetHC(im2_3f)
sal2 = (sal2*255).astype(np.int32) # 0.0~1.0区间范围的像素值 映射 到 0~255的范围并转换类型
plt_shows(['im2','HC im2'],[im2_3f,sal2])

sal3 = GetHC(im3_3f)
sal3 = (sal3*255).astype(np.int32) # 0.0~1.0区间范围的像素值 映射 到 0~255的范围并转换类型
plt_shows(['im3','HC im3'],[im3_3f,sal3])

Python计算机视觉基础实验3-显著性检测(HC&FC)_FT算法_06

Python计算机视觉基础实验3-显著性检测(HC&FC)_HC算法_07

Python计算机视觉基础实验3-显著性检测(HC&FC)_Lab颜色空间_08

FC算法显著性检测

def ft_detect(img):
    '''FC算法显著性检测'''
    # 输入图像进行高斯滤波,去除高频信息
    img_gauss = cv2.GaussianBlur(img,(5,5), 0.5)

    # 将原图与滤波后的图像从BGR颜色空间转换到RGB再转到Lab颜色空间
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    img_gauss = cv2.cvtColor(img_gauss, cv2.COLOR_BGR2RGB)
    
    img_lab = cv2.cvtColor(img, cv2.COLOR_RGB2LAB)
    gauss_lab = cv2.cvtColor(img_gauss, cv2.COLOR_RGB2LAB)
    # 在原图上计算Lab空间颜色向量平均值
    l_mean = np.mean(img_lab[:,:,0])
    a_mean = np.mean(img_lab[:,:,1])
    b_mean = np.mean(img_lab[:,:,2])
    
    # 计算三个通道均值与高斯滤波后图像的欧氏距离之和
    S_xy = np.square(np.array([l_mean, a_mean, b_mean]) - gauss_lab)
    S_xy = np.sum(S_xy,axis=2)
    S_xy = S_xy/np.max(S_xy)
    
    return S_xy


# 读取图片(cv2默认读取的是BGR三通道)
img = cv2.imread("images_dir/im1.jpg")
fc_result = ft_detect(img)
plt_shows(['im1','FC im1'],[img,fc_result])


img = cv2.imread("images_dir/im2.jpg")
fc_result = ft_detect(img)
plt_shows(['im2','FC im2'],[img,fc_result])

img = cv2.imread("images_dir/im3.jpg")
fc_result = ft_detect(img)
plt_shows(['im3','FC im3'],[img,fc_result])

Python计算机视觉基础实验3-显著性检测(HC&FC)_FT算法_09

Python计算机视觉基础实验3-显著性检测(HC&FC)_HC算法_10

Python计算机视觉基础实验3-显著性检测(HC&FC)_Lab颜色空间_11

Python计算机视觉基础实验3-显著性检测(HC&FC)_Lab颜色空间_12

Python计算机视觉基础实验3-显著性检测(HC&FC)_Lab颜色空间_13

Python计算机视觉基础实验3-显著性检测(HC&FC)_图像显著性检测_14


四、实验小结

HC方法只用到了颜色特征, 一个像素的显著值是通过与图像中的所有其它像素的色差来定义的。由于测量没考虑空间关系,同样颜色值的像素具有相同的显著值;缩减颜色空间。三通道图像可能的像素值为255*255*255个。这就导致无法使用直方图来进行加速程序,因此我们将每个通道量化为12个颜色,这样就是12*12*12=1278个可能的像素值。

此外,HC算法还从图像中筛选出了可以覆盖图像95%像素的颜色值,剩余的5%的颜色值用直方图中的临近像素替代,从而进一步减少可能的颜色值。为了达到更好的效果,直方图优化后,需要进行一步平滑操作。

FC相对HC算法较简单,它是基于Lab颜色空间像素向量与平均像素向量欧氏距离的显著性检测算法,偏向于利用图像像素的“空间”。



五、参考博文

计算机视觉——图像视觉显著性检测_图像显著性检测_@李忆

OpenCV—python 图像显著性检测算法—HC/RC/LC/FT

【手撕算法】FT显著性检测算法



上一篇:Python计算机视觉基础实验2-边缘检测和角点检测

举报

相关推荐

0 条评论