程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 一個簡單的統計圖像主顏色的算法(C#源代碼)

一個簡單的統計圖像主顏色的算法(C#源代碼)

編輯:C#入門知識

 前段日子有朋友咨詢了下分析圖像主顏色的算法,我對這一塊也沒有什麼深入的研究,參考了一些小代碼,然後自己寫了一個很簡單的小工具,現共享給大家。

      界面截圖如下:

     

      算法的原理很簡單,就是統計出圖像中各種顏色的分布情況,然後取前N個顏色作為主成分。

      當然,實際上如果直接對圖像的各通道256個色階進行統計,得到的結果可能是沒有意義的,所以一般都需要先把256個色階線性的隱射到更少的色階范圍。

      主要的代碼如下:

   

 static unsafe class Statistics
    {        //'*****************************************************************************************
      //'** 開發日期 : 2013-6-21
      //'** 作 者 : laviewpbt
      //'** 聯系方式: 33184777
      //'** 修改日期 : 2013-6-21
      //'** 版 本 : Version 1.1.1
      //'** 轉載請不要刪除以上信息
      //'****************************************************************************************

        [StructLayout(LayoutKind.Sequential)]
        public struct MajorColor : IComparable<MajorColor>
        {
            internal int Color;
            internal int Amount;
            public MajorColor(int Color, int Amount)
            {
                this.Color = Color;
                this.Amount = Amount;
            }
            public int CompareTo(MajorColor obj)
            {
                return this.Amount.CompareTo(obj.Amount);
            }
        }

        // http://www.coolphptools.com/color_extract
        // http://www.wookmark.com/image/268753/30-inspiring-examples-of-levitation-photography-inspirationfeed-com
        public static List<MajorColor> PrincipalColorAnalysis(Bitmap Bmp, int PCAAmount, int Delta = 24)
        {
            List<MajorColor> MC = new List<MajorColor>();

            int X, Y, Width, Height, Stride, Index, TotalColorAmount = 0;
            int HalfDelta;
            byte* Pointer, Scan0;
            BitmapData BmpData = Bmp.LockBits(new Rectangle(0, 0, Bmp.Width, Bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            Height = Bmp.Height; Width = Bmp.Width; Stride = BmpData.Stride; Scan0 = (byte*)BmpData.Scan0;

            int[] Table = new int[256 * 256 * 256];
            int[] NonZero = new int[Width * Height];
            int[] Map = new int[256];

            if (Delta > 2)
                HalfDelta = Delta / 2 - 1;
            else
                HalfDelta = 0;

            for (Y = 0; Y < 256; Y++)
            {
                Map[Y] = ((Y + HalfDelta) / Delta) * Delta;
                if (Map[Y] > 255) Map[Y] = 255;
            }
            for (Y = 0; Y < Height; Y++)
            {
                Pointer = Scan0 + Stride * Y;
                for (X = 0; X < Width; X++)
                {
                    Index = (Map[*Pointer] << 16) + (Map[*(Pointer + 1)] << 8) + Map[*(Pointer + 2)];
                    if (Table[Index] == 0)                  //      還沒有出現過該顏色
                    {
                        NonZero[TotalColorAmount] = Index;  //      記錄下有顏色的位置,同時也記錄下了該顏色
                        TotalColorAmount++;                 //      顏色總數+1
                    }
                    Table[Index]++;                         //      對應的顏色數加1
                    Pointer += 3;                          //      移動到下一個像素
                }
            }
            MajorColor[] Result = new MajorColor[TotalColorAmount];
            for (Y = 0; Y < TotalColorAmount; Y++)
            {
                Result[Y].Amount = Table[NonZero[Y]];
                Result[Y].Color = NonZero[Y];
            }  
            Array.Sort(Result);                             // 系統自帶的這個排序算法比一般自己寫的都要快
            Array.Reverse(Result); 

            for (Y = 0; Y < PCAAmount; Y++)
                MC.Add(new MajorColor(Result[Y].Color, Result[Y].Amount));
            Bmp.UnlockBits(BmpData);
            GC.Collect();                                   // 立即釋放掉分配的64MB的內存
            return MC;
        }
    }

     統計顏色這一塊,其實我一直在尋找一種即不用占很大內存,速度又快的算法,但是一直沒有想到好辦法。 上面的代碼中是分配了64MB的內存來索引計數的,雖然對於很小的圖像也需要這麼大的內存占用量,但是我經過對比發現,比用Dictionary之類的基於字典的統計方法還是要快很多的。

     關於排序,我一直認為自己能寫出比系統更快的算法,但是最終我還是選擇了如上代碼中的簡便方式。在對Amount進行排序的同時,Color的值也跟著隨動了。

     在這種占用比較大內存的代碼中,我認為應該立即調用GC.Collect()釋放掉內存。

     關於Delta的取值,似乎不太好確定,這個只能說試驗確定吧,一般取16-32之間比較合理。

     兩個參考鏈接處也有一些比較好的算法的,不過裡面的代碼是PHP的,改寫成C#的應該說還是有一定的難度的,有興趣的朋友可以自己參考著學習下吧。

     從個人的理解來看,我覺得這種顏色主成分分析 還可以利用 類似於彩色轉索引時 找最佳索引表時用的八叉樹算法;也可以用FCM或者KMEANS之類的聚類算法來實現。待時間充足時我回去實際驗證下。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved