public async Task Main() { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); TrainingApi trainingApi = new TrainingApi() { ApiKey = CustomVisionAPIKey }; trainingApi.HttpClient.Timeout = TimeSpan.FromMinutes(TimeoutMins); if (!QuickTest) { Console.WriteLine($"Creating Custom Vision Project: {ProjectName}"); var project = trainingApi.CreateProject(ProjectName); Console.WriteLine($"Scanning subfolders within: {ImagePath}"); List <Tag> allTags = new List <Tag>(); foreach (var folder in Directory.EnumerateDirectories(ImagePath)) { string folderName = Path.GetFileName(folder); var tagNames = folderName.Contains(",") ? folderName.Split(',').Select(t => t.Trim()).ToArray() : new string[] { folderName }; // Create tag for each comma separated value in subfolder name foreach (var tag in tagNames) { // Check we've not already created this tag from another subfolder if (!allTags.Any(t => t.Name.Equals(tag))) { Console.WriteLine($"Creating Tag: {tag}"); var imageTag = trainingApi.CreateTag(project.Id, tag); allTags.Add(imageTag); } } } foreach (var currentFolder in Directory.EnumerateDirectories(ImagePath)) { string folderName = Path.GetFileName(currentFolder); var tagNames = folderName.Contains(",") ? folderName.Split(',').Select(t => t.Trim()).ToArray() : new string[] { folderName }; try { // Load the images to be uploaded from disk into memory Console.WriteLine($"Uploading: {ImagePath}\\{folderName} images..."); var images = Directory.GetFiles(currentFolder).ToList(); var folderTags = allTags.Where(t => tagNames.Contains(t.Name)).Select(t => t.Id).ToList(); var imageFiles = images.Select(img => new ImageFileCreateEntry(Path.GetFileName(img), File.ReadAllBytes(img), folderTags)).ToList(); var imageBatch = new ImageFileCreateBatch(imageFiles); var summary = await trainingApi.CreateImagesFromFilesAsync(project.Id, new ImageFileCreateBatch(imageFiles)); // List any images that didn't make it foreach (var imageResult in summary.Images.Where(i => !i.Status.Equals("OK"))) { Console.WriteLine($"{ImagePath}\\{folderName}\\{imageResult.SourceUrl}: {imageResult.Status}"); } Console.WriteLine($"Uploaded {summary.Images.Where(i => i.Status.Equals("OK")).Count()}/{images.Count()} images successfully from {ImagePath}\\{folderName}"); } catch (Exception exp) { Console.WriteLine($"Error processing {currentFolder}: {exp.Source}:{exp.Message}"); } } try { // Train CV model and set iteration to the default Console.WriteLine($"Training model"); var iteration = trainingApi.TrainProject(project.Id); while (iteration.Status.Equals("Training")) { Thread.Sleep(1000); iteration = trainingApi.GetIteration(project.Id, iteration.Id); Console.WriteLine($"Model status: {iteration.Status}"); } if (iteration.Status.Equals("Completed")) { iteration.IsDefault = true; trainingApi.UpdateIteration(project.Id, iteration.Id, iteration); Console.WriteLine($"Iteration: {iteration.Id} set as default"); } else { Console.WriteLine($"Iteration status: {iteration.Status}"); } } catch (Exception exp) { Console.WriteLine($"Error training model (check you have at least 5 images per tag and 2 tags)"); Console.WriteLine($"Error {exp.Source}: {exp.Message}"); } } else { // Quick test existing (trained) model Console.WriteLine($"Custom Vision Quick test: {ProjectName} with image {ImagePath}"); // Retrieve CV project var projects = trainingApi.GetProjects(); var project = projects.Where(p => p.Name.Equals(ProjectName)).FirstOrDefault(); if (project == null) { Console.WriteLine($"Can't find Custom Vision Project: {ProjectName}"); return; } // Read test image if (!File.Exists(ImagePath)) { Console.WriteLine($"Can't find image: {ImagePath}"); return; } var image = new MemoryStream(File.ReadAllBytes(ImagePath)); // Get the default iteration to test against and check results var iterations = trainingApi.GetIterations(project.Id); var defaultIteration = iterations.Where(i => i.IsDefault == true).FirstOrDefault(); if (defaultIteration == null) { Console.WriteLine($"No default iteration has been set"); return; } var result = trainingApi.QuickTestImage(project.Id, image, defaultIteration.Id); foreach (var prediction in result.Predictions) { Console.WriteLine($"Tag: {prediction.Tag} Probability: {prediction.Probability}"); } } // fin stopwatch.Stop(); Console.WriteLine($"Done."); Console.WriteLine($"Total time: {stopwatch.Elapsed}"); }
static void Main(string[] args) { var keys = GetApiKeys(); var trainingApi = new TrainingApi { ApiKey = keys.TrainingKey }; var predictionEndpoint = new PredictionEndpoint { ApiKey = keys.PredictionKey }; var projects = trainingApi.GetProjects(); var herbProject = projects.FirstOrDefault(p => p.Name == "Herbs"); Console.WriteLine("Press 1 to predict and 2 to train:"); var pathChoice = Console.ReadLine(); if ("1".Equals(pathChoice)) { Console.WriteLine("Press 1 to predict on a URL or 2 to predict on a local file:"); var predictType = Console.ReadLine(); if ("1".Equals(predictType)) { Console.WriteLine("Input the URL to an image to test:"); var imageUrl = Console.ReadLine(); if (herbProject != null) { var result = predictionEndpoint.PredictImageUrl(herbProject.Id, new Microsoft.Cognitive.CustomVision.Prediction.Models.ImageUrl(imageUrl)); PrintResults(result); } } else { Console.WriteLine("Input path to image to test:"); var imagePath = Console.ReadLine(); if (!File.Exists(imagePath)) { Console.WriteLine("File does not exist. Press enter to exit."); Console.ReadLine(); return; } Console.WriteLine("Image predictions:"); var imageFile = File.OpenRead(imagePath); if (herbProject != null) { var result = predictionEndpoint.PredictImage(herbProject.Id, imageFile); PrintResults(result); } else { Console.WriteLine("Project doesn't exist."); } } Console.ReadLine(); } else { Console.WriteLine("Input path to image to train model with:"); var imagePath = Console.ReadLine(); Console.WriteLine("What tag would you give this image? Rosemary, cilantro, or basil?"); var imageTag = Console.ReadLine(); var capitilizedTag = char.ToUpper(imageTag.First()) + imageTag.Substring(1).ToLower(); if (!File.Exists(imagePath)) { Console.WriteLine("File does not exist. Press enter to exit."); Console.ReadLine(); return; } var imageFile = File.OpenRead(imagePath); var tags = trainingApi.GetTags(herbProject.Id); var matchedTag = tags.Tags.FirstOrDefault(t => t.Name == capitilizedTag); var memoryStream = new MemoryStream(); imageFile.CopyTo(memoryStream); var fileCreateEntry = new ImageFileCreateEntry(imageFile.Name, memoryStream.ToArray()); var fileCreateBatch = new ImageFileCreateBatch { Images = new List <ImageFileCreateEntry> { fileCreateEntry }, TagIds = new List <Guid> { matchedTag.Id } }; var result = trainingApi.CreateImagesFromFiles(herbProject.Id, fileCreateBatch); var resultImage = result.Images.FirstOrDefault(); switch (resultImage.Status) { case "OKDuplicate": Console.WriteLine("Image is already used for training. Please use another to train with"); Console.ReadLine(); break; default: break; } var iteration = trainingApi.TrainProject(herbProject.Id); while (iteration.Status != "Completed") { System.Threading.Thread.Sleep(1000); iteration = trainingApi.GetIteration(herbProject.Id, iteration.Id); } iteration.IsDefault = true; trainingApi.UpdateIteration(herbProject.Id, iteration.Id, iteration); Console.WriteLine("Done training!"); Console.ReadLine(); } }
public static async Task TrainAsync(ParsingOptions options) { // Create the Api, passing in the training key var trainingApi = new TrainingApi { ApiKey = options.TrainingKey }; CognitiveServiceTrainerStorage storageImages = new CognitiveServiceTrainerStorage(); if (options.Delete) { try { await DeleteImagesAndTagsAsync(options, trainingApi); storageImages.DeleteDB(); Console.WriteLine("Images and tags successfully deleted."); } catch { } if (string.IsNullOrEmpty(options.Folder)) { return; } } var fullFolder = Path.GetFullPath(options.Folder); if (!Directory.Exists(fullFolder)) { Console.WriteLine($"Error: folder \"{fullFolder}\" does not exist."); Console.WriteLine(string.Empty); return; } try { //await DeleteImagesAndTagsAsync(options, trainingApi); foreach (var dir in Directory.EnumerateDirectories(fullFolder).Where(f => !Path.GetFileName(f).StartsWith("!"))) { var tagName = Path.GetFileName(dir).ToLower(); Console.WriteLine($"\nCheck latest images uploaded '{tagName}'..."); IList <Microsoft.Cognitive.CustomVision.Training.Models.Image> imagesAlreadyExists = new List <Microsoft.Cognitive.CustomVision.Training.Models.Image>(); IList <Microsoft.Cognitive.CustomVision.Training.Models.Image> imagesAlreadyExistsTmp = new List <Microsoft.Cognitive.CustomVision.Training.Models.Image>(); int skip = 0; while ((imagesAlreadyExistsTmp = await trainingApi.GetTaggedImagesAsync(options.ProjectId, tagIds: new[] { tagName }, take: 50, skip: skip)).Any()) { skip += 50; foreach (var item in imagesAlreadyExistsTmp) { imagesAlreadyExists.Add(item); } } Console.WriteLine($"\nCreating tag '{tagName}'..."); var tagExist = storageImages.FindTag(tagName, options.ProjectId); Tag tag = null; if (tagExist == null) { tag = await trainingApi.CreateTagAsync(options.ProjectId, tagName); storageImages.InsertTag(new Storage.Collections.StorageTag { IdCustomVision = tag.Id, TagName = tag.Name, ProjectId = options.ProjectId }); } else { if (trainingApi.GetTag(tagExist.ProjectId, tagExist.IdCustomVision) == null) { await trainingApi.DeleteTagAsync(options.ProjectId, tagExist.IdCustomVision); tag = await trainingApi.CreateTagAsync(options.ProjectId, tagName); storageImages.InsertTag(new Storage.Collections.StorageTag { IdCustomVision = tag.Id, TagName = tag.Name, ProjectId = options.ProjectId }); } else { tag = new Tag(tagExist.IdCustomVision, tagName); } } var images = Directory.EnumerateFiles(dir, "*.*", SearchOption.AllDirectories) .Where(s => s.EndsWith(".jpg", StringComparison.InvariantCultureIgnoreCase) || s.EndsWith(".jpeg", StringComparison.InvariantCultureIgnoreCase) || s.EndsWith(".png", StringComparison.InvariantCultureIgnoreCase) || s.EndsWith(".bmp", StringComparison.InvariantCultureIgnoreCase)).ToList(); List <ImageDto> tempImages = new List <ImageDto>(); for (int i = 0; i < images.Count; i++) { Stream imageToUpload = null; string image = images.ElementAt(i); var imageName = Path.GetFileName(image); var storageImage = storageImages.FindImage(image, options.ProjectId); if (storageImage == null || !imagesAlreadyExists.Any(x => x.Id == storageImage.IdCustomVision)) { // Resizes the image before sending it to the service. using (var input = new MemoryStream(File.ReadAllBytes(image))) { if (options.Width.GetValueOrDefault() > 0 || options.Height.GetValueOrDefault() > 0) { imageToUpload = await ResizeImageAsync(input, options.Width.GetValueOrDefault(), options.Height.GetValueOrDefault()); } else { imageToUpload = input; } tempImages.Add(new ImageDto { FullName = image, FileName = imageName, Content = imageToUpload.ToByteArray(), Tag = tag }); imageToUpload.Dispose(); } } else { Console.WriteLine($"Image already exist {imageName}..."); } //Persist batch images if (tempImages.Count % 32 == 0 && tempImages.Any() || (i == images.Count - 1)) { await UploadImagesAsync(tempImages); tempImages.Clear(); tempImages.Capacity = 0; } } } // Now there are images with tags start training the project Console.WriteLine("\nTraining..."); var iteration = await trainingApi.TrainProjectAsync(options.ProjectId); // The returned iteration will be in progress, and can be queried periodically to see when it has completed while (iteration.Status == "Training") { await Task.Delay(1000); // Re-query the iteration to get it's updated status iteration = trainingApi.GetIteration(options.ProjectId, iteration.Id); } // The iteration is now trained. Make it the default project endpoint iteration.IsDefault = true; trainingApi.UpdateIteration(options.ProjectId, iteration.Id, iteration); Console.WriteLine("Training completed.\n"); } catch (Exception ex) { Console.WriteLine($"\nUnexpected error: {ex.GetBaseException()?.Message}.\n"); } async Task UploadImagesAsync(List <ImageDto> images) { ImageFileCreateBatch imageFileCreateBatch = new ImageFileCreateBatch(); imageFileCreateBatch.Images = new List <ImageFileCreateEntry>(); foreach (var img in images) { imageFileCreateBatch.Images.Add(new ImageFileCreateEntry { Name = img.FullName, TagIds = new [] { img.Tag.Id }, Contents = img.Content }); Console.WriteLine($"Uploading image {img.FileName}..."); } await Policy .Handle <Exception>() .WaitAndRetryAsync(retries) .ExecuteAsync(async() => { ImageCreateSummary reponseCognitiveService = await trainingApi.CreateImagesFromFilesAsync(options.ProjectId, imageFileCreateBatch); if (reponseCognitiveService.Images != null) { for (int i = 0; i < reponseCognitiveService.Images.Count; i++) { var img = reponseCognitiveService.Images.ElementAt(i); // https://docs.microsoft.com/en-us/rest/api/cognitiveservices/customvisiontraining/createimagesfrompredictions/createimagesfrompredictions if ((img.Status == "OK" || img.Status == "OKDuplicate") && img.Image != null) { var uploadedImage = img.Image; var tagsToStore = uploadedImage.Tags? .Select(x => new Storage.Collections.StorageImageTag() { Created = x.Created, TagId = x.TagId }).ToList(); storageImages.InsertImage(new Storage.Collections.StorageImage() { ProjectId = options.ProjectId, FullFileName = imageFileCreateBatch.Images.ElementAt(i).Name, IdCustomVision = uploadedImage.Id, ImageUri = uploadedImage.ImageUri, Created = uploadedImage.Created, Height = uploadedImage.Height, Tags = tagsToStore, ThumbnailUri = uploadedImage.ThumbnailUri, Width = uploadedImage.Width }); } else { Console.WriteLine($"API bad response: {img.Status }"); throw new InvalidOperationException($"API bad response: {img.Status }"); } } } }); await Task.Delay(500); } }