private void getLab(int argb, out CIELABConvertor.Lab lab1) { Color c = Color.FromArgb(argb); if (!pixelMap.TryGetValue(argb, out lab1)) { lab1 = CIELABConvertor.RGB2LAB(c); pixelMap[argb] = lab1; } }
private ushort closestColorIndex(ColorPalette palette, int nMaxColors, int pixel) { ushort k = 0; ushort[] closest; Color c = Color.FromArgb(pixel); if (!closestMap.TryGetValue(pixel, out closest)) { closest = new ushort[5]; closest[2] = closest[3] = ushort.MaxValue; CIELABConvertor.Lab lab1; getLab(pixel, out lab1); for (; k < nMaxColors; k++) { Color c2 = palette.Entries[k]; CIELABConvertor.Lab lab2; getLab(c2.ToArgb(), out lab2); closest[4] = (ushort)(sqr(lab2.alpha - lab1.alpha) + CIELABConvertor.CIEDE2000(lab2, lab1)); //closest[4] = (short) (Math.abs(lab2.alpha - lab1.alpha) + Math.abs(lab2.L - lab1.L) + Math.abs(lab2.A - lab1.A) + Math.abs(lab2.B - lab1.B)); if (closest[4] < closest[2]) { closest[1] = closest[0]; closest[3] = closest[2]; closest[0] = (ushort)k; closest[2] = closest[4]; } else if (closest[4] < closest[3]) { closest[1] = (ushort)k; closest[3] = closest[4]; } } if (closest[3] == ushort.MaxValue) { closest[2] = 0; } } if (closest[2] == 0 || (rand.Next(short.MaxValue) % (closest[3] + closest[2])) <= closest[3]) { k = closest[0]; } else { k = closest[1]; } closestMap[pixel] = closest; return(k); }
private int pnnquan(int[] pixels, ColorPalette palette, int nMaxColors) { var bins = new Pnnbin[65536]; /* Build histogram */ for (int i = 0; i < pixels.Length; ++i) { // !!! Can throw gamma correction in here, but what to do about perceptual // !!! nonuniformity then? Color c = Color.FromArgb(pixels[i]); int index = getARGBIndex(pixels[i]); CIELABConvertor.Lab lab1; getLab(pixels[i], out lab1); if (bins[index] == null) { bins[index] = new Pnnbin(); } bins[index].ac += c.A; bins[index].Lc += lab1.L; bins[index].Ac += lab1.A; bins[index].Bc += lab1.B; bins[index].cnt++; } /* Cluster nonempty bins at one end of array */ int maxbins = 0; var heap = new int[65537]; for (int i = 0; i < bins.Length; ++i) { if (bins[i] == null) { continue; } float d = 1.0f / (float)bins[i].cnt; bins[i].ac *= d; bins[i].Lc *= d; bins[i].Ac *= d; bins[i].Bc *= d; bins[maxbins++] = bins[i]; } for (int i = 0; i < maxbins - 1; i++) { bins[i].fw = (i + 1); bins[i + 1].bk = i; } // !!! Already zeroed out by calloc() // bins[0].bk = bins[i].fw = 0; int h, l, l2; /* Initialize nearest neighbors and build heap of them */ for (int i = 0; i < maxbins; i++) { find_nn(bins, i); /* Push slot on heap */ double err = bins[i].err; for (l = ++heap[0]; l > 1; l = l2) { l2 = l >> 1; if (bins[h = heap[l2]].err <= err) { break; } heap[l] = h; } heap[l] = i; } /* Merge bins which increase error the least */ int extbins = maxbins - nMaxColors; for (int i = 0; i < extbins;) { Pnnbin tb = null; /* Use heap to find which bins to merge */ for (; ;) { int b1 = heap[1]; tb = bins[b1]; /* One with least error */ /* Is stored error up to date? */ if ((tb.tm >= tb.mtm) && (bins[tb.nn].mtm <= tb.tm)) { break; } if (tb.mtm == 0xFFFF) /* Deleted node */ { b1 = heap[1] = heap[heap[0]--]; } else /* Too old error value */ { find_nn(bins, b1); tb.tm = i; } /* Push slot down */ double err = bins[b1].err; for (l = 1; (l2 = l + l) <= heap[0]; l = l2) { if ((l2 < heap[0]) && (bins[heap[l2]].err > bins[heap[l2 + 1]].err)) { l2++; } if (err <= bins[h = heap[l2]].err) { break; } heap[l] = h; } heap[l] = b1; } /* Do a merge */ var nb = bins[tb.nn]; float n1 = tb.cnt; float n2 = nb.cnt; float d = 1.0f / (n1 + n2); tb.ac = d * (n1 * tb.ac + n2 * nb.ac); tb.Lc = d * (n1 * tb.Lc + n2 * nb.Lc); tb.Ac = d * (n1 * tb.Ac + n2 * nb.Ac); tb.Bc = d * (n1 * tb.Bc + n2 * nb.Bc); tb.cnt += nb.cnt; tb.mtm = ++i; /* Unchain deleted bin */ bins[nb.bk].fw = nb.fw; bins[nb.fw].bk = nb.bk; nb.mtm = 0xFFFF; } heap = null; /* Fill palette */ int k = 0; for (int i = 0; ; ++k) { CIELABConvertor.Lab lab1; lab1.alpha = (int)Math.Round(bins[i].ac); lab1.L = bins[i].Lc; lab1.A = bins[i].Ac; lab1.B = bins[i].Bc; palette.Entries[k] = CIELABConvertor.LAB2RGB(lab1); if (hasTransparency && palette.Entries[k] == m_transparentColor) { Color temp = palette.Entries[0]; palette.Entries[0] = palette.Entries[k]; palette.Entries[k] = temp; } if ((i = bins[i].fw) == 0) { break; } } return(k); }
private void find_nn(Pnnbin[] bins, int idx) { int nn = 0; double err = int.MaxValue; var bin1 = bins[idx]; int n1 = bin1.cnt; CIELABConvertor.Lab lab1; lab1.alpha = bin1.ac; lab1.L = bin1.Lc; lab1.A = bin1.Ac; lab1.B = bin1.Bc; for (int i = bin1.fw; i != 0; i = bins[i].fw) { double n2 = bins[i].cnt; double nerr2 = (n1 * n2) / (n1 + n2); if (nerr2 >= err) { continue; } CIELABConvertor.Lab lab2; lab2.alpha = bins[i].ac; lab2.L = bins[i].Lc; lab2.A = bins[i].Ac; lab2.B = bins[i].Bc; double nerr = nerr2 * sqr(lab2.alpha - lab1.alpha); if (nerr >= err) { continue; } double deltaL_prime_div_k_L_S_L = CIELABConvertor.L_prime_div_k_L_S_L(lab1, lab2); nerr += nerr2 * sqr(deltaL_prime_div_k_L_S_L); if (nerr >= err) { continue; } double a1Prime, a2Prime, CPrime1, CPrime2; double deltaC_prime_div_k_L_S_L = CIELABConvertor.C_prime_div_k_L_S_L(lab1, lab2, out a1Prime, out a2Prime, out CPrime1, out CPrime2); nerr += nerr2 * sqr(deltaC_prime_div_k_L_S_L); if (nerr >= err) { continue; } double barCPrime, barhPrime; double deltaH_prime_div_k_L_S_L = CIELABConvertor.H_prime_div_k_L_S_L(lab1, lab2, a1Prime, a2Prime, CPrime1, CPrime2, out barCPrime, out barhPrime); nerr += nerr2 * sqr(deltaH_prime_div_k_L_S_L); if (nerr >= err) { continue; } nerr += nerr2 * CIELABConvertor.R_T(barCPrime, barhPrime, deltaC_prime_div_k_L_S_L, deltaH_prime_div_k_L_S_L); if (nerr >= err) { continue; } err = nerr; nn = i; } bin1.err = err; bin1.nn = nn; }