基於Simple Image Statistics(簡單映像統計,SIS)的映像二值化演算法。

來源:互聯網
上載者:User

  這是個簡單的演算法,是全域二值演算法的一種,演算法執行速度快。

     演算法過程簡單描述如下: 

  對於每一個像素,做如下處理 

       1、計算當前像素水平和垂直方向的梯度。 (two gradients are calculated  |I(x + 1, y) - I(x - 1, y)| and |I(x, y + 1) - I(x, y - 1)|);

       2、取兩個梯度的最大值作為權重。(weight is calculated as maximum of two gradients);

       3、更新權重的和。(sum of weights is updated (weightTotal += weight));

       4、更新加權像素之和 (sum of weighted pixel values is updated (total += weight * I(x, y)));

      之後,最終的閾值去加權像素之和和權重之和相除的值。

      這個演算法在 Image Processing Lab in c# 的代碼中有相關的說明。 

      從實際的操作上講,我認為二值處理應該只針對灰階映像進行處理,這樣才意義明確,因此,我在代碼中給出了判斷一副映像是否是灰階映像的一個函數:

  private bool IsGrayBitmap(Bitmap Bmp)    {        bool IsGray;        if (Bmp.PixelFormat == PixelFormat.Format8bppIndexed)           // .net中灰階首先必然是索引映像        {            IsGray = true;            if (Bmp.Palette.Entries.Length != 256)                      // 這個要求其實在PS中是不存在的                IsGray = false;            else            {                for (int X = 0; X < Bmp.Palette.Entries.Length; X++)    // 看看調色盤的每一個分兩值是不是都相等,且必須還要等於其在調色盤中出現的順序                {                    if (Bmp.Palette.Entries[X].R != X || Bmp.Palette.Entries[X].G != X || Bmp.Palette.Entries[X].B != X)                    {                        IsGray = false;                        break;                    }                }            }        }        else        {            IsGray = false;        }        return IsGray;    }

  實際上,在PS的概念中,灰階映像的調色盤個數不一定是256,只要調色盤的每個元素的分量值都相等,並且都等於其在調色盤中出現的順序,PS就認為他是灰階映像。

     為了處理方便,我加入了一個將其他模式的映像轉換為灰階模式映像的函數:

    private Bitmap ConvertToGrayModeBitmap(Bitmap Bmp)    {        int X, Y, SrcStride, DestStride, Width, Height;        byte* SrcData, DestData;        BitmapData BmpData = Bmp.LockBits(new Rectangle(0, 0, Bmp.Width, Bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);        Bitmap GrayBmp = new Bitmap(Bmp.Width, Bmp.Height, PixelFormat.Format8bppIndexed);        ColorPalette Pal = GrayBmp.Palette;        for (Y = 0; Y < Pal.Entries.Length; Y++) Pal.Entries[Y] = Color.FromArgb(255, Y, Y, Y);            // 設定灰階映像的調色盤        GrayBmp.Palette = Pal;        //  LockBits 在第一個參數和映像一樣大,以及讀取格式和原始一樣的情況下,調用函數的時間為0,且每次調用後BitmapData的Scan0都相同,而在        // 其他的大部分情況下同樣參數調用該函數返回的Scan0都不同,這就說明在在程式內部,GDI+為在建立映像時還是分配了和對應位元影像一樣大小記憶體空間,        // 這樣我們就可以再載入時調用一次該函數,並記住Scan0的值,然後直接用指標操作這一片地區,就相當於操作了映像。而不用每次都LOCK和UNLOCK了        // 從這個層次上說,該函數和GetDibits類似。        BitmapData GrayBmpData = GrayBmp.LockBits(new Rectangle(0, 0, GrayBmp.Width, GrayBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);        Width = BmpData.Width; Height = BmpData.Height;        SrcStride = BmpData.Stride; DestStride = GrayBmpData.Stride;           //  這個值並不一定就等於width*height*色深/8        for (Y = 0; Y < Height; Y++)        {            SrcData = (byte*)BmpData.Scan0 + Y * SrcStride;                  // 必須在某個地方開啟unsafe功能,其實C#中的unsafe很safe,搞的好嚇人。            DestData = (byte*)GrayBmpData.Scan0 + Y * DestStride;            for (X = 0; X < Width; X++)            {                *DestData = (byte)((*SrcData * 7472 + *(SrcData + 1) * 38469 + *(SrcData + 2) * 19595) >> 16);        //這裡可以有不同的演算法                SrcData += 3;                DestData++;            }        }        Bmp.UnlockBits(BmpData);        GrayBmp.UnlockBits(GrayBmpData);        return GrayBmp;    }

      在很多人心目中所謂的灰階映像就是R=G=B這樣的映像,只能說這些人還是門外漢,太不專業了。 這樣的映像只能算是顏色分量相同的彩色映像罷了,再次予以糾正。

      由於上述所描述的演算法涉及到了映像的四領域,因此我們採用類似PhotoShop演算法原理解析系列 - 風格化---》尋找邊緣 一文中的哨兵演算法,對備份的映像擴充邊界,擴充部分的資料以原始映像邊界處的值填充。因為只涉及到了四領域,因此需要在映像寬度和高度上分別增加2個像素即可。

      關於填充資料,我還是喜歡自己分配記憶體,而且我更傾向於直接使用API,這個可能與個人習慣有關吧,你們也可以按照自己的方式來處理。 

    private byte GetSimpleStatisticsThreshold(Bitmap GrayBmp)    {        int Width, Height, Stride, X, Y;        int CloneStride, Ex, Ey;        int Weight = 0;        long SumWeight = 0;                 //  對於大映像這個數字會溢出,所以用long類型的變數        byte* Pointer, Scan0, CloneData;        BitmapData GrayBmpData = GrayBmp.LockBits(new Rectangle(0, 0, GrayBmp.Width, GrayBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);        Width = GrayBmp.Width; Height = GrayBmp.Height; Stride = GrayBmpData.Stride; CloneStride = Width + 2; Scan0 = (byte*)GrayBmpData.Scan0;        CloneData = (byte*)GlobalAlloc(GPTR, CloneStride * (Height * 2));        for (Y = 0; Y < Height; Y++)        {            *(CloneData + (Y + 1) * CloneStride) = *(Scan0 + Y * Stride);                           //  填充左側第一列像素(不包括第一個和最後一個點)            CopyMemory(CloneData + CloneStride * (Y + 1) + 1, Scan0 + Y * Stride, Width);            *(CloneData + (Y + 1) * CloneStride + Width + 1) = *(Scan0 + Y * Stride + Width - 1);   //  填充最右側那一列的資料        }        CopyMemory(CloneData, CloneData + CloneStride, CloneStride);                                //   第一行        CopyMemory(CloneData + (Height + 1) * CloneStride, CloneData + Height * CloneStride, CloneStride);    //   最後一行        for (Y = 0; Y < Height; Y++)        {            Pointer = CloneData + (Y + 1) * CloneStride + 1;            for (X = 0; X < Width; X++)            {                Ex = *(Pointer - 1) - *(Pointer + 1);                if (Ex < 0) Ex = -Ex;                Ey = *(Pointer - CloneStride) - *(Pointer + CloneStride);                if (Ey < 0) Ey = -Ey;                if (Ex > Ey)                {                    Weight += Ex;                    SumWeight += *Pointer * Ex;                }                else                {                    Weight += Ey;                    SumWeight += *Pointer * Ey;                }                Pointer++;            }        }        GlobalFree((IntPtr)CloneData);        GrayBmp.UnlockBits(GrayBmpData);        if (Weight == 0) return *(Scan0);  //  說明所有的顏色值都相同        return (byte)(SumWeight / Weight);    }

  一般情況下,為了程式的速度考慮,對於一些小函數我建議直接自己展開,比如上面的ABS函數,直接寫成if (Ex < 0) Ex = -Ex會快一些的。你通過下面的反組譯碼可以看出不同:

                Ex = Math.Abs(Ex);00000161  js          00000167 00000163  mov         eax,esi 00000165  jmp         0000016E 00000167  mov         ecx,esi 00000169  call        638C54E4 0000016e  mov         esi,eax                 if (Ex < 0) Ex = -Ex;00000170  test        eax,eax 00000172  jge         00000176 00000174  neg         esi 

  

     分割的效果可能還是要拿具體的映像說事,這裡不做過多評論。 

     工程:http://files.cnblogs.com/Imageshop/ThresholdUseSIS.rar

*****************************基本上我不提供原始碼,但是我會盡量用文字把對應的演算法描述清楚或提供參考文檔**************************

*******************************因為靠自己的努力和實踐寫出來的效果才真正是自己的東西,人一定要靠自己****************************

***************************作者: laviewpbt   時間: 2013.7.15    聯絡QQ:  33184777  轉載請保留本行資訊*************************

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.