zoukankan      html  css  js  c++  java
  • 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来次程序,并行计算图片的主颜色区间,这样效率提高了很多

    将权限管理、工作流管理做到我能力的极致,一个人只能做好那么很少的几件事情。
  • 相关阅读:
    Objective-C语法之NSDictionary和NSMutableDictionary
    Objective-C语法之指针型参数
    Objective-C语法之nonatomic和atomic之间的区别
    Objective-C语法之NSSortDescriptor
    Objective-C语法之NSPredicate的使用
    SimPholders2 模拟器 App 文件路径查看工具
    清除 Xcode 项目缓存
    Xcode 6 免证书真机调试
    [转]iOS证书(.p12)和描述文件(.mobileprovision)申请
    WWDC 2015大会到来了
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2876993.html
Copyright © 2011-2022 走看看