Beispiel #1
0
        private Dictionary <String, List <PaletteData> > LoadFilePalettes(String file)
        {
            //load art palettes
            String[] lines = File.ReadAllLines(file);

            Dictionary <String, List <PaletteData> > plist = new Dictionary <String, List <PaletteData> >();

            for (int i = 1; i < lines.Count(); i++)
            {
                String      line   = lines[i];
                String[]    fields = line.Replace("\"", "").Split('\t');
                PaletteData data   = new PaletteData();
                data.id        = Int32.Parse(fields[0]);
                data.workerNum = Int32.Parse(fields[1]);
                String   key    = fields[2];
                String[] colors = fields[3].Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
                foreach (String s in colors)
                {
                    String[] comp = s.Split(',');
                    Color    c    = Color.FromArgb(Int32.Parse(comp[0]), Int32.Parse(comp[1]), Int32.Parse(comp[2]));
                    CIELAB   l    = Util.RGBtoLAB(c);
                    data.colors.Add(c);
                    data.lab.Add(l);
                }
                if (!plist.ContainsKey(key))
                {
                    plist.Add(key, new List <PaletteData>());
                }
                plist[key].Add(data);
            }
            return(plist);
        }
        public static Color ToColor(CIELAB fab)
        {
            Color ret = fab.CovnertTo();

            ret.a = fab.alpha;
            return(ret);
        }
Beispiel #3
0
 public Cluster()
 {
     lab = new CIELAB();
     sumlab = new CIELAB();
     count = 0;
     id = -1;
 }
Beispiel #4
0
 public void AddColor(CIELAB color, double weight=1)
 {
     sumlab.L += color.L*weight;
     sumlab.A += color.A*weight;
     sumlab.B += color.B*weight;
     count += weight;
 }
Beispiel #5
0
            public double DistanceTo(CIELAB c)
            {
                double dL = L - c.L;
                double da = a - c.a;
                double db = b - c.b;

                return(Math.Sqrt(0.5 * dL * dL + da * da + db * db));
            }
        public static CIELAB FromColor(Color color)
        {
            CIELAB ret = new CIELAB();

            ret.CovnertFrom(color);
            ret.alpha = color.a;
            return(ret);
        }
Beispiel #7
0
        public static CIELAB ConvertToCIELAB(Color c)
        {
            if (cielabLookup.ContainsKey(c))
            {
                return(cielabLookup[c]);
            }
            double rf = ConvertChannel(c.R) * 100;
            double gf = ConvertChannel(c.G) * 100;
            double bf = ConvertChannel(c.B) * 100;

            double var_X = rf * 0.4124 + gf * 0.3576 + bf * 0.1805;
            double var_Y = rf * 0.2126 + gf * 0.7152 + bf * 0.0722;
            double var_Z = rf * 0.0193 + gf * 0.1192 + bf * 0.9505;

            var_X = var_X / 95.047;          //Observer = 2°, Illuminant = D65
            var_Y = var_Y / 100.000;
            var_Z = var_Z / 108.883;

            double toPow = 1.0 / 3.0;

            if (var_X > 0.008856)
            {
                var_X = Math.Pow(var_X, toPow);
            }
            else
            {
                var_X = (7.787 * var_X) + (16 / 116);
            }
            if (var_Y > 0.008856)
            {
                var_Y = Math.Pow(var_Y, toPow);
            }
            else
            {
                var_Y = (7.787 * var_Y) + (16 / 116);
            }
            if (var_Z > 0.008856)
            {
                var_Z = Math.Pow(var_Z, toPow);
            }
            else
            {
                var_Z = (7.787 * var_Z) + (16 / 116);
            }

            CIELAB ans = new CIELAB();

            ans.L = (116 * var_Y) - 16;
            ans.a = 500 * (var_X - var_Y);
            ans.b = 200 * (var_Y - var_Z);

            cielabLookup[c] = ans;
            return(ans);
        }
        CIELAB[,] template; //relative LAB difference (or change to RGB difference?)

        #endregion Fields

        #region Constructors

        public ColorTemplate(Bitmap image, PaletteData palette)
        {
            //create the segmentation based on the palette
            //TODO: There's a problem that sometimes a palette color won't appear in the image (perhaps due to color blending), and so the slot will have no pixels associated with it
            int width = image.Width;
            int height = image.Height;
            slots = new Segmentation(palette.colors.Count(), width, height);
            segToColor = new int[slots.numSegments];

            template = new CIELAB[width, height];

            CIELAB[,] recolored = ModeRecolor(Util.Map<Color, CIELAB>(Util.BitmapToArray(image), c => Util.RGBtoLAB(c)), palette);

            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {
                    template[i, j] = new CIELAB();
                    int bestIdx = -1;
                    CIELAB lab = recolored[i, j];
                    double bestDist = Double.PositiveInfinity;

                    for (int c = 0; c < palette.lab.Count(); c++)
                    {
                        double dist = palette.lab[c].SqDist(lab);
                        if (dist < bestDist)
                        {
                            bestDist = dist;
                            bestIdx = c;

                            if (dist < 0.00001)
                                break;

                        }
                    }

                    slots.assignments[i, j] = bestIdx;
                    slots.counts[bestIdx]++;
                    segToColor[bestIdx] = bestIdx;
                }
            }

            originalPalette = new PaletteData(palette);
        }
        private bool ProcessImage(Color[,] image, CIELAB[,] imageLAB)
        {
            double thresh = 5;
            UnionFind<CIELAB> uf = new UnionFind<CIELAB>((a,b)=>(a.SqDist(b)<=thresh));
            int[,] assignments = uf.ConnectedComponents(imageLAB);//Util.Map<Color, CIELAB>(image, Util.RGBtoLAB));
            int numC = -1;
            for(int i=0; i<image.GetLength(0); i++)
                for (int j=0; j<image.GetLength(1); j++)
                    numC = Math.Max(numC, assignments[i,j]+1);
            if (numC >= 2)
                RemoveBackground(image, imageLAB);

            //if it is a black and white image (with num connected components >= 2), it's not a valid color image
            return !(isBlackWhite(image, imageLAB) && numC >= 2);
        }
        private Dictionary<Features, double> CalculateFeatures(PaletteData data, FeatureParams options)
        {
            String log = dir + "/out/timelog.txt";
            Stopwatch watch = new Stopwatch();

            //unpack options
            SortedSet<Features> included = options.included;
            CIELAB[,] imageLAB = options.imageLAB;
            double[,] map = options.map;
            double[] colorToSaliency = options.colorToSaliency;
            int[] colorToCounts = options.colorToCounts;
            double totalSaliency = options.totalSaliency;
            ColorNames colorNames = options.colorNames;
            double factor = options.factor;
            double[] colorToCNSaliency = options.colorToCNSaliency;
            double minE = options.minE;
            double maxE = options.maxE;
            Dictionary<CIELAB, int> swatchIndexOf = options.swatchIndexOf;
            Dictionary<Tuple<int, int>, double> imageSwatchDist = options.imageSwatchDist;
            double afactor = options.afactor;
            double[] swatchToPurity = options.swatchToPurity;
            Dictionary<int, double> pToScore = options.pToScore;
            double maxN = options.maxN;
            double avgN = options.avgN;
            double avgE = options.avgE;
            double[] segToSaliency = options.segmentToSaliency;
            Segmentation seg = options.seg;
            int[] swatchIdxToBin = options.swatchIdxToBin;
            int[,] binAssignments = options.binAssignments;
            double totalCapturable = options.totalCapturableSaliency;
            double[,] pixelToDists = options.pixelToDists;
            double[,] pixelToCNDists = options.pixelToNDists;

            double[] segmentToSD = options.segmentToSD;
            double totalSD = options.totalSD;

            //calculate the palette features
            Dictionary<Features, double> features = new Dictionary<Features, double>();

            double sdmin2 = 1;
            double sdmax2 = 0;
            double sdavg2 = 0;

            int width = options.imageLAB.GetLength(0);
            int height = options.imageLAB.GetLength(1);

            int n = data.lab.Count();

            double[] elementToSaliency = new double[n];
            int[] elementToCount = new int[n];

            //Calculate the error of recoloring the image, each pixel weighted the same, and subtract from 1
            double error = 0;
            //Coverage weighted by saliency of pixel
            double errorWeighted = 0;

            //error of recoloring the image, distance is the names distance metric
            double nerror = 0;
            double nerrorWeighted = 0;

            double errorSeg = 0;
            double errorSegSal = 0;

            double colorsPSeg = 0;
            double colorsPSegW = 0;

            double withinVariance = 0;
            double betweenVariance = 0;

            double sqerror = 0;
            double sqerrorW = 0;
            double sqerrorSeg = 0;
            double sqerrorSegSal = 0;

            double nsqerrorSeg = 0;
            double nsqerrorSegSal = 0;

            double nerrorSeg = 0;
            double nerrorSegSal = 0;

            double nsqerror = 0;
            double nsqerrorW = 0;

            double softerror = 0;
            double softwerror = 0;
            double softerrorseg = 0;
            double softerrorwseg = 0;
            double nsofterror = 0;
            double nsoftwerror = 0;
            double nsofterrorseg = 0;
            double nsofterrorwseg = 0;

            double nmeansegerror = 0;
            double nmeansegwerror = 0;

            double softerrorsegsd = 0;
            double nsofterrorsegsd = 0;
            double errorsegsd = 0;
            double nerrorsegsd = 0;
            double nsqerrorsegsd = 0;
            double sqerrorsegsd = 0;
            double meansegsd = 0;
            double nmeansegsd = 0;

            watch.Start();

            int numPixels = width * height;

            //compute the memberships
            double[,] memberships = new double[numPixels, n];
            double[,] nmemberships = new double[numPixels, n];

            //LAB, and SAT coverage
            double[] Lbounds = new double[] { double.PositiveInfinity, double.NegativeInfinity };
            double[] Abounds = new double[] { double.PositiveInfinity, double.NegativeInfinity };
            double[] Bbounds = new double[] { double.PositiveInfinity, double.NegativeInfinity };
            double[] Satbounds = new double[] { double.PositiveInfinity, double.NegativeInfinity };

            for (int i = 0; i < n; i++)
            {
                CIELAB lab = data.lab[i];
                Color c = data.colors[i];
                double s = c.GetSaturation();

                Lbounds[0] = Math.Min(Lbounds[0], lab.L);
                Lbounds[1] = Math.Max(Lbounds[1], lab.L);

                Abounds[0] = Math.Min(Abounds[0], lab.A);
                Abounds[1] = Math.Max(Abounds[1], lab.A);

                Bbounds[0] = Math.Min(Bbounds[0], lab.B);
                Bbounds[1] = Math.Max(Bbounds[1], lab.B);

                Satbounds[0] = Math.Min(Satbounds[0], s);
                Satbounds[1] = Math.Max(Satbounds[1], s);

            }
            double Lspan = Lbounds[1] - Lbounds[0];
            double Aspan = Abounds[1] - Abounds[0];
            double Bspan = Bbounds[1] - Bbounds[0];
            double Satspan = Satbounds[1] - Satbounds[0];

            double Lcov = Math.Min(Lspan, options.Lspan) / Math.Max(Math.Max(Lspan, options.Lspan), 1);
            double Acov = Math.Min(Aspan, options.Aspan) / Math.Max(Math.Max(Aspan, options.Aspan), 1);
            double Bcov = Math.Min(Bspan, options.Bspan) / Math.Max(Math.Max(Bspan, options.Bspan), 1);
            double Scov = Math.Min(Satspan, options.Satspan) / Math.Max(Math.Max(Satspan, options.Satspan), 1);

            double meansegError = 0;
            double meansegErrorWeighted = 0;

            double[,] colorSegDist = new double[n, seg.numSegments];

            double[] segToDist = new double[seg.numSegments];

            for (int j = 0; j < seg.numSegments; j++)
            {
                //find the closest color for each segment
                double bestDist = Double.PositiveInfinity;
                for (int i = 0; i < n; i++)
                {
                    colorSegDist[i, j] = Math.Sqrt(data.lab[i].SqDist(seg.segToMeanColor[j]));
                    segToDist[j] += colorSegDist[i, j];

                    bestDist = Math.Min(colorSegDist[i, j], bestDist);
                }

                double nbestDist = Double.PositiveInfinity;
                for (int i = 0; i < n; i++)
                {
                    double ndist = GetCNDist(colorNames.GetBin(data.lab[i]), colorNames.GetBin(seg.segToMeanColor[j]));
                    nbestDist = Math.Min(ndist, nbestDist);

                }

                meansegError += bestDist;
                meansegErrorWeighted += bestDist * segToSaliency[j];

                nmeansegerror += nbestDist;
                nmeansegwerror += nbestDist * segToSaliency[j];

                meansegsd += bestDist * segmentToSD[j];
                nmeansegsd += nbestDist * segmentToSD[j];

            }

            meansegError /= seg.numSegments * factor;
            meansegErrorWeighted /= totalSaliency * factor;

            nmeansegerror /= seg.numSegments;
            nmeansegwerror /= totalSaliency;

            meansegsd /= totalSD * factor;
            nmeansegsd /= totalSD;

            //now calculate entropy per segment
            //probability is 1-(dist/totaldist)
            double[] segEntropy = new double[seg.numSegments];
            for (int i = 0; i < seg.numSegments; i++)
            {
                for (int j = 0; j < n; j++)
                {
                    double prob = 1 - colorSegDist[j, i] / segToDist[i];
                    segEntropy[i] += prob * Math.Log(prob);
                }
            }

            //average and/or weight by saliency of segment
            for (int i = 0; i < seg.numSegments; i++)
            {
                colorsPSeg += segEntropy[i];
                colorsPSegW += segEntropy[i] * segToSaliency[i];
            }

            colorsPSeg /= seg.numSegments;
            colorsPSegW /= totalSaliency;

            double[,] nsubs = new double[width, height];
            double[,] subs = new double[width, height];

            if (included.Overlaps(options.NameCovFeatures) ||
                included.Overlaps(options.CoverageFeatures) ||
                included.Overlaps(options.SaliencyDensFeatures))
            {

                double[,] errors = new double[width, height];
                int[,] assignment = new int[width, height]; //the pixel to the nearest palette color

                double[,] nerrors = new double[width, height];
                double[,] nerrorsWeighted = new double[width, height];

                double[] clusterSqError = new double[n];

                int[,] nassignment = new int[width, height];

                Parallel.For(0, width, i =>
               {
                   for (int j = 0; j < height; j++)
                   {
                       double bestDist = Double.PositiveInfinity;
                       int bestIdx = -1;
                       for (int s = 0; s < data.lab.Count(); s++)
                       {
                           double dist = data.lab[s].SqDist(options.imageLAB[i, j]);
                           if (dist < bestDist)
                               bestIdx = s;
                           bestDist = Math.Min(dist, bestDist);

                       }
                       assignment[i, j] = bestIdx;
                       double sqrtBestDist = Math.Sqrt(bestDist);
                       errors[i, j] = sqrtBestDist;
                       clusterSqError[bestIdx] += bestDist;
                   }

                   if (included.Overlaps(options.NameCovFeatures))
                   {
                       for (int j = 0; j < height; j++)
                       {
                           int bestIdx = -1;
                           double bestDist = Double.PositiveInfinity;
                           for (int s = 0; s < data.lab.Count(); s++)
                           {
                               int sIdx = swatchIndexOf[data.lab[s]];
                               double dist = GetCNDist(binAssignments[i, j], swatchIdxToBin[sIdx]);//imageSwatchDist[new Tuple<int, int>(binAssignments[i,j], swatchIdxToBin[sIdx])]; //imageSwatchDist[new Tuple<int, int>(colorNames.GetBin(imageLAB[i, j]), colorNames.GetBin(data.lab[s]))];

                               if (dist < bestDist)
                                   bestIdx = s;

                               bestDist = Math.Min(dist, bestDist);
                           }
                           double val = map[i, j];
                           nerrors[i, j] = bestDist;
                           nerrorsWeighted[i, j] = bestDist * val;
                           nassignment[i, j] = bestIdx;
                       }
                   }

                   double epsilon = 0.0001;

                   for (int j = 0; j < height; j++)
                   {
                       //calculate the memberships
                       double[] dists = new double[n];
                       double[] ndists = new double[n];

                       double subtotal = 0;
                       double nsubtotal = 0;

                       int idx = j * width + i;

                       for (int k = 0; k < n; k++)
                       {
                           int sidx = swatchIndexOf[data.lab[k]];

                           dists[k] = Math.Max(pixelToDists[idx, sidx], epsilon);
                           ndists[k] = Math.Max(pixelToCNDists[idx, sidx], epsilon);

                           subtotal += (1.0 / dists[k]);
                           nsubtotal += (1.0 / ndists[k]);
                       }
                       for (int k = 0; k < n; k++)
                       {
                           memberships[idx, k] = 1.0 / (dists[k] * subtotal);
                           nmemberships[idx, k] = 1.0 / (ndists[k] * nsubtotal);
                       }

                       for (int k = 0; k < n; k++)
                       {
                           int sidx = swatchIndexOf[data.lab[k]];

                           double u = memberships[idx, k];
                           subs[i, j] += u * u * Math.Max(pixelToDists[idx, sidx], epsilon);

                           double nu = nmemberships[idx, k];
                           nsubs[i, j] += nu * nu * Math.Max(pixelToCNDists[idx, sidx], epsilon);
                       }
                   }

               });

                //find J

                double[] segToJ = new double[seg.numSegments];
                double[] nsegToJ = new double[seg.numSegments];

                for (int i = 0; i < width; i++)
                {
                    for (int j = 0; j < height; j++)
                    {
                        int idx = j * width + i;

                        double sub = subs[i, j];
                        double nsub = nsubs[i, j];

                        softerror += sub;
                        nsofterror += nsub;

                        softwerror += sub * map[i, j];
                        nsoftwerror += nsub * map[i, j];

                        int segIdx = seg.assignments[i, j];
                        segToJ[segIdx] += sub;
                        nsegToJ[segIdx] += nsub;
                    }
                }

                softerror /= numPixels * factor * factor;
                nsofterror /= numPixels;

                softwerror /= totalSaliency * factor * factor;
                nsoftwerror /= totalSaliency;

                for (int s = 0; s < seg.numSegments; s++)
                {
                    softerrorseg += segToJ[s] / seg.counts[s];
                    softerrorwseg += segToSaliency[s] * segToJ[s] / seg.counts[s];

                    nsofterrorseg += nsegToJ[s] / seg.counts[s];
                    nsofterrorwseg += segToSaliency[s] * nsegToJ[s] / seg.counts[s];

                    softerrorsegsd += segmentToSD[s] * segToJ[s] / seg.counts[s];
                    nsofterrorsegsd += segmentToSD[s] * nsegToJ[s] / seg.counts[s];

                }
                softerrorseg /= seg.numSegments * factor * factor;
                softerrorwseg /= totalSaliency * factor * factor;

                nsofterrorseg /= seg.numSegments;
                nsofterrorwseg /= totalSaliency;

                softerrorsegsd /= totalSD * factor * factor;
                nsofterrorsegsd /= totalSD;

                //calculate within and between palette cluster variance
                //between variance
                CIELAB betweenMean = new CIELAB();

                for (int j = 0; j < n; j++)
                {
                    betweenMean = betweenMean + data.lab[j];
                }
                betweenMean = betweenMean / (double)n;
                for (int j = 0; j < n; j++)
                {
                    betweenVariance += betweenMean.SqDist(data.lab[j]);
                }
                betweenVariance /= (double)n * factor * factor;

                double[] counts = new double[n];

                for (int i = 0; i < width; i++)
                {
                    for (int j = 0; j < height; j++)
                    {
                        counts[assignment[i, j]]++;
                    }
                }

                for (int i = 0; i < n; i++)
                {
                    withinVariance += clusterSqError[i] / Math.Max(1, counts[i]);
                }
                withinVariance /= n * (factor * factor);

                double[] segmentToError = new double[segToSaliency.Count()];
                double[] segToSqError = new double[segToSaliency.Count()];
                Dictionary<int, SortedSet<int>> segToColors = new Dictionary<int, SortedSet<int>>();

                //now aggregate
                for (int i = 0; i < width; i++)
                {
                    for (int j = 0; j < height; j++)
                    {
                        double sqError = errors[i, j] * errors[i, j];
                        double val = map[i, j];
                        errorWeighted += val * errors[i, j];
                        sqerrorW += val * sqError;
                        error += errors[i, j];
                        sqerror += sqError;

                        int idx = assignment[i, j];
                        if (idx >= 0)
                        {
                            elementToSaliency[idx] += val;
                            elementToCount[idx]++;
                        }
                        int segIdx = seg.assignments[i, j];
                        segmentToError[segIdx] += errors[i, j] / seg.counts[segIdx];
                        segToSqError[segIdx] += sqError / seg.counts[segIdx];

                    }
                }

                for (int i = 0; i < seg.numSegments; i++)
                {

                    errorSeg += segmentToError[i];
                    errorSegSal += segmentToError[i] * segToSaliency[i];

                    sqerrorSeg += segToSqError[i];
                    sqerrorSegSal += segToSqError[i] * segToSaliency[i]; //TODO: this is weighted by segment percent salient density

                    errorsegsd += segmentToError[i] * segmentToSD[i];
                    sqerrorsegsd += segToSqError[i] * segmentToSD[i];

                }

                errorSeg /= seg.numSegments * factor;
                errorSegSal /= totalSaliency * factor;

                sqerrorSeg /= seg.numSegments * factor * factor;
                sqerrorSegSal /= totalSaliency * factor * factor;

                errorWeighted /= totalSaliency * factor;
                error /= width * height * factor;

                sqerrorW /= totalSaliency * factor * factor;
                sqerror /= width * height * factor * factor;

                errorsegsd /= totalSD * factor;
                sqerrorsegsd /= totalSD * factor * factor;

                //now sum it all up
                for (int i = 0; i < width; i++)
                {
                    for (int j = 0; j < height; j++)
                    {
                        nerrorWeighted += nerrorsWeighted[i, j];
                        nerror += nerrors[i, j];
                    }
                }

                nerrorWeighted /= totalSaliency;
                nerror /= width * height;

                double[] nsegmentToError = new double[segToSaliency.Count()];
                double[] nsegToSqError = new double[segToSaliency.Count()];

                //now aggregate
                for (int i = 0; i < width; i++)
                {
                    for (int j = 0; j < height; j++)
                    {
                        double sqError = nerrors[i, j] * nerrors[i, j];
                        double val = map[i, j];
                        nerrorWeighted += val * nerrors[i, j];
                        nsqerrorW += val * sqError;
                        nerror += nerrors[i, j];
                        nsqerror += sqError;

                        int idx = nassignment[i, j];
                        if (idx >= 0)
                        {
                            elementToSaliency[idx] += val;
                            elementToCount[idx]++;
                        }
                        int segIdx = seg.assignments[i, j];
                        nsegmentToError[segIdx] += nerrors[i, j] / seg.counts[segIdx];
                        nsegToSqError[segIdx] += sqError / seg.counts[segIdx];

                    }
                }

                for (int i = 0; i < seg.numSegments; i++)
                {

                    nerrorSeg += nsegmentToError[i];
                    nerrorSegSal += nsegmentToError[i] * segToSaliency[i];

                    nsqerrorSeg += nsegToSqError[i];
                    nsqerrorSegSal += nsegToSqError[i] * segToSaliency[i]; //TODO: this is weighted by segment percent salient density

                    nerrorsegsd += nsegmentToError[i] * segmentToSD[i];
                    nsqerrorsegsd += nsegToSqError[i] * segmentToSD[i];

                }

                nerrorSeg /= seg.numSegments;
                nerrorSegSal /= totalSaliency;

                nsqerrorSeg /= seg.numSegments;
                nsqerrorSegSal /= totalSaliency;

                nerrorWeighted /= totalSaliency;
                nerror /= width * height;

                nsqerrorW /= totalSaliency;
                nsqerror /= width * height;

                nerrorsegsd /= totalSD;
                nsqerrorsegsd /= totalSD;

            }
            Log(log, "Coverage and Name Coverage" + watch.ElapsedMilliseconds);
            watch.Restart();

            for (int i = 0; i < n; i++)
            {
                double dens = elementToSaliency[i] / Math.Max(1, elementToCount[i]);
                sdmin2 = Math.Min(dens, sdmin2);
                sdmax2 = Math.Max(dens, sdmax2);
                sdavg2 += dens;
            }
            sdavg2 /= data.lab.Count();

            Log(log, "SaliencyDensClust " + watch.ElapsedMilliseconds);
            watch.Restart();

            //total captured salience (salience sum/salience of the image)
            double stotal = 0;

            //average salience density of each swatch (and min/max)
            double sdmin = 1;
            double sdmax = 0;
            double sdavg = 0;

            foreach (CIELAB c in data.lab)
            {
                double val = colorToSaliency[swatchIndexOf[c]] / Math.Max(1, colorToCounts[swatchIndexOf[c]]);
                sdmin = Math.Min(val, sdmin);
                sdmax = Math.Max(val, sdavg);
                sdavg += val;
                stotal += colorToSaliency[swatchIndexOf[c]];
            }
            sdavg /= data.lab.Count();
            stotal /= totalCapturable;

            Log(log, "SaliencyDens " + watch.ElapsedMilliseconds);
            watch.Restart();

            double cnmin = 1;
            double cnmax = 0;
            double cnavg = 0;

            for (int i = 0; i < n; i++)
            {
                for (int j = i + 1; j < n; j++)
                {
                    double dist = 1 - colorNames.CosineDistance(data.lab[i], data.lab[j]);
                    cnavg += dist;
                    cnmin = Math.Min(cnmin, dist);
                    cnmax = Math.Max(cnmax, dist);
                }
            }
            cnavg /= n * (n - 1) / 2;

            Log(log, "NamesDiff " + watch.ElapsedMilliseconds);
            watch.Restart();

            //Calculate the color name diversity, avg color name distance to closest neighbor
            double cndiv = 0;

            for (int c = 0; c < n; c++)
            {
                double bestDist = 1;
                for (int i = 0; i < n; i++)
                {
                    if (c == i)
                        continue;
                    double curDist = 1 - colorNames.CosineDistance(data.lab[i], data.lab[c]);
                    if (curDist < bestDist)
                        bestDist = curDist;
                }

                cndiv += bestDist / n;
            }

            Log(log, "NamesClosestAvg " + watch.ElapsedMilliseconds);
            watch.Restart();

            //calculate color name salience (average, min, max)
            double csmin = 1;
            double csmax = 0;
            double csavg = 0;

            for (int i = 0; i < n; i++)
            {
                double salience = (colorToCNSaliency[swatchIndexOf[data.lab[i]]] - minE) / (maxE - minE);//colorNames.NormalizedSaliency(data.lab[i], minE, maxE);
                csavg += salience;
                csmin = Math.Min(csmin, salience);
                csmax = Math.Max(csmax, salience);
            }
            csavg /= n;

            Log(log, "NameSalience " + watch.ElapsedMilliseconds);
            watch.Restart();

            //Calculate the diversity, avg distance to closest neighbor
            double div = 0;
            double divr = 0;

            for (int c = 0; c < n; c++)
            {
                double bestDist = Double.PositiveInfinity;
                for (int i = 0; i < n; i++)
                {
                    if (c == i)
                        continue;
                    double curDist = data.lab[c].SqDist(data.lab[i]);
                    if (curDist < bestDist)
                        bestDist = curDist;
                }

                div += Math.Sqrt(bestDist); // (n * factor);
            }
            double temp = div;
            div = temp / (n * factor);
            divr = temp / (n * afactor);

            Log(log, "DiffClosestAvg " + watch.ElapsedMilliseconds);
            watch.Restart();

            //calculate average distance of one color to the rest of the colors
            double dmin = 1;
            double dmax = 0;
            double davg = 0;

            double dminr = 1;
            double dmaxr = 0;
            double davgr = 0;

            for (int i = 0; i < n; i++)
            {
                for (int j = i + 1; j < n; j++)
                {
                    double dist = Math.Sqrt(data.lab[i].SqDist(data.lab[j]));
                    davg += dist;
                    dmin = Math.Min(dmin, dist);
                    dmax = Math.Max(dmax, dist);
                }
            }

            double dtemp = dmin;
            dmin = dtemp / factor;
            dminr = dtemp / afactor;
            dtemp = dmax;
            dmax = dtemp / factor;
            dmaxr = dtemp / afactor;

            davg /= n * (n - 1) / 2;

            dtemp = davg;
            davg = dtemp / factor;
            davgr = dtemp / afactor;

            Log(log, "LABDiff " + watch.ElapsedMilliseconds);
            watch.Restart();

            double puritymin = double.PositiveInfinity;
            double puritymax = double.NegativeInfinity;
            double purityavg = 0;

            double purityminr = puritymin;
            double puritymaxr = puritymax;
            double purityavgr = 0;

            foreach (CIELAB c in data.lab)
            {
                double val = swatchToPurity[swatchIndexOf[c]];
                puritymin = Math.Min(puritymin, val);
                puritymax = Math.Max(puritymax, val);
                purityavg += val;
            }
            double ptemp = puritymin;
            puritymin = ptemp / factor;
            purityminr = ptemp / factor;

            ptemp = puritymax;
            puritymax = ptemp / factor;
            puritymaxr = ptemp / afactor;

            ptemp = purityavg / data.lab.Count();
            purityavg = ptemp / factor;
            purityavgr = ptemp / afactor;

            //Record all the features
            features.Add(Features.CovUnweighted, 1 - error);
            features.Add(Features.CovWeightedSaliency, 1 - errorWeighted);
            features.Add(Features.ErrorUnweighted, error);
            features.Add(Features.ErrorWeightedSaliency, errorWeighted);
            features.Add(Features.NCov, 1 - nerror);
            features.Add(Features.NCovWeightedSaliency, 1 - nerrorWeighted);
            features.Add(Features.NError, nerror);
            features.Add(Features.NErrorWeightedSaliency, nerrorWeighted);
            features.Add(Features.DAvg, davg);
            features.Add(Features.DClosestAvg, div);
            features.Add(Features.DMin, dmin);
            features.Add(Features.DMax, dmax);
            features.Add(Features.NClosestAvg, cndiv);
            features.Add(Features.NDiffAvg, cnavg / maxN);
            features.Add(Features.NDiffMin, cnmin / maxN);
            features.Add(Features.NDiffMax, cnmax / maxN);
            features.Add(Features.NSaliencyMin, csmin);
            features.Add(Features.NSaliencyMax, csmax);
            features.Add(Features.NSaliencyAvg, csavg);
            features.Add(Features.SaliencyDensMin, sdmin);
            features.Add(Features.SaliencyDensMax, sdmax);
            features.Add(Features.SaliencyDensAvg, sdavg);
            features.Add(Features.SaliencyTotal, stotal);
            features.Add(Features.SaliencyDensClustMin, sdmin2);
            features.Add(Features.SaliencyDensClustMax, sdmax2);
            features.Add(Features.SaliencyDensClustAvg, sdavg2);
            features.Add(Features.DMinR, dminr);
            features.Add(Features.DMaxR, dmaxr);
            features.Add(Features.DAvgR, davgr);
            features.Add(Features.DClosestAvgR, divr);
            features.Add(Features.PurityMin, puritymin);
            features.Add(Features.PurityMax, puritymax);
            features.Add(Features.PurityAvg, purityavg);
            features.Add(Features.PurityMinR, purityminr);
            features.Add(Features.PurityMaxR, puritymaxr);
            features.Add(Features.PurityAvgR, purityavgr);

            features.Add(Features.NDiffMinR, cnmin / avgN);
            features.Add(Features.NDiffMaxR, cnmax / avgN);
            features.Add(Features.NDiffAvgR, cnavg / avgN);
            features.Add(Features.NSaliencyAvgR, csavg / avgE);
            features.Add(Features.NSaliencyMaxR, csmax / avgE);
            features.Add(Features.NSaliencyMinR, csmin / avgE);
            features.Add(Features.NClosestAvgR, cndiv / avgE);

            features.Add(Features.ErrorWeightedSegment, errorSegSal);
            features.Add(Features.ErrorUnweightedSegment, errorSeg);

            features.Add(Features.ColorsPerSeg, colorsPSeg);
            features.Add(Features.ColorsPerSegWeighted, colorsPSegW);

            features.Add(Features.MeanSegError, meansegError);
            features.Add(Features.MeanSegErrorWeighted, meansegErrorWeighted);

            features.Add(Features.LCov, Lcov);
            features.Add(Features.ACov, Acov);
            features.Add(Features.BCov, Bcov);
            features.Add(Features.SatCov, Scov);

            features.Add(Features.BetweenVar, betweenVariance);
            features.Add(Features.WithinVar, withinVariance);
            features.Add(Features.SqErrorUnweighted, sqerror);
            features.Add(Features.SqErrorWeightedSaliency, sqerrorW);
            features.Add(Features.SqErrorWSeg, sqerrorSegSal);
            features.Add(Features.SqErrorSeg, sqerrorSeg);

            features.Add(Features.NSqError, nsqerror);
            features.Add(Features.NSqErrorW, nsqerrorW);
            features.Add(Features.NSqErrorSeg, nsqerrorSeg);
            features.Add(Features.NSqErrorWSeg, nsqerrorSegSal);
            features.Add(Features.NErrorSeg, nerrorSeg);
            features.Add(Features.NErrorWSeg, nerrorSegSal);

            features.Add(Features.SoftError, softerror);
            features.Add(Features.SoftWError, softwerror);
            features.Add(Features.SoftErrorSeg, softerrorseg);
            features.Add(Features.SoftErrorWSeg, softerrorwseg);

            features.Add(Features.NSoftError, nsofterror);
            features.Add(Features.NSoftWError, nsoftwerror);
            features.Add(Features.NSoftErrorSeg, nsofterrorseg);
            features.Add(Features.NSoftErrorWSeg, nsofterrorwseg);

            features.Add(Features.NMeanSegError, nmeansegerror);
            features.Add(Features.NMeanSegErrorW, nmeansegwerror);

            features.Add(Features.MeanSegErrorDensity, meansegsd);
            features.Add(Features.NMeanSegErrorDensity, nmeansegsd);

            features.Add(Features.ErrorSegSD, errorsegsd);
            features.Add(Features.NErrorSegSD, nerrorsegsd);
            features.Add(Features.SqErrorSegSD, sqerrorsegsd);
            features.Add(Features.NSqErrorSegSD, nsqerrorsegsd);
            features.Add(Features.SoftErrorSegSD, softerrorsegsd);
            features.Add(Features.NSoftErrorSegSD, nsofterrorsegsd);

            return features;
        }
Beispiel #11
0
        public static bool ColorsMatch(Color a, Color b, double distance)
        {
            CIELAB aLab = ConvertToCIELAB(a);

            return(aLab.DistanceTo(b) < distance);
        }
        //Precompute some parameters
        private FeatureParams SetupFeatureParams(SortedSet<Features> included, String key, String saliencyPattern, bool debug = false, int maxIters=50)
        {
            FeatureParams options = new FeatureParams();

            //find the normalization factor for swatches
            double factor = 0;
            double afactor = 0;

            PaletteData swatches = GetPaletteSwatches(key, maxIters);
            int scount = swatches.lab.Count();
            for (int i = 0; i < swatches.lab.Count(); i++)
            {
                for (int j = i + 1; j < swatches.lab.Count(); j++)
                {
                    double dist = swatches.lab[i].SqDist(swatches.lab[j]);
                    factor = Math.Max(factor, dist);
                    afactor += Math.Sqrt(dist);
                }
            }
            factor = Math.Sqrt(factor);
            afactor /= ((scount - 1) * scount / 2);

            //open the image
            Bitmap orig = new Bitmap(Image.FromFile(dir + "/" + key));
            Bitmap image = orig;
            if (debug)
                image = new Bitmap(orig, orig.Width / 4, orig.Height / 4);

            CIELAB[,] imageLAB = new CIELAB[image.Width, image.Height];
            //convert the image to LAB
            for (int i = 0; i < image.Width; i++)
                for (int j = 0; j < image.Height; j++)
                    imageLAB[i, j] = Util.RGBtoLAB(image.GetPixel(i, j));

            double[,] pixelToDists = new double[image.Width * image.Height, scount];
            double[,] pixelToNDists = new double[image.Width * image.Height, scount];

            //calculate the assignment of a pixel to a swatch
            for (int i = 0; i < image.Width; i++)
            {
                for (int j = 0; j < image.Height; j++)
                {
                    int idx = j * image.Width + i;

                    for (int s = 0; s < swatches.lab.Count(); s++)
                    {
                        pixelToDists[idx, s] = imageLAB[i, j].SqDist(swatches.lab[s]);

                        int a = colorNames.GetBin(imageLAB[i, j]);
                        int b = colorNames.GetBin(swatches.lab[s]);

                        double ndist = GetCNDist(a, b);
                        pixelToNDists[idx, s] = ndist * ndist;
                    }
                }
            }

            //calculate the unnormalized purity of each swatch
            double[] purityAvg = new double[scount];
            int purityCount = (int)Math.Round(image.Width * image.Height / 20.0);
            //for each swatch, find the top 5% pixels closest to it (used by Donovan)
            for (int s = 0; s < scount; s++)
            {
                CIELAB swatch = swatches.lab[s];
                List<double> dists = new List<double>(image.Width * image.Height);

                for (int i = 0; i < image.Width; i++)
                {
                    for (int j = 0; j < image.Height; j++)
                    {
                        dists.Add(imageLAB[i, j].SqDist(swatch));
                    }
                }

                dists.Sort();

                //now pop off the top
                for (int i = 0; i < purityCount; i++)
                {
                    purityAvg[s] += Math.Sqrt(dists[i]);
                }
                purityAvg[s] /= purityCount;

            }

            //read the saliency map, and calculate saliency for each swatch
            String mapPath = Util.ConvertFileName(key, saliencyPattern);//"gbvs_" + key;
            Bitmap map = new Bitmap(Image.FromFile(dir + "/saliency/" + mapPath), image.Width, image.Height);

            double[,] mapData = new double[image.Width, image.Height];
            for (int i = 0; i < image.Width; i++)
            {
                for (int j = 0; j < image.Height; j++)
                {
                    mapData[i, j] = map.GetPixel(i, j).G / 255.0;
                }
            }

            Dictionary<CIELAB, int> swatchIndexOf = new Dictionary<CIELAB, int>();
            for (int i = 0; i < swatches.lab.Count(); i++)
            {
                swatchIndexOf.Add(swatches.lab[i], i);
            }

            //for each cluster, figure out it's total/average saliency
            double[] colorToSaliency = new double[swatches.lab.Count()];
            int[] colorToCounts = new int[swatches.lab.Count()];

            double thresh = Double.PositiveInfinity;
            double totalSaliency = 0;

            for (int i = 0; i < map.Width; i++)
            {
                for (int j = 0; j < map.Height; j++)
                {
                    //find the image color, and its closest swatch, within some max threshold
                    CIELAB c = imageLAB[i, j];
                    double bestDist = thresh;
                    int bestSwatch = -1;
                    for (int s = 0; s < swatches.lab.Count(); s++)
                    {
                        if (swatches.lab[s].SqDist(c) < bestDist)
                        {
                            bestDist = swatches.lab[s].SqDist(c);
                            bestSwatch = s;
                        }
                    }

                    double value = map.GetPixel(i, j).G / 255.0;

                    if (bestSwatch >= 0)
                    {
                        colorToSaliency[bestSwatch] += value;
                        colorToCounts[bestSwatch]++;
                    }

                    totalSaliency += value;
                }
            }

            List<double> sorted = colorToSaliency.ToList<double>();
            sorted.Sort();
            sorted.Reverse();
            double totalCapturable = 0;
            for (int i = 0; i < 5; i++)
            {
                totalCapturable += sorted[i];
            }

            double minE = Double.PositiveInfinity;
            double maxE = Double.NegativeInfinity;
            double[] colorToCNSaliency = new double[swatches.lab.Count()];
            double avgE = 0;
            for (int i = 0; i < swatches.lab.Count(); i++)
            {
                CIELAB s = swatches.lab[i];
                double sal = colorNames.Saliency(s);
                minE = Math.Min(minE, sal);
                maxE = Math.Max(maxE, sal);
                avgE += sal;
                colorToCNSaliency[i] = sal;
            }
            avgE /= swatches.lab.Count();
            avgE = (avgE - minE) / (maxE - minE);

            //find the avg and max color name distance between swatches
            double maxN = Double.NegativeInfinity;
            double avgN = 0;
            for (int i = 0; i < scount; i++)
            {
                for (int j = i + 1; j < scount; j++)
                {
                    double val = 1 - colorNames.CosineDistance(swatches.lab[i], swatches.lab[j]);
                    avgN += val;
                    maxN = Math.Max(maxN, val);
                }
            }
            avgN /= ((scount - 1) * scount / 2);

            //cache the distance between image colors and swatches
            Dictionary<Tuple<int, int>, double> imageSwatchDist = new Dictionary<Tuple<int, int>, double>();
            int[,] binAssignments = new int[image.Width, image.Height];
            int[] swatchIdxToBin = new int[swatches.colors.Count()];

            if (included.Overlaps(options.NameCovFeatures))
            {
                for (int i = 0; i < image.Width; i++)
                {
                    for (int j = 0; j < image.Height; j++)
                    {
                        CIELAB c = imageLAB[i, j];
                        for (int s = 0; s < swatches.lab.Count(); s++)
                        {
                            int a = colorNames.GetBin(imageLAB[i, j]);
                            int b = colorNames.GetBin(swatches.lab[s]);

                            binAssignments[i, j] = a;
                            swatchIdxToBin[s] = b;

                            GetCNDist(a, b);

                        }
                    }
                }
            }

            //segment to total saliency
            Segmentation seg = LoadSegAssignments(key, image.Width, image.Height, imageLAB);
            double[] segmentToSaliency = new double[seg.numSegments];
            double[] segmentToSD = new double[seg.numSegments];

            for (int i = 0; i < image.Width; i++)
            {
                for (int j = 0; j < image.Height; j++)
                {
                    int id = seg.assignments[i, j];
                    segmentToSaliency[id] += mapData[i, j];
                    segmentToSD[id] += mapData[i, j];
                }
            }

            double totalSD = 0;
            for (int i = 0; i < seg.numSegments; i++)
            {
                segmentToSD[i] /= seg.counts[i];
                totalSD += segmentToSD[i];
            }

            //find the l,a,b, sat bounds
            double[] Lbounds = new double[] { double.PositiveInfinity, double.NegativeInfinity };
            double[] Abounds = new double[] { double.PositiveInfinity, double.NegativeInfinity };
            double[] Bbounds = new double[] { double.PositiveInfinity, double.NegativeInfinity };
            double[] Satbounds = new double[] { double.PositiveInfinity, double.NegativeInfinity };

            for (int i = 0; i < swatches.lab.Count(); i++)
            {
                CIELAB lab = swatches.lab[i];
                Color rgb = swatches.colors[i];
                Lbounds[0] = Math.Min(Lbounds[0], lab.L);
                Lbounds[1] = Math.Max(Lbounds[1], lab.L);

                Abounds[0] = Math.Min(Abounds[0], lab.A);
                Abounds[1] = Math.Max(Abounds[1], lab.A);

                Bbounds[0] = Math.Min(Bbounds[0], lab.B);
                Bbounds[1] = Math.Max(Bbounds[1], lab.B);

                double s = rgb.GetSaturation();
                Satbounds[0] = Math.Min(Lbounds[0], s);
                Satbounds[1] = Math.Max(Lbounds[1], s);
            }

            //pack up

            options.included = included;
            options.imageLAB = imageLAB;
            options.map = mapData;
            options.colorToSaliency = colorToSaliency;
            options.colorToCounts = colorToCounts;
            options.totalSaliency = totalSaliency;
            options.colorNames = colorNames;
            options.factor = factor;
            options.colorToCNSaliency = colorToCNSaliency;
            options.minE = minE;
            options.maxE = maxE;
            options.swatchIndexOf = swatchIndexOf;
            options.imageSwatchDist = imageSwatchDist;
            options.afactor = afactor;
            options.swatchToPurity = purityAvg;
            //options.pToScore = pToScore;
            options.maxN = maxN;
            options.avgN = avgN;
            options.avgE = avgE;
            options.segmentToSaliency = segmentToSaliency;
            options.seg = seg;

            options.Lspan = Lbounds[1] - Lbounds[0];
            options.Aspan = Abounds[1] - Abounds[0];
            options.Bspan = Bbounds[1] - Bbounds[0];
            options.Satspan = Satbounds[1] - Satbounds[0];

            options.swatchIdxToBin = swatchIdxToBin;
            options.binAssignments = binAssignments;

            options.totalCapturableSaliency = totalCapturable;
            options.pixelToDists = pixelToDists;

            options.pixelToNDists = pixelToNDists;

            options.totalSD = totalSD;
            options.segmentToSD = segmentToSD;

            return options;
        }
        public void LoadFromFile(String imageFile, String segFile)
        {
            Bitmap image = new Bitmap(imageFile);
            Bitmap map = new Bitmap(segFile);

            int width = image.Width;
            int height = image.Height;

            Bitmap resized = Util.ResizeBitmapNearest(map, width, height);

            assignments = new int[width, height];

            List<Color> unique = new List<Color>();
            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {
                    Color c = resized.GetPixel(i, j);
                    if (!unique.Contains(c))
                        unique.Add(c);

                    int idx = unique.IndexOf(c);
                    assignments[i, j] = idx;
                }
            }

            numSegments = unique.Count();
            counts = new int[numSegments];
            segToMeanColor = new CIELAB[numSegments];
            for (int i = 0; i < numSegments; i++)
            {
                segToMeanColor[i] = new CIELAB();
            }
            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {

                    int idx = assignments[i, j];
                    counts[idx]++;
                    segToMeanColor[idx] += Util.RGBtoLAB(image.GetPixel(i, j));
                }
            }

            for (int i = 0; i < numSegments; i++)
            {
                segToMeanColor[i] /= counts[i];
            }
        }
Beispiel #14
0
        private double[] GetProbabilities(String query, Func<CIELAB, CIELAB, double> distFunc, Kernel kernel, double whiteThresh=20)
        {
            CIELAB white = new CIELAB(100, 0, 0);

            //load the histogram
            double[, ,] hist = new double[Lbins, Abins, Bbins];
            String histFile = Path.Combine(cacheDir, query)+".txt";

            String[] hlines = File.ReadAllLines(histFile);
            for (int i = 0; i < hlines.Count(); i++)
            {
                int L = i / (Abins * Bbins);
                int plane = (i % (Abins * Bbins));
                int A = plane / Bbins;
                int B = plane % Bbins;

                hist[L, A, B] = Double.Parse(hlines[i]);
            }

            int ncolors = paletteLAB.Count();
            double[] freq = new double[20];

            Parallel.For(0, ncolors, l =>
            {
                double count = 0;

                for (int i = 0; i < Lbins; i++)
                {
                    for (int j = 0; j < Abins; j++)
                    {
                        for (int k = 0; k < Bbins; k++)
                        {
                            double val = hist[i, j, k];

                            System.Diagnostics.Debug.Assert(!double.IsNaN(val));

                            CIELAB lab = new CIELAB(i * binSize, j * binSize - 100, k * binSize - 100);

                            if (white.SqDist(lab) < whiteThresh * whiteThresh)
                                continue;
                            if (val <= 0)
                                continue;

                            count += val;

                            freq[l] += val * kernel.Eval(distFunc(paletteLAB[l], lab));
                        }
                    }
                }
                if (count > 0)
                {
                    freq[l] /= count;
                }
            });

            //now renormalize
            double totalFreq = 0;
            for (int i=0; i<freq.Count(); i++)
                totalFreq += freq[i];

            //this should only happen if the histogram is empty or has no valid bins
            if (totalFreq == 0)
                totalFreq = 1;

            for (int i=0; i<freq.Count(); i++)
                freq[i] /= totalFreq;

            return freq;
        }
        private int GetBinId(CIELAB color)
        {
            int Lbin = GetBin(color.L, minL, maxL, Lbins);
            int Abin = GetBin(color.A, minA, maxA, Abins);
            int Bbin = GetBin(color.B, minB, maxB, Bbins);

            return (int)(Abins * Bbins * Lbin + Abins * Bbin + Abin);
        }
        public double CosineDistance(CIELAB a, CIELAB b)
        {
            int i = GetBin(a);
            int j = GetBin(b);
            int C = colors.Count();
            int W = terms.Count();

            double sa = 0, sb = 0, sc = 0;
            int ta;
            int tb;
            for (var w = 0; w < W; w++)
            {
                ta = T[i * W + w];
                tb = T[j * W + w];
                sa += ta * ta;
                sb += tb * tb;
                sc += ta * tb;
            }

            return sc / Math.Max((Math.Sqrt(sa * sb)),1);
        }
 public double NormalizedSaliency(CIELAB a, double minE, double maxE)
 {
     //hardcoded to XKCD
     //double minE = -4.5;
     //double maxE = 0;
     return (Saliency(a) - minE) / (maxE - minE);
 }
        public Bitmap Render()
        {
            //render the template (assuming possible values are between -200 and 200)
            //must clamp to be valid RGB values
            int width = template.GetLength(0);
            int height = template.GetLength(1);
            Bitmap result = new Bitmap(width, height);

            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {
                    CIELAB lab = template[i, j];

                    //add gray to the lab value
                    CIELAB gray = new CIELAB(53.5850, 0, 0);

                    //convert (first clamp to reasonable LAB values?)
                    Color rgb = Util.LABtoRGB(lab + gray);
                    //Color rgb = Util.LABtoRGB(lab * gray);

                    result.SetPixel(i, j, rgb);
                }
            }

            return result;
        }
Beispiel #19
0
 public void Reset()
 {
     lab = MeanColor();
     count = 0;
     sumlab = new CIELAB(0, 0, 0);
 }
Beispiel #20
0
        private void RemoveBackground(Color[,] image, CIELAB[,] imageLAB)
        {
            //check perimeter to see if it's mostly black or white

            //RGB to LAB
            CIELAB black = new CIELAB(0, 0, 0);
            CIELAB white = new CIELAB(100, 0, 0);

            int width = image.GetLength(0);
            int height = image.GetLength(1);

            CIELAB[,] labs = imageLAB;//Util.Map<Color, CIELAB>(image, (c) => Util.RGBtoLAB(c));

            int numBlack = 0;
            int numWhite = 0;
            int thresh = 3 * 3;
            List<Point> perimeterIdx = new List<Point>();
            double totalPerimeter = 4 * width + 4 * height;
            double bgThresh = totalPerimeter*0.75;

            for (int i = 0; i < width; i++)
            {
                //top
                for (int j = 0; j < 2; j++)
                {
                    if (black.SqDist(labs[i, j])<thresh)
                        numBlack++;
                    if (white.SqDist(labs[i, j]) < thresh)
                        numWhite++;
                    perimeterIdx.Add(new Point(i, j));
                }

                //bottom
                for (int j = height - 2; j < height; j++)
                {
                    perimeterIdx.Add(new Point(i, j));
                    if (black.SqDist(labs[i, j]) < thresh)
                        numBlack++;
                    if (white.SqDist(labs[i, j]) < thresh)
                        numWhite++;
                }
            }

            for (int j = 0; j < height; j++)
            {
                //left
                for (int i=0; i<2; i++)
                {
                    perimeterIdx.Add(new Point(i,j));
                    if (black.SqDist(labs[i, j]) < thresh)
                        numBlack++;
                    if (white.SqDist(labs[i, j]) < thresh)
                        numWhite++;
                }

                //right
                for (int i=width-2; i<width; i++)
                {
                    perimeterIdx.Add(new Point(i, j));
                    if (black.SqDist(labs[i, j]) < thresh)
                        numBlack++;
                    if (white.SqDist(labs[i, j]) < thresh)
                        numWhite++;
                }
            }

            if (numBlack >= bgThresh || numWhite >= bgThresh)
            {
                //connected components
                UnionFind<CIELAB> uf = new UnionFind<CIELAB>((a,b) => a.SqDist(b)<thresh);
                int[,] cc = uf.ConnectedComponents(labs);

                SortedSet<int> ids = new SortedSet<int>();

                //go around the perimeter to collect the right ids
                foreach (Point p in perimeterIdx)
                {
                    if (numWhite > numBlack)
                    {
                        if (labs[p.X, p.Y].SqDist(white) < thresh)
                            ids.Add(cc[p.X, p.Y]);
                    }
                    else
                    {
                        if (labs[p.X, p.Y].SqDist(black) < thresh)
                            ids.Add(cc[p.X, p.Y]);
                    }
                }

                //fill the bitmap with transparency
                for (int i = 0; i < width; i++)
                {
                    for (int j = 0; j < height; j++)
                    {
                        if (ids.Contains(cc[i, j]))
                            image[i, j] = Color.FromArgb(0, 0, 0, 0);
                    }
                }
            }
        }
        public PixelCluster(int i, CIELAB color)
        {
            id = i;
            lab = color;
            count = 1;

            neighbors = new SortedSet<int>();
            parentId = i;
            children = new int[] { };
        }
        public PixelCluster()
        {
            id = -1;
            lab = new CIELAB();
            count = 1;

            neighbors = new SortedSet<int>();
            parentId = -1;
            children = new int[] { };
        }
Beispiel #23
0
        private bool isBlackWhite(Color[,] image, CIELAB[,] imageLAB)
        {
            double nonGray = 0;
            double thresh = 0.001;
            CIELAB white = new CIELAB(100, 0, 0);
            CIELAB black = new CIELAB(0,0,0);

            int width = image.GetLength(0);
            int height = image.GetLength(1);

            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {
                    Color color = image[i, j];
                    CIELAB lab = imageLAB[i, j];//Util.RGBtoLAB(color);
                    bool gray = color.GetSaturation() <= 0.2 || lab.SqDist(white) <= 5 || lab.SqDist(black) <= 5;

                    if (!gray)
                        nonGray++;
                }
            }
            return nonGray/(width*height) < thresh;
        }
Beispiel #24
0
 public double DistanceTo(CIELAB c) {
     double dL = L - c.L;
     double da = a - c.a;
     double db = b - c.b;
     return Math.Sqrt(0.5 * dL * dL + da * da + db * db);
 }
Beispiel #25
0
        public static List<Cluster> InitializePictureSeeds(List<CIELAB> colors, int k)
        {
            //initialize k seeds, randomly choose colors in LAB space
            //find extents
            List<Cluster> seeds = new List<Cluster>();
            Random random = new Random();

            //sample colors in LAB bounding box
            double Lmin = double.PositiveInfinity;
            double Lmax = double.NegativeInfinity;
            double Amin = double.PositiveInfinity;
            double Amax = double.NegativeInfinity;
            double Bmin = double.PositiveInfinity;
            double Bmax = double.NegativeInfinity;

            for (int i = 0; i < colors.Count(); i++)
            {
                CIELAB lab = colors[i];
                Lmin = Math.Min(Lmin, lab.L);
                Lmax = Math.Max(Lmax, lab.L);
                Amin = Math.Min(Amin, lab.A);
                Amax = Math.Max(Amax, lab.A);
                Bmin = Math.Min(Bmin, lab.B);
                Bmax = Math.Max(Bmax, lab.B);
            }

            //initialize the seeds (stratified) randomly
            //within the bounding box
            if (k <= 10)
            {
                for (int i = 0; i < k; i++)
                {
                    double L = random.NextDouble() * (Lmax - Lmin) + Lmin;
                    double A = random.NextDouble() * (Amax - Amin) + Amin;
                    double B = random.NextDouble() * (Bmax - Bmin) + Bmin;
                    CIELAB seed = new CIELAB(L, A, B);
                    Cluster cluster = new Cluster();
                    cluster.id = i;
                    cluster.lab = seed;
                    seeds.Add(cluster);
                }
            }
            else
            {
                //stratified
                //find closest floor perfect square.
                //TODO: need to generalize this better, doesn't work for non-perfect squares
                int sideLength = 2;

                int numSamples = (int)Math.Floor(k / (double)(sideLength*sideLength*sideLength));
                int i = 0;

                for (int l = 0; l < sideLength; l++)
                {
                    double dLmax = (Lmax - Lmin) / sideLength * (l+1) + Lmin;
                    double dLmin = (Lmax - Lmin) / sideLength * l + Lmin;

                    for (int a = 0; a < sideLength; a++)
                    {
                        double dAmax = (Amax - Amin) / sideLength * (a + 1) + Amin;
                        double dAmin = (Amax - Amin) / sideLength * a + Amin;

                        for (int b = 0; b < sideLength; b++)
                        {
                            double dBmax = (Bmax - Bmin) / sideLength * (b + 1) + Bmin;
                            double dBmin = (Bmax - Bmin) / sideLength * b + Bmin;

                            int dSamples = numSamples;

                            if (b == sideLength - 1 && a == sideLength - 1 && l == sideLength - 1)
                            {
                                //figure out leftovers
                                dSamples = k - numSamples*(sideLength * sideLength * sideLength - 1);

                            }
                            for (int s = 0; s < dSamples; s++)
                            {
                                double L = random.NextDouble() * (dLmax - dLmin) + dLmin;
                                double A = random.NextDouble() * (dAmax - dAmin) + dAmin;
                                double B = random.NextDouble() * (dBmax - dBmin) + dBmin;
                                CIELAB seed = new CIELAB(L, A, B);
                                Cluster cluster = new Cluster();
                                cluster.id = i;
                                cluster.lab = seed;
                                seeds.Add(cluster);
                                i++;
                            }

                        }
                    }
                }

            }

            return seeds;
        }
Beispiel #26
0
 public double GetCNDist(CIELAB a, CIELAB b)
 {
     int i = colorNames.GetBin(a);
     int j = colorNames.GetBin(b);
     if (CNcache[i, j] < 0)
     {
         double dist = 1 - colorNames.CosineDistance(i,j);
         CNcache[i, j] = dist;
         CNcache[j, i] = dist;
     }
     return CNcache[i, j];
 }
 private int ClosestColorIndex(CIELAB color, List<CIELAB> palette)
 {
     int bestc = 0;
     Double bestDist = Double.PositiveInfinity;
     for (int c = 0; c < palette.Count(); c++)
     {
         double dist = color.SqDist(palette[c]);
         if (dist < bestDist)
         {
             bestDist = dist;
             bestc = c;
         }
     }
     return bestc;
 }
        //Load PaletteData
        private Dictionary <String, PaletteData> LoadFilePalettes(String file)
        {
            FileInfo finfo = new FileInfo(file);

            Dictionary <String, PaletteData> plist = new Dictionary <String, PaletteData>();

            String[] lines = File.ReadAllLines(file);

            //extracted palettes file format
            if (finfo.Extension == ".tsv")
            {
                for (int i = 1; i < lines.Count(); i++)
                {
                    String      line   = lines[i];
                    String[]    fields = line.Replace("\"", "").Split('\t');
                    PaletteData data   = new PaletteData();
                    data.id        = Int32.Parse(fields[0]);
                    data.workerNum = Int32.Parse(fields[1]);
                    String   key    = fields[2];
                    String[] colors = fields[3].Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
                    foreach (String s in colors)
                    {
                        String[] comp = s.Split(',');
                        Color    c    = Color.FromArgb(Int32.Parse(comp[0]), Int32.Parse(comp[1]), Int32.Parse(comp[2]));
                        CIELAB   l    = Util.RGBtoLAB(c);
                        data.colors.Add(c);
                        data.lab.Add(l);
                    }
                    if (!plist.ContainsKey(key))
                    {
                        plist.Add(key, data);
                    }
                    else
                    {
                        throw new IOException("More than one palette per key");
                    }
                }
            }
            else //pattern template file format, populate the pattern id to template id field
            {
                for (int i = 0; i < lines.Count(); i++)
                {
                    String   line   = lines[i];
                    String[] fields = line.Replace("\"", "").Split(',');

                    PaletteData data = new PaletteData();
                    data.id         = Int32.Parse(fields[1]);
                    data.workerName = fields[0];
                    String[] colors = fields[3].Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
                    colors = colors.Distinct <String>().ToArray <String>();
                    String key = fields[1] + ".png";
                    foreach (String s in colors)
                    {
                        Color c = ColorTranslator.FromHtml("#" + s);
                        data.colors.Add(c);
                        data.lab.Add(Util.RGBtoLAB(c));
                    }

                    //ignore missing palette data
                    if (!plist.ContainsKey(key) && colors.Count() > 0)
                    {
                        plist.Add(key, data);
                    }
                    else if (plist.ContainsKey(key))
                    {
                        throw new IOException("More than one palette per key");
                    }

                    String templateId = fields[4];
                    if (!patternToTemplate.ContainsKey(data.id.ToString()))
                    {
                        patternToTemplate.Add(data.id.ToString(), templateId);
                    }
                }
            }
            return(plist);
        }
Beispiel #29
0
        public static double CMeansPicture(List<CIELAB> colors, List<Cluster> seeds, int m=2)
        {
            //cluster colors given seeds
            //return score
            List<double> weights = new List<double>();
            double[,] memberships = new double[colors.Count(), seeds.Count()]; //pixel to cluster
            int numSeeds = seeds.Count();
            int numColors = colors.Count();

            int maxIters = 50;//100;
            double epsilon = 0.0001;

            double J = Double.PositiveInfinity;

            int changes = 0;
            for (int t = 0; t < maxIters; t++)
            {
                changes = 0;
                for (int i = 0; i < colors.Count(); i++)
                {
                    //calculate the memberships
                    double[] dists = new double[numSeeds];
                    double factor = 0;
                    for (int k = 0; k < numSeeds; k++)
                    {
                        dists[k] = Math.Max(epsilon, Math.Pow(Math.Sqrt(colors[i].SqDist(seeds[k].lab)), 2.0/(m-1)));
                        factor += (1.0 / dists[k]);
                    }
                    for (int k = 0; k < numSeeds; k++)
                    {
                        double oldval = memberships[i, k];
                        memberships[i, k] = 1.0 / (dists[k] * factor);
                        if (oldval != memberships[i, k])
                            changes++;
                    }
                }

                //update the centers
                for (int k = 0; k < numSeeds; k++)
                {
                    CIELAB center = new CIELAB();
                    double total = 0;
                    for (int i = 0; i < numColors; i++)
                    {
                        double u = Math.Pow(memberships[i, k], m);
                        center += colors[i]*u;
                        total += u;
                    }
                    center = center / total;
                    seeds[k].lab = center;
                }

                //find J
                double thisJ = 0;
                for (int i = 0; i < numColors; i++)
                {
                    for (int k = 0; k < numSeeds; k++)
                    {
                        double u = memberships[i, k];
                        thisJ += Math.Pow(u, m)* Math.Max(epsilon, seeds[k].lab.SqDist(colors[i]));
                    }
                }

                if (thisJ >= J)
                    break;

                J = thisJ;

                if (changes == 0)
                    break;
            }

            return J;
        }
        private CIELAB[,] EdgeUnmixRecolor(CIELAB[,] image, PaletteData palette)
        {
            int width = image.GetLength(0);
            int height = image.GetLength(1);
            CIELAB[,] result = new CIELAB[width, height];

            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {
                    double[] scores = new double[palette.colors.Count()];

                    //check if the color is in the palette
                    CIELAB color = image[i, j];
                    if (palette.lab.Contains(color))
                    {
                        result[i, j] = color;
                        continue;
                    }

                    //otherwise, find the best color
                    //see if this is an edge
                    //if it is, assign the color to be one of the edge color candidates
                    HashSet<CIELAB> tbCandidates = new HashSet<CIELAB>();
                    HashSet<CIELAB> lrCandidates = new HashSet<CIELAB>();
                    List<HashSet<CIELAB>> unmixCandidates = new List<HashSet<CIELAB>> { tbCandidates, lrCandidates };

                    //check edge types
                    //horizontal and vertical
                    HashSet<CIELAB> oneSide = new HashSet<CIELAB>();
                    HashSet<CIELAB> otherSide = new HashSet<CIELAB>();
                    Point[] top = new Point[] { new Point(i - 1, j - 1), new Point(i, j - 1), new Point(i + 1, j - 1) };
                    Point[] bottom = new Point[] { new Point(i - 1, j + 1), new Point(i - 1, j), new Point(i - 1, j + 1) };
                    Point[] left = new Point[] { new Point(i - 1, j - 1), new Point(i - 1, j), new Point(i - 1, j + 1) };
                    Point[] right = new Point[] { new Point(i + 1, j - 1), new Point(i + 1, j), new Point(i + 1, j + 1) };

                    List<Point[]> oneCompare = new List<Point[]> { top, left };
                    List<Point[]> otherCompare = new List<Point[]> { bottom, right };

                    bool edge = false;
                    for (int c = 0; c < oneCompare.Count(); c++)
                    {
                        oneSide.Clear();
                        otherSide.Clear();

                        foreach (Point p in oneCompare[c])
                        {
                            if (Util.InBounds(p.X, p.Y, width, height))
                            {
                                CIELAB rc = ClosestColor(image[p.X, p.Y], palette.lab);
                                //check if in the set
                                if (oneSide.Contains(rc))
                                {
                                    //yes, we found a majority
                                    unmixCandidates[c].Add(image[p.X, p.Y]);
                                    break;
                                }
                                else
                                {
                                    oneSide.Add(rc);
                                }
                            }
                        }

                        foreach (Point p in otherCompare[c])
                        {

                            if (Util.InBounds(p.X, p.Y, width, height))
                            {
                                CIELAB rc = ClosestColor(image[p.X, p.Y], palette.lab);
                                //check if in the set
                                if (otherSide.Contains(rc))
                                {
                                    //yes, we found a majority
                                    unmixCandidates[c].Add(rc);
                                    break;
                                }
                                else
                                {
                                    otherSide.Add(rc);
                                }
                            }
                        }

                        //is it an edge?
                        if (unmixCandidates[c].Count() >= 2)
                        {
                            result[i, j] = ClosestColor(image[i, j], unmixCandidates[c].ToList<CIELAB>());
                            edge = true;
                            break;
                        }
                    }

                    //TODO:
                    //45 degrees

                    //45 degrees-flipped

                    if (!edge)
                    {
                        for (int dx = -1; dx <= 1; dx++)
                        {
                            for (int dy = -1; dy <= 1; dy++)
                            {
                                int x = i + dx;
                                int y = j + dy;
                                double weight = (dx == 0 && dy == 0) ? 4 : 1;
                                if (Util.InBounds(x, y, width, height))
                                {
                                    int bestc = ClosestColorIndex(image[x, y], palette.lab);
                                    scores[bestc] -= weight;
                                }
                            }
                        }

                        //pick the color with the min score
                        double minScore = Double.PositiveInfinity;
                        int bestIdx = 0;
                        for (int c = 0; c < palette.colors.Count(); c++)
                        {
                            if (scores[c] < minScore)
                            {
                                minScore = scores[c];
                                bestIdx = c;
                            }
                        }

                        result[i, j] = palette.lab[bestIdx];
                    }

                }
            }

            return result;
        }
        public int GetBin(CIELAB x)
        {
            int L = (int)(5 * Math.Round(x.L/5));
               int A = (int)(5 * Math.Round(x.A/5));
               int B = (int)(5 * Math.Round(x.B/5));
               String s = L+","+A+","+B;

               if (map.ContainsKey(s))
               {
               return map[s];
               }
               else {
                //look at nearby bins
               CIELAB[] neighbors = new CIELAB[] {new CIELAB(L, A, B + 5), new CIELAB(L, A, B - 5), new CIELAB(L, A + 5, B), new CIELAB(L, A - 5, B), new CIELAB(L - 5, A, B), new CIELAB(L + 5, A, B)};
               double bestDist = Double.PositiveInfinity;
               String best = "";

               for (int i = 0; i < neighbors.Count(); i++)
               {
                   String key = neighbors[i].L + "," + neighbors[i].A + "," + neighbors[i].B;
                   double dist = neighbors[i].SqDist(x);
                   if (dist < bestDist && map.ContainsKey(key))
                   {
                       bestDist = dist;
                       best = key;
                   }
               }
               if (best != "")
               {
                   //cache the best
                   return map[best];
               }
               else
                   //give up
                   return -1;
               }
        }
        private CIELAB[,] ModeRecolor(CIELAB[,] image, PaletteData palette)
        {
            int width = image.GetLength(0);
            int height = image.GetLength(1);
            CIELAB[,] result = new CIELAB[width, height];

            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {
                    double[] scores = new double[palette.colors.Count()];

                    //check if the color is in the palette
                    CIELAB color = image[i, j];
                    if (palette.lab.Contains(color))
                    {
                        result[i, j] = color;
                        continue;
                    }

                    //otherwise, find the best color by mode
                    for (int dx = -1; dx <= 1; dx++)
                    {
                        for (int dy = -1; dy <= 1; dy++)
                        {
                            int x = i + dx;
                            int y = j + dy;
                            double weight = (dx == 0 && dy == 0) ? 4 : 1;
                            if (Util.InBounds(x, y, width, height))
                            {
                                int bestc = ClosestColorIndex(image[x, y], palette.lab);
                                scores[bestc] -= weight;
                            }
                        }
                    }

                    //pick the color with the min score
                    double minScore = Double.PositiveInfinity;
                    int bestIdx = 0;
                    for (int c = 0; c < palette.colors.Count(); c++)
                    {
                        if (scores[c] < minScore)
                        {
                            minScore = scores[c];
                            bestIdx = c;
                        }
                    }

                    result[i, j] = palette.lab[bestIdx];

                }
            }

            return result;
        }
 public double Saliency(CIELAB a)
 {
     double H = 0;
     int W = terms.Count();
     int i=GetBin(a);
     for (int w = 0; w < W; w++)
     {
         double p = T[i * W + w];
         p /= ccount[i];
         if (p > 0)
             H += p * Math.Log(p) / Math.Log(2);
     }
     return H;
 }
        //Load the segmentation file for a particular image
        private Segmentation LoadSegAssignments(String key, int width, int height, CIELAB[,] imageLAB)
        {
            Bitmap map = new Bitmap(dir + "/segments/" + key);
            Bitmap resized = Util.ResizeBitmapNearest(map, width, height);

            Segmentation s = new Segmentation();
            s.assignments = new int[width, height];

            List<Color> unique = new List<Color>();
            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {
                    Color c = resized.GetPixel(i, j);
                    if (!unique.Contains(c))
                        unique.Add(c);

                    int idx = unique.IndexOf(c);
                    s.assignments[i, j] = idx;

                }
            }

            s.numSegments = unique.Count();
            s.counts = new int[s.numSegments];
            s.segToMeanColor = new CIELAB[s.numSegments];
            for (int i = 0; i < s.numSegments; i++)
            {
                s.segToMeanColor[i] = new CIELAB();
            }
            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {

                    int idx = s.assignments[i, j];
                    s.counts[idx]++;
                    s.segToMeanColor[idx] += imageLAB[i, j];
                }
            }

            for (int i = 0; i < s.numSegments; i++)
            {
                s.segToMeanColor[i] /= s.counts[i];
            }

            return s;
        }
Beispiel #35
0
        public static CIELAB ConvertToCIELAB(Color c) {
            if (cielabLookup.ContainsKey(c)) {
                return cielabLookup[c];
            }
            double rf = ConvertChannel(c.R) * 100;
            double gf = ConvertChannel(c.G) * 100;
            double bf = ConvertChannel(c.B) * 100;

            double var_X = rf * 0.4124 + gf * 0.3576 + bf * 0.1805;
            double var_Y = rf * 0.2126 + gf * 0.7152 + bf * 0.0722;
            double var_Z = rf * 0.0193 + gf * 0.1192 + bf * 0.9505;

            var_X = var_X / 95.047;          //Observer = 2°, Illuminant = D65
            var_Y = var_Y / 100.000;
            var_Z = var_Z / 108.883;

            double toPow = 1.0 / 3.0;
            if (var_X > 0.008856) {
                var_X = Math.Pow(var_X, toPow);
            } else {
                var_X = (7.787 * var_X) + (16 / 116);
            }
            if (var_Y > 0.008856) {
                var_Y = Math.Pow(var_Y, toPow);
            } else {
                var_Y = (7.787 * var_Y) + (16 / 116);
            }
            if (var_Z > 0.008856) {
                var_Z = Math.Pow(var_Z, toPow);
            } else {
                var_Z = (7.787 * var_Z) + (16 / 116);
            }

            CIELAB ans = new CIELAB();
            ans.L = (116 * var_Y) - 16;
            ans.a = 500 * (var_X - var_Y);
            ans.b = 200 * (var_Y - var_Z);

            cielabLookup[c] = ans;
            return ans;
        }
Beispiel #36
0
 /**
  * Return a theme with colors snapped to the given swatches
  */
 private PaletteData SnapColors(PaletteData data, PaletteData swatches)
 {
     PaletteData result = new PaletteData();
     List<CIELAB> matchedColors = new List<CIELAB>();
     //match each color to closest swatch color
     foreach (CIELAB kc in data.lab)
     {
         double bestDist = Double.PositiveInfinity;
         CIELAB best = new CIELAB();
         foreach (CIELAB s in swatches.lab)
         {
             double dist = s.SqDist(kc);
             if (dist < bestDist)
             {
                 bestDist = dist;
                 best = s;
             }
         }
         matchedColors.Add(best);
     }
     result.lab = matchedColors;
     foreach (CIELAB l in data.lab)
         result.colors.Add(Util.LABtoRGB(l));
     return result;
 }