public async void CreateImagesFromFiles() { var dataFileName = "hemlock_1.jpg"; using (MockContext context = MockContext.Start(this.GetType())) { HttpMockServer.Initialize(this.GetType(), "CreateImagesFromFiles", RecorderMode); using (ICustomVisionTrainingClient client = GetTrainingClient()) { var newProject = client.CreateProjectAsync(projName, projDescription, ProjectBuilderHelper.FoodDomain).Result; var tag = client.CreateTagAsync(newProject.Id, tagName).Result; var fileName = Path.Combine("TestImages", "tag1", dataFileName); var fileImages = new ImageFileCreateEntry[] { new ImageFileCreateEntry(dataFileName, File.ReadAllBytes(fileName)) }; var tags = new Guid[] { tag.Id }; var imageCreatedFromFile = client.CreateImagesFromFilesAsync(newProject.Id, new ImageFileCreateBatch(fileImages, tags)).Result; Assert.True(imageCreatedFromFile.IsBatchSuccessful); Assert.Equal(1, imageCreatedFromFile.Images.Count); Assert.Contains(dataFileName, imageCreatedFromFile.Images[0].SourceUrl); Assert.Equal("OK", imageCreatedFromFile.Images[0].Status); Assert.NotEqual(Guid.Empty, imageCreatedFromFile.Images[0].Image.Id); Assert.NotEqual(0, imageCreatedFromFile.Images[0].Image.Width); Assert.NotEqual(0, imageCreatedFromFile.Images[0].Image.Height); Assert.NotEmpty(imageCreatedFromFile.Images[0].Image.OriginalImageUri); Assert.NotEmpty(imageCreatedFromFile.Images[0].Image.ResizedImageUri); Assert.NotEmpty(imageCreatedFromFile.Images[0].Image.ThumbnailUri); await client.DeleteProjectAsync(newProject.Id); } } }
public async void QuerySuggestedImageCount() { using (MockContext context = MockContext.Start(this.GetType())) { HttpMockServer.Initialize(this.GetType(), "QuerySuggestedImageCount", RecorderMode); using (var project = CreateTrainedImageClassificationProject()) using (ICustomVisionTrainingClient client = BaseTests.GetTrainingClient()) { // Add an untagged image we expect to be classified as tag1 var images = new ImageFileCreateEntry[] { new ImageFileCreateEntry("suggest_ic.jpg", File.ReadAllBytes(Path.Combine("TestImages", "suggest_ic.jpg"))) }; var imageResult = await client.CreateImagesFromFilesAsync(project.ProjectId, new ImageFileCreateBatch(images)); Assert.True(imageResult.IsBatchSuccessful); Assert.Equal(1, imageResult.Images.Count); // Ask for suggestions var suggestions = client.SuggestTagsAndRegions(project.ProjectId, project.IterationId, new Guid[] { imageResult.Images[0].Image.Id }); // Validate result Assert.Equal(1, suggestions.Count); var tags = await client.GetTagsAsync(project.ProjectId, project.IterationId); // We expect to get Tag1 as the primary suggestion, so query with a high prediction var countMapping = await client.QuerySuggestedImageCountAsync(project.ProjectId, project.IterationId, new TagFilter() { Threshold = 0.75, TagIds = new Guid[] { tags[0].Id } }); Assert.Equal(1, countMapping.Count); Assert.Equal(1, countMapping[tags[0].Id.ToString()]); // We expect to get Tag2 to have a low probabilty, make sure we don't find it with a high threshold countMapping = await client.QuerySuggestedImageCountAsync(project.ProjectId, project.IterationId, new TagFilter() { Threshold = 0.75, TagIds = new Guid[] { tags[1].Id } }); Assert.Equal(1, countMapping.Count); Assert.Equal(0, countMapping[tags[1].Id.ToString()]); // Get results for all tags with a high threshold. countMapping = await client.QuerySuggestedImageCountAsync(project.ProjectId, project.IterationId, new TagFilter() { Threshold = 0.75, TagIds = tags.Select(t => t.Id).ToList() }); Assert.Equal(2, countMapping.Count); Assert.Equal(1, countMapping[tags[0].Id.ToString()]); Assert.Equal(0, countMapping[tags[1].Id.ToString()]); } } }
private async Task TagTestImages(Guid projectId, string folder, string tagName) { // Create the tag and get the ID var tag = await m_TrainingClient.CreateTagAsync(projectId, tagName); var tagIds = new List <Guid> { tag.Id }; // Get a list of the training images in the folder. var files = Directory.GetFiles(folder); int count = 0; var images = new List <ImageFileCreateEntry>(); foreach (var file in files) { // Cerate an ImageFileCreateEntry for the file var imageFileCreateEntry = new ImageFileCreateEntry { Name = Path.GetFileName(file), Contents = File.ReadAllBytes(file) }; images.Add(imageFileCreateEntry); count++; if (count == ImagesPerClass) { break; } // The maximum ImageFileCreateEntry size is 64 if (images.Count == 64) { // Create a batch, execute it and clear the list var batch = new ImageFileCreateBatch { Images = images, TagIds = tagIds }; await m_TrainingClient.CreateImagesFromFilesAsync(projectId, batch); images.Clear(); } } // Create a batch and execute it if (images.Count > 0) { var batch = new ImageFileCreateBatch { Images = images, TagIds = tagIds }; await m_TrainingClient.CreateImagesFromFilesAsync(projectId, batch); } }
public async Task PublishImages(IList <string> imageFilePaths, string customVisionProjectName) { var imageFileEntries = new List <ImageFileCreateEntry>(); foreach (var imageFilePath in imageFilePaths) { var fileName = Path.GetFileNameWithoutExtension(imageFilePath); var entry = new ImageFileCreateEntry(fileName, await File.ReadAllBytesAsync(imageFilePath)); imageFileEntries.Add(entry); } var projectId = await _projectService.GetProjectId(customVisionProjectName); await _trainingApi.CreateImagesFromFilesAsync(projectId, new ImageFileCreateBatch(imageFileEntries)); // TODO - validations //64 item limit on publish batches //6MB limit on file sizes }
public async void QuerySuggestedImages() { using (MockContext context = MockContext.Start(this.GetType())) { HttpMockServer.Initialize(this.GetType(), "QuerySuggestedImages", RecorderMode); using (var project = CreateTrainedImageClassificationProject()) using (ICustomVisionTrainingClient client = BaseTests.GetTrainingClient()) { // Add an untagged image we expect to be classified as tag1 var images = new ImageFileCreateEntry[] { new ImageFileCreateEntry("suggest_ic.jpg", File.ReadAllBytes(Path.Combine("TestImages", "suggest_ic.jpg"))) }; var imageResult = await client.CreateImagesFromFilesAsync(project.ProjectId, new ImageFileCreateBatch(images)); Assert.True(imageResult.IsBatchSuccessful); Assert.Equal(1, imageResult.Images.Count); // Ask for suggestions var suggestions = client.SuggestTagsAndRegions(project.ProjectId, project.IterationId, new Guid[] { imageResult.Images[0].Image.Id }); // Validate result Assert.Equal(1, suggestions.Count); // Get tags and build a query var tags = await client.GetTagsAsync(project.ProjectId, project.IterationId); var query = new SuggestedTagAndRegionQueryToken() { MaxCount = 10, TagIds = tags.Select(t => t.Id).ToList(), Threshold = 0 }; // This will return all suggested images (1 in this case) var suggestedImages = await client.QuerySuggestedImagesAsync(project.ProjectId, project.IterationId, query); Assert.Equal(1, suggestedImages.Results.Count); } } }
public async void SuggestTagAndRegions() { using (MockContext context = MockContext.Start(this.GetType())) { HttpMockServer.Initialize(this.GetType(), "SuggestTagAndRegions", RecorderMode); using (var project = CreateTrainedImageClassificationProject()) using (ICustomVisionTrainingClient client = BaseTests.GetTrainingClient()) { // Add an untagged image we expect to be classified as tag1 var images = new ImageFileCreateEntry[] { new ImageFileCreateEntry("suggest_ic.jpg", File.ReadAllBytes(Path.Combine("TestImages", "suggest_ic.jpg"))) }; var imageResult = await client.CreateImagesFromFilesAsync(project.ProjectId, new ImageFileCreateBatch(images)); Assert.True(imageResult.IsBatchSuccessful); Assert.Equal(1, imageResult.Images.Count); // Ask for suggestions var suggestions = client.SuggestTagsAndRegions(project.ProjectId, project.IterationId, new Guid[] { imageResult.Images[0].Image.Id }); // Validate result Assert.Equal(1, suggestions.Count); Assert.Equal(project.ProjectId, suggestions[0].Project); Assert.Equal(project.IterationId, suggestions[0].Iteration); Assert.NotEqual(0, suggestions[0].Predictions.Count); foreach (var prediction in suggestions[0].Predictions) { var tag = await client.GetTagAsync(project.ProjectId, prediction.TagId, project.IterationId); Assert.Equal(tag.Name, prediction.TagName); Assert.Equal(tag.Id, prediction.TagId); Assert.InRange(prediction.Probability, 0, 1); } // We expect Tag1 to have the highest probability Assert.Equal("Tag1", suggestions[0].Predictions.OrderByDescending(p => p.Probability).First().TagName); } } }
public static ImageFileCreateEntry GetImageFileEntry(string filepath, List <Tag> tags) { List <Guid> tagsIDs = new List <Guid>(); ImageFileCreateEntry imageFile = null; foreach (Tag t in tags) { tagsIDs.Add(t.Id); } try { Console.WriteLine($"\nCreate image entry for file '{Path.GetFileName(filepath)}' located at '{filepath}'."); imageFile = new ImageFileCreateEntry(Path.GetFileName(filepath), File.ReadAllBytes(filepath), tagsIDs); } catch (Exception e) { Console.WriteLine($"\n{e.GetType().Name}: Could not create image file entry for image '{filepath}'."); } return(imageFile); }
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(); } }
static async Task Main(string[] args) { const string FORMAT_LABELBOX = "labelbox"; const string FORMAT_CNTK = "cntk"; var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json"); var configuration = builder.Build(); // ------------------------------- bool online = bool.Parse(configuration["online"]); string format = configuration["bboxFormat"]; if (format == FORMAT_LABELBOX) { if (Directory.EnumerateFiles(configuration["imagesPath"]).Count() > 0) { Console.Write("Download directory is not empty.\nDo you want to re-download images and potentially overwrite existing files? ('y' to accept, Enter to continue)"); var input = Console.ReadLine(); if (input.ToLowerInvariant() == "y") { await DownloadLabelBox(configuration["labelBoxFile"], configuration["imagesPath"], configuration["imageExtension"]); } } else { await DownloadLabelBox(configuration["labelBoxFile"], configuration["imagesPath"], configuration["imageExtension"]); } } var imageFileEntries = new List <ImageFileCreateEntry>(); var imageFiles = Directory.EnumerateFiles(configuration["imagesPath"], $"*.{configuration["imageExtension"]}"); var imageBatches = Batch(imageFiles, 60); int fullWidth = 800; // default - will try to detect later int fullHeight = 600; var client = new CustomVisionTrainingClient() { ApiKey = configuration["trainingKey"], Endpoint = configuration["endpoint"] }; Guid projectId = string.IsNullOrWhiteSpace(configuration["visionProjectId"]) ? Guid.Empty : Guid.Parse(configuration["visionProjectId"]); if (online) { var domains = client.GetDomains(); var objDetectionDomain = domains.FirstOrDefault(d => d.Type == "ObjectDetection" && d.Exportable == true); if (projectId == Guid.Empty) { Console.WriteLine("Creating new project."); projectId = client.CreateProject(configuration["projectName"], domainId: objDetectionDomain.Id).Id; } else { var tags = client.GetTags(projectId); tagIdMapping = new Dictionary <string, Guid>(tags.Select((t) => new KeyValuePair <string, Guid>(t.Name, t.Id))); tagCounting = new Dictionary <string, int>(tags.Select((t) => new KeyValuePair <string, int>(t.Name, t.ImageCount))); } } foreach (var batchItem in imageBatches) { Console.WriteLine("Processing batch..."); foreach (var fileName in batchItem) { var fn = Path.GetFileNameWithoutExtension(fileName); using (System.Drawing.Image measureImg = System.Drawing.Image.FromFile(fileName)) { fullWidth = measureImg.Width; fullHeight = measureImg.Height; } Console.WriteLine($"Image: {fn} {fullWidth}x{fullHeight}"); var newEntry = new ImageFileCreateEntry() { Name = fn, Contents = File.ReadAllBytes(fileName), Regions = new List <Region>(), }; if (format == FORMAT_CNTK) { Console.WriteLine("Processing " + fn); var classes = File.ReadAllLines(Path.Join(configuration["imagesPath"], fn + ".bboxes.labels.tsv")); var boxes = File.ReadAllLines(Path.Join(configuration["imagesPath"], fn + ".bboxes.tsv")); for (int i = 0; i < boxes.Length; i++) { if (string.IsNullOrWhiteSpace(classes[i])) { continue; } // break boxes and normalize them (0-1) var coords = boxes[i].Split('\t'); double left = double.Parse(coords[0], CultureInfo.InvariantCulture) / fullWidth; double top = double.Parse(coords[1], CultureInfo.InvariantCulture) / fullHeight; double width = (double.Parse(coords[2], CultureInfo.InvariantCulture) / fullWidth) - left; double height = (double.Parse(coords[3], CultureInfo.InvariantCulture) / fullHeight) - top; // create tag if needed if (!tagIdMapping.ContainsKey(classes[i])) { if (online) { var createdTag = client.CreateTag(projectId, classes[i]); tagIdMapping.Add(createdTag.Name, createdTag.Id); tagCounting.Add(createdTag.Name, 0); } else { tagIdMapping.Add(classes[i], Guid.Empty); tagCounting.Add(classes[i], 0); } Console.WriteLine("New tag " + classes[i]); } // add boxes newEntry.Regions.Add(new Region(tagIdMapping[classes[i]], left, top, width, height)); tagCounting[classes[i]]++; Console.WriteLine($"TagCounting: {classes[i]} = {tagCounting[classes[i]]}"); } imageFileEntries.Add(newEntry); } if (format == FORMAT_LABELBOX) { var labels = File.ReadAllLines(Path.Join(configuration["imagesPath"], fn + ".txt")); foreach (var l in labels) { var explodedL = l.Split('\t'); // 7->75 9 75 29 87 29 87 9 112 8 112 28 123 28 123 8 var labelName = explodedL[0]; // 7 var coordinates = explodedL[1].Split(' '); // one label can have multiple rectangles do { var coords = coordinates.Take(8).ToArray(); // 75 9 75 29 87 29 87 9 112 8 112 28 123 28 123 8 // no coordinates, skip this label if (string.IsNullOrWhiteSpace(explodedL[1])) { continue; } // candidate for top left point double left = double.Parse(coords[0]); double top = double.Parse(coords[1]); // go through the rest of the coords to find if the candidate is really top-left for (int i = 2; i < 8; i += 2) { if (double.Parse(coords[i]) <= left && double.Parse(coords[i + 1]) <= top) { left = double.Parse(coords[i]); top = double.Parse(coords[i + 1]); } } // candidate for bottom right point double right = double.Parse(coords[4]); double bottom = double.Parse(coords[5]); // go through the coords to find if the candidate is really bottom-right for (int i = 0; i < 8; i += 2) { if (double.Parse(coords[i]) >= right && double.Parse(coords[i + 1]) >= bottom) { right = double.Parse(coords[i]); bottom = double.Parse(coords[i + 1]); } } // normalize for Custom Vision left = left / fullWidth; top = top / fullHeight; right = right / fullWidth; bottom = bottom / fullHeight; // calculate width, height for custom vision double width = Difference(right, left); double height = Difference(bottom, top); if (!tagIdMapping.ContainsKey(labelName)) { if (online) { var createdTag = client.CreateTag(projectId, labelName); tagIdMapping.Add(createdTag.Name, createdTag.Id); tagCounting.Add(createdTag.Name, 0); } else { tagIdMapping.Add(labelName, Guid.Empty); tagCounting.Add(labelName, 0); } Console.WriteLine("New tag " + labelName); } // add boxes newEntry.Regions.Add(new Region(tagIdMapping[labelName], left, top, width, height)); tagCounting[labelName]++; Console.WriteLine($"TagCounting: {labelName} = {tagCounting[labelName]}"); }while ((coordinates = coordinates.Skip(8).ToArray()).Length > 0); } // no boxes, skip this image if (newEntry.Regions.Count == 0) { continue; } imageFileEntries.Add(newEntry); } } if (imageFileEntries.Count > 0 && online) { Console.WriteLine("Uploading batch..."); client.CreateImagesFromFiles(projectId, new ImageFileCreateBatch(imageFileEntries)); } // store everything to a text file for further inspection if (bool.Parse(configuration["dumpResults"])) { File.AppendAllText(Path.Join(configuration["imagesPath"], "dump.txt"), JsonConvert.SerializeObject(imageFileEntries)); } imageFileEntries.Clear(); } if (bool.Parse(configuration["tagCleanup"])) { Console.WriteLine("Cleanup..."); foreach (var tag in tagCounting) { // Less than 15 images per tag is not enough for Custom Vision. // Not enough images for this tag => gone. if (tag.Value < 15) { if (online) { Console.WriteLine($"Deleting tag {tag.Key} with {tag.Value} images."); client.DeleteTag(projectId, tagIdMapping[tag.Key]); } else { Console.WriteLine($"Would delete tag {tag.Key} with {tag.Value} images."); } } } } Console.WriteLine("Done."); Console.ReadKey(); }