对于jpg图像文件获得3-4个平均主色 [英] For an jpg image file get 3-4 average main colors
问题描述
我想要能够检测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屋!