二值化函數cvThreshold()參數CV_THRESH_OTSU的疑惑【轉】


查看OpenCV文檔cvThreshold(),在二值化函數cvThreshold(const CvArr* src, CvArr* dst, double threshold, double max_value, int threshold_type)中,參數threshold_type有5種類型:

  • THRESH_BINARY
  • THRESH_BINARY_INV
  • THRESH_TRUNC
  • THRESH_TOZERO
  • THRESH_TOZERO_INV

問題來了:為什么可以在threshold_type參數中使用CV_THRESH_OTSU,在哪里可以查看這種OTSU,它用的什么方法?經多次驗證,二值化的效果很好,且速度很快。
已經有一些同志在使用:
例證1:例證1
例證2:例證2
例證3:例證3

    我的遭遇:為了二值化一個比較大的圖像(10M,3840*2748),痛苦的看了各種論文,嘗試了各種的二值化方法:一維OTSU,快速迭代的一維OTSU,二維的OTSU,快速迭代的二維OTSU。但現實的殘酷的!只有二維的OTSU和快速迭代的OTSU可用,但前者處理時間讓人難以接受,后者也要500多個ms。        快速迭代的二維OTSU算法是根據吳一全老師的《二維最大類間方差閾值分割的快速迭代算法》(論文下載鏈接,提取碼:fd0b)來實現的。但是令我哭笑不得的是,無意中在網上發現了一種方法threshold_type使用參數使用CV_THRESH_OTSU時,時間卻大大的縮短。程序如下(
程序下載鏈接提取碼:fd0b),圖像下載  (提取碼:24f3)。

/* otsu_2d:二維最大類間方差閾值分割的快速迭代算法   吳一全 */
#include <iostream>
#include <cv.h>
#include <highgui.h>

using namespace std;
double TwoDimentionOtsu(IplImage *image);
int main()  
{ 
        IplImage* srcImage = cvLoadImage( "E:/image_1/14.bmp",0 ); 
        assert(NULL != srcImage);

        cvNamedWindow("src",0);
        cvShowImage("src",srcImage); 

        clock_t start_time=clock();

        //計算最佳閾值 
        double threshold = TwoDimentionOtsu(srcImage);//70,125

        clock_t end_time=clock();
        cout<< "Running time is: "<<static_cast<double>(end_time-start_time)/CLOCKS_PER_SEC*1000<<"ms"<<endl;//輸出運行時間

        cout << "threshold=" << threshold << endl;

        IplImage* biImage = cvCreateImage(cvGetSize(srcImage),8,1);  
        //對圖像二值化
        //cvThreshold(srcImage,biImage,255,255, CV_THRESH_OTSU | CV_THRESH_BINARY);
        cvThreshold(srcImage,biImage,threshold,255, CV_THRESH_BINARY);

        cvNamedWindow("binary",0);  
        cvShowImage("binary",biImage);  

        cvWaitKey(0);  

        cvReleaseImage(&srcImage);  
        cvReleaseImage(&biImage);  

        cvDestroyAllWindows();  

        return 0;  
} 
double TwoDimentionOtsu(IplImage *image)
{
        double t0 = 0, s0 = 0, t = 0, s = 0;
        int width = image->width;  
        int height = image->height; 
        double dHistogram[256][256]={0.0};                                //建立二維灰度直方圖  
        unsigned long sum0 = 0,sum1 = 0;                                //sum0記錄所有的像素值的總和,sum1記錄3x3窗口的均值的總和
        uchar* data = (uchar*)image->imageData; 
        for(int i=0; i<height; i++)  
        {  
                for(int j=0; j<width; j++)  
                {  
                        unsigned char nData1 = data[i * image->widthStep + j];//nData1記錄當前點(i,j)的像素值
                        sum0 += nData1;
                        unsigned char nData2 = 0;  //nData2記錄以當前點(i,j)為中心三領域像素值的平均值
                        int nData3 = 0;                                        //nData3記錄以當前點(i,j)為中心三領域像素值之和,注意9個值相加可能超過一個字節  
                        for(int m=i-1; m<=i+1; m++)  
                        {  
                                for(int n=j-1; n<=j+1; n++)  
                                {  
                                        if((m>=0)&&(m<height)&&(n>=0)&&(n<width))
                                                nData3 += data[m * image->widthStep + n];
                                }  
                        }  
                        nData2 = (unsigned char)(nData3/9);    //對於越界的索引值進行補零,鄰域均值
                        sum1 += nData2;
                        dHistogram[nData1][nData2]++;  
                }  
        }

        long N = height*width;                //總像素數  
        t = sum0/N;                                        //圖像灰度級均值
        s = sum1/N;                                        //鄰域平均灰度級的均值

        s0 = s;
        t0 = t; 
        for(int j=0; j<256; j++)
                for(int i=0; i<256; i++) 
                {
                        dHistogram[i][j] = dHistogram[i][j]/N;  //得到歸一化的概率分布 
                }

                double w0 = 0.0,w1 = 0.0,u0i = 0.0,u1i = 0.0,u0j = 0.0,u1j = 0.0;

                do
                {
                        t0 = t;
                        s0 = s;
                        w0 = w1 = u0i = u1i = u0j = u1j = 0.0;
                        for (int i = 0,j; i < 256; i++)
                        {
                                for (j = 0; j < s0; j++)
                                {
                                        w0 += dHistogram[i][j];
                                        u0j += dHistogram[i][j] * j;
                                }

                                for (; j < 256; j++)
                                {
                                        w1 += dHistogram[i][j];
                                        u1j += dHistogram[i][j] * j;
                                }

                        }
                        for (int j = 0,i = 0; j < 256; j++)
                        {
                                for (i = 0; i < t0; i++)
                                        u0i += dHistogram[i][j] * i;
                                for (; i < 256; i++)
                                        u1i += dHistogram[i][j] * i;
                        }
                        u0i /= w0;
                        u1i /= w1 ;
                        u0j /= w0;
                        u1j /= w1;

                        t = (u0i + u1i)/2;
                        s = (u0j + u1j)/2;
                }while ( t != t0);//是否可以用這個做為判斷條件,有待考究,請高手指點

                return t;//只用t做為閾值,個人也感覺不妥,但沒有找到更好的方法,請高手指點

}

 

輸出結果:

 cvThreshold()參數設為CV_THRESH_OTSU,輸入結果:
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM