ABONAMENTE VIDEO REDACȚIA
RO
EN
NOU
Numărul 150
Numărul 149 Numărul 148 Numărul 147 Numărul 146 Numărul 145 Numărul 144 Numărul 143 Numărul 142 Numărul 141 Numărul 140 Numărul 139 Numărul 138 Numărul 137 Numărul 136 Numărul 135 Numărul 134 Numărul 133 Numărul 132 Numărul 131 Numărul 130 Numărul 129 Numărul 128 Numărul 127 Numărul 126 Numărul 125 Numărul 124 Numărul 123 Numărul 122 Numărul 121 Numărul 120 Numărul 119 Numărul 118 Numărul 117 Numărul 116 Numărul 115 Numărul 114 Numărul 113 Numărul 112 Numărul 111 Numărul 110 Numărul 109 Numărul 108 Numărul 107 Numărul 106 Numărul 105 Numărul 104 Numărul 103 Numărul 102 Numărul 101 Numărul 100 Numărul 99 Numărul 98 Numărul 97 Numărul 96 Numărul 95 Numărul 94 Numărul 93 Numărul 92 Numărul 91 Numărul 90 Numărul 89 Numărul 88 Numărul 87 Numărul 86 Numărul 85 Numărul 84 Numărul 83 Numărul 82 Numărul 81 Numărul 80 Numărul 79 Numărul 78 Numărul 77 Numărul 76 Numărul 75 Numărul 74 Numărul 73 Numărul 72 Numărul 71 Numărul 70 Numărul 69 Numărul 68 Numărul 67 Numărul 66 Numărul 65 Numărul 64 Numărul 63 Numărul 62 Numărul 61 Numărul 60 Numărul 59 Numărul 58 Numărul 57 Numărul 56 Numărul 55 Numărul 54 Numărul 53 Numărul 52 Numărul 51 Numărul 50 Numărul 49 Numărul 48 Numărul 47 Numărul 46 Numărul 45 Numărul 44 Numărul 43 Numărul 42 Numărul 41 Numărul 40 Numărul 39 Numărul 38 Numărul 37 Numărul 36 Numărul 35 Numărul 34 Numărul 33 Numărul 32 Numărul 31 Numărul 30 Numărul 29 Numărul 28 Numărul 27 Numărul 26 Numărul 25 Numărul 24 Numărul 23 Numărul 22 Numărul 21 Numărul 20 Numărul 19 Numărul 18 Numărul 17 Numărul 16 Numărul 15 Numărul 14 Numărul 13 Numărul 12 Numărul 11 Numărul 10 Numărul 9 Numărul 8 Numărul 7 Numărul 6 Numărul 5 Numărul 4 Numărul 3 Numărul 2 Numărul 1
×
▼ LISTĂ EDIȚII ▼
Numărul 54
Abonament PDF

Utilizarea Azure Function pentru a obţine coordonatele GPS ale unei imagini

Radu Vunvulea
Solution Architect
@iQuest



PROGRAMARE

Să analizăm Azure Functions din perspectiva programatorului. În acest articol vom scrie o funcţie Azure (Azure Function) care adaugă coordonatele GPS pentru o imagine sub formă de watermark. Dacă vă interesează doar partea de cod, fără explicaţii, vă invit să vizitaţi GitHub

Ce sunt Azure Functions?

Cea mai bună explicaţie pe care o putem da este că Azure Functions sunt AWS Lambda din lumea Azure. Funcţiile ne permit să executăm codul fără server, fără a ne gândi unde este găzduit codul. Pur şi simplu scrieţi codul şi rulaţi-l ca Azure Function.
Caracterizând Azure Functions, reies următoarele trăsături:

Misiunea noastră

Misiunea noastră este de a scrie o funcţie Azure care:

Pasul unu  - Creaţi o funcţie Azure

Primul pas este crearea unei funcţii Azure. Acest lucru se poate realiza de pe Azure Portal, iar o documentaţie explicită în acest sens se poate găsi aici. Aici putem crea iniţial o funcţie 'GenericWebhookCSharp', apoi vom elimina părţile de care nu avem nevoie.

După ce facem acest lucru, vom accesa tabul Integrate şi ne vom specifica triggerul. Ştergeţi toate triggerele şi outputurile care sunt deja definite. Le vom defini din nou.

Triggere (declanşatori) şi bindings (relaţiile de legare)

