private static MunsellDat FindMunsell(MunsellHue hue, double value, double chroma, bool retry = false) { var rec = Table.Where(x => x.H == hue && Math.Abs(x.V - value) < float.Epsilon && Math.Abs(x.C - chroma) < float.Epsilon).FirstOrDefault(); if (rec != null) { return(rec.Clone()); } else { if (!retry) { return(null); } //見つからなかった場合は彩度を下げて再検索 for (var i = chroma - 2.0; i >= 2.0; i -= 2.0) { var m = Table.Where(x => x.H == hue && Math.Abs(x.V - value) < float.Epsilon && Math.Abs(x.C - i) < float.Epsilon).FirstOrDefault(); if (m != null) { return(m.Clone()); } } return(null); } }
private static MunsellDat FindMunsell(MunsellHue hue, double value, double chroma, bool retry = false) { var rec = Table.Where(x => x.H.Base == hue.Base && x.H.Number == hue.Number && x.V == value && x.C == chroma).FirstOrDefault(); if (rec != null) { return(rec.Clone()); } else { if (!retry) { return(null); } //見つからなかった場合は彩度を下げて再検索 for (var i = chroma - 2.0; i >= 2.0; i -= 2.0) { var m = Table.Where(x => x.H == hue && x.V == value && x.C == i).FirstOrDefault(); if (m != null) { return(m.Clone()); } } return(null); } }
private static Lch ConvertLch(IMunsell item, MunsellHue hue) { //見込み明度 var tmpV = Math.Round(item.V, MidpointRounding.AwayFromZero); if (Math.Abs(tmpV) < float.Epsilon) { tmpV = 1.0d; } if (Math.Abs(tmpV - 10.0) < float.Epsilon) { tmpV = 9.0d; } //彩度max min var c = Table.Where(x => x.H == hue && Math.Abs(x.V - tmpV) < float.Epsilon) .GroupBy(x => x.V).Select(x => new { min = x.Min(y => y.C), max = x.Max(y => y.C) }).First(); Lch lch; //彩度の範囲内 if (c.min <= item.C && item.C <= c.max) { //2刻みの値ならテーブル値を取得 if (Enumerable.Range((int)c.min, (int)c.max) .Where(x => x % 2 == 0).Select(x => (double)x).Contains(item.C)) { lch = FindMunsell(hue, tmpV, item.C).Lch; } //上記以外は両隣の彩度を基準とする else { var lowC = Math.Floor(item.C / 2d) * 2d; var highC = Math.Ceiling(item.C / 2d) * 2d; var lowLch = FindMunsell(hue, tmpV, lowC).Lch; var highLch = FindMunsell(hue, tmpV, highC).Lch; //補完値計算 lch = GetInterpolatedValue(item, lowLch, highLch); } } //彩度の範囲外 else { var tmpC = item.C > c.max ? c.max : c.min; lch = FindMunsell(hue, tmpV, tmpC).Lch; //補完値 lch.C = lch.C / tmpC * item.C; } lch.L -= ConvertVtoL(tmpV - item.V); return(lch); }
private static bool CloseEnough(MunsellHue a, MunsellHue b) { if (a.Base == b.Base) { return(Math.Abs(a.Number - b.Number) <= 1.5); } else if ((a.Base == b.Base - 1 || a.Base - 1 == b.Base) || (a.Base == HueBase.R && b.Base == HueBase.RP) || (a.Base == HueBase.RP && b.Base == HueBase.R)) { return(Math.Abs(a.Number % 10 - b.Number % 10) <= 1.5); } else { return(false); } }
internal static IRgb ToColor(IMunsell item) { //明度max or minは白黒で返す if (item.V <= 0.0d) { return new Rgb { R = 0, G = 0, B = 0 } } ; if (item.V >= 10.0d) { return new Rgb { R = 255, G = 255, B = 255 } } ; if (item.H.Base == HueBase.N) { return(ConvertAchromatic(item)); } //2.5刻み値ジャストならマンセル検索→Lab値変換 if (System.Linq.Enumerable.Range(1, 4).Select(x => x * 2.5d).Contains(item.H.Number)) { return(ConvertLch(item, item.H).To <Rgb>()); } //2.5刻み値以外は両隣の色相から補完値を計算 var lowH = new MunsellHue(Math.Floor(item.H.Number / 2.5d) * 2.5d, item.H.Base); var highH = new MunsellHue(Math.Ceiling(item.H.Number / 2.5d) * 2.5d, item.H.Base); var rate = (item.H.Number % 2.5d) / 2.5d; //2.5の間の割合 //両隣の色相の補完値同士の補完値を求める var lch = GetInterpolatedValue(item, ConvertLch(item, lowH), ConvertLch(item, highH), rate); return(lch.To <Rgb>()); }
internal static void ToColorSpace(IRgb color, IMunsell item) { if ((int)Math.Round(color.R, 0) == (int)Math.Round(color.G, 0) && (int)Math.Round(color.G, 0) == (int)Math.Round(color.B, 0)) { var lab = color.To <Lab>(); item.H = new MunsellHue { Base = HueBase.N }; item.V = Math.Round(ConvertLtoV(lab.L), 1); return; } var lch = color.To <Lch>(); var q = Table.Select(x => new { diff = Math.Abs(x.Lch.L - lch.L) + Math.Abs(x.Lch.C - lch.C) + Math.Abs(x.Lch.H - lch.H), self = x }); var min = q.Min(x => x.diff); var munsell = q.Where(x => Math.Abs(x.diff - min) < float.Epsilon).FirstOrDefault().self; if (min < 3.0d) { item.H = munsell.H; item.V = munsell.V; item.C = munsell.C; return; } var hue = new MunsellHue { Base = munsell.H.Base, Number = munsell.H.Number }; MunsellHue newHue; if (munsell.Lch.H > lch.H) { hue.Number -= 2.5d; newHue = new MunsellHue { Base = hue.Base, Number = hue.Number }; } else { hue.Number += 2.5d; newHue = new MunsellHue { Base = munsell.H.Base, Number = munsell.H.Number }; } var munsellX = FindMunsell(hue, munsell.V, munsell.C, true); newHue.Number += Math.Round((lch.H - Math.Min(munsell.Lch.H, munsellX.Lch.H)) / Math.Abs(munsell.Lch.H - munsellX.Lch.H) * 2.5d, 1, MidpointRounding.AwayFromZero); double newChroma; //彩度max min var c = Table.Where(x => x.H == munsell.H && Math.Abs(x.V - munsell.V) < float.Epsilon) .GroupBy(x => x.V).Select(x => new { min = x.Min(y => y.C), max = x.Max(y => y.C) }).First(); if (c.min < munsell.C && munsell.C < c.max) { var chroma = munsell.Lch.C > lch.C ? munsell.C - 2.0d : munsell.C + 2.0d; var munsellY = FindMunsell(munsell.H, munsell.V, chroma); newChroma = Math.Round(Math.Min(munsell.C, munsellY.C) + (lch.C - Math.Min(munsell.Lch.C, munsellY.Lch.C)) / Math.Abs(munsell.Lch.C - munsellY.Lch.C) * 2.0d, 1, MidpointRounding.AwayFromZero); } else { newChroma = Math.Round(munsell.C / munsell.Lch.C * lch.C, 1, MidpointRounding.AwayFromZero); } var newValue = Math.Round(ConvertLtoV(lch.L), 1, MidpointRounding.AwayFromZero); item.H = newHue; item.C = newChroma; item.V = newValue; }
/// <summary> /// RGBカラーからMUNSELLカラースベースへ変換する /// </summary> /// <param name="rgb">RGBカラー</param> /// <returns>MUNSELLカラースベース</returns> internal static Munsell ToColor(Rgb rgb) { Munsell item = new Munsell(); Lch lch = LchConverter.ToColor(rgb); if (rgb.R == rgb.G && rgb.G == rgb.B) { Lab lab = LabConverter.ToColor(rgb); item.H = new MunsellHue { Base = HueBase.N }; item.V = Math.Round(ConvertLtoV(lab.L), 1); return(item); } var q = Table.Select(x => new { diff = Math.Abs(x.Lch.L - lch.L) + Math.Abs(x.Lch.C - lch.C) + Math.Abs(x.Lch.H - lch.H), self = x }); var min = q.Min(x => x.diff); var munsell = q.Where(x => x.diff == min).FirstOrDefault().self; if (min < 3.0) { item.H = munsell.H; item.V = munsell.V; item.C = munsell.C; return(item); } var hue = new MunsellHue { Base = munsell.H.Base, Number = munsell.H.Number }; MunsellHue newHue; if (munsell.Lch.H > lch.H) { hue.Number -= 2.5; newHue = new MunsellHue { Base = hue.Base, Number = hue.Number }; } else { hue.Number += 2.5; newHue = new MunsellHue { Base = munsell.H.Base, Number = munsell.H.Number }; } var munsellX = FindMunsell(hue, munsell.V, munsell.C, true); newHue.Number += Math.Round((lch.H - Math.Min(munsell.Lch.H, munsellX.Lch.H)) / Math.Abs(munsell.Lch.H - munsellX.Lch.H) * 2.5, 1, MidpointRounding.AwayFromZero); double newChroma; //彩度max min var c = Table.Where(x => x.H == munsell.H && x.V == munsell.V) .GroupBy(x => x.V).Select(x => new { min = x.Min(y => y.C), max = x.Max(y => y.C) }).First(); if (c.min < munsell.C && munsell.C < c.max) { var chroma = munsell.Lch.C > lch.C ? munsell.C - 2.0 : munsell.C + 2.0; var munsellY = FindMunsell(munsell.H, munsell.V, chroma); newChroma = Math.Round(Math.Min(munsell.C, munsellY.C) + (lch.C - Math.Min(munsell.Lch.C, munsellY.Lch.C)) / Math.Abs(munsell.Lch.C - munsellY.Lch.C) * 2.0, 1, MidpointRounding.AwayFromZero); } else { newChroma = Math.Round(munsell.C / munsell.Lch.C * lch.C, 1, MidpointRounding.AwayFromZero); } var newValue = Math.Round(ConvertLtoV(lch.L), 1, MidpointRounding.AwayFromZero); item.H = newHue; item.C = newChroma; item.V = newValue; return(item); }