This post builds on my Smartish Edge Camera – Azure Storage Service, Azure IoT Hub, and Azure IoT Central projects adding optional camera and marked-up image upload to Azure Blob Storage for Azure IoT Hubs and Azure IoT Central.
The “new improved” process of uploading files to an Azure IoT Hub and Azure IoT Central is surprisingly complex to use and make robust(I think the initial approach with DeviceClient.UploadToBlobAsync which is now “deprecated” was easier to use).
public async Task UploadImage(List<YoloPrediction> predictions, string filepath, string blobpath)
{
var fileUploadSasUriRequest = new FileUploadSasUriRequest()
{
BlobName = blobpath
};
FileUploadSasUriResponse sasUri = await _deviceClient.GetFileUploadSasUriAsync(fileUploadSasUriRequest);
var blockBlobClient = new BlockBlobClient(sasUri.GetBlobUri());
var fileUploadCompletionNotification = new FileUploadCompletionNotification()
{
// Mandatory. Must be the same value as the correlation id returned in the sas uri response
CorrelationId = sasUri.CorrelationId,
IsSuccess = true
};
try
{
using (FileStream fileStream = File.OpenRead(filepath))
{
Response<BlobContentInfo> response = await blockBlobClient.UploadAsync(fileStream); //, blobUploadOptions);
fileUploadCompletionNotification.StatusCode = response.GetRawResponse().Status;
if (fileUploadCompletionNotification.StatusCode != ((int)HttpStatusCode.Created))
{
fileUploadCompletionNotification.IsSuccess = false;
fileUploadCompletionNotification.StatusDescription = response.GetRawResponse().ReasonPhrase;
}
}
}
catch (RequestFailedException ex)
{
fileUploadCompletionNotification.StatusCode = ex.Status;
fileUploadCompletionNotification.IsSuccess = false;
fileUploadCompletionNotification.StatusDescription = ex.Message;
}
finally
{
await _deviceClient.CompleteFileUploadAsync(fileUploadCompletionNotification);
}
}
If there is an object with a label in the PredictionLabelsOfInterest list, the camera and marked-up images can (configured with ImageCameraUpload & ImageMarkedupUpload) be uploaded to an Azure Storage Blob container associated with an Azure IoT Hub/ Azure IoT Central instance.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"Application": {
"DeviceID": "",
"ImageTimerDue": "0.00:00:15",
"ImageTimerPeriod": "0.00:00:30",
"ImageCameraFilepath": "ImageCamera.jpg",
"ImageMarkedUpFilepath": "ImageMarkedup.jpg",
"ImageCameraUpload": false,
"ImageMarkedupUpload": true,
"ImageUploadFilepath": "ImageMarkedup.jpg",
"YoloV5ModelPath": "YoloV5/yolov5s.onnx",
"PredictionScoreThreshold": 0.7,
"PredictionLabelsOfInterest": [
"bicycle",
"person"
],
"PredictionLabelsMinimum": [
"bicycle",
"car",
"person"
],
"ImageCameraFilenameFormat": "{0:yyyyMMdd}/{0:HHmmss}.jpg"
},
"SecurityCamera": {
"CameraUrl": "",
"CameraUserName": "",
"CameraUserPassword": ""
},
"RaspberryPICamera": {
"ProcessWaitForExit": 1000,
"Rotation": 180
},
"AzureIoTHub": {
"ConnectionString": ""
},
"AzureIoTHubDPS": {
"GlobalDeviceEndpoint": "global.azure-devices-provisioning.net",
"IDScope": "",
"GroupEnrollmentKey": ""
},
"AzureStorage": {
"ImageCameraFilenameFormat": "{0:yyyyMMdd}/camera/{0:HHmmss}.jpg",
"ImageMarkedUpFilenameFormat": "{0:yyyyMMdd}/markedup/{0:HHmmss}.jpg"
}
}
The Blob’s path is prefixed with the device id (My Azure Storage Service created an Azure Blob Storage container for each device).
The format of the Azure Storage Blob path is configurable(ImageCameraFilenameFormat & ImageMarkedUpFilenameFormat + Universal Coordinated Time(UTC)) so images can be grouped.
After creating a new Azure IoT Hub uploads started failing with an exception and there weren’t a lot of useful search results (April 2022). I found error this was caused by missing or incorrect Azure Storage Account configuration.
{"Message":"{\"errorCode\":400022,\"trackingId\":\"1175af36ec884cc4a54978f77b877a01-G:0-TimeStamp:04/12/2022 10:19:04\",\"message\":\"BadRequest\",\"timestampUtc\":\"2022-04-12T10:19:04.5925999Z\"}","ExceptionMessage":""}
at Microsoft.Azure.Devices.Client.Transport.HttpClientHelper.<ExecuteAsync>d__23.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Azure.Devices.Client.Transport.HttpClientHelper.<PostAsync>d__19`2.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
at Microsoft.Azure.Devices.Client.Transport.HttpTransportHandler.<GetFileUploadSasUriAsync>d__15.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at devMobile.IoT.MachineLearning.SmartEdgeCameraAzureIoTService.Worker.<UploadImage>d__14.MoveNext() in C:\Users\BrynLewis\source\repos\AzureMLNetSmartEdgeCamera\SmartEdgeCameraAzureIoTService\Worker.cs:line 430
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at devMobile.IoT.MachineLearning.SmartEdgeCameraAzureIoTService.Worker.<ImageUpdateTimerCallback>d__10.MoveNext() in C:\Users\BrynLewis\source\repos\AzureMLNetSmartEdgeCamera\SmartEdgeCameraAzureIoTService\Worker.cs:line 268
While testing the application I noticed an “unexpected” object detected in my backyard…
The mentalstack/yolov5-net and NuGet have been incredibly useful and MentalStack team have done a marvelous job building and supporting this project. For this project my test-rig consisted of a Unv ADZK-10 Security Camera, Power over Ethernet(PoE) and my HP Prodesk 400G4 DM (i7-8700T).