Example #1
0
        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;
            }
        }
Example #2
0
        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);
        }
Example #3
0
        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);
        }
Example #4
0
        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;
        }