一、前言
當用照像機拍攝一副黑紙白字的紙張時,照相機獲得的圖像並不是真正的黑白圖像。不管從什么角度拍攝,這幅圖像實際上是灰度或者彩色的。除非仔細的設置燈光,否則照相機所拍攝的放在桌子上的紙張圖像並不能代表原始效果。不像在掃描儀或打印機內部,想控制好桌子表面的光源是非常困難的。這個開放的空間可能會受到台燈、吊燈、窗戶、移動的影子等影響。人類的視覺系統能自動補償這些,但是機器沒有考慮到這些因素因此拍出的效果會很差。
計算機視覺中攝像頭會產生一副具有不同等級的灰度圖像,但在許多應用都必須清楚的知道圖像的那一部分是純黑或純白,以便將文字傳遞給OCR軟件去識別,選擇復制操作,或多個圖像合成,系統就不可以使用抖動的圖像,系統僅僅需要簡單的線條、文字或相對大塊的黑色和白色。從灰度圖像獲得這種黑白圖像的過程通常稱作為閾值化。
本文描述了一種局部二值化圖像,在設置鄰域窗體的基礎上,獲取此鄰域的閾值,針對不同區域設置不同閾值,但此算法效率不是很快,還應改善,本文算法原理來源於參考文獻。
二、二值化原理
閾值化圖像其實就是對灰度圖像進行二值化操作,根本原理是利用設定的閾值判斷圖像像素為0還是255,所以在圖像二值化中閾值的設置很重要。圖像的二值化分為全局二值化和局部二值化,其區別在於閾值是否在一張圖像進行統一。
2.1 全局化閾值
全局閾值法方法就是將圖像中低於某個閾值的像素設置為黑色,而其他的設置為白色。常見的算法就是選擇所有可能取值的中間值,因此對於8位深的圖像(范圍從0到255),128將會被選中。這個方法在圖像黑色像素確實在128以下,而白色也在128以上時工作的很好。但是如果圖像過或欠曝光,圖像可能全白或全黑。基本思路為首先找到圖像中所有像素的最大值和最小值,然后取中點作為閾值。一個更好的選擇閾值的方法是不僅查看圖像實際的范圍,還要看其分布。比如說,你希望圖像類似於一副黑色線條畫,或者在白紙上的文字效果,那么你就期望大部分像素是背景顏色,而少部分是黑色。一副像素的直方圖可能如圖1所示。
圖1
上圖中,可以發現一個背景顏色的大峰值,以及一個黑色墨水的小的峰值。根據周圍的光線整個曲線可想向左或者向右偏移,但是在任何情況下,最理想的閾值就是在兩個峰值之間的波谷處。這在理論上很好,但是他在實際中到底表現如何呢。
圖 2
圖2及其直方圖顯示整個技術工作的很好。平滑后的直方圖顯示出2個潛在的峰值,通過擬合直方圖曲線或簡單的取兩個峰值之間的平均值來計算出一個近似理想閾值並不是一件困難的事情。這不是一個典型的圖像,因為他有大量的黑色和白色的像素點。算法必須還要對類似圖3這樣的圖像進行閾值處理。這這幅圖像的直方圖中,較小的黑色峰值已經掩埋在噪音中,因此要可靠地在峰值之間確定哈一個最小值是不太可能的。
圖 3
在任何情況下,一個大的(背景)峰值總是存在的並且也容易找到,因此,一個有用的閾值策略可描述如下:
1) 計算直方圖。
2) 按照一定的半徑對直方圖數據進行平滑,並計算平滑后數據的最大值。平滑的目的減少噪音對最大值的影響,如圖2和圖3所示。
3) 根據上述峰值和最小值(不包括在直方圖中為0的項)的距離按照一定的比例選擇閾值。
試驗表明這個距離的一半能夠對很大范圍內的圖像產生相當好的效果,從非常亮到幾乎完全黑的圖像。比如,在圖3中,峰值在215處,而最小值為75,因此可以使用的閾值為145。圖4是四副在不同的光照條件下抓取的圖像以及根據上述基於直方圖技術閾值處理后的效果。盡管私服圖像有這較廣的光照范圍(可以從直方圖中看出),該算法都選擇了較為合適的閾值,而閾值處理后的圖像基本一樣。
圖 4
這個基於直方圖的全局閾值技術對於如上面所舉的那些光線條件均勻或那些光線變化不多的部分圖像處理的很好。但是對於在正常辦公室光照條件下他無法獲得滿意的結果。因為對整個圖像使用一個相同的閾值,圖像的部分區域變得太白而其他地區又太黑。因此大部分文字變得不可讀,如圖5所示。
圖 5
從光照不均勻的紙張圖像中產生較好的二值化圖像需要一種自適應的閾值算法。這個技術根據每個像素的背景亮度來改變閾值。下面的討論都配以圖5先顯示新算法的效果。這是一個具有挑戰性的測試,因為圖像邊緣有光源,並且其在白色背景上有黑色文字(PaperWorks整個詞,以及黑色背景中的白色文字(“XEROX”),還有白色背景中的灰色文字(”The best way。。。”)還有不同的陰影和一個在單詞“PaperWorks”下很細小的水平黑色線。
2.2 自適應閾值
一個理想的自適應閾值算法應該能夠對光照不均勻的圖像產生類似上述全局閾值算法對光照均勻圖像產生的效果一樣好。 為了補償或多或少的照明,每個像素的亮度需要正規化,之后才能決定某個像素時黑色還是白色。問題是如何決定每個點的背景亮度。一個簡單的方式就是在拍攝需要二值圖片之前先拍一張空白的頁面。這個空白的頁面可以當做參考圖像。對於每一個要處理的像素,在處理前對應的參考圖像像素都將從其中減去。
只要在參考圖像和實際要處理的圖像拍攝時光照條件沒有發生任何變化,這個方法能產生非常好的效果,但是,光照條件會收到人、台燈或其他移動物體的影子的影響。如果房間有窗戶,那么光照條件還會隨着時間變化而改變。通過一些關於圖像實際該是什么樣的假設來估計每個像素的背景亮度。Haralick 和 Shapiro提出了以下建議:區域需和灰度調統一;區域內部應該簡單,沒有過多的小孔;相鄰的區域應該有顯著的不同值;每個部分的邊緣也應該簡單,不應凹凸不平,其空間上要准確。
根據Pratt的理論,對於圖像二值化,還沒有任何量化性能指標提出過。似乎主要評價算法性能的方式就是簡單看看結果然后判斷其是否很好。對於文字圖像,有一個可行的量化辦法:不同光照條件下的圖片使用不同的二值化算法處理的后的結果被送往OCR系統,然后將OCR識別的結果和原文字比較。雖然該法可能有用,但是他不能用在以下描述的算法中,因為不可能給出一個看起來好的標准。對於一些交互式的應用,比如復制黏貼操作用戶必須等到二值的處理。因此另外一個重要的指標就是速度。以下部分提出了不同的自適應閾值算法已經他們產生的結果。
三、基於Wall算法的自適應閾值
R. J. Wall開發的根據背景亮度動態計算閾值的算法描述可見《Castleman, K. Digital Image Processing. Prentice-Hall Signal Pro-cessing Series, 1979.》 。以下描述基本是按照其論文的。首先,將圖像分成較小的塊,然后分別計算每塊的直方圖。根據每個直方圖的峰值,然后為每個塊計算其閾值。然后,每個像素點的閾值根據相鄰的塊的閾值進行插值獲得。
3.1 算法原理
算法基本的細想就是遍歷圖像,計算一個移動的平均值。如果某個像素明顯的低於這個平均值,則設置為黑色,否則設置為白色。僅需一個遍歷就夠了,用硬件去實現算法也很簡答。注意到下面的算法和IBM 1968年用硬件實現的算法的相似性是比較有趣的。
假設Pn為圖像中位於點n處的像素。此刻我們假設圖像是由所有行按順序連接起來的一個單行。這導致了在每行開始的時候會產生一些異常,但這個異常要比每行都從零開始要小。
假設fs(n)是點n處最后 s個像素的總和:
最后的圖像T(n)是1(黑色)或0(白色)則依賴於其是否比其前s個像素的平均值的百分之t的暗。
對於s使用圖像的1/8寬而t取值15似乎對不同的圖像都能產生較好的效果。圖8顯示了使用該算法從左到右掃描行的結果。
圖9是使用相同算法從右到左處理的結果,注意在這個圖像中,最左側的較小的文字是不完整的。在字符PaperWorks中也有更多的孔洞。同樣,最右側的黑色邊緣也窄很多。這主要是由於圖像的背景光源是從左到右逐漸變黑的。從上面的東西來看,Wellner 自適應濾波閾值實際上就是對像素做指定半徑的一維平滑,然后原像素和平滑后的值做比較來決定黑還是白。文章中很大一部分篇幅都是在討論取樣的那些像素的方向問題,是完全在左側、完全在右側還是左右對稱,抑或是考慮到前一行的效果。 但是,總的來看,他只考慮到了行方向上的像素對平滑的影響。之后,Derek Bradley和Gerhard Roth 在他們的論文Adaptive Thresholding Using the Integral Image 中 提出了以W*W為模板的矩形區域的二維平滑值來代替一維加權值。從而拋開了一維平滑的方向性問題。
3.2 算法源碼
下文是根據算法原理利用c++實現的功能,其中結合opencv的積分圖像,可以快速獲取某一矩形區域像素和。
void thresholdIntegral(cv::Mat &inputMat, cv::Mat &outputMat) { // accept only char type matrices CV_Assert(!inputMat.empty()); CV_Assert(inputMat.depth() == CV_8U); CV_Assert(inputMat.channels() == 1); CV_Assert(!outputMat.empty()); CV_Assert(outputMat.depth() == CV_8U); CV_Assert(outputMat.channels() == 1); // rows -> height -> y int nRows = inputMat.rows; // cols -> width -> x int nCols = inputMat.cols; // create the integral image cv::Mat sumMat; cv::integral(inputMat, sumMat); CV_Assert(sumMat.depth() == CV_32S); CV_Assert(sizeof(int) == 4); int S = MAX(nRows, nCols)/8; double T = 0.15; // perform thresholding int s2 = S/2; int x1, y1, x2, y2, count, sum; // CV_Assert(sizeof(int) == 4); int *p_y1, *p_y2; uchar *p_inputMat, *p_outputMat; for( int i = 0; i < nRows; ++i) { y1 = i-s2; y2 = i+s2; if (y1 < 0){ y1 = 0; } if (y2 >= nRows) { y2 = nRows-1; } p_y1 = sumMat.ptr<int>(y1); p_y2 = sumMat.ptr<int>(y2); p_inputMat = inputMat.ptr<uchar>(i); p_outputMat = outputMat.ptr<uchar>(i); for ( int j = 0; j < nCols; ++j) { // set the SxS region x1 = j-s2; x2 = j+s2; if (x1 < 0) { x1 = 0; } if (x2 >= nCols) { x2 = nCols-1; } count = (x2-x1)*(y2-y1); // I(x,y)=s(x2,y2)-s(x1,y2)-s(x2,y1)+s(x1,x1) sum = p_y2[x2] - p_y1[x2] - p_y2[x1] + p_y1[x1]; if ((int)(p_inputMat[j] * count) < (int)(sum*(1.0-T))) p_outputMat[j] = 255; else p_outputMat[j] = 0; } } }
本文是利用python的opencv進行是極限,當然最后也是利用python相關代碼進行執行,但是代碼執行效率有點滿,效果還是可以的。
def adaptiveThreshold(filename, sub_thresh = 0.15): image = cv2.imread(filename) gray_image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) # 計算積分圖像 integralimage = cv2.integral(gray_image, cv2.CV_32F) width = gray_image.shape[1] height = gray_image.shape[0] win_length = int(width / 10) image_thresh = np.zeros((height, width, 1), dtype = np.uint8) # perform threshholding for j in range(height): for i in range(width): x1 = i - win_length x2 = i + win_length y1 = j - win_length y2 = j + win_length # check the border if(x1 < 0): x1 = 0 if(y1 < 0): y1 = 0 if(x2 > width): x2 = width -1 if(y2 > height): y2 = height -1 count = (x2- x1) * (y2 - y1) # I(x,y) = s(x2,y2) - s(x1,y2) - s(x2, y1) + s(x1, y1) sum = integralimage[y2, x2] - integralimage[y1, x2] -integralimage[y2, x1] -integralimage[y1, x1] if (int)(gray_image[j, i] * count) < (int) (sum * (1.0 - sub_thresh)): image_thresh[j, i] = 0 else: image_thresh[j, i] = 255 return image_thresh
上面三張圖片第一張是原圖片,第二張利用自適應閾值算法,第三章利用opencv的adaptiveThreshold算法,可以明顯發現開發的算法有效的一直噪聲,對圖像進行有效的二值化操作。
參考文檔:http://http://www.ppgia.pucpr.br/~facon/Binarizacao/1993thresholdAdaptativeWellner.pdf
http://files.cnblogs.com/files/Imageshop/AdaptiveThresholdingUsingtheIntegralImage.pdf













