好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

C# 分析图片的主颜色

C# 分析图片的主颜色

最近工作上的需要,要对40多万张照片进行主颜色的区分计算,有时候知识就是力量,若40万张图片靠人工来识别主颜色,那不知道需要多少人分辨多久才可以做好,而且还容易分辨出错,若这个事情能用程序来处理又快又好,程序足足写了1周才稳定下来,程序跑了1天就把40万张照片的主颜色全部计算出来了,技术就是力量的价值观又一次得到了验证。

   朋友多好办事,其中得到一个好朋友的帮助,改进了颜色区分判断的函数,也对工作起了很大作用。朋友多好办事、平时真需要多与各种朋友往来。

   分析图片的主颜色区间过程中遇到的主要难题:

 1: 网上类似的C#的参考代码比较少。
 2: 我们本身不是研究图像处理技术的。
 3: 需要把40万张图片进行处理。
 4: 网络服务器不稳定时,也需要能稳定运行。
 5: 颜色计算的程序效率要高,不能占用过多服务器资源,需要运行稳定。

计算好的图片的分布情况,还比较均匀。

测试程序的效果如下:

参考代码如下:

 //  -----------------------------------------------------------------
  //   All Rights Reserved , Copyright (C) 2012 , Hairihan TECH, Ltd. 
  //  ----------------------------------------------------------------- 

 using   System;
  using   System.Collections.Generic;
  using   System.Data;
  using   System.Drawing;
  using   System.Runtime.InteropServices;
  using   System.Windows.Forms;
  using   System.Configuration;
  using   System.Threading;

  using   DotNet.Utilities;

  namespace   PrimaryColors
{
      ///   <summary> 
     ///   FormPrimaryColors 主颜色计算
      ///  
     ///   修改纪录
      ///          2013.01.22 版本:1.0 吉日嘎拉  创建。  
      ///         
     ///   版本:1.0
      ///   <author> 
     ///          <name>  吉日嘎拉  </name> 
     ///          <date>  2013.01.22  </date> 
     ///   </author>  
     ///   </summary> 
     public   partial   class   FormPrimary : Form
    {
          public   FormPrimary()
        {
            InitializeComponent();
        }

          private   void   GetColor()
        {
            btnColor.BackColor  = Color.FromArgb( this .tbR.Value,  this .tbG.Value,  this  .tbB.Value);
              this .btnColor.Text =  this .tbR.Value.ToString() +  "  :  "  +  this .tbG.Value.ToString() +  "  :  "  +  this  .tbB.Value.ToString();
              //   this.Text = btnColor.BackColor.GetHue().ToString("f0") + "-" + btnColor.BackColor.GetSaturation().ToString("f2") + "-" + btnColor.BackColor.GetBrightness().ToString("f2"); 
         }

          private   void  tb_Scroll( object   sender, EventArgs e)
        {
            GetColor();
        }

          //   取设备场景,返回设备场景句柄 
        [DllImport( "  user32.dll  "  )]
          private   static   extern   IntPtr GetDC(IntPtr hdc);

          //   取指定点颜色 
        [DllImport( "  gdi32.dll  "  )]
          private   static   extern   int   GetPixel(IntPtr hdc, Point point);

          private   void  picImage_Click( object   sender, EventArgs e)
        {
              //  取置顶点坐标  
            Point point =  new   Point(MousePosition.X, MousePosition.Y);
              //  取到设备场景(0就是全屏的设备场景)  
            IntPtr hdc = GetDC( new  IntPtr( 0  ));

              int  c = GetPixel(hdc, point); //  取指定点颜色  
             int  r = (c &  0xFF ); //  转换R  
             int  g = (c &  0xFF00 ) /  256 ; //  转换G  
             int  b = (c &  0xFF0000 ) /  65536 ; //  转换B 

             this .tbR.Value =  r;
              this .tbG.Value =  g;
              this .tbB.Value =  b;
            GetColor();
        }

          private   void  FormPrimaryColors_DragOver( object   sender, DragEventArgs e)
        {
              if   (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                e.Effect  =  DragDropEffects.Move;
            }
        }

          private   void  FormPrimaryColors_DragDrop( object   sender, DragEventArgs e)
        {
              if   (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                  string [] folder = ( string  [])e.Data.GetData(DataFormats.FileDrop);
                  for  ( int  i =  0 ; i <= folder.Length -  1 ; i++ )
                {
                      if   (System.IO.File.Exists(folder[i]))
                    {
                        SetTargetImage(folder[i]);
                    }
                }
            }
        }

          private   void  Analyse( bool  fromFile =  true  )
        {
              int  boundaryValue =  15  ;
              if  (! string .IsNullOrEmpty( this  .txtBoundaryValue.Text))
            {
                boundaryValue  =  int .Parse( this  .txtBoundaryValue.Text);
            }

              //   获取颜色表 
            Dictionary<Color,  int > colors =  new  Dictionary<Color,  int > ();
              //   初始化计数器 
             for  ( int  i =  0 ; i < flpColors2.Controls.Count; i++ )
            {
                  if  (! colors.ContainsKey(flpColors2.Controls[i].BackColor))
                {
                    colors.Add(flpColors2.Controls[i].BackColor,   0  );
                }
            }

              //   进行颜色区间计算 
             int  width =  0  ;
              if  (! string .IsNullOrEmpty( this  .txtWidth.Text))
            {
                width  =  int .Parse( this  .txtWidth.Text);
            }

              string  character =  string  .Empty;
            Double binary  =  0  ;
              //   "红色","橙色","黄色","绿色","青色","蓝色","紫色","粉红色","白色","灰色","黑色","棕色"
              //   进行图像分析 
             if   (fromFile)
            {
                colors  = PrimaryColors.AnalyseFromFile( this .txtFile.Text, width, colors, boundaryValue,  out  character,  out   binary);
            }
              else  
            {
                colors  = PrimaryColors.AnalyseFromUrl( this .txtFile.Text, width, colors, boundaryValue,  out  character,  out   binary);
            }

              this .txtCharacter.Text =  character;
              this .txtBinary.Text =  binary.ToString();

              //   显示计数器 
             for  ( int  i =  0 ; i < flpColors2.Controls.Count; i++ )
            {
                  //   初始化 
                flpColors2.Controls[i].Text =  colors[flpColors2.Controls[i].BackColor].ToString();
            }
        }

          ///   <summary> 
         ///   把目标颜色归类为主颜色
          ///   </summary> 
         ///   <param name="color">  当前颜色  </param> 
         ///   <param name="boundaryValue">  边界值  </param> 
         private   void  ConvertIntoPrimaryColor(Color color,  int   tolerance)
        {
            Color nearest_color  =  Color.Empty;
              //   算法一 
             for  ( int  i =  0 ; i < flpColors1.Controls.Count; i++ )
            {
                Color o  =  flpColors1.Controls[i].BackColor;

                  //   compute the Euclidean distance between the two colors
                  //   note, that the alpha-component is not used in this example 
                 double  dbl_test_red = Math.Pow(Convert.ToDouble(((Color)o).R) - color.R,  2.0  );
                  double  dbl_test_green = Math.Pow(Convert.ToDouble(((Color)o).G) - color.G,  2.0  );
                  double  dbl_test_blue = Math.Pow(Convert.ToDouble(((Color)o).B) - color.B,  2.0  );

                  double  temp = Math.Sqrt(dbl_test_blue + dbl_test_green +  dbl_test_red);
                  //   explore the result and store the nearest color 
                 if  (temp <  tolerance)
                {
                    flpColors1.Controls[i].Tag  = ( int )flpColors1.Controls[i].Tag +  1  ;
                      break  ;
                }
            }

              //    算法二 
             Color backColor;
              for  ( int  i =  0 ; i < flpColors1.Controls.Count; i++ )
            {
                backColor  =  flpColors1.Controls[i].BackColor;
                  if  ((color.R + tolerance > backColor.R && color.R - tolerance <  backColor.R)
                     && (color.G + tolerance > backColor.G && color.G - tolerance <  backColor.G)
                     && (color.B + tolerance > backColor.B && color.B - tolerance <  backColor.B))
                {
                    flpColors1.Controls[i].Tag  = ( int )flpColors1.Controls[i].Tag +  1  ;
                      break  ;
                }
            }
        }

          private   void  btnAnalyse_Click( object   sender, EventArgs e)
        {
            Analyse(  true  );
        }

          private   void  btnColor_Click( object   sender, EventArgs e)
        {
              int  width =  0  ;
              if  (! string .IsNullOrEmpty( this  .txtWidth.Text))
            {
                width  =  int .Parse( this  .txtWidth.Text);
            }
              this .picTarget.Image = PrimaryColors.GetThumbnailImageFromUrl( this  .txtFile.Text, width);

            Analyse(  false  );
        }

          private   void  SetTargetImage( string   fileName)
        {
            txtFile.Text  =  fileName;
              int  width =  int .Parse( this  .txtWidth.Text);
              this .picImage.BackgroundImage =  Image.FromFile(fileName);
            picTarget.Image  =  PrimaryColors.GetThumbnailImageFromFile(fileName, width);
        }

          private   void  btnSelect_Click( object   sender, EventArgs e)
        {
            OpenFileDialog openFileDialog  =  new   OpenFileDialog();
            openFileDialog.Filter  =  "  jpg files (*.jpg)|*.jpg|All files (*.*)|*.*  "  ;
            openFileDialog.FilterIndex  =  1  ;
            openFileDialog.RestoreDirectory  =  true  ;
              if  (openFileDialog.ShowDialog() ==  DialogResult.OK)
            {
                SetTargetImage(openFileDialog.FileName);
            }
        }

          private   void  btnConverter_Click( object   sender, EventArgs e)
        {
              this .txtBinary.Text = PrimaryColors.CharacterToBinary( this  .txtCharacter.Text).ToString();
        }

          private   void  btnBinaryToCharacter_Click( object   sender, EventArgs e)
        {
              this .txtCharacter.Text = Convert.ToString( int .Parse( this .txtBinary.Text),  2  );
              this .txtCharacter.Text =  this .txtCharacter.Text.PadLeft( 12 ,  '  0  '  );
        }

          private   int   DbAnalyse()
        {
              int  returnValue =  0  ;
              int  width =  0  ;
              if  (! string .IsNullOrEmpty( this  .txtWidth.Text))
            {
                width  =  int .Parse( this  .txtWidth.Text);
            }
              int  boundaryValue =  15  ;
              if  (! string .IsNullOrEmpty( this  .txtBoundaryValue.Text))
            {
                boundaryValue  =  int .Parse( this  .txtBoundaryValue.Text);
            }
              //   获取颜色表 
            Dictionary<Color,  int > colors =  new  Dictionary<Color,  int > ();
              //   初始化计数器 
             for  ( int  i =  0 ; i < flpColors2.Controls.Count; i++ )
            {
                  if  (! colors.ContainsKey(flpColors2.Controls[i].BackColor))
                {
                    colors.Add(flpColors2.Controls[i].BackColor,   0  );
                }
            }

              string  imgUrl =  string  .Empty;
              string  character =  string  .Empty;
            Double binary  =  0  ;
              string  id =  string  .Empty;
            
              //   计算过一遍的不要再重复计算
              //   SELECT COUNT(1) FROM HuiTu_Pic_20130123.dbo.ah_pic_pass
              //   int maxId = 0; 
            
             string  commandText =  string  .Empty;

              //   commandText = "SELECT MAX(pic_id) FROM ah_pic_pass WHERE pic_Colors IS NOT NULL OR pic_Colors != 0";
              //   Object maxObject = DbHelper.ExecuteScalar(commandText);
              //   if (maxObject != null && maxObject != DBNull.Value)
              //   {
              //      maxId = int.Parse(maxObject.ToString());
              //   } 
 
            commandText  =  "  SELECT TOP 1000 id, imgUrl FROM pic WHERE Colors IS NOT NULL ORDER BY NEWID()  "  ;
            DataTable dt  =  DbHelper.Fill(commandText);
            returnValue  =  dt.Rows.Count;
              int  y =  0  ;
              foreach  (DataRow dr  in   dt.Rows)
            {
                y ++ ;
                id  = dr[ "  id  "  ].ToString();
                imgUrl  = dr[ "  imgUrl  "  ].ToString();
                  //   this.txtFile.Text = imgUrl; 
                PrimaryColors.AnalyseFromUrl(imgUrl, width, colors, boundaryValue,  out  character,  out   binary);
                  if  (binary >  0  )
                {
                    commandText  =  "  UPDATE pic SET Colors =   "  + binary +  "   WHERE id =   "  +  id;
                    DbHelper.ExecuteNonQuery(commandText);
                      this .Text = y.ToString() +  "  /  "  + dt.Rows.Count.ToString() +  "  :  "  +  id;
                }
            }
              return   returnValue;
        }

          private   void  btnDbAnalyse_Click( object   sender, EventArgs e)
        {
            System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls  =  false  ;
              this .btnDbAnalyse.Enabled =  false  ;
              //   数据库连接串 
            DbHelper.DbConnection = ConfigurationManager.AppSettings[ "  BusinessDbConnection  "  ];
              this  .bgwColors.RunWorkerAsync();
              //   Thread thread = new Thread(new ThreadStart(DbAnalyse));
              //   thread.Start(); 
         }

          private   void  FormPrimary_Load( object   sender, EventArgs e)
        {
              this .txtWidth.Text = ConfigurationManager.AppSettings[ "  Width  "  ];
              this .txtBoundaryValue.Text = ConfigurationManager.AppSettings[ "  BoundaryValue  "  ];
        }

          private   void  btn_Click( object   sender, EventArgs e)
        {
              this .txtFile.Text =  string .Format( "  {0},{1},{2}  "  , ((Button)sender).BackColor.R, ((Button)sender).BackColor.G, ((Button)sender).BackColor.B);
        }

          private   void  bgwColors_DoWork( object   sender, System.ComponentModel.DoWorkEventArgs e)
        {
              int  returnValue =  0  ;
              while  (! this  .IsDisposed)
            {
                  try  
                {
                    returnValue  =  DbAnalyse();
                }
                  catch   (Exception ex)
                {
                      //   在本地记录异常 
                     FileUtil.WriteException(ex);
                }
                  if  (returnValue ==  0  )
                {
                    Thread.Sleep(  1000  *  100  );
                }
            }
        }

          //   01:[ok] 图片上的颜色点选功能的优化。
          //   02:[ok] 变量名控件名优化。
          //   03:[ok] 图片可以支持拖拽。
          //   04:[ok] 图片压缩参数可以设置。
          //   05:[ok] 图片可以直接通过路径读取。
          //   06:[ok] 可以读取 web.config 里的参数。
          //   07:[ok] 代码加上注释。
          //   08:[ok] 3个主颜色能计算出来。
          //   09:[ok] 3个主颜色可以按2进制保存。
          //   10:[ok] 图像的缩放的实现。
          //   11:[ok] 可以按dll方式调用。
          //   12:[ok] 从网址计算图片的主颜色。
          //   13:[ok] 可以保存数据库。
          //   14:[ok] 相关的查询页面可以进行颜色区分,这个目录都能进行主颜色计算。
          //   15:[ok] 20000张图片的识别测试,看看识别效率如何。 
     }
} 

 //  -----------------------------------------------------------------
  //   All Rights Reserved , Copyright (C) 2012 , Hairihan TECH, Ltd. 
  //  ----------------------------------------------------------------- 

 using   System;
  using   System.Collections.Generic;
  using   System.Drawing;
  using   System.Configuration;
  using   System.Linq;

  namespace   PrimaryColors
{
      ///   <summary> 
     ///   FormPrimaryColors 主颜色计算
      ///  
     ///   修改纪录
      ///          2013.01.22 版本:1.0 吉日嘎拉  创建。  
      ///         
     ///   版本:1.0
      ///   <author> 
     ///          <name>  吉日嘎拉  </name> 
     ///          <date>  2013.01.22  </date> 
     ///   </author>  
     ///   </summary> 
     public   partial   class   PrimaryColors
    {
          public   PrimaryColors()
        {
        }

          ///   <summary> 
         ///   将类似2进制字符串 10010110111 转换为 数值型 
          ///   </summary> 
         ///   <param name="character">  字符串  </param> 
         ///   <returns>  返回数值类型  </returns> 
         public   static   double  CharacterToBinary( string   character)
        {
              double  returnValue =  0  ;
              int  j =  0  ;
              //   截取字符串 
             for  ( int  i =  0 ; i < character.Length; i++ )
            {
                  if  (character[i].Equals( '  1  '  ))
                {
                      //   2的几次幂 
                    j = character.Length - i -  1  ;
                    returnValue  = returnValue + System.Math.Pow( 2  , j);
                }
            }
              return   returnValue;
        }

          ///   <summary> 
         ///   获取主颜色字符串编码例如 01011010
          ///   </summary> 
         ///   <param name="colors">  颜色字典  </param> 
         ///   <returns>  主颜色表  </returns> 
         private   static   string  GetPrimaryColors(Dictionary<Color,  int >  colors)
        {
              string  returnValue =  string  .Empty;
              //   这里计算已经获得了接近的颜色总数 
            Double pixelCount =  0  ;
              foreach  (KeyValuePair<Color,  int > pair  in   colors)
            {
                pixelCount  +=  pair.Value;
            }
              //   进行转换 
            List<KeyValuePair<Color,  int >> keyValueList =  new  List<KeyValuePair<Color,  int >> (colors);
              //   排序 
             keyValueList.Sort(
                  delegate (KeyValuePair<Color,  int >  firstPair,
                KeyValuePair <Color,  int >  nextPair)
                {
                      return   nextPair.Value.CompareTo(firstPair.Value);
                });

              //   主颜色计算 
             bool  isPrimaryColor =  false  ;
              foreach  (KeyValuePair<Color,  int > pair  in   colors)
            {
                isPrimaryColor  =  false  ;
                  if  (pair.Key == keyValueList[ 0  ].Key
                     || pair.Key == keyValueList[ 1  ].Key
                     || pair.Key == keyValueList[ 2  ].Key)
                      //   要求颜色能达到一定的比例才计算在主颜色里 
                     if  (pair.Value > pixelCount / (colors.Count /  2  ))
                    {
                        isPrimaryColor  =  true  ;
                    }
                  //   字符串颜色表 
                returnValue += isPrimaryColor ?  "  1  "  :  "  0  "  ;
            }
              return   returnValue;
        }

          public   static   bool  ThumbnailCallback() {  return   false  ; }

          private   static  System.Net.WebClient webClient =  null  ;

          private   static   System.Net.WebClient CurrentWebClient
        {
              get  
            {
                  if  (webClient ==  null  )
                {
                    webClient  =  new   System.Net.WebClient();
                  }
                  return   webClient;
            }
        }

          ///   <summary> 
         ///   获取压缩的图片
          ///   </summary> 
         ///   <param name="imgUrl">  网址路径  </param> 
         ///   <param name="width">  宽度  </param> 
         ///   <returns>  自动压缩后的图片  </returns> 
         public   static  Bitmap GetThumbnailImageFromUrl( string  imgUrl,  int   width)
        {
              //   远程图片路径 
              //   string imgUrl =    http://www. 海日涵 .com/img/baidu_sylogo1.gif   ;
              //   读取远程图片数据  
             byte [] bytes =  CurrentWebClient.DownloadData(imgUrl);
              //   将二进制转换为图片对象   
            System.Drawing.Image image = System.Drawing.Image.FromStream( new   System.IO.MemoryStream(bytes));
              int  height =  image.Height;
              if  (width ==  0  )
            {
                width  =  image.Width;
            }
              else  
            {
                height  = (width * image.Height) /  image.Width;
            }
              return   new   Bitmap(image.GetThumbnailImage(width, height, ThumbnailCallback, IntPtr.Zero));
        }

          ///   <summary> 
         ///   获取压缩的图片
          ///   </summary> 
         ///   <param name="fileName">  文件名  </param> 
         ///   <param name="width">  宽度  </param> 
         ///   <returns>  自动压缩后的图片  </returns> 
         public   static  Bitmap GetThumbnailImageFromFile( string  fileName,  int   width)
        {
            Image image  =  Image.FromFile(fileName);
              int  height =  image.Height;
              if  (width ==  0  )
            {
                width  =  image.Width;
            }
              else  
            {
                height  = (width * image.Height) /  image.Width;
            }
              return   new   Bitmap(image.GetThumbnailImage(width, height, ThumbnailCallback, IntPtr.Zero));
        }

          ///   <summary> 
         ///   分析图片颜色
          ///   </summary> 
         ///   <param name="fileName">  文件名  </param> 
         ///   <param name="width">  压缩宽度  </param> 
         ///   <param name="colors">  需要匹配的颜色列表  </param> 
         ///   <param name="boundaryValue">  色差系数  </param> 
         ///   <param name="binary">  转换为对应的数值  </param> 
         ///   <returns>  分析结果列表  </returns> 
         public   static  Dictionary<Color,  int > AnalyseFromFile( string  fileName,  int  width, Dictionary<Color,  int > colors,  int  boundaryValue,  out   string  character,  out   Double binary)
        {   
            Bitmap bitmap  =  GetThumbnailImageFromFile(fileName, width);
              return  Analyse(bitmap, width, colors, boundaryValue,  out  character,  out   binary);
        }

          public   static  Dictionary<Color,  int > AnalyseFromUrl( string  imgUrl,  int  width, Dictionary<Color,  int > colors,  int  boundaryValue,  out   string  character,  out   Double binary)
        {
            Bitmap bitmap  =  GetThumbnailImageFromUrl(imgUrl, width);
              return  Analyse(bitmap, width, colors, boundaryValue,  out  character,  out   binary);
        }

          public   static  Dictionary<Color,  int > Analyse(Bitmap bitmap,  int  width, Dictionary<Color,  int > colors,  int  boundaryValue,  out   string  character,  out   Double binary)
        {
            colors  =  ConvertIntoPrimaryColor(bitmap, colors, boundaryValue);
            character  =  GetPrimaryColors(colors);
            binary  =  CharacterToBinary(character);
              return   colors;
        }
    }
} 


欢迎高手贴出个更加强大的图片主颜色分析算法。

同时运行10来次程序,并行计算图片的主颜色区间,这样效率提高了很多

将权限管理、工作流管理做到我能力的极致,一个人只能做好那么很少的几件事情。

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于C# 分析图片的主颜色的详细内容...

  阅读:52次