对于jpg图像文件获得3-4个平均主色 [英] For an jpg image file get 3-4 average main colors

查看:151
本文介绍了对于jpg图像文件获得3-4个平均主色的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想要能够检测jpg图像文件的3-4种主要颜色。



以下示例图片和示例代码:



- red,black,white



- white,green,pink



- blue,yellow,black



我修改了一些代码以获取以下内容

  public static int RoundColorToGroup(int i)
{
int r =((int)Math.Round(i / 10.0))* 10;
if(r> 255)
r = 255;
return r;
}

[TestMethod]
public void AverageColorTest_WebExample()
{
Bitmap bm = new Bitmap(C:\\Users \ \XXXX\\Desktop\\example1.jpg);

int width = bm.Width;
int height = bm.Height;
int red = 0;
int green = 0;
int blue = 0;
int minDiversion = 15; //丢弃在颜色值(白色,灰色或黑色)之间至少有minDiversion差异的像素
int dropped = 0; // keep track of dropped pixels

int bppModifier = bm.PixelFormat == System.Drawing.Imaging.PixelFormat.Format24bppRgb? 3:4; //切割角,将在除32和24位图像之外的任何其他位上失败
BitmapData srcData = bm.LockBits(new System.Drawing.Rectangle(0,0,bm.Width,bm.Height),ImageLockMode.ReadOnly ,bm.PixelFormat);
int stride = srcData.Stride;
IntPtr Scan0 = srcData.Scan0;

字典< string,Int64> dicColors = new Dictionary< string,long>(); // color,pixelcount ie('#FFFFFF',100);

unsafe
{
byte * p =(byte *)(void *)Scan0;

for(int y = 0; y {
for(int x = 0; x< width; x ++)

int idx =(y * stride)+ x * bppModifier;
red = p [idx + 2];
green = p [idx + 1];
blue = p [idx];

red = RoundColorToGroup(red);
green = RoundColorToGroup(green);
blue = RoundColorToGroup(blue);

if(Math.Abs​​(red-green)> minDiversion || Math.Abs​​(red-blue)> minDiversion || Math.Abs​​(green - blue)> minDiversion)
{
string htmlColorGroup = ColorTranslator.ToHtml(Color.FromArgb(red,green,blue));

if(dicColors.ContainsKey(htmlColorGroup))
{
dicColors [htmlColorGroup] ++;
}
else
{
dicColors.Add(htmlColorGroup,1);
}
}
else
{
丢弃++;
}
}
}
}

dicColors = dicColors.OrderByDescending(x => x.Value).ToDictionary(pair => pair .Key,pair => pair.Value);

Console.WriteLine(dicColors.ElementAt(0).Key); // should ouput main color 1
Console.WriteLine(dicColors.ElementAt(1).Key); // should ouput main color 2
Console.WriteLine(dicColors.ElementAt(2).Key); // should ouput main color 3

}




  • output for example1.jpg is(#FF6E8C,#FF6482,#FA6E8C) - 3 shades of red / pink - should be red,black and white

  • (#FFDC50,#640A28,#640C28)的输出(#F0C8C8,#C8DC6E,#E6C8C8) - 粉红色和绿色的2种色调 - 应为浅粉红色,绿色,白色
  • 8C1E3C) - 3个蓝色阴影 - 应为蓝色,黄色,黑色


理想情况下需要忽略背景颜色(#FFFFFF)



可以在线复制粘贴html颜色这里

解决方案

非常感谢您的提示,并 article



这里是解决方案:




  • GetWebColors() - 填充所有已命名网络颜色的列表
    http://www.w3schools.com/html/html_colornames.asp


  • GetNamedWebColor_NearestMatch(Color) - 对于任何给定的颜色(有16,777,216!)返回最接近'Named'的网络颜色(140的140)


  • GetMainXColors



调整:ammend下面的此的代码值(即,对于色调,我们希望颜色之间有10度的变化。)

  float minHueDiff =(float)10; // 0 to 360 
float minBrightDiff =(float)0.1; // 0 to 1
float minSatDiff =(float)0.1; // 0至1

现在输出




  • 输出example1.jpg is:Gainsboro,Crimson,DarkSlateGray:now close to white,red,black

    输出example2.jpg is:Gainsboro,DarkKhaki,Pink:now close to white,green,pink


  • 输出example3.jpg is:Black,DarkSlateBlue,Gold:correct



代码:

  void AverageColorTest_WebExample_FineTuned()
{
Bitmap bm = new Bitmap(C:\\Users\\XXX\\Desktop\\\example1.jpg);

int width = bm.Width;
int height = bm.Height;
int red = 0;
int green = 0;
int blue = 0;
float minDiversion = 30/100; //丢弃在颜色值(白色,灰色或黑色)之间至少有minDiversion差异的像素
int dropped = 0; // keep track of dropped pixels

int bppModifier = bm.PixelFormat == System.Drawing.Imaging.PixelFormat.Format24bppRgb? 3:4; //切割角,将在除32和24位图像之外的任何其他位上失败
BitmapData srcData = bm.LockBits(new System.Drawing.Rectangle(0,0,bm.Width,bm.Height),ImageLockMode.ReadOnly ,bm.PixelFormat);
int stride = srcData.Stride;
IntPtr Scan0 = srcData.Scan0;

字典< Color,Int64> dicColors = new Dictionary< Color,long>(); // color,pixelcount ie('#FFFFFF',100);

unsafe
{
byte * p =(byte *)(void *)Scan0;

for(int y = 0; y {
for(int x = 0; x< width; x ++)

int idx =(y * stride)+ x * bppModifier;
red = p [idx + 2];
green = p [idx + 1];
blue = p [idx];

if(red == 255&& green == 255&& blue == 255)
continue;

Color GroupedColor = GetNamedWebColor_NearestMatch(red,green,blue);

if(dicColors.ContainsKey(GroupedColor))
{
dicColors [GroupedColor] ++;
}
else
{
dicColors.Add(GroupedColor,1);
}
}
}
}

//排序颜色字典,以便最常用的是顶部
dicColors = dicColors.OrderByDescending (x => x.Value).ToDictionary(pair => pair.Key,pair => pair.Value);

列表< Color> MainColors = null;
Int16 numberOf = 3;
float minHueDiff =(float)10;
float minBrightDiff =(float)0.1;
float minSatDiff =(float)0.1;

MainColors = GetMainXColors(dicColors.Keys.ToList(),numberOf,minHueDiff,minBrightDiff,minSatDiff);

foreach(MainColors中的Color MainColor)
{
Console.WriteLine(ColorTranslator.ToHtml(MainColor)); //应该输出主颜色
}

}

///< summary>
///返回第一个x在
中传递的min HSL属性不同的许多颜色///< / summary>
///< param name =listIn>< / param>
///< param name =ReturnMaxNumberOfColors>< / param>
///< param name =minHueDiff>< / param>
///< param name =minBrightDiff>< / param>
///< param name =minSatDiff>< / param>
///< return>< / returns>
private static List< Color> GetMainXColors(List< Color> listIn,Int32 ReturnMaxNumberOfColors,float minHueDiff,float minBrightDiff,float minSatDiff)
{
List< Color> response = new List< Color>();

Int32 i = 0;
while(response.Count< ReturnMaxNumberOfColors& i< listIn.Count)
{
bool blnUniqueMainColor = true; //想要主色,即深棕色,金色,银色,不是3色的棕色
color nextColor = listIn [i];

float brightness = nextColor.GetBrightness();
float sat = nextColor.GetSaturation();
float hue = nextColor.GetHue();

for(Int32 j = 0; j< response.Count; j ++)
{

float brightnessOther = response [j] .GetBrightness
float satOther = response [j] .GetSaturation();
float hueOther = response [j] .GetHue();

// hue是360度的颜色,以计算色调差异
//当任何一个输出180时,需要减去360(即红色在0和359,diff应该是1等等)
if(hue-hueOther> 180)hue - = 360;
if(hueOther - hue> 180)hueOther - = 360;

float brightdiff = Math.Abs​​(brightness - brightnessOther);
float satdiff = Math.Abs​​(sat - satOther);
float huediff = Math.Abs​​(hue - hueOther);
int matchHSL = 0;

if(brightdiff< = minBrightDiff)
matchHSL ++;

if(satdiff< = minSatDiff)
matchHSL ++;

if(huediff< = minHueDiff)
matchHSL ++;

if(matchHSL!= 0& satdiff!= 1))
{
blnUniqueMainColor = false;
break;
}
}
if(blnUniqueMainColor)
{//颜色不同于HSL的最小值,因此添加到响应
response.Add(nextColor);
}
i ++;
}
返回响应;
}

private static List< Color> WebColors;
///< summary>
///返回给定颜色空间中的最近颜色
///< / summary>
///< param name =input_color>要近似的颜色< / param>
///< return>最近的颜色< / returns>
public static Color GetNamedWebColor_NearestMatch(double dbl_input_red,double dbl_input_green,double dbl_input_blue)
{
//将颜色空间作为ArrayList获取
if(WebColors == null)
WebColors = GetWebColors();
//要计算的欧几里得距离
//将其设置为任意数字
//必须大于最大可能距离(约441.7)
double distance = 500.0;
//存储临时结果
double temp;
// RGB-测试颜色的值
double dbl_test_red;
double dbl_test_green;
double dbl_test_blue;
//初始化结果
color nearest_color = Color.Empty;
foreach(WebColors中的颜色o)
{
//计算两种颜色之间的欧几里德距离
//注意,在此示例中不使用alpha组件
dbl_test_red = Math.Pow(Convert.ToDouble((Color)o).R) - dbl_input_red,2.0);
dbl_test_green = Math.Pow(Convert.ToDouble((Color)o).G) - dbl_input_green,2.0);
dbl_test_blue = Math.Pow(Convert.ToDouble((Color)o).B) - dbl_input_blue,2.0);
temp = Math.Sqrt(dbl_test_blue + dbl_test_green + dbl_test_red);
//探索结果并存储最近的颜色
if(temp< distance)
{
distance = temp;
nearest_color =(Color)o;
}
}
return nearest_color;
}

///< summary>
///返回一个用WebColors填充的ArrayList
///< / summary>
///< return> WebColors< / returns>
///< remarks>< / remarks>
private static List< Color> GetWebColors()
{
List< string> listIgnore = new List< string>();
listIgnore.Add(transparent);

类型color =(typeof(Color));
PropertyInfo [] propertyInfos = color.GetProperties(BindingFlags.Public | BindingFlags.Static);
List< Color> colors = new List< Color>();

foreach(propertyInfo中的PropertyInfo pi)
{
if(pi.PropertyType.Equals(typeof(Color)))
{
颜色c = (Color)pi.GetValue((object)(typeof(Color)),null);
if(listIgnore.Contains(c.Name.ToLower()))
continue;
colors.Add(c);
}
}
返回颜色;
}


I would like to be able to detect the 3-4 main colors of a jpg image file.

Example images and sample code below:

- red, black, white

- white, green, pink

- blue, yellow, black

I have modified some code to get the below, but still has trouble grouping colors.

    public static int RoundColorToGroup(int i)
    {
        int r = ((int)Math.Round(i / 10.0)) * 10;
        if (r > 255)
            r = 255;
        return r;
    }

    [TestMethod]
    public void AverageColorTest_WebExample()
    {          
        Bitmap bm = new Bitmap("C:\\Users\\XXXX\\Desktop\\example1.jpg");

        int width           = bm.Width;
        int height          = bm.Height;
        int red             = 0;
        int green           = 0;
        int blue            = 0;
        int minDiversion    = 15; // drop pixels that do not differ by at least minDiversion between color values (white, gray or black)
        int dropped         = 0; // keep track of dropped pixels                

        int bppModifier     = bm.PixelFormat == System.Drawing.Imaging.PixelFormat.Format24bppRgb ? 3 : 4; // cutting corners, will fail on anything else but 32 and 24 bit images
        BitmapData srcData  = bm.LockBits(new System.Drawing.Rectangle(0, 0, bm.Width, bm.Height), ImageLockMode.ReadOnly, bm.PixelFormat);
        int stride          = srcData.Stride;
        IntPtr Scan0        = srcData.Scan0;

        Dictionary<string, Int64> dicColors = new Dictionary<string, long>(); // color, pixelcount i.e ('#FFFFFF',100);

        unsafe
        {
            byte* p = (byte*)(void*)Scan0;

            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    int idx = (y * stride) + x * bppModifier;
                    red     = p[idx + 2];
                    green   = p[idx + 1];
                    blue    = p[idx];

                    red     = RoundColorToGroup(red);
                    green   = RoundColorToGroup(green);
                    blue    = RoundColorToGroup(blue);

                    if (Math.Abs(red - green) > minDiversion || Math.Abs(red - blue) > minDiversion || Math.Abs(green - blue) > minDiversion)
                    {
                        string htmlColorGroup = ColorTranslator.ToHtml(Color.FromArgb(red, green, blue));

                        if (dicColors.ContainsKey(htmlColorGroup))
                        {
                            dicColors[htmlColorGroup]++;
                        }
                        else
                        {
                            dicColors.Add(htmlColorGroup, 1);
                        }
                    }
                    else
                    {
                        dropped++;
                    }
                }
            }
        }

        dicColors = dicColors.OrderByDescending(x => x.Value).ToDictionary(pair => pair.Key, pair => pair.Value);

        Console.WriteLine(dicColors.ElementAt(0).Key); // should ouput main color 1
        Console.WriteLine(dicColors.ElementAt(1).Key); // should ouput main color 2
        Console.WriteLine(dicColors.ElementAt(2).Key); // should ouput main color 3

    }

  • output for example1.jpg is (#FF6E8C, #FF6482, #FA6E8C) - 3 shades of red/pink - should be red, black and white
  • output for example2.jpg is (#F0C8C8,#C8DC6E,#E6C8C8) - 2 shades of pinkand green - should be light pink, green, white
  • output for example3.jpg is (#FFDC50,#640A28,#8C1E3C) - 3 shades of blue - should be blue, yellow, black

Ideally needs to ignore background color (#FFFFFF) and black outlining/shading.

Can copy paste html colors online here

解决方案

Many thanks to your hints, and this article.

Here is solution:

  • GetWebColors() - Populates list of all 'Named' Web Colors (http://www.w3schools.com/html/html_colornames.asp)

  • GetNamedWebColor_NearestMatch(Color) - For any given color (there are 16,777,216!) returns nearest 'Named' web color (1 of 140)

  • GetMainXColors() - Returns first X many colors that differ by min HSL properties passed in (this will stop 3 shades of same color being passed back)

Tuning: ammend below code values for this (i.e for hue, we want 10 degree min change between colors.)

    float minHueDiff        = (float)10;  // 0 to 360
    float minBrightDiff     = (float)0.1; // 0 to 1
    float minSatDiff        = (float)0.1; // 0 to 1

Now Outputs

  • output for example1.jpg is: Gainsboro, Crimson, DarkSlateGray : now close to white, red, black

  • output for example2.jpg is: Gainsboro, DarkKhaki, Pink : now close to white, green, pink

  • output for example3.jpg is: Black, DarkSlateBlue, Gold : correct

Code:

public void AverageColorTest_WebExample_FineTuned()
{                
    Bitmap bm = new Bitmap("C:\\Users\\XXX\\Desktop\\example1.jpg");

    int width = bm.Width;
    int height = bm.Height;
    int red = 0;
    int green = 0;
    int blue = 0;
    float minDiversion = 30 / 100; // drop pixels that do not differ by at least minDiversion between color values (white, gray or black)
    int dropped = 0; // keep track of dropped pixels                

    int bppModifier = bm.PixelFormat == System.Drawing.Imaging.PixelFormat.Format24bppRgb ? 3 : 4; // cutting corners, will fail on anything else but 32 and 24 bit images
    BitmapData srcData = bm.LockBits(new System.Drawing.Rectangle(0, 0, bm.Width, bm.Height), ImageLockMode.ReadOnly, bm.PixelFormat);
    int stride = srcData.Stride;
    IntPtr Scan0 = srcData.Scan0;

    Dictionary<Color, Int64> dicColors = new Dictionary<Color, long>(); // color, pixelcount i.e ('#FFFFFF',100);

    unsafe
    {
        byte* p = (byte*)(void*)Scan0;

        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                int idx = (y * stride) + x * bppModifier;
                red = p[idx + 2];
                green = p[idx + 1];
                blue = p[idx];

                if (red == 255 && green == 255 && blue == 255)
                continue;

                Color GroupedColor = GetNamedWebColor_NearestMatch(red, green, blue);

                if (dicColors.ContainsKey(GroupedColor))
                {
                    dicColors[GroupedColor]++;
                }
                else
                {
                    dicColors.Add(GroupedColor, 1);
                }
            }
        }
    }

    // sort dictionary of colors so that most used is at top
    dicColors = dicColors.OrderByDescending(x => x.Value).ToDictionary(pair => pair.Key, pair => pair.Value);

    List<Color> MainColors  = null;
    Int16 numberOf          = 3;
    float minHueDiff        = (float)10;
    float minBrightDiff     = (float)0.1;
    float minSatDiff        = (float)0.1;

    MainColors = GetMainXColors(dicColors.Keys.ToList(), numberOf, minHueDiff, minBrightDiff, minSatDiff);

    foreach (Color MainColor in MainColors)
    {
        Console.WriteLine(ColorTranslator.ToHtml(MainColor)); // should ouput main colors
    }

}   

/// <summary>
/// returns first x many colors that differ by min HSL properties passed in
/// </summary>
/// <param name="listIn"></param>
/// <param name="ReturnMaxNumberOfColors"></param>
/// <param name="minHueDiff"></param>
/// <param name="minBrightDiff"></param>
/// <param name="minSatDiff"></param>
/// <returns></returns>
private static List<Color> GetMainXColors(List<Color> listIn, Int32 ReturnMaxNumberOfColors, float minHueDiff, float minBrightDiff, float minSatDiff)
{
    List<Color> response = new List<Color>();

    Int32 i = 0;
    while (response.Count < ReturnMaxNumberOfColors && i < listIn.Count)
    {
        bool  blnUniqueMainColor = true; // want main colors ie dark brown, gold, silver, not 3 shades of brown
        Color nextColor          = listIn[i];

        float brightness    = nextColor.GetBrightness();
        float sat           = nextColor.GetSaturation();
        float hue           = nextColor.GetHue();

        for (Int32 j = 0; j < response.Count; j++)
        {

            float brightnessOther   = response[j].GetBrightness();
            float satOther          = response[j].GetSaturation();
            float hueOther          = response[j].GetHue();

            // hue is 360 degrees of color, to calculate hue difference                        
            // need to subtract 360 when either are out by 180 (i.e red is at 0 and 359, diff should be 1 etc)
            if (hue - hueOther > 180) hue -= 360;
            if (hueOther - hue > 180) hueOther -= 360;

            float brightdiff        = Math.Abs(brightness - brightnessOther);
            float satdiff           = Math.Abs(sat - satOther);
            float huediff           = Math.Abs(hue - hueOther);
            int matchHSL            = 0;

            if (brightdiff <= minBrightDiff)
                matchHSL++;

            if (satdiff <= minSatDiff)
                matchHSL++;

            if (huediff <= minHueDiff) 
                matchHSL++;

            if (matchHSL != 0  & satdiff != 1))
            {
                blnUniqueMainColor = false;
                break;
            }
        }
        if (blnUniqueMainColor)
        {       // color differs by min ammount of HSL so add to response
            response.Add(nextColor);
        }
        i++;
    }
    return response;
}   

private static List<Color> WebColors;
/// <summary>
/// Returns the "nearest" color from a given "color space"
/// </summary>
/// <param name="input_color">The color to be approximated</param>
/// <returns>The nearest color</returns>        
public static Color GetNamedWebColor_NearestMatch(double dbl_input_red, double dbl_input_green, double dbl_input_blue)
{
    // get the colorspace as an ArrayList
    if (WebColors == null) 
        WebColors = GetWebColors();
    // the Euclidean distance to be computed
    // set this to an arbitrary number
    // must be greater than the largest possible distance (appr. 441.7)
    double distance = 500.0;
    // store the interim result
    double temp;
    // RGB-Values of test colors
    double dbl_test_red;
    double dbl_test_green;
    double dbl_test_blue;
    // initialize the result
    Color nearest_color = Color.Empty;
    foreach (Color o in WebColors)
    {
        // compute the Euclidean distance between the two colors
        // note, that the alpha-component is not used in this example                
        dbl_test_red    = Math.Pow(Convert.ToDouble(((Color)o).R) - dbl_input_red, 2.0);
        dbl_test_green  = Math.Pow(Convert.ToDouble(((Color)o).G) - dbl_input_green, 2.0);
        dbl_test_blue   = Math.Pow(Convert.ToDouble(((Color)o).B) - dbl_input_blue, 2.0);
        temp            = Math.Sqrt(dbl_test_blue + dbl_test_green + dbl_test_red);
        // explore the result and store the nearest color
        if (temp < distance)
        {
            distance = temp;
            nearest_color = (Color)o;
        }
    }            
    return nearest_color;
}

/// <summary>
/// Returns an ArrayList filled with "WebColors"
/// </summary>
/// <returns>WebColors</returns>
/// <remarks></remarks>
private static List<Color> GetWebColors()
{
    List<string> listIgnore = new List<string>();
    listIgnore.Add("transparent");

    Type color = (typeof(Color));
    PropertyInfo[] propertyInfos = color.GetProperties(BindingFlags.Public | BindingFlags.Static);
    List<Color> colors = new List<Color>();

    foreach (PropertyInfo pi in propertyInfos)
    {
        if (pi.PropertyType.Equals(typeof(Color)))
        {
            Color c = (Color)pi.GetValue((object)(typeof(Color)), null);
            if (listIgnore.Contains(c.Name.ToLower()))
            continue;
            colors.Add(c);
        }
    }
    return colors;
}

这篇关于对于jpg图像文件获得3-4个平均主色的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