一種快速自適應的映像二值化方法介紹 (Wellner 1993)

來源:互聯網
上載者:User

在手機模式識別的時候, 我們首先viewfinder裡面拿到的frame通常是RGB的或者YUV的, 如果我們需要用來做模式識別的話, 通常需要首先把彩色圖首先轉化成灰階圖. 對於RGB映像而言, 網上有充足的公式, 比如Y = 0.299R + 0.587G + 0.114B 等等. 如果是YUV的話, 直接用Y就是灰階圖了. 順帶說一句, 這種灰階圖通常我們用.raw檔案來表示, 用photoshop或者irfanview是可以直接開啟看效果的. 比如說這裡就有一個灰階圖的例子

這個圖就是現在很流行的所謂Data Matrix的sample, 我們用手機的照相機拿到的灰階圖. 現在我們要把它變化成為黑白圖(二值圖). 在網上廣為流傳著很多辦法. 什麼雙峰法, P參數法等等. 今天的辦法和這些都不相同. 這個方法就是被稱之為Quick Adaptive Thresholding algorithm, 提出這個觀點的人名字叫做Pierre D. Wellner.  這裡的網頁上就有這個演算法的說明:

http://www.xrce.xerox.com/Publications/Attachments/1993-110/EPC-1993-110.pdf

這個演算法的基本思想要確定一個像素的黑或者白, 用他周圍, 或者掃描順序上的其他點的一些平均值來評估閥值就可以了.用閥值和像素值比較即可. 我們現在定義出這樣的模型, 比方說我們用P(n)來表示第n個點的灰階值. T(n)來表示二值化後的值

用fs

(n)

來表示第n個點之前s個點的灰階值的和, 就是

 

用這個s和另一個變數t就可以簡單的說明P(n)應該是0還是1了, 這個公式就是

而且根據經驗值來看, 這裡的s和t最佳的取值範圍是s= image.width/8, 而t=15的時候效果最好.

好的, 到這裡為止, 我們的理解就是一個點t(n), 他是0還是1取決於什麼呢? 就是前面s個點的和除以s (就是前s個點的平均值)*0.85 如果這個點的灰階值<前面的值, 那麼就是1黑色, 如果大於就是0白色. 是不是非常簡單? 至少到現在為止確實是的.但是這個演算法有個問題, 我們忽略了一個問題, 就是我們現在定義T(n)的時候, 用的是平均值, 也就是說之前掃描過的若干點對於當前點的影響或者說權重是一樣的, 也就是說當前點1個像素距離的像素和s-1個像素點的距離的像素的灰階值對當前點的影響是一樣的. 顯然根據我們直觀的理解來看, 應該是離當前點越近的像素對當前點的影響越大, 越遠則越小. 所以演算法的作者發明了這個個更合適更高效的替代值gs

(n). 這個值的意義就是:

可以看到, 這裡的gs

(n)

和fs

(n)

的區別在於fs

(n)

直接是不做任何修正的s個灰階值的和, 而gs

(n)則是一定比例的灰階值的和, 可以看到, 離這個n越近的像素的比重越高, 越遠越低. 顯然這樣描述對把握像素的顏色更為準確. 而且這裡的

gs

(n)和

gs

(n-1)通過加法和乘法就可以遞迴得到, 計算效率是比較高的.

 

即使到了這一步了, 還有一個問題存在, 就是我現在的顏色計算其實依賴於我的掃描順序, 也就是說P(n)的這個序列的定義就是我的掃描順序(一般都是水平掃描的). 這樣的話, 我的像素值實際上取決於我水平位置上的鄰接點的灰階值, 可是豎直方向的像素如何關聯起來呢? 這裡也有一個說明, 我們可以維護前面依次水平掃描產生的g_prev(n)序列, 在某個g(n)被使用之前, 我們可以讓他和前一個g_prev(n)取一個平均值, 這樣的話, 這個最終的值就更有說服力了.

好了, 到現在為止, 我們描述了整個演算法的全過程, 在加上我們定義的初始g(n)值127*s(127表示0-255之間的中間值)就可以開始實現演算法了

void quickAdaptiveThreshold(unsigned char* grayscale, unsigned char*& thres, int width, int height )<br />{</p><p>/** /<br />* | FOREGROUND, if pn < ((gs(n) + gs(n-w)) / (2*s)) *<br />* color(n) = | ((100-t)/100)<br />* | BACKGROUND_QR, otherwise<br />* /<br />* where pn = gray value of current pixel,<br />* s = width of moving average, and<br />* t = threshold percentage of brightness range<br />* gs(n) = gs(n-1) * (1-1/s) + pn<br />* gs(n-w) = gs-value of pixel above current pixel<br />*<br />*/<br />int t = 15;<br />int s = width >> 3; // s: number of pixels in the moving average (w = image width)<br />const int S = 9; // integer shift, needed to avoid floating point operations<br />const int power2S = 1 << S;<br />// for speedup: multiply all values by 2^s, and use integers instead of floats<br />int factor = power2S * (100-t) / (100*s); // multiplicand for threshold<br />int gn = 127 * s; // initial value of the moving average (127 = average gray value)<br />int q = power2S - power2S / s; // constant needed for average computation<br />int pn, hn;<br />unsigned char *scanline = NULL;<br />int *prev_gn = NULL;</p><p>prev_gn = new int[width];<br />for (int i = 0; i < width; i++) {<br />prev_gn[i] = gn;<br />}<br />thres = new unsigned char[width*height];<br />for (int y = 0; y < height; y ++ )<br />{<br />int yh = y * width;<br />scanline = grayscale + y * width;<br />for ( int x = 0; x <width; x ++ )<br />{<br />pn = scanline[x] ;<br />gn = ((gn * q) >> S) + pn;<br />hn = (gn + prev_gn[x]) >> 1;<br />prev_gn[x] = gn;<br />pn < (hn*factor) >> S ? thres[yh+x] = 0 : thres[yh+x] = 0xff;<br />}<br />y ++ ;<br />if ( y == height)<br />break;<br />yh = y * width;<br />scanline = grayscale + y * width;<br />for ( int x = width-1; x >= 0; x --)<br />{<br />pn = scanline[x] ;<br />gn = ((gn * q) >> S) + pn;<br />hn = (gn + prev_gn[x]) >> 1;<br />prev_gn[x] = gn;<br />pn < (hn*factor) >> S ? thres[yh+x] = 0 : thres[yh+x] = 0xff;<br />}<br />}<br />delete prev_gn;<br />}

這個演算法也不是我發明創造的, 這個演算法從http://mikie.iki.fi/lxr/source/VisualCodeSystem/src/RecognitionAlgorithm.cpp?v=v3

這個網址上看過來. 不過是去除了一些Symbian的痕迹, 還有有的細節上做了一些改進, 讓代碼更加合理了些. 經過這個演算法, 我們可以來看看效果了


原圖1:                                                                       二值圖1:

 

原圖2:                                                                           二值圖2:

我對這個效果還是比較滿意的. 哈哈. 一直要寫這個文章, 今天終於寫完了, 心裡真是痛快了.

 

 

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.