【短道速滑十】從單幅影象中評估加性噪音的均方差。

2023-01-03 12:00:14

  在Halcon中有這樣一個函數:

  estimate_noise estimate_noise — Estimate the image noise from a single image.

  Signature

    estimate_noise(Image : : Method, Percent : Sigma)

  Description

  The operator estimate_noise estimates the standard deviation of additive noise within the domain of the image that is passed in Image. The standard deviation is returned in Sigma.

    The operator is useful in the following use cases:

    determination of MinContrast for matching,

    determination of the amplitude for edge filters,

    camera evaluation,

    monitoring errors in camera operation (e.g., user overdrives camera gain).

  即從單幅影象中評估影象噪音的均方差,這個運算元可以用於計算匹配時的最小對比度(發現新大陸了,原路模板匹配還可以用這個做自動化)、邊緣檢測濾波器的幅度、攝像機評估、控相機操作中的錯誤(例如使用者過度調節相機增益)。

       我覺得還可以把他作為自動去噪的一個參考指標。

  Halcon裡提供了四個評估噪音的方法:: 'foerstner', 'immerkaer', 'least_squares', 'mean',其本身最推薦的方法是immerkaer,如其幫助檔案裡所說:

  Use the method 'immerkaer', instead of the methods 'foerstner', 'least_squares', or 'mean'. The method 'immerkaer' does not rely on the existence of homogeneous image regions, and hence is almost always applicable.

  關於immerkaer方法,開放的Halcon基本上提供了完整的演演算法思路:

    'immerkaer': If Method is set to 'immerkaer', first the following filter mask is applied to the input image:  

                                                           

    The advantage of this method is that M is almost insensitive to image structure but only depends on the noise in the image. Assuming a Gaussian distributed noise, its standard deviation is finally obtained as

                                               

            where N is the number of image pixels to which M is applied. Note that the result obtained by this method is independent of the value passed in Percent.

  這個M運算元明顯就是類似一個邊緣檢測的運算元,然後把所有這個運算元的結果相加,再求某個意義下的平均值,Halcon說這個方法的好處是對影象的結構不敏感,而只完全依賴於影象的噪音本身。 

  我想有了這個提示,要實現這個功能應該就是很簡單的過程了。 

  我的一個實現如下所示:

//    模擬實現halcon的estimate_noise函數

int IM_EstimateNoise(unsigned char *Src, int Width, int Height, int Stride, float &Sigma)
{
    int Channel = Stride / Width;
    if (Src == NULL)                                return IM_STATUS_NULLREFRENCE;
    if ((Width <= 0) || (Height <= 0))                return IM_STATUS_INVALIDPARAMETER;
    if (Channel != 1)                                return IM_STATUS_NOTSUPPORTED;
    unsigned int Sum = 0;
    for (int Y = 1; Y < Height - 1; Y++)
    {
        unsigned char *LinePL = Src + (Y - 1) * Stride;
        unsigned char *LinePC = Src + (Y - 0) * Stride;
        unsigned char *LinePN = Src + (Y + 1) * Stride;
        for (int X = 1; X < Width - 1; X++)
        {
            int L = LinePL[X - 1] - 2 * LinePL[X] + LinePL[X + 1];
            int C = -2 * LinePC[X - 1] + 4 * LinePC[X] - 2 * LinePC[X + 1];
            int N = LinePN[X - 1] - 2 * LinePN[X] + LinePN[X + 1];
            Sum += IM_Abs(L + C + N);
        }
    }
    Sigma = sqrtf(IM_PI / 2) / (6 * Width * Height) * Sum;
    return IM_STATUS_OK;
}

  為了簡化程式碼,沒有考慮影象周邊單位畫素的資訊了,如果要嚴格意義的實現,也應該不是很難吧。 

  我們比較下halcon的結果和上面這段程式碼的結果,使用Halcon自帶的測試程式碼和圖片:

dev_update_off ()
dev_close_window ()
dev_open_window (0, 0, 512, 512, 'black', WindowHandle)
set_display_font (WindowHandle, 14, 'mono', 'true', 'false')
TestImages := ['for6','pumpe','die/die_02','clip','ic0','horses','board/board-01','combine']
NumImages := |TestImages|
for I := 0 to NumImages - 1 by 1
    read_image (Image, TestImages[I])
    dev_resize_window_fit_image (Image, 0, 0, -1, -1)
    dev_display (Image)
    for AddedNoise := 0 to 15 by 5
        gauss_distribution (AddedNoise + 1e-2, Distribution)
        add_noise_distribution (Image, ImageNoise, Distribution)
        write_image (ImageNoise, 'bmp', 0, 'C:/Users/Administrator/Desktop/1.bmp')
        estimate_noise (ImageNoise, 'foerstner', 20, SigmaFoerstner)
        estimate_noise (ImageNoise, 'immerkaer', 20, SigmaImmerkaer)
        estimate_noise (ImageNoise, 'least_squares', 20, SigmaLeastSquares)
        estimate_noise (ImageNoise, 'mean', 20, SigmaMean)
        dev_display (ImageNoise)
        disp_message (WindowHandle, 'Added Gaussian noise: Sigma = ' + AddedNoise, 'window', 12, 12, 'black', 'true')
        Message := 'Estimated image noise (Sigma):'
        Message[1] := 'Method \'foerstner\':     ' + SigmaFoerstner$'5.2f'
        Message[2] := 'Method \'immerkaer\':     ' + SigmaImmerkaer$'5.2f'
        Message[3] := 'Method \'least_squares\': ' + SigmaLeastSquares$'5.2f'
        Message[4] := 'Method \'mean\':          ' + SigmaMean$'5.2f'
        disp_message (WindowHandle, Message, 'windowe', 40, 12, 'black', 'true')
        disp_continue_message (WindowHandle, 'black', 'true')
        stop ()
    endfor
endfor  

           

               噪音影象                                          Halcon的結果

  使用上述C的程式碼獲取的結果為: 5.240565,和Halcon的結果基本一致。

 我們再找一些正常的圖片看看這個噪音值是否合理:

      

          噪音值 0.7763                                    噪音值 2.6604

  基本啥都比較小。 

       

                            噪音值  7.2155                                             噪音值  20.04

  對於高斯噪音,如上所示,還是能明顯的區別出來的。

  不過測試也表面,有些圖的噪音雖然視覺看起來比較明顯,但是用這引數去衡量時,確是很小,這個可能是因為他針對的是加性噪音做的評估吧。

  參考資料:

    W. Förstner: 「Image Preprocessing for Feature Extraction in Digital Intensity, Color and Range Images「, Springer Lecture Notes on Earth Sciences, Summer School on Data Analysis and the Statistical Foundations of Geomatics, 1999
    J. Immerkaer: 「Fast Noise Variance Estimation「, Computer Vision and Image Understanding, Vol. 64, No. 2, pp. 300-302, 1996