PDA

View Full Version : RGB to Palette mapping



cardano7
دوشنبه 02 اسفند 1389, 11:33 صبح
سلام
من می خواهم یک متغیر رنگ را به یک palette که مجموعه ای از رنگ هاست نگاشت کنم. شبیه هر رنگی که بود، اون را انتخاب کنه.
در ابتدا برنامه ای نوشتم که این کار را بکنه اما کار به اون سادگی که فکر می کردم نبود.
یک کلاس دارم برای ذخیره ی نام و مقدار رنگ ها:



public class ColorValue
{
public string name;
public Color color;
public ColorValue(string name, Color color)
{
if (name.IndexOf("Color [") >= 0)
name = name.Substring("Color [".Length, name.Length - 1 - "Color [".Length);
this.name = name;
this.color = color;
}
}

و بعد رنگ هایی را که می خواهم به مجموعه اضافه می کنم:



List<ColorValue> color_value = new List<ColorValue>();

color_value.Add(new ColorValue(Color.Black.ToString(), Color.Black));
color_value.Add(new ColorValue(Color.Blue.ToString(), Color.Blue));
color_value.Add(new ColorValue(Color.Blue.ToString(), Color.DarkBlue));
color_value.Add(new ColorValue(Color.Blue.ToString(), Color.LightBlue));
color_value.Add(new ColorValue(Color.Brown.ToString(), Color.Brown));
color_value.Add(new ColorValue(Color.Cyan.ToString(), Color.Cyan));
color_value.Add(new ColorValue(Color.Cyan.ToString(), Color.DarkCyan));
color_value.Add(new ColorValue(Color.Cyan.ToString(), Color.LightCyan));
color_value.Add(new ColorValue(Color.Gray.ToString(), Color.Gray));
color_value.Add(new ColorValue(Color.Gray.ToString(), Color.DarkGray));
color_value.Add(new ColorValue(Color.Gray.ToString(), Color.LightGray));
color_value.Add(new ColorValue(Color.Green.ToString(), Color.Green));
color_value.Add(new ColorValue(Color.Green.ToString(), Color.DarkGreen));
color_value.Add(new ColorValue(Color.Green.ToString(), Color.LightGreen));
color_value.Add(new ColorValue(Color.Orange.ToString(), Color.Orange));
color_value.Add(new ColorValue(Color.Pink.ToString(), Color.Pink));
color_value.Add(new ColorValue(Color.Purple.ToString(), Color.Purple));
color_value.Add(new ColorValue(Color.Red.ToString(), Color.Red));
color_value.Add(new ColorValue(Color.White.ToString(), Color.White));
color_value.Add(new ColorValue(Color.Yellow.ToString(), Color.Yellow));
color_value.Add(new ColorValue(Color.Yellow.ToString(), Color.Wheat));


در ابتدا برنامه را این طوری نوشتم که بیاد ببینه کدوم فاصله ی قرمز و سبز و آبی اون به رنگ های پلت نزدیک تره و اون را انتخاب کنه:



double diff = double.MaxValue;
int index = -1;

foreach (ColorValue cv in color_value)
{
double dr = (cv.color.R - color.R);
double dg = (cv.color.G - color.G);
double db = (cv.color.B - color.B);
double d = dr * dr + dg * dg + db * db;
if (d < diff)
{
index = ColorItem.IndexOf(cv.name);
diff = d;
}
}


اما این راه یک سری مشکلات داره. مثلا گاهی رنگ من آبی خیلی روشن هست و به همین دلیل با سفید اشتباه گرفته میشه. برای همین اومدم و شدت رنگ را هم دخالت دادم . یعنی اول عامل شدت را کنسل می کنم و اون را به عنوان پارامتر چهارم در کنار R , G , B به حساب میارم. و با رنگ های پالت مقایسه می کنم:


double diff = double.MaxValue;
int index = -1;

foreach (ColorValue cv in color_value)
{
double I_cv_color = Math.Sqrt(cv.color.R * cv.color.R + cv.color.G * cv.color.G + cv.color.B * cv.color.B + 1);
double I_color = Math.Sqrt(color.R * color.R + color.G * color.G + color.B * color.B + 1);
double I_Mut = Math.Sqrt(I_cv_color * I_color);
double dr = (cv.color.R * I_color - color.R * I_cv_color) / I_Mut;
double dg = (cv.color.G * I_color - color.G * I_cv_color) / I_Mut;
double db = (cv.color.B * I_color - color.B * I_cv_color) / I_Mut;
double di = I_cv_color - I_color;
double d = dr * dr + dg * dg + db * db + di * di;
if (d < diff)
{
index = ColorItem.IndexOf(cv.name);
diff = d;
}
}

با این روش نتیجه بهتر میشه اما هنوز ایراد داره مثلا خیلی رنگ ها مثل آبی و فیروزه ای را با هم قاطی می کنه.

آیا کسی روش استانداردی برای این کار سراغ داره؟ توجه داشته باشید که باید پلت دلخواه باشه و برنامه نباید برای یک پلت از پیش تعیین شده نوشته بشه. چون این بخش قراره مقدار اولیه ی یک شبکه عصبی را تعیین کنه و کوانتیزه کردن رنگ روی یک پلت از پیش مشخص این برنامه را بی معنی می کنه. هر چند این برنامه را دارم برای دل خودم می نویسم و نه استادی که بهم بخواد گیر بده.

mehdi.mousavi
دوشنبه 02 اسفند 1389, 14:01 عصر
سلام.
خیلی خوشم اومد ازاین سوال، برای همین قدری در موردش تحقیق کردم تا ببینم روش صحیح انجام این کار چی هستش.

میزان تفاوت دو رنگ نسبت به همدیگه (در Lab Color Space (http://en.wikipedia.org/wiki/Lab_color_space)) رو میگن dE یا Delta-E (http://www.colorwiki.com/wiki/Delta_E:_The_Color_Difference) و dE 1.0 حداقل تفاوتی هستش که چشم انسان قادر به تشخیص اونه. یعنی اگر این میزان .9 باشه، چشم (انسان) نمیتونه تفاوت رو تشخیص بده. ما برای سنجش تفاوت دو رنگ، چهار روش (http://en.wikipedia.org/wiki/Color_difference) داریم:


CIE76
CIE94
CIEDE2000
CMC I

شما ابتدا باید RGB مورد نظر رو با فرمولی (http://cookbooks.adobe.com/post_Useful_color_equations__RGB_to_LAB_converter-14227.html) به Lab Color تبدیل کنید.
سپس، تفاوت میزان CIE76 (یا بر اساس دیگر فرمولهای فوق الذکر) تک تک رنگهای موجود در Pallet رو با رنگ بدست اومده بررسی کنید.
هر چی این تفاوت کمتر باشه، اون رنگ به رنگ موجود در Pallet شما نزدیک تره.

موفق باشید.