static async Task WorkOnProject(string trainingEndpoint, string trainingKey, string projectName, string COCOInstancesFilePathOrUrl, IList <string> categories, uint numberOfImages, bool isDetectionModel, bool train) { Console.Write($"\nLoading, parsing and preparing {COCOInstancesFilePathOrUrl}..."); if (numberOfImages == 0 || numberOfImages > customVisionImageLimit) { numberOfImages = 50000; } var data = await COCODatasetFactory.LoadFromCOCOAnnotationJSONFileAsync(COCOInstancesFilePathOrUrl, categories, numberOfImages); Console.WriteLine("done."); Console.WriteLine("Prepared COCO dataset with {0} categories and {1} images.\n", data.categoriesAndIds.Count, data.traningSet.Count); Console.Write($"Initializing Custom Vision Project {COCOInstancesFilePathOrUrl}..."); ProjectTraningConfiguration trainingConfig = await ProjectTraningConfiguration.CreateProjectAsync(trainingEndpoint, trainingKey, projectName, true, isDetectionModel); Console.WriteLine("done."); Console.Write($"\nLoading traning images to Custom Vision Project..."); CustomVisionProjectTraning traning = new CustomVisionProjectTraning(trainingConfig); int ignoredImages = await traning.LoadWithCOCO(data, isDetectionModel); if (ignoredImages == 0) { Console.WriteLine("done."); } else { Console.WriteLine($"done. Ignored {ignoredImages} image(s)."); } if (train) { Console.Write($"\nTraning Custom Vision Project..."); await traning.Train(); Console.WriteLine("done."); } else { Console.WriteLine("Traning is not requested."); } }
public async Task <int> LoadWithCOCO(COCODatasetFactory imageSet, bool isDetectionModel) { var imageUrlBatches = new List <List <ImageUrlCreateEntry> >(); var imageUrlEntries = new List <ImageUrlCreateEntry>(); var tagsInBatch = new List <Guid>(); Dictionary <int, Guid> categoriesToTags = new Dictionary <int, Guid>(); int imagesIgnored = 0; foreach (var cat in imageSet.categoriesAndIds.Keys) { string name = imageSet.categoriesAndIds[cat]; var tags = configuration.tagList.Where(tag => tag.Name == name); if (tags.Any()) { categoriesToTags.Add(cat, tags.First().Id); } else { var tag = await configuration.AddTagAsync(name); categoriesToTags.Add(cat, tag.Id); } } for (int i = 0; i < imageSet.traningSet.Values.Count; i++) { var image = imageSet.traningSet.Values.ElementAt(i); List <Region> regions = null; IList <Guid> tags = null; if (isDetectionModel) { regions = new List <Region>(); foreach (var bb in image.bounding_boxes) { Region region = new Region(); region.TagId = categoriesToTags[bb.category_id]; if (!tagsInBatch.Contains(region.TagId)) { tagsInBatch.Add(region.TagId); } // normalized bounding box region.Left = bb.bboxArray[0] / image.imageWidth; region.Width = bb.bboxArray[2] / image.imageWidth; region.Top = bb.bboxArray[1] / image.imageHeight; region.Height = bb.bboxArray[3] / image.imageHeight; //x1, x2, y1, y2 //[bb[0], bb[0]+bb[2], bb[1], bb[1]+bb[3]] regions.Add(region); } if (regions.Count == 0) { continue; } } else { tags = image.bounding_boxes.Select(bb => categoriesToTags[bb.category_id]).ToList(); if (tags.Count == 0) { continue; } foreach (var t in tags) { if (!tagsInBatch.Contains(t)) { tagsInBatch.Add(t); } } } // Traning batch cannot have more than 20 different tags (across all regions) bool tooManyTags = false; if (tagsInBatch.Count > 19) { //If more than 20 tags, "rewind" one image back, we will add this image into the next batch i--; tooManyTags = true; } if (!tooManyTags) // if not too many tags with this image included, add this image { ImageUrlCreateEntry entry = isDetectionModel ? new ImageUrlCreateEntry(image.coco_url, null, regions) : new ImageUrlCreateEntry(image.coco_url, tags); imageUrlEntries.Add(entry); } // 64 is maximim batch size for Custom Vision, 20 is maximum number of distinct tags per batch // If exceeds, add this batch to the list, and start a new batch if (imageUrlEntries.Count > 63 || tooManyTags) { imageUrlBatches.Add(imageUrlEntries); tagsInBatch.Clear(); imageUrlEntries = new List <ImageUrlCreateEntry>(); } } if (imageUrlEntries.Count > 0) { imageUrlBatches.Add(imageUrlEntries); } foreach (var batch in imageUrlBatches) { if (batch.Count == 0) { continue; } var createImagesResult = await configuration.trainingApi.CreateImagesFromUrlsWithHttpMessagesAsync(configuration.project.Id, new ImageUrlCreateBatch(batch)); if (createImagesResult.Response.IsSuccessStatusCode) { if (!createImagesResult.Body.IsBatchSuccessful) { //Maybe it's actually OK or we submitted duplicates (OKDuplicate Status) var notOKImages = createImagesResult.Body.Images.Where(im => (im.Status != "OKDuplicate" && im.Status != "OK" && im.Status != "ErrorTagLimitExceed")); // Custom Vision may decline an image with too many regions, but reports it as too many tags if (notOKImages != null && notOKImages.Count() > 0) { var message = await createImagesResult.Response.Content.ReadAsStringAsync(); throw new Exception($"Failed to create a trainig image batch (batch is not successful). Result:\n {message}"); } else { var tooManyTags = createImagesResult.Body.Images.Where(im => (im.Status == "ErrorTagLimitExceed")); if (tooManyTags != null) { imagesIgnored += tooManyTags.Count(); } } } } else { var message = createImagesResult.Response.Content.ReadAsStringAsync(); throw new Exception($"Failed to create a trainig image batch ({createImagesResult.Response.ReasonPhrase})."); } } return(imagesIgnored); }