最近一直在用FPGA調(diào)圖像處理相關(guān)的算法,主要是集中在圖像增強(qiáng)和增晰方面。
現(xiàn)在來介紹一個(gè)復(fù)雜度不高,但確實(shí)也還比較好用的圖像增強(qiáng)算法-直方圖均衡。
直方圖均衡的作用,上面也說了,是圖像增強(qiáng),那是增強(qiáng)什么呢?是增強(qiáng)圖像的對(duì)比度。
先來看下在Matlab中實(shí)現(xiàn)直方圖均衡的效果吧。
圖片比較隨意,直接對(duì)電腦桌面截了一張圖,大家應(yīng)該能有一個(gè)直觀的感受,知道直方圖均衡的作用和效果了吧! ? ?
直方圖均衡在對(duì)一些低照度圖像增晰、去霧處理等應(yīng)用場(chǎng)景,效果還是挺不錯(cuò)的。
對(duì)于直方圖均衡的實(shí)現(xiàn)步驟,在網(wǎng)上一搜一大把,大部分講的都是用Matlab或者C語言的代碼實(shí)現(xiàn)。 ?
那我們也先來結(jié)合Matlab代碼來介紹一下直方圖均衡的實(shí)現(xiàn)步驟,之后再說說用FPGA是怎么做的。
?
% [ 第一步 ] 統(tǒng)計(jì)每個(gè)像素值出現(xiàn)次數(shù) count = zeros(1, 256); for i = 1 : R for j = 1 : C count(1, fx(i, j) + 1) = count(1, fx(i, j) + 1) + 1; end end第一步,是對(duì)于一整幅圖像進(jìn)行像素值統(tǒng)計(jì),當(dāng)然咱們這是對(duì)灰度圖像進(jìn)行直方圖均衡,所以統(tǒng)計(jì)的像素值大小范圍是 0-255。 ?
這一步是統(tǒng)計(jì) 0-255 這 256 個(gè)像素點(diǎn)的值,在整幅圖像中出現(xiàn)的次數(shù)。
% [ 第二步 ] 統(tǒng)計(jì)每個(gè)像素值出現(xiàn)的概率, 得到概率直方圖 T = zeros(1, 256); T = double(T); count = double(count); for i = 1 : 256 ????T(1,?i)?=?count(1,?i)?/?(R?*?C);?%?R?和?C為圖像長(zhǎng)和寬 end? 第二步,將像素點(diǎn)統(tǒng)計(jì)出來的出現(xiàn)次數(shù)除以圖像的總像素點(diǎn),計(jì)算出各個(gè)灰度值出現(xiàn)的概率。
?
?
% [ 第三步 ] 求累計(jì)概率,得到累計(jì)直方圖 for i = 2 : 256 T(1, i) = T(1, i - 1) + T(1, i); end第三步,單看代碼,似乎不太好理解,這一步算的累計(jì)概率是怎么算的呢?
?
第 1 個(gè)結(jié)果,像素值為 0 的概率 + 像素值為 1 的概率; 第 2 個(gè)結(jié)果,像素值為 0 的概率 +像素值為 1 的概率 + 像素值為 2 的概率; 。。。 第 255 個(gè)結(jié)果,像素值為 0 的概率 +像素值為 1 的概率 + 像素值為 2 的概率 + ... + 像素值為 255 的概率。其實(shí)就是等于 1 。
其實(shí)這一步,總共會(huì)得出 256 個(gè)結(jié)果,還要算上像素值為 0 的概率,因?yàn)樵诘诙街幸呀?jīng)算出來了單個(gè)像素點(diǎn)的概率,所以就沒有再次算了, for 循環(huán)里面寫的就是 2 - 256 了。
?
% [ 第四步 ] 完善映射函數(shù) for i = 1 : 256 T(1, i) = T(1, i) * 255; end
?
第四步,是完善映射函數(shù)。
從原圖到直方圖均衡化之后的圖像,之間肯定存在一個(gè)函數(shù) f(x),類似的 img_out = f(img_in)?。 ?
也就是從原圖進(jìn)行直方圖均衡需要有一個(gè)函數(shù)表示他們之間的關(guān)系。
而這一步就是完善這個(gè)函數(shù),其實(shí)就是對(duì)第三步得出的累計(jì)概率分布結(jié)果 * 255 。 ? ?
注意,這個(gè)映射函數(shù)其實(shí)是一個(gè)離散值,總共有 256 個(gè)值。
?
% [ 第五步 ] 完成每個(gè)像素點(diǎn)的映射 fy = double(fx); for i = 1 : R for j = 1 : C fy(i, j) = T(1, fy(i, j) + 1); end end
?
這是最后一步了,把原圖經(jīng)過第四步的映射函數(shù),得到直方圖均衡化之后的圖像結(jié)果。
這一步,給大家再解釋一下,其實(shí)就是把輸入圖像的像素值,作為映射函數(shù)這個(gè)一維數(shù)組相應(yīng)元素的下標(biāo),去找到對(duì)應(yīng)下標(biāo)元素的值。并把這個(gè)值作為直方圖均衡化的結(jié)果輸出。
對(duì)于直方圖均衡的處理,從流程上來看,其實(shí)是很簡(jiǎn)單的。 ?
當(dāng)然對(duì)于有些書,會(huì)給你列出一堆的公式。作為一個(gè)畢業(yè)多年的老油條,對(duì)那些公式早就不敏感了。 ?
所以在文章里面,就直接開門見山的給大家說步驟了,這也算是對(duì)那些也像我這種對(duì)數(shù)學(xué)公式早已不感冒的朋友的一份愛惜吧!
對(duì)于原理部分,貼著 Matlab 代碼說完了,那就是再說說在FPGA里面的實(shí)現(xiàn)步驟吧。
用FPGA來實(shí)現(xiàn)直方圖均衡,我把上面的步驟稍微做了一點(diǎn)改變。 ?
第一步,依然統(tǒng)計(jì)0-255這256個(gè)像素點(diǎn)出現(xiàn)的次數(shù)。
第二步,沒有計(jì)算各個(gè)像素點(diǎn)出現(xiàn)的概率,而是統(tǒng)計(jì)各個(gè)像素點(diǎn)出現(xiàn)的累計(jì)次數(shù)。
也是得到 256 個(gè)結(jié)果: 第 1 個(gè)結(jié)果,像素點(diǎn)為0出現(xiàn)的次數(shù); 第 2 個(gè)結(jié)果,像素點(diǎn)為 0 出現(xiàn)的次數(shù) + 像素點(diǎn)為1出現(xiàn)的次數(shù); 第 3 個(gè)結(jié)果,像素點(diǎn)為?0 出現(xiàn)的次數(shù) + 像素點(diǎn)為1出現(xiàn)的次數(shù) + 素點(diǎn)為2出現(xiàn)的次數(shù);
依次類推。
第三步,將第二步的結(jié)果 *255*2,就是比剛剛在 Matlab 中,多乘上了 2 ,這樣做的目的,是因?yàn)樵贔PGA里面沒有進(jìn)行小數(shù)運(yùn)算,所以先乘上 2 ,?為后面的運(yùn)算稍稍提高一下精度。
第四步,將第三步的結(jié)果除以整幅圖片的像素點(diǎn)個(gè)數(shù)。
第五步,完成直方圖均衡對(duì)輸入像素點(diǎn)值的映射。 ?
這里要注意,因?yàn)閿z像頭那邊的數(shù)據(jù)是連續(xù)過來的,如果像 Matlab 那樣,先計(jì)算好當(dāng)前圖像的映射函數(shù),再把當(dāng)前圖像作為輸入,給到映射函數(shù)再得出直方圖均衡的結(jié)果,這個(gè)時(shí)間是等不起的。 ?
所以這里我進(jìn)行了一定的簡(jiǎn)化,我是以前一幀圖像完善的映射函數(shù)作為當(dāng)前幀圖像的映射函數(shù)。 ?
這種做法,其實(shí)是完全可行的,咱們攝像頭圖像的幀速率一般是 30fps ,也就是在 1 秒鐘里面可以有 30 幅圖像,只要攝像頭采集的畫面變化速度在一定范圍里面,咱們這樣做是完全 OK 的,而且我用的攝像頭是MT9V034,是全局曝光的。
是的,這就是我這幾天剛剛組裝好的 MT9V034 攝像頭,打樣了幾片PCB,裝上了 CS 大鏡頭,OV5640 瞬間 變身小弟,哈哈?。?!感覺綠色的板子看著有點(diǎn)low,之后再換成黑色阻焊看看!??
對(duì)于圖像的增強(qiáng)效果還是很明顯的。
在調(diào)試直方圖均衡這個(gè)算法的時(shí)候,剛開始調(diào)的很郁悶。
完全搞不明白為啥,極其郁悶。
反復(fù)檢查了代碼,也跑了仿真,沒發(fā)現(xiàn)問題啊。?
難不成直方圖均衡后的效果就是這個(gè)鬼樣子。然后也在網(wǎng)上搜,看看有沒有用FPGA做直方圖均衡后的效果圖,來比較一下。
搜了半天,找到的全都是用Matlab 或者其他什么軟件語言做的效果圖,我要這些效果圖干啥,勞資這邊多的是!
之后又在知網(wǎng)下了好幾篇FPGA的直方圖均衡的論文,想看看論文里面有沒有效果圖,大多又是Matlab的效果圖,無語。。。
郁悶了一晚上,到第二天上午,把腦袋放空,重新歸零?;貞涍@種現(xiàn)象,想起來在之前調(diào)的一個(gè)圖像增強(qiáng)算法也出現(xiàn)了類似的效果。 ?
一時(shí)記不起來當(dāng)時(shí)是怎么解決的,非常后悔,要是當(dāng)時(shí)寫了一份調(diào)試記錄該多好。劃重點(diǎn),寫調(diào)試記錄,是非常重要的!
想了很久,當(dāng)時(shí)出現(xiàn)這種情況,似乎是VGA顯示的數(shù)據(jù),對(duì)要顯示的數(shù)據(jù)源截位有問題。 ?
舉個(gè)例子,VGA要顯示灰度圖像的8個(gè)bit,但是你要給VGA顯示的圖像數(shù)據(jù)有16個(gè)bit,理應(yīng)是把16bit的高8bit給VGA顯示,但是卻把[12:5]?這8個(gè)bit 給了VGA顯示。
但是這個(gè)問題,是在哪個(gè)地方出現(xiàn)了截位的問題呢?又想了很久,懷疑是除法器的IP Core。
除法器的dout_tdata,這個(gè)端口,包含了商的結(jié)果和小數(shù)部分,那商的結(jié)果也就是整數(shù)部分占多少bit,小數(shù)部分占多少bit?
在代碼里面,在對(duì)div_dout這個(gè)信號(hào)去把商的結(jié)果拿出來,在這個(gè)環(huán)節(jié)出現(xiàn)了截位的問題。果真,改了之后,結(jié)果馬上正常了。
審核編輯:劉清
評(píng)論
查看更多