public void SetAspectRatio() { const double left = 0; const double top = 0; const double right = 100; const double bottom = 100; var rect = new DRectangle(left, top, right, bottom); rect = DRectangle.SetAspectRatio(rect, 2); Assert.AreEqual(rect.Left, -20.917784899841294); Assert.AreEqual(rect.Top, 14.791107550079353); Assert.AreEqual(rect.Right, 120.91778489984129); Assert.AreEqual(rect.Bottom, 85.208892449920654); try { rect = DRectangle.SetAspectRatio(rect, 0); Assert.Fail($"{nameof(DRectangle.SetAspectRatio)} should throw {nameof(ArgumentOutOfRangeException)} if ration is 0"); } catch (ArgumentOutOfRangeException e) { } }
private static int ClusterDataset(CommandLineApplication commandLine) { // make sure the user entered an argument to this program if (commandLine.RemainingArguments.Count != 1) { Console.WriteLine("The --cluster option requires you to give one XML file on the command line."); return(ExitFailure); } var clusterOption = commandLine.Option("-cluster|--cluster", "", CommandOptionType.SingleValue); var sizeOption = commandLine.Option("-size|--size", "", CommandOptionType.SingleValue); var clusterValue = clusterOption.HasValue() ? clusterOption.Value() : "2"; var sizeValue = sizeOption.HasValue() ? sizeOption.Value() : "8000"; if (!uint.TryParse(clusterValue, out var numClusters)) { return(ExitFailure); } if (!uint.TryParse(sizeValue, out var chipSize)) { return(ExitFailure); } var argument = commandLine.RemainingArguments[0]; using (var data = Dlib.ImageDatasetMetadata.LoadImageDatasetMetadata(argument)) { Environment.CurrentDirectory = Path.GetDirectoryName(argument); double aspectRatio = MeanAspectRatio(data); var images = new Array <Array2D <RgbPixel> >(); var feats = new List <Matrix <double> >(); var dataImages = data.Images; //console_progress_indicator pbar(dataImages.Length); // extract all the object chips and HOG features. Console.WriteLine("Loading image data..."); for (var i = 0; i < dataImages.Length; ++i) { //pbar.print_status(i); if (!HasNonIgnoredBoxes(dataImages[i])) { continue; } using (var img = Dlib.LoadImage <RgbPixel>(dataImages[i].FileName)) { var boxes = dataImages[i].Boxes; for (var j = 0; j < boxes.Length; ++j) { if (boxes[j].Ignore || boxes[j].Rect.Area < 10) { continue; } var rect = new DRectangle(boxes[j].Rect); rect = DRectangle.SetAspectRatio(rect, aspectRatio); using (var chipDetail = new ChipDetails(rect, chipSize)) { var chip = Dlib.ExtractImageChip <RgbPixel>(img, chipDetail); Dlib.ExtractFHogFeatures <RgbPixel>(chip, out var feature); feats.Add(feature); images.PushBack(chip); } } } } if (!feats.Any()) { Console.WriteLine("No non-ignored object boxes found in the XML dataset. You can't cluster an empty dataset."); return(ExitFailure); } Console.WriteLine("\nClustering objects..."); var assignments = AngularCluster(feats, numClusters).ToList(); // Now output each cluster to disk as an XML file. for (uint c = 0; c < numClusters; ++c) { // We are going to accumulate all the image metadata for cluster c. We put it // into idata so we can sort the images such that images with central chips // come before less central chips. The idea being to get the good chips to // show up first in the listing, making it easy to manually remove bad ones if // that is desired. var idata = new List <Pair <double, Image> >(dataImages.Length); var idx = 0; for (var i = 0; i < dataImages.Length; ++i) { idata.Add(new Pair <double, Image> { Second = new Image() }); idata[i].First = double.PositiveInfinity; idata[i].Second.FileName = dataImages[i].FileName; if (!HasNonIgnoredBoxes(dataImages[i])) { continue; } var idataBoxes = new List <Box>(); var boxes = dataImages[i].Boxes; for (var j = 0; j < boxes.Length; ++j) { idataBoxes.Add(boxes[j]); if (boxes[j].Ignore || boxes[j].Rect.Area < 10) { continue; } // If this box goes into cluster c then update the score for the whole // image based on this boxes' score. Otherwise, mark the box as // ignored. if (assignments[idx].C == c) { idata[i].First = Math.Min(idata[i].First, assignments[idx].Distance); } else { idataBoxes.Last().Ignore = true; } ++idx; } idata[i].Second.Boxes = idataBoxes.ToArray(); } // now save idata to an xml file. idata.Sort((a, b) => { var diff = a.First - b.First; return(diff > 0 ? 1 : diff < 0 ? -1 : 0); }); using (var cdata = new Dataset()) { cdata.Comment = $"{data.Comment}\n\n This file contains objects which were clustered into group {c + 1} of {numClusters} groups with a chip size of {chipSize} by imglab."; cdata.Name = data.Name; var cdataImages = new List <Image>(); for (var i = 0; i < idata.Count; ++i) { // if this image has non-ignored boxes in it then include it in the output. if (!double.IsPositiveInfinity(idata[i].First)) { cdataImages.Add(idata[i].Second); } } cdata.Images = cdataImages.ToArray(); var outfile = $"cluster_{c + 1:D3}.xml"; Console.WriteLine($"Saving {outfile}"); Dlib.ImageDatasetMetadata.SaveImageDatasetMetadata(cdata, outfile); } } // Now output each cluster to disk as a big tiled jpeg file. Sort everything so, just // like in the xml file above, the best objects come first in the tiling. assignments.Sort(); for (uint c = 0; c < numClusters; ++c) { var temp = new Array <Array2D <RgbPixel> >(); for (var i = 0; i < assignments.Count(); ++i) { if (assignments[i].C == c) { temp.PushBack(images[(int)assignments[i].Index]); } } var outfile = $"cluster_{c + 1:D3}.jpg"; Console.WriteLine($"Saving {outfile}"); using (var tile = Dlib.TileImages(temp)) Dlib.SaveJpeg(tile, outfile); } } return(ExitSuccess); }