HOG圖像特征提取及其SK-imgae實現(xiàn)

前言


hog(image, orientations=9, pixels_per_cell=(8, 8), cells_per_block=(3, 3),
        block_norm='L2-Hys', visualize=False, transform_sqrt=False,
        feature_vector=True, multichannel=None)

圖像標準化

在這一步,我們的主要目的是為了預(yù)處理圖像,減少光照等帶來的影響。
f(I) = I^{ \gamma }

  • 此處我們選擇\gamma值小于1便會使圖像整體灰度變大,如果我們選擇\gamma值大于1便會使圖像整體灰度變小?;叶鹊拇笮∧撤N程度上決定了圖片的亮暗,灰度越小,圖片越發(fā)昏暗,反之亦然。

if transform_sqrt:
    image = np.sqrt(image)
  • skimage在此處的實現(xiàn)極為簡潔,直接使用了開方來對圖片進行處理。

圖像平滑

  • 去除灰度圖像的噪點,一般選取離散高斯平滑模板進行平滑,高斯函數(shù)在不同平滑的尺度下進行對灰度圖像進行平滑操作。Dalal的實驗中moving from σ=0 to σ=2 reduces the recall rate from 89% to 80% at FPPW,反應(yīng)給出做了圖像平滑之后HOG效果反而變差。我們在實驗過程中也得出了相似的結(jié)論,很容易讓人想到,HOG是基于圖像邊緣梯度的算法,但平滑過程有可能破壞邊緣的梯度信息,從而影響HOG的效果。

梯度計算

  • 首先是像素點梯度的計算,我們使用I(x, y)來表示圖像上(x, y)像素點的像素值。那么每個像素點的水平和豎直方向的梯度(Gradient)可以分別被表示為:
    G_{x}(x, y)=I(x+1, y)-I(x-1, y)

G_{y}(x, y)=I(x, y+1)-I(x, y-1)

橫縱梯度表示

  • 那么顯然,作為兩個梯度矢量,它們的幅度值和角度\alpha也可以分別表示為:
    \begin{aligned} G(x, y) &=\sqrt{G_{x}(x, y)^{2}+G_{y}(x, y)^{2}} \\ \alpha &=\arctan \frac{G_{y}(x, y)}{G_{x}(x, y)} \end{aligned}

    if image.dtype.kind == 'u':
        # convert uint image to float
        # to avoid problems with subtracting unsigned numbers
        image = image.astype('float')

    if multichannel:
        g_row_by_ch = np.empty_like(image, dtype=np.double)
        g_col_by_ch = np.empty_like(image, dtype=np.double)
        g_magn = np.empty_like(image, dtype=np.double)

        for idx_ch in range(image.shape[2]):
            g_row_by_ch[:, :, idx_ch], g_col_by_ch[:, :, idx_ch] = \
                _hog_channel_gradient(image[:, :, idx_ch])
            g_magn[:, :, idx_ch] = np.hypot(g_row_by_ch[:, :, idx_ch],
                                            g_col_by_ch[:, :, idx_ch])

        # For each pixel select the channel with the highest gradient magnitude
        idcs_max = g_magn.argmax(axis=2)
        rr, cc = np.meshgrid(np.arange(image.shape[0]),
                             np.arange(image.shape[1]),
                             indexing='ij',
                             sparse=True)
        g_row = g_row_by_ch[rr, cc, idcs_max]
        g_col = g_col_by_ch[rr, cc, idcs_max]
    else:
        g_row, g_col = _hog_channel_gradient(image)
  • 從HOG的實現(xiàn)中我們可以看到,這里是先將圖片以float的形式讀入,防止出現(xiàn)uint(小) - uint(大) 越界出現(xiàn)正數(shù)的情況。
  • 接著是對于多信道的一個判斷,如果圖像是多信道的話,我們會分信道進行梯度統(tǒng)計,如果是灰度圖片,會直接只進行一次梯度統(tǒng)計處理。梯度統(tǒng)計的代碼如下:
def _hog_channel_gradient(channel):
    """Compute unnormalized gradient image along `row` and `col` axes.

    Parameters
    ----------
    channel : (M, N) ndarray
        Grayscale image or one of image channel.

    Returns
    -------
    g_row, g_col : channel gradient along `row` and `col` axes correspondingly.
    """
    g_row = np.empty(channel.shape, dtype=np.double)
    g_row[0, :] = 0
    g_row[-1, :] = 0
    g_row[1:-1, :] = channel[2:, :] - channel[:-2, :]
    g_col = np.empty(channel.shape, dtype=np.double)
    g_col[:, 0] = 0
    g_col[:, -1] = 0
    g_col[:, 1:-1] = channel[:, 2:] - channel[:, :-2]

    return g_row, g_col

  • 接著,我們要將這些像素點整合為一個個的cell,選取的方式有正方形取點R-HOG,圓形取點C-HOG,和中心切割型取點Single centre C-HOG,而Dadel的論文指出:

We evaluated two variants of the C-HOG geometry, ones with a single circular central cell (similar to the GLOH feature), and ones whose cen-tralcellis divided into angular sectors as in shape contexts.We present results only for the circular-centrevariants, as these have fewer spatial cells than the divided centre ones and give the same per-formance in practice.

  • 由于中心切割型要消耗更多的4cell,但效果卻基本與圓形取點C-HOG相吻合,所以我們通常選用R-HOG和C-HOG二者之一。此處我們選擇R-HOG這一常用的HOG結(jié)構(gòu)。

  • 下一步便是pixels per cell參數(shù)的選取,此處我們?nèi)绻x擇(4x4)作為參數(shù),那么就代表由4x4個像素構(gòu)成一個cell,這時要對每一個cell當中的各個像素進行梯度向量的統(tǒng)計,此處我們選擇使用直方圖來進行統(tǒng)計,對應(yīng)的橫軸坐標就是向量的角度。這里簡單起見會考慮用若干個區(qū)間來覆蓋向量角度,Dadal論文當中采用的是9份,skimage官方的demo中采用的是8份,這里我們不妨選取9份作為例子。

orientation選取為8舉例
  • 這樣一來從0°到180°(如果是0°到360°則需考慮方向的正負)即可以分為20°的每份來作為梯度向量統(tǒng)計直方圖的橫軸,對應(yīng)的縱軸方向則填充像素點對應(yīng)的梯度的幅度值。

  • 同理,我們選擇cell per block參數(shù),例如也選取(4x4)。那么對于每一個block,都由對應(yīng)數(shù)量的cell合成。此時我們得到的塊特征向量長度應(yīng)該是4x4x9

C-HOG示意圖,ppc=cpb=(4, 4)

s_row, s_col = image.shape[:2]
    c_row, c_col = pixels_per_cell
    b_row, b_col = cells_per_block

    n_cells_row = int(s_row // c_row)  # number of cells along row-axis
    n_cells_col = int(s_col // c_col)  # number of cells along col-axis

    # compute orientations integral images
    orientation_histogram = np.zeros((n_cells_row, n_cells_col, orientations))

    _hoghistogram.hog_histograms(g_col, g_row, c_col, c_row, s_col, s_row,
                                 n_cells_col, n_cells_row,
                                 orientations, orientation_histogram)

    # now compute the histogram for each cell
    hog_image = None

    if visualize:
        from .. import draw

        radius = min(c_row, c_col) // 2 - 1
        orientations_arr = np.arange(orientations)
        # set dr_arr, dc_arr to correspond to midpoints of orientation bins
        orientation_bin_midpoints = (
            np.pi * (orientations_arr + .5) / orientations)
        dr_arr = radius * np.sin(orientation_bin_midpoints)
        dc_arr = radius * np.cos(orientation_bin_midpoints)
        hog_image = np.zeros((s_row, s_col), dtype=float)
        for r in range(n_cells_row):
            for c in range(n_cells_col):
                for o, dr, dc in zip(orientations_arr, dr_arr, dc_arr):
                    centre = tuple([r * c_row + c_row // 2,
                                    c * c_col + c_col // 2])
                    rr, cc = draw.line(int(centre[0] - dc),
                                       int(centre[1] + dr),
                                       int(centre[0] + dc),
                                       int(centre[1] - dr))
                    hog_image[rr, cc] += orientation_histogram[r, c, o]
  • 這里我們可以看到hog_histograms是一個bultins的函數(shù),我們無法看到它內(nèi)部的實現(xiàn),但我們猜測應(yīng)該是通過移動掃描窗口來實現(xiàn)直方圖的cell統(tǒng)計。為了保證效率,采取了c實現(xiàn)。
  • 這里還有一個visualize的實現(xiàn),是在之前詢問我們是否返回一個hog的可視圖。如果選擇是是,這里就會根據(jù)之前統(tǒng)計值引入draw作圖。

歸一化

  • 使局部光照對比度歸一化,壓縮光照,明暗,邊緣對比度對圖片帶來的影響。這一步是基于block進行的,也就是說每一個cell,可能同時屬于不同的block,那么它就會在不同的block被分別均一化。
  • 設(shè)v為沒有歸一化的feature vector,此處的均一化,我們通常有以下四種方式可選:
    • L_{1}-norm: v \leftarrow \frac{v}{|v|_{1}+\xi}
    • L_{1}-sqrt: v \leftarrow \sqrt{\frac{v}{\|v\|_{1}+\xi}}
    • L_{2}-norm: v \leftarrow \frac{v}{\sqrt{\|v\|_{12}^{2}+\xi^{2}}}:加一個極小的\xi以防止分母為0
    • L_{2}-H y s:在L_{2}的基礎(chǔ)上限制v的最大值為0.2,再歸一化。

  • 這里的塊均一化方法同時支持了我們上面所描述的四種方法。
def _hog_normalize_block(block, method, eps=1e-5):
    if method == 'L1':
        out = block / (np.sum(np.abs(block)) + eps)
    elif method == 'L1-sqrt':
        out = np.sqrt(block / (np.sum(np.abs(block)) + eps))
    elif method == 'L2':
        out = block / np.sqrt(np.sum(block ** 2) + eps ** 2)
    elif method == 'L2-Hys':
        out = block / np.sqrt(np.sum(block ** 2) + eps ** 2)
        out = np.minimum(out, 0.2)
        out = out / np.sqrt(np.sum(out ** 2) + eps ** 2)
    else:
        raise ValueError('Selected block normalization method is invalid.')

    return out
  • 再來看一下具體的實現(xiàn)過程,n_blocks_row 對應(yīng)的是block的行數(shù),需要對應(yīng)的cell在行上平均分布開的數(shù)目減去對應(yīng)的cells_per_block的行數(shù)再加上1。列的計算依然。由此,我們可以推斷出對應(yīng)的特征向量維數(shù)應(yīng)該是之前每一個block對應(yīng)的維數(shù)4x4x9再乘上對應(yīng)的block數(shù)目(8-4+1)x(8-4+1),最終等于3600維。選取了不同的參數(shù)也可以根據(jù)此判據(jù)來進行計算。
    n_blocks_row = (n_cells_row - b_row) + 1
    n_blocks_col = (n_cells_col - b_col) + 1
    normalized_blocks = np.zeros((n_blocks_row, n_blocks_col,
                                  b_row, b_col, orientations))

    for r in range(n_blocks_row):
        for c in range(n_blocks_col):
            block = orientation_histogram[r:r + b_row, c:c + b_col, :]
            normalized_blocks[r, c, :] = \
                _hog_normalize_block(block, method=block_norm)

參考文章

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容