public SegmentMesh(ColorTemplate template) { int numGroups = template.NumSlots(); imageWidth = template.Width(); imageHeight = template.Height(); groups = new List<SegmentGroup>(); segments = new List<Segment>(); //now populate the segments and segment groups //filter out groups that have no members (due to quantization) Dictionary<int, int> slotToGroup = new Dictionary<int, int>(); for (int i = 0; i < numGroups; i++) { if (template.PixelsInSlot(i) > 0) { slotToGroup.Add(i, groups.Count()); groups.Add(new SegmentGroup(template.OriginalSlotColor(i))); } } UnionFind<Color> uf = new UnionFind<Color>((a, b) => (a.GetHashCode() == b.GetHashCode())); Bitmap image = template.DebugQuantization(); assignments = uf.ConnectedComponentsNoiseRemoval(Util.BitmapToArray(image)); Dictionary<int, Segment> idToSegment = new Dictionary<int, Segment>(); int totalAdjacencyPerimeter = 0; Dictionary<int, HashSet<Point>> idToPerimeter = new Dictionary<int, HashSet<Point>>(); //populate segments int width = image.Width; int height = image.Height; for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { int id = assignments[i, j]; if (!idToSegment.ContainsKey(id)) { idToSegment.Add(id, new Segment(id)); idToPerimeter.Add(id, new HashSet<Point>()); } idToSegment[id].points.Add(new Point(i, j)); idToSegment[id].groupId = slotToGroup[template.GetSlotId(i, j)]; //look for 8-neighbor adjacencies, 2 pixels away //TODO: measure adjacency strength and filter? for (int dx = -2; dx <= 2; dx++) { for (int dy = -2; dy <= 2; dy++) { int x = i + dx; int y = j + dy; if (x >= 0 && x < width && y >= 0 && y < height) { int nid = assignments[x, y]; if (nid != id) { idToSegment[id].adjacencies.Add(nid); //keep track of the total perimeter, and individual segment perimeters //don't double count the same point for each segment if (!idToPerimeter[id].Contains(new Point(x, y))) { idToPerimeter[id].Add(new Point(x, y)); totalAdjacencyPerimeter++; //keep track of the adjacecy strength per segment if (!idToSegment[id].adjacencyStrength.ContainsKey(nid)) idToSegment[id].adjacencyStrength.Add(nid, 0); idToSegment[id].adjacencyStrength[nid]++; } else { Console.WriteLine("Point already counted"); } } } else { //might as well be consistent here, and take into account the borders of the image when determining //enclosure. We don't need to do this for the totalAdjacencyPerimeter, because that is just a normalization //for the adjacency strengths of segments within the image if (!idToPerimeter[id].Contains(new Point(x,y))) { idToPerimeter[id].Add(new Point(x,y)); } } } } } } //finalize segment list and adjacency list Dictionary<int, int> idToIdx = new Dictionary<int, int>(); foreach (int id in idToSegment.Keys) { segments.Add(idToSegment[id]); idToIdx.Add(id, segments.Count() - 1); } //finalize adjacencies for (int i = 0; i < segments.Count(); i++) { SortedSet<int> renamedAdj = new SortedSet<int>(); foreach (int a in segments[i].adjacencies) renamedAdj.Add(idToIdx[a]); segments[i].adjacencies = renamedAdj; //finalize adjacency strengths int sid = assignments[segments[i].points.First().X, segments[i].points.First().Y]; Dictionary<int, double> renamedAdjStrength = new Dictionary<int, double>(); foreach (int nid in segments[i].adjacencyStrength.Keys) { double pixelsAdj = segments[i].adjacencyStrength[nid]; renamedAdjStrength.Add(idToIdx[nid], pixelsAdj / totalAdjacencyPerimeter); segments[i].enclosureStrength.Add(idToIdx[nid], new Tuple<double, double>(pixelsAdj / idToPerimeter[sid].Count(), pixelsAdj / idToPerimeter[nid].Count())); } segments[i].adjacencyStrength = renamedAdjStrength; } //finalize groups for (int i = 0; i < segments.Count(); i++) { int groupId = segments[i].groupId; groups[groupId].members.Add(i); } //finalize segment list ClassifySegments(); foreach (Segment s in segments) { ComputeFeatures(s); } foreach (SegmentGroup g in groups) { ComputeFeatures(g); } }
//render an image of the original pattern, plus columns of patterns generated by different models (trained on diff styles) private void VisAllStyles() { Directory.CreateDirectory(outdir + "\\stylecolor\\"); Directory.CreateDirectory(outdir + "\\styles\\"); //read in the patterns and save out their layers List<PatternItem> patterns = PatternIO.GetPatterns(Path.Combine(imagedir,"../styles")); foreach (PatternItem p in patterns) { String basename = p.Name; //Read the style description if available String specs = Path.Combine(outdir, "styles", p.Directory, Util.ConvertFileName(basename, "", ".txt")); if (!File.Exists(specs)) continue; Bitmap image = new Bitmap(p.FullPath); if (!palettes.ContainsKey(basename)) continue; PaletteData palette = palettes[basename]; ColorTemplate template = new ColorTemplate(image, palette); int[] slotToColor = new int[template.NumSlots()]; for (int i = 0; i < slotToColor.Count(); i++) slotToColor[i] = i; List<List<String>> lines = File.ReadAllLines(specs).Select(line => line.Split(',').ToList<String>()).ToList<List<String>>(); if (lines.Count() == 0) continue; Dictionary<String, int> styleToRowIdx = new Dictionary<String, int>(); String origStyle = ""; foreach (List<String> line in lines) { origStyle = line[0]; if (!styleToRowIdx.ContainsKey(line[1])) styleToRowIdx.Add(line[1], 0); } //read the score and if it's the original or not double score = 0; int iwidth = 100; int iheight = 100; int padding = 15; int headerSpace = 30; int width = (styleToRowIdx.Keys.Count() + 1) * iwidth; int height = (lines.Count() / styleToRowIdx.Keys.Count()) * iheight + headerSpace; Font font = new Font("Arial", 10); Bitmap vis = new Bitmap(width, height); Graphics g = Graphics.FromImage(vis); //write out the headings, highlight the original style heading var headers = styleToRowIdx.Keys.ToList<String>(); int ncol = headers.Count()+1; Color headerColor = Color.Black; g.DrawString("original", font, new SolidBrush(headerColor), 0, 0); for (int i=0; i<headers.Count(); i++) { if (headers[i] == origStyle) g.DrawString(headers[i], font, new SolidBrush(headerColor), (i+1)*iwidth, 0); else g.DrawString(headers[i], font, new SolidBrush(headerColor), (i+1)*iwidth, 0); } //draw the original Bitmap original = (renderFinal)? image: template.DebugQuantization(); g.DrawImage(original, 0, headerSpace, iwidth-padding, iheight-padding); original.Dispose(); PaletteData data = new PaletteData(); Dictionary<int, int> groupToSlot = new Dictionary<int, int>(); int ngroups = 0; for (int i = 0; i < slotToColor.Count(); i++) { slotToColor[i] = i; data.colors.Add(new Color()); data.lab.Add(new CIELAB()); if (template.PixelsInSlot(i) > 0) groupToSlot.Add(ngroups++, i); } foreach (List<String> line in lines) { //draw the colorized thumbnail in the right location String style = line[1]; String[] colors = line[2].Split('^'); int colorIdx = 0; foreach (String color in colors) { //rgb floats int[] fields = color.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries).Select<String, int>(s => ((int)(Math.Round(double.Parse(s) * 255)))).ToArray<int>(); Color c = Color.FromArgb(fields[0], fields[1], fields[2]); data.colors[groupToSlot[colorIdx]] = c; data.lab[groupToSlot[colorIdx]] = Util.RGBtoLAB(c); colorIdx++; } int x = (headers.IndexOf(style)+1)*iwidth; int y = styleToRowIdx[style]*iheight+headerSpace; styleToRowIdx[style]++; Bitmap result = (renderFinal)?GetFinalRendering(Util.ConvertFileName(basename, "",""), data):template.SolidColor(data, slotToColor); //sometimes we can't get the nice image, so render the quantized image in this case if (result == null) result = template.SolidColor(data, slotToColor); g.DrawImage(result, x, y, iwidth-padding, iheight-padding); result.Dispose(); } PatternIO.SavePattern(vis, p, Path.Combine(outdir, "stylecolor")); vis.Dispose(); } }
private void Recolor() { String suboutdir = Path.Combine(outdir, "recolored"); Directory.CreateDirectory(suboutdir); //read in the patterns and save out their layers List<PatternItem> patterns = PatternIO.GetPatterns(imagedir); foreach (PatternItem p in patterns) { String basename = p.Name; if (!palettes.ContainsKey(basename)) continue; PaletteData palette = palettes[basename]; //Read the recoloring description if available String specs = Path.Combine(outdir, "specs", p.Directory, Util.ConvertFileName(basename, "", ".txt")); if (!File.Exists(specs)) continue; Bitmap image = new Bitmap(p.FullPath); //TODO: save and reload color templates functionality ColorTemplate template = new ColorTemplate(image, palette); PaletteData data = new PaletteData(); String[] lines = File.ReadAllLines(specs); int[] slotToColor = new int[template.NumSlots()]; Dictionary<int, int> groupToSlot = new Dictionary<int, int>(); int ngroups = 0; for (int i = 0; i < slotToColor.Count(); i++) { slotToColor[i] = i; if (template.PixelsInSlot(i) > 0) groupToSlot.Add(ngroups++, i); } //TODO: handle recoloring when # groups is less than number of original slots, because of quantization issues. //Right now, this is rather ugly.. data.colors = new List<Color>(); data.lab = new List<CIELAB>(); for (int i = 0; i < slotToColor.Count(); i++) { data.colors.Add(new Color()); data.lab.Add(new CIELAB()); } int groupid = 0; foreach (String line in lines) { //rgb floats int[] fields = line.Split(new string[]{" "},StringSplitOptions.RemoveEmptyEntries).Select<String, int>(s=>((int)(Math.Round(double.Parse(s)*255)))).ToArray<int>(); Color color = Color.FromArgb(fields[0], fields[1], fields[2]); data.colors[groupToSlot[groupid]] = color; data.lab[groupToSlot[groupid]] = Util.RGBtoLAB(color); groupid++; } Bitmap orig = (renderFinal)? image : template.DebugQuantization(); PatternIO.SavePattern(orig, p, suboutdir, "_original"); orig.Dispose(); Bitmap result = (renderFinal)? GetFinalRendering(Util.ConvertFileName(basename, "",""), data): template.SolidColor(data, slotToColor); //sometimes we can't get the nice image, so render the quantized image in this case. TODO: or maybe skip rendering this visualization at all if (result == null) result = template.SolidColor(data, slotToColor); PatternIO.SavePattern(result, p, suboutdir, "_recolored"); result.Dispose(); } }
private void RenderPreviews() { //render the segment previews for the visualization Directory.CreateDirectory(Path.Combine(outdir, "\\previews\\")); //read in the patterns and save out their layers List<PatternItem> patterns = PatternIO.GetPatterns(imagedir); int hpadding = 30; foreach (PatternItem p in patterns) { Bitmap image = new Bitmap(p.FullPath); String basename = p.Name; if (!palettes.ContainsKey(basename)) continue; PaletteData palette = palettes[basename]; ColorTemplate template = new ColorTemplate(image, palette); SegmentMesh mesh = new SegmentMesh(template); //create a pattern directory (Not using PatternIO here, since each pattern has its own directory anyways) String patternDir = Path.Combine(outdir, "previews", Util.ConvertFileName(basename, "","")); Directory.CreateDirectory(patternDir); //for each segment, pair of adjacent segments, and group, output a preview image List<Segment> segs = mesh.getSegments(); List<SegmentGroup> groups = mesh.getGroups(); Bitmap original = (renderFinal)? image : template.DebugQuantization(); Bitmap previewBase = new Bitmap(original.Width*2+hpadding, original.Height); //draw the original image on the right Graphics g = Graphics.FromImage(previewBase); g.DrawImage(original, original.Width+hpadding, 0); //draw a grayscaled image on the left for (int i = 0; i < original.Width; i++) { for (int j = 0; j < original.Height; j++) { int gray = (int)Math.Round(255*original.GetPixel(i, j).GetBrightness()); previewBase.SetPixel(i,j, Color.FromArgb(gray, gray, gray)); } } //color in orange and blue for (int i = 0; i < segs.Count(); i++) { Bitmap unary = new Bitmap(previewBase); foreach (var point in segs[i].points) unary.SetPixel(point.X, point.Y, Color.Orange); unary.Save(Path.Combine(patternDir, "s" + i + ".png")); foreach (int j in segs[i].adjacencies) { if (j > i) { Bitmap binary = new Bitmap(unary); Segment neighbor = segs[j]; foreach (var point in neighbor.points) binary.SetPixel(point.X, point.Y, Color.ForestGreen); binary.Save(Path.Combine(patternDir, "s" + i + "-s" + j + ".png")); binary.Dispose(); } } unary.Dispose(); } for (int i = 0; i < groups.Count(); i++) { Bitmap group = new Bitmap(previewBase); foreach (int j in groups[i].members) { Segment member = segs[j]; //color in the points foreach (var point in member.points) group.SetPixel(point.X, point.Y, Color.Orange); } group.Save(Path.Combine(patternDir, "g" + i + ".png")); group.Dispose(); } original.Dispose(); previewBase.Dispose(); } }
//Test the quantization, connected components on different color layers, and output the descriptor private void OutputPatterns_Click(object sender, RoutedEventArgs e) { //Create output directories Directory.CreateDirectory(outdir + "\\cc\\"); Directory.CreateDirectory(outdir + "\\quantized\\"); Directory.CreateDirectory(outdir + "\\mesh\\"); //read in the patterns and save out their layers String[] files = System.IO.Directory.GetFiles(System.IO.Path.Combine(imagedir)); List<PatternItem> patterns = PatternIO.GetPatterns(imagedir); foreach (PatternItem p in patterns) { Bitmap image= new Bitmap(p.FullPath); String basename = p.Name; //TODO: sometimes keys are not found in patterns.csv...will need to look into recovering missing info. For now, just ignore those patterns if (!palettes.ContainsKey(basename)) continue; PaletteData palette = palettes[basename]; ColorTemplate template = new ColorTemplate(image, palette); //output the template descriptor SegmentMesh mesh = new SegmentMesh(template); PatternIO.SaveMesh(mesh, p, Path.Combine(outdir, "mesh")); if (outputDebugImages) { Bitmap result = template.DebugQuantization(); PatternIO.SavePattern(result, p, Path.Combine(outdir, "quantized"), "_quantized"); PatternIO.SavePattern(result, p, Path.Combine(outdir, "quantized"), "_original"); //save the connected components UnionFind<Color> uf = new UnionFind<Color>((a, b) => (a.GetHashCode() == b.GetHashCode())); Color[,] resultArray = Util.BitmapToArray(result); int[,] cc = uf.ConnectedComponentsNoiseRemoval(resultArray); int numColors = palette.colors.Count(); for (int i = 0; i < numColors; i++) { Bitmap debug = uf.RenderComponents(cc, resultArray, palette.colors[i]); PatternIO.SavePattern(debug, p, Path.Combine(outdir, "cc"), "_" + i); debug.Dispose(); } result.Dispose(); } image.Dispose(); } }