關於我自己

我的相片
累計超過15年的工作經驗,包括10年的設備製造業經驗,超過6年的光電零組件製造業經驗;10年的海外工作經驗,其中至今有5年以上的派駐經驗。 1.專注AOI檢測於10年以上(從光機設計、圖像分析、設備挑選、處理速度) 2.機器學習推論-onnx整合傳統演算法從瑕疵抓取、分析、分類 3.遷移式機器學習模式(工業應用、醫療應用) 4.整合:Python推論引擎、C++運算能力、C#友善介面 彈性使用 5.擅長利用專家模型推進專案,並熟悉使用OpenVINO、TensorRT、ONNXRUNTIME框架進行有效的實作與測試。

2011年10月7日 星期五

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

http://blog.csdn.net/cay22/article/details/5546729

在手機模式識別的時候我們首先viewfinder裡面拿到的frame通常是RGB的或者YUV如果我們需要用來做模式識別的話通常需要首先把彩色圖首先轉化成灰度圖對於RGB圖像而言網上有充足的公式比如Y = 0.299R + 0.587G + 0.114B 等等如果是YUV的話直接用Y就是灰度圖了順帶說一句這種灰度圖通常我們用.raw文件來表示photoshop或者irfanview是可以直接打開看效果的比如說這裡就有一個灰度圖的例子
 這個圖就是現在很流行的所謂Data Matrixsample, 我們用手機的照相機拿到的灰度圖現在我們要把它變化成為黑白圖(二值圖). 在網上廣為流傳著很多辦法什麼雙峰法, 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)來表示二值化後的值

f­s (n) 來表示第n個點之前s個點的灰度值的和就是

 


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

 
而且根據經驗值來看這裡的st最佳的取值范圍是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) f­s (n) 的區別在於f­s (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之間的中間值)就可以開始實現算法了

view plaincopy to clipboardprint?
void quickAdaptiveThreshold(unsigned char* grayscale, unsigned char*& thres, int width, int height )  
{  
      
    /**           / 
    *            | FOREGROUND, if pn < ((gs(n) + gs(n-w)) / (2*s)) * 
    * color(n) = |                     ((100-t)/100) 
    *            | BACKGROUND_QR, otherwise 
    *            / 
    * where pn = gray value of current pixel, 
    *        s = width of moving average, and 
    *        t = threshold percentage of brightness range 
    *    gs(n) = gs(n-1) * (1-1/s) + pn 
    *    gs(n-w) = gs-value of pixel above current pixel 
    * 
    */ 
    int t = 15;   
    int s = width >> 3; // s: number of pixels in the moving average (w = image width)  
    const int S = 9; // integer shift, needed to avoid floating point operations  
    const int power2S = 1 << S;  
    // for speedup: multiply all values by 2^s, and use integers instead of floats  
    int factor = power2S * (100-t) / (100*s); // multiplicand for threshold  
    int gn = 127 * s; // initial value of the moving average (127 = average gray value)  
    int q = power2S - power2S / s; // constant needed for average computation  
    int pn, hn;  
    unsigned char *scanline = NULL;  
    int *prev_gn = NULL;  
          
    prev_gn = new int[width];  
    for (int i = 0; i < width; i++) {  
        prev_gn[i] = gn;   
    }  
    thres = new unsigned char[width*height];  
    for (int y = 0; y < height; y ++ )  
    {  
        int yh = y * width;  
        scanline = grayscale + y * width;  
        for ( int x = 0; x   
        {  
            pn = scanline[x] ;  
            gn = ((gn * q) >> S) + pn;   
            hn = (gn + prev_gn[x]) >> 1;  
            prev_gn[x] = gn;          
            pn < (hn*factor) >> S ? thres[yh+x] = 0 : thres[yh+x] = 0xff;  
        }  
        y ++ ;  
        if ( y == height)  
            break;  
        yh = y * width;  
        scanline = grayscale + y * width;  
        for ( int x = width-1; x >= 0; x --)  
        {  
            pn = scanline[x] ;  
            gn = ((gn * q) >> S) + pn;   
            hn = (gn + prev_gn[x]) >> 1;  
            prev_gn[x] = gn;          
            pn < (hn*factor) >> S ? thres[yh+x] = 0 : thres[yh+x] = 0xff;  
        }  
    }  
    delete prev_gn;  
} 
這個算法也不是我發明創造的這個算法從
這個網址上看過來不過是去除了一些Symbian的痕跡還有有的細節上做了一些改進讓代碼更加合理了些經過這個算法我們可以來看看效果了
    
原圖1:                                                           二值圖1:

  
原圖2:                                                                            二值圖2:

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/hhygcy/archive/2009/06/12/4264857.aspx

沒有留言: