OpenCL 學習step by step (7) 灰度圖Histogram計算(1)

2020-12-04 CSDN技術社區

histogram翻譯成中文就是直方圖,在計算機圖像處理和視覺技術中,通常用histogram來進行圖像匹配,從而完成track,比如meanshift跟蹤算法中,經常要用到圖像的直方圖。

灰度圖的histogram計算,首先要選擇bin(中文可以稱作槽)的數量,對於灰度圖,像素的範圍通常是[0-255],所以bin的數目就是256,然後我們循環整幅圖像,統計出每種像素值出現的次數,放到對應的bin中。比如bin[0]中放的就是整幅圖像中灰度值為0的像素個數,bin[1]中放的就是整幅圖像中灰度值為1的像素個數……

下面的直方圖就是灰度圖lenna對應的直方圖。

灰度圖直方圖的cpu計算特別簡單,定義一個數組hostBin[256],初始化所有數組元素為0,然後循環整幅圖像,得到直方圖,代碼如下:

//cpu求直方圖

void cpu_histgo()

    {

    int i, j;

    for(i = 0; i < height; ++i)

        {

        for(j = 0; j < width; ++j)

            {

            //printf("data: %d\n", data[i * width + j] );

            hostBin[data[i * width + j]]++;

            //printf("hostbin %d=%d\n", data[i * width + j], hostBin[data[i * width + j]]);

            }

        }

    }

如何使用opencl,來計算灰度圖,就沒有那麼簡單了。我們知道gpu的優勢是並行計算,如何把圖像分塊,來並行計算直方圖,是我們討論的重點。下面是一副512*512圖像的thread,workgroup劃分:

我們設定圖像的寬度是bins的整數倍,即256的倍數,高度是workgroup size(本程序中,設置為128)的倍數,如果圖像高寬不是bins和workgroup size的倍數,則我們通過下面的公式把圖像的寬度和高度變成它們的倍數:

//width是binSize的整數倍,height是groupsize的整數倍

width = (width / binSize ? width / binSize: 1) * binSize;

height = (height / groupSize ? height / groupSize: 1) * groupSize;

則512*512的圖像可以分為8個work group,每個workgroup包括128個thread,每個thread計算256個像素的直方圖,並把結果放到該thread對應的local memroy空間,在kenrel代碼結束前,合併一個workgroup中所有thread的直方圖,生成一個workgroup塊的直方圖,最後在host端,合併8個workgroup塊的直方圖,產生最終的直方圖。

OpenCL的memory對象主要有3個,dataBuffer用來傳入圖像數據,而minDeviceBinBuf大小是workgroup number *256, 即每個workgroup對應一個bin,另外一個kernel函數的第二個參數,它的大小為workgroup size*256, 用於workgroup中的每個線程存放自己256個像素的直方圖結果。

//創建2個OpenCL內存對象

dataBuf = clCreateBuffer(

    context,

    CL_MEM_READ_ONLY,

    sizeof(cl_uchar) * width  * height,

    NULL,

    0);

//該對象存放每個block塊的直方圖結果

midDeviceBinBuf = clCreateBuffer(

    context,

    CL_MEM_WRITE_ONLY,

    sizeof(cl_uint) * binSize * subHistgCnt,

    NULL,

    0);

   …

    status = clSetKernelArg(kernel, 1, groupSize * binSize * sizeof(cl_uchar), NULL); //local memroy size, lds for amd

下面看看kernel代碼是如何計算workgroup塊的直方圖。

__kernel

void histogram256(__global const uchar* data,

                  __local uchar* sharedArray,

                  __global uint* binResult)

{

    size_t localId = get_local_id(0);

    size_t globalId = get_global_id(0);

    size_t groupId = get_group_id(0);

    size_t groupSize = get_local_size(0);

下面這部分代碼初始化每個thread對應的local memory,也就是對應的256個bin中計數清零。sharedArray大小是workgroup size * 256 = 128 * 256

//初始化共享內存

    for(int i = 0; i < BIN_SIZE; ++i)

        sharedArray[localId * BIN_SIZE + i] = 0;

通過barrier設置workgroup中所有thread的同步點,保證所有thread都完成初始化操作。

    barrier(CLK_LOCAL_MEM_FENCE);

下面的代碼,計算thread中,256個像素的直方圖,比如對於workgroup 0中的thread 0它計算的256個像素為綠條的部分像素,注意:每個thread的包含的像素並不是連續的

    

//計算thread直方圖

    for(int i = 0; i < BIN_SIZE; ++i)

    {

        uint value = (uint)data[groupId * groupSize * BIN_SIZE + i * groupSize + localId];

        sharedArray[localId * BIN_SIZE + value]++;

    }

通過fence,保證每個thread都完成各自的直方圖計算。

    barrier(CLK_LOCAL_MEM_FENCE); 

下面是合併各個thread的直方圖形成整個workgroup像素塊的直方圖,每個thread合併2個bin,比如thread 0,合併bin0和bin128。

   //合併workgroup中所有線程的直方圖,產生workgroup直方圖

    for(int i = 0; i < BIN_SIZE / groupSize; ++i)

    {

        uint binCount = 0;

        for(int j = 0; j < groupSize; ++j)

            binCount += sharedArray[j * BIN_SIZE + i * groupSize + localId];

        binResult[groupId * BIN_SIZE + i * groupSize + localId] = binCount;

    }

}

最終在host端,我們還要把每個workgroup塊的直方圖合併成得到整個圖像的直方圖,主要代碼如下:

// 合併子塊直方圖值

for(i = 0; i < subHistgCnt; ++i)

    {

    for( j = 0; j < binSize; ++j)

        {

        deviceBin[j] += midDeviceBin[i * binSize + j];

        }

    }

完整的代碼請參考:

工程文件gclTutorial7

代碼下載:http://files.cnblogs.com/mikewolf2002/gclTutorial.zip

原文作者:邁克老狼

相關焦點

  • Step on your toes?
    It can bring us as much pain as if they really step on our toes while queuing up for a ticket in the cinema.
  • StepChain 0.0.7 發布,Java 通用業務處理框架
    0.0.7更新日誌:1、新增支持Processor定時調度FixedRate、FixedDelay。2、修復0.0.6版本bug.
  • 專家論壇|從「step-up」到「step-jump」—— 感染壞死性胰腺炎「跨階梯」治療
    參考文獻(在框內滑動手指即可瀏覽)[1]    van Santvoort HC, Besselink MG, Bakker OJ, et al. A step-up approach or open necrosectomy for necrotizing pancreatitis[J].
  • 對於自然辯證法的逐步思考(step by step,基於對生命的新理解)
    對於自然辯證法的逐步思考(step by step,基於對生命的理解)自然辯證法≈物質辯證法+意識辯證法自然辯證法≈物質辯證法+意識辯證法*i自然≈物質+意識*i
  • 圖像學習之如何理解方向梯度直方圖(Histogram Of Gradient)
    第一步:預處理Patch可以是任意的尺寸,但是有一個固定的比例,比如當patch長寬比1:2,那patch大小可以是100*200, 128*256或者1000*2000但不可以是101*205。這裡有張圖是720*475的,我們選100*200大小的patch來計算HOG特徵,把這個patch從圖片裡面摳出來,然後再把大小調整成64*128。
  • 剖析Trancestep創始人Au5巔峰之作
    圖片來自XING KONG 圖片版權屬於原主 2013年,Au5以《Blossom》作為成名曲,為Trancestep這個他自己特色的標籤在早期就設立了極高的標準
  • OpenCV-Python 直方圖-1:查找、繪製和分析|二十六
    它是我們計算直方圖的通道的索引。例如,如果輸入為灰度圖像,則其值為[0]。對於彩色圖像,您可以傳遞[0],[1]或[2]分別計算藍色,綠色或紅色通道的直方圖。mask:圖像掩碼。為了找到完整圖像的直方圖,將其指定為「無」。但是,如果要查找圖像特定區域的直方圖,則必須為此創建一個掩碼圖像並將其作為掩碼。(我將在後面顯示一個示例。)
  • 程式設計師1小時完成深度學習Resnet,谷歌tensorflow多次圖像大賽冠軍
    閱前須知:為了使本文結構精簡,理解簡單,所以會儘量少涉及到有關數學公式,降低學習門檻,帶領讀者快速搭建ResNet-34經典模型並投入訓練。,且相較同深度的plain而言精度更高在以往的學習之中,我們知道深度網絡隨著層數的增加,很容易造成「退化」和「梯度消失」的問題,訓練數據的過擬合。
  • Python學習筆記:隨機漫步圖和三翼面圖
    ( x_distance ) x_distance=choice([0,1,2,3,4]) x_step=x_direction*x_distance y_direction = choice([1, -1]) y_distance = choice([0, 1, 2, 3, 4]) y_step
  • 手把手 | OpenAI開發可拓展元學習算法Reptile,能快速學習
    其性能可以和MAML(model-agnostic meta-learning,由伯克利AI研究所研發的一種應用廣泛的元學習算法)相媲美,操作簡便且計算效率更高。下面是應用了Reptile算法的單樣本分類(1-shot classification)的互動演示,大家可以嘗試一下。嘗試單擊「Edit All」按鈕,繪製三個不同的形狀或符號,然後在右側的輸入區中繪製其中一個,並查看Reptile如何對它進行分類。前三張圖是標記樣本,每圖定義一個類別。
  • 阿迪達斯 Adidas Drop Step 校園復古板鞋 男鞋休閒鞋
    鞋老闆「kafion」帶大家看看板鞋 運動品牌 adidas Originals 日前公布了一款名為 Dropstep 的休閒鞋,並帶來這款全新配色。
  • 圖論與圖學習(一):圖的基本概念
    近日,數據科學家 Mal Fabien 在其博客上發布了涉及圖論、圖算法和圖學習的系列文章《圖論與圖學習》。本文是其中第一篇,介紹了圖的一些基礎知識並給出了 Python 示例。更多文章和對應代碼可訪問:https://github.com/maelfabien/Machine_Learning_Tutorials。本文涵蓋以下主題:圖是什麼?如何存儲圖?
  • 視頻處理之灰度圖
    灰度圖概念灰度圖 ,Gray Scale Image 或是Grey Scale Image,又稱灰階圖。
  • 最簡單的深度學習TensorFlow應用舉例!
    小編我的電腦很一般,沒有32G內存,也沒有1080,就windows上直接裝了23333windows+python 3.6+pycharm+tensorflow cpu話不多說,直接線性回歸,上圖。代碼截圖#接下來貼代碼#辰星樹洞import numpy as np #這是Python的一種開源的數值計算擴展
  • OpenAI提出Reptile:可擴展的元學習算法
    元學習是學習如何學習的過程。元學習算法會學習任務的一個分布,每項任務都是學習問題,並輸出快速學習器,學習器可從少量樣本中學習並進行泛化。一個得到充分研究的元學習問題是few-shot分類,其中每項任務都是分類問題,學習器只能看到1-5個輸入 - 輸出樣本(每個類別),之後學習器必須對新輸入進行分類。