/// <summary> /// Uses Cognitive Services Custom Vision API to determine whether an object was found in an image /// with some degree of certainty /// </summary> /// <param name="imageUrl"></param> /// <returns>Boolean</returns> private static async Task <bool> ObjectFound(string imageUrl, double minimumScore = 0.5) { bool objectFound = false; try { // Initialize prediction endpoint PredictionEndpoint predictionEndpoint = new PredictionEndpoint() { ApiKey = ConfigurationManager.AppSettings["CustomVisionApiKey"] }; // Call custom vision prediction API to predict image ImagePredictionResultModel predictionResult = await predictionEndpoint.PredictImageUrlAsync( new Guid(ConfigurationManager.AppSettings["CustomVisionProjectId"]), new ImageUrl(imageUrl)); // Query for the object tag var objectTag = predictionResult.Predictions.Where(x => x.Tag == "Object").FirstOrDefault(); // Check if the object tag probability surpassed the minimum score if (objectTag != null && objectTag.Probability >= minimumScore) { objectFound = true; } } catch (Exception e) { throw e; } // Return result return(objectFound); }
/// <summary> /// /// </summary> /// <param name="imageUrl"></param> /// <returns></returns> public static async Task <ImagePredictionResultModel> PredictImageUrl(string imageUrl) { PredictionEndpoint endpoint = new PredictionEndpoint() { ApiKey = predictionKey, }; var result = await endpoint.PredictImageUrlAsync(new Guid(projectId), new ImageUrl(imageUrl), iterationId : new Guid("3a06600a-2917-41de-9a9a-95a70157b007")); return(result); }
public static async Task <HttpResponseMessage> ImageUpload( [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "images/upload")] HttpRequestMessage req, [Table("Users")] IQueryable <User> users, [Table("CreateImageFromUrls")] IQueryable <CreateImageFromUrlsEntity> imageUrls, [Queue("create-image-from-urls")] ICollector <CreateImageFromUrlsRequest> queueItems, [Table("CreateImageFromUrls")] ICollector <CreateImageFromUrlsEntity> outImageUrlTable, [Table("PredictedInfo")] ICollector <PredictedInfo> outPredictedTable, TraceWriter log, Microsoft.Azure.WebJobs.ExecutionContext context) { log.Info("C# HTTP trigger function processed a request."); // collect input dynamic data = await req.Content.ReadAsAsync <object>(); string imageData = data.image; ICollection <string> tags = data.tags.ToObject <List <string> >(); string modelName = data.modelName; log.Info($"modelName={modelName}tags={string.Join(",", tags)}, image={imageData}"); var dataUrlReg = Regex.Match(imageData, @"data:image/(?<type>.+?);base64,(?<data>.+)"); var image = Convert.FromBase64String(dataUrlReg.Groups["data"].Value); var extension = dataUrlReg.Groups["type"].Value; // client var CV_ProjectId = Environment.GetEnvironmentVariable("CV_ProjectId"); var CV_TrainingKey = Environment.GetEnvironmentVariable("CV_TrainingKey"); var CV_PredictionKey = Environment.GetEnvironmentVariable("CV_PredictionKey"); var trainingApi = new TrainingApi() { ApiKey = CV_TrainingKey }; var predictionEndpoint = new PredictionEndpoint() { ApiKey = CV_PredictionKey }; var projectId = Guid.Parse(CV_ProjectId); // collect user var user = User.FromRequest(users, req, Thread.CurrentPrincipal); var iuser = user as IUser; // 既存のタグのみを受け付ける var existTags = await trainingApi.GetTagsAsync(projectId); tags = tags.Intersect(existTags.Select(x => x.Name)).ToList(); // setup blob // https://docs.microsoft.com/ja-jp/azure/cosmos-db/table-storage-design-guide#log-tail-pattern var invertedTicks = string.Format("{0:D19}", DateTime.MaxValue.Ticks - DateTime.UtcNow.Ticks); var blobName = invertedTicks + "-" + Guid.NewGuid().ToString(); var blockBlob = CommonHelper.PhotoBlobReference(blobName); var blockBlobThumbnail = CommonHelper.PhotoThumbnailBlobReference(blobName); blockBlob.Properties.ContentType = "image/jpeg"; blockBlobThumbnail.Properties.ContentType = "image/jpeg"; var url = blockBlob.Uri.ToString(); var source = "Upload"; var key = blockBlob.Name; log.Info($"pre upload. blobUri={url}"); // XXX この辺りの一連の手続きをいい感じに関数分割してフロー処理できると可用性と変更影響範囲が良くなりそう // ただ、この程度の規模のサービスで1関数で同期的に処理する平易さ以上のメリットを得られるかは疑問 // normalize image MemoryStream normalizedImage = NormalizeImage(image); AnchorLocation anchor = DetectFocusAnchor(log, normalizedImage, context.FunctionAppDirectory); log.Info($"Anchor: {anchor.ToString()}"); MemoryStream thumbnailImage = ToThumbnailImage(image, anchor); // upload image normalizedImage.Position = 0; thumbnailImage.Position = 0; await blockBlob.UploadFromStreamAsync(normalizedImage); await blockBlobThumbnail.UploadFromStreamAsync(thumbnailImage); log.Info($"after upload."); // queue image for training // 使用しているAPIの都合上、BLOBがアップロード後である必要がある TrainingImageLogic.AddImage( imageUrls, queueItems, outImageUrlTable, log, source, url, key, tags, user, modelName ); log.Info($"after queue image data."); // predict image // https://docs.microsoft.com/ja-jp/azure/cognitive-services/custom-vision-service/csharp-tutorial var imageUrl = new ImageUrl() { Url = url }; // XXX Storage emurator で通すならNgrock等の工夫が必要。単にDevelop用のStorage Accountを取ってしまった方が楽かも。 var predictResult = await predictionEndpoint.PredictImageUrlAsync(projectId, imageUrl); log.Info($"after prediction."); var predicted = new PredictedInfo() { PartitionKey = source, RowKey = key, Result = predictResult, User = user, ModelName = modelName }; outPredictedTable.Add(predicted); return(req.CreateJsonResponse(HttpStatusCode.OK, new { name = blockBlob.Name, url, result = predictResult })); }