În cazul de faţă va trebui să folosim External File, iar în câmpul connection vom crea unul nou care va face referire la OneDrive. Este important de ştiut că, deşi credenţialele de acces sunt nişte interogări, acestea nu sunt stocate în Azure Functions. Acestea sunt stocate drept API Connections, iar aceeaşi conexiune poate fi folosită în funcţii multiple dacă este necesar. Acest trigger va fi apelat când un fişier nou va fi copiat pe calea dată. Drept output, vom folosi acelaşi External File. Acum, putem refolosi aceeaşi conexiune pe care am creat-o pentru trigger - onedrive_ONEDRIVE. 

Acum, să analizăm legăturile (bindings). 

{
  "bindings": [
    {
      "type": "apiHubFileTrigger",
      "name": "inputFile",
      "path": "Imagini/Peliculă/{filename}",
      "connection": "onedrive_ONEDRIVE",
      "direction": "in"
    },
    {
      "type": "apiHubFile",
      "name": "outputFile",
      "path": "Imagini/Peliculă/pictureswithgpswatermark/{rand-guid}.jpg",
      "connection": "onedrive_ONEDRIVE",
      "direction": "out"
    }
  ],
  "disabled": false
}

Primul binding specifică triggerul. După cum se poate observa, direction este "in", iar connection este racordat la OneDrive. Câmpul path este relativ la OneDrive-ul nostru. În cazul nostru, fişierul monitorizat este 'Imagini/Peliculă'. {filename} este parametrul de fişier. În Azure Function, ne vom referi la input file utilizând atributul 'name' - inputFile.

În mod similar, avem outputul care este înregistrat în fişierul "Imagini/Peliculă/pictureswithgpswatermark". '{rand-guid}' este folosit pentru a se genera un nume aleatoriu pentru fiecare imagine. 

Scrierea unei funcţii de log

După cum putem observa, inputFile şi outputFile sunt parametrii metodei Run. Această metodă este punctul de intrare de fiecare dată când rulează triggere. Dacă doriţi să scrieţi ceva în loguri, puteţi folosi cu succes TraceWriter, care trebui specificat drept parametru.

public static void Run(Stream inputFile, 
   Stream outputFile, TraceWriter log)
{
     log.Info("Image Process Starts"); 

     log.Info("Image Process Ends");
}

Putem să ne definim propriile clase, librării bazate pe referinţe sau pachete Nuget. Pentru a putea lucra cu acest tip de fişiere şi bindings, trebuie să adăugăm o referinţă în ApiHub. Altfel, rezultatul va fi o eroare criptată:

Exception while executing function: Functions.SaasFileTriggerCSharp1. Microsoft.Azure.WebJobs.Host: One or more errors occurred. Exception binding parameter 'input'. Microsoft.Azure.ApiHub.Sdk

Referinţa este adăugată, dar specifică Azure Functions pentru a încărca ansamblul (the assembly) din propriul shared repository.

#r "Microsoft.Azure.WebJobs.Extensions.ApiHub" //

Salvare şi Rulare

Înainte de a apăsa butonul Save, asiguraţi-vă că Logs window este vizibilă. Acest lucru este util, deoarece, de fiecare dată când apăsaţi butonul Save, funcţia este compilată. Orice eroare din timpul buildului este afişată în Logs window.

De acum înainte, de fiecare dată când copiaţi/încărcaţi un fişier nou în fişierul OneDrive, funcţia voastră va fi apelată automat. În loguri veţi putea vedea logurile de output.

Citirea localizării GPS

Pentru a putea citi localizarea GPS din imagini, vom folosi ExifLib. Acest pachet Nuget ne permite să citim informaţia GPS cu uşurinţă. Pentru a face o operaţiune push pentru Nuget package, vom deschide project.json şi vom adăuga o dependinţă pachetului Nuget. Mai jos găsiţi cum ar trebuie să arate JSON. Am mai adăugat pachetul Nuget care va fi utilizat ulterior pentru a scrie coordonatele pe imagine.

{
  "frameworks": {
    "net46":{
      "dependencies": {
        "ExifLib": "1.7.0.0",
        "System.Drawing.Primitives": "4.3.0"
      }
    }
   }
}

Când apăsaţi butonul Save, veţi observa că funcţia este compilată, că pachetul Nuget şi toate dependinţele pachetelor sunt descărcate. Când rulăm codul care extrage locaţia GPS vom lua în considerare cazurile în care imaginea nu are această informaţie.

private static string GetCoordinate(Stream image, TraceWriter log)
{
    log.Info("Extract location information");
  ExifReader exifReader = new ExifReader(image);
  double[] latitudeComponents;
  exifReader.GetTagValue(ExifTags.GPSLatitude,
    out latitudeComponents);
  double[] longitudeComponents;
  exifReader.GetTagValue(ExifTags.GPSLongitude, 
     out longitudeComponents);

  log.Info("Prepare string content");
  string location = string.Empty;
  if (latitudeComponents == null ||
    longitudeComponents == null)
  {
    location = "No GPS location";
  }
  else
  {
    double latitude = 0;
    double longitude = 0;
    latitude = latitudeComponents[0] + 
      latitudeComponents[1] / 60 + 
      latitudeComponents[2] / 3600;

    longitude = longitudeComponents[0] + 
      longitudeComponents[1] / 60 + 
      longitudeComponents[2] / 3600;

    location = $"Latitude: '{latitude}' | 
      Longitude: '{longitude}'";
    }

    return location;
}

Următorul pas este apelarea metodei noastre din metoda Run (string locationText = GetCoordinate(inputFile, log). După ce salvăm, localizarea GPS pentru fiecare imagine se poate găsi în log window.

string locationText = GetCoordinate(inputFile, log);
log.Info($"Text to be written: '{locationText}'");
---- log window ----
2016-12-06T00:06:35.680 Image Process Starts
2016-12-06T00:06:35.680 Extract location information
2016-12-06T00:06:35.680 Prepare string content
2016-12-06T00:06:35.680 Text to be written: 'Latitude: '46.7636219722222' | Longitude: '23.5550620833333''

Scrierea coordonatelor watermark

Ultimul pas este scrierea textului pe imagine şi copierea streamului de imagine în output. Codul este acelaşi precum cel de care e nevoie de aplicaţia consolă pentru acelaşi task.

private static void WriteWatermark(string watermarkContent, Stream originalImage, Stream newImage, TraceWriter log)
{
  log.Info("Write text to picture");
  using (Image inputImage = Image
    .FromStream(originalImage, true))
  {
  using (Graphics graphic = Graphics
   .FromImage(inputImage))
   {
   graphic.SmoothingMode = SmoothingMode.HighQuality;
   graphic.InterpolationMode = InterpolationMode
    .HighQualityBicubic;
   graphic.PixelOffsetMode = PixelOffsetMode
    .HighQuality;
   graphic.DrawString(watermarkContent, 
     new Font("Tahoma", 100, FontStyle.Bold), 
     Brushes.Red, 200, 200);
   graphic.Flush();

   log.Info("Write to the output stream");
   inputImage.Save(newImage, ImageFormat.Jpeg);
    }
  }
}

Nu uitaţi să resetaţi poziţia cursorului streamului inputFile înainte de a apela WriteWatermark. Acest lucru este necesar, deoarece citirea coordonatelor va muta cursorul din poziţia 0. La final, metoda Run trebuie să arate astfel:

public static void Run(Stream inputFile, 
  Stream outputFile, TraceWriter log)
{
 log.Info("Image Process Starts"); 
 string locationText = GetCoordinate(inputFile, log);
 log.Info($"Text to be written: '{locationText}'");

 // Reset position. After Exif operations the cursor  
 // location is not on position 0 anymore;

 inputFile.Position = 0;
 WriteWatermark(locationText, inputFile, outputFile,
   log);

  log.Info("Image Process Ends");
}

Rezultatul final

Rezultatul final al funcţiei noastre va fi o imagine cu coordonatele scrise în ROŞU. A se vedea în figura alăturată.

Concluzie

În acest articol am analizat cum putem scrie o funcţie Azure care adaugă, sub forma unui watermark, coordonatele GPS ale locului unde a fost făcută poza.

Azure Functions este un tool puternic ce ne permite să ne concentrăm doar pe cod fără a ne gândi la infrastructură sau la alte aspecte.

Codul complet se găseşte pe GitHub.

NUMĂRUL 149 - Development with AI

Sponsori

  • Accenture
  • BT Code Crafters
  • Accesa
  • Bosch
  • Betfair
  • MHP
  • BoatyardX
  • .msg systems
  • P3 group
  • Ing Hubs
  • Cognizant Softvision
  • Colors in projects