Zapojte detekci objektů do své webové aplikace – 2. díl

6. 2. 2021 Azure, Programming

Předmětem předešlého článku bylo představení naší aplikace a využití služby Custom Vision pro detekci objektů. Zde se hlouběji ponoříme do kódu naší aplikace a ukážeme si klíčové části, které se vám při tvorbě podobné aplikace mohou hodit – implementace exportovaného modelu, tvorba boxů kolem objektů a práce s frameworkem Flask.

Požadavky

Instalace nezbytných balíčků

Námi zvolený exportovaný model používá jeden z nejznámějších frameworků pro hluboké učení tensorflow. Výhodný je pro nás především kvůli tomu, že takto exportovaný model je napsán v jazyku Python. Ten používá také framework pro tvorbu webových aplikací Flask, jenž využijeme na serveru aplikace. Zmíněné balíčky spolu s pár dalšími nainstalujeme v konzoli pomocí pip.

pip install tensorflow 
pip install flask
pip install Pillow
pip install numpy

Flask

Předností Flasku je především jeho jednoduchost. Jako první vytvoříme soubor applicaiton.py a uvnitř instanci Flask aplikace.

import flask
app = Flask(__name__)

Na konec souboru musíme ještě umístit podmínku, která bude aplikaci spouštět.

if __name__ == '__main__': 
      app.run() 

Mezi ně teď můžeme definovat routes – cesty, pomocí kterých můžeme v aplikaci uživatele přesměrovat. Každá z nich pak dokáže vykonávat jiný účel. Jako první musíme definovat základní cestu, jejíž instrukce se vykonají při příchodu uživatele na stránku.

@app.route('/'):
def home():
      ...
      return render_template('index.html') #vykreslí html dokument

Poslední důležitá věc, která musí zaznít, je fakt, že Flask požaduje pevně danou strukturu složek.

To bylo to nejpodstatnější, co budeme pro tento projekt potřebovat. Do budoucna se vám může hodit detailní dokumentace Flasku v angličtině. Teď se pojďme podívat, jak lze Flasku předat data uživatele.

Odeslání dat

V duchu dodržení rozumné délky tohoto článku se zde nebudu věnovat tomu, jakým způsobem používat na stránce webkameru tak, jak to dělá stránka naše. Jedná se jen o jeden z mnoha způsobů, jak snímek od uživatele získat. Pokud by vás tato nebo kterákoli jiná nezmíněná část aplikace zajímala, najdete kompletní okomentovaný kód na našem GitHubu.

Jako nejjednodušší metoda předání dat se nabízí odeslání přes formulář.

<form action="/detect" method="POST" id="send">
<input type="hidden" id="image" name="image"> </form>

Atribut action říká, kam bude uživatel po odeslání přesměrován. V našem případě to bude bude route, kterou jsme si definovali v backendu aplikace. Další atribut method pak definuje, jestli chceme data odeslat –⁠ „POST“ nebo přijmout –⁠ „GET“.

Form pak můžeme odeslat v HTML přidáním tlačítka:

 <button type="submit" form="send" value="image">Submit</button>

Pokud potřebujete před odesláním provést nějakou formu logiky nebo úpravu dat, nabízí se odeslání přes JavaScript. V případě naší aplikace je náš snímek uložen jako prvek canvas. Ten jako takový odeslat nejde, proto ho v JavaScriptu upravíme na vhodný formát a formulář odešleme přes něj. Nejdříve si vyžádáme potřebné prvky podle jejich id.

<script>
const inputImage = document.getElementById('image');
const formImage = document.getElementById('send'); 
const submit = document.getElementById('submit'); //element tlačítka

Abychom canvas mohli odeslat na server, musíme ho převést na informaci ve formátu base64, který převádí binární informaci na řetězec znaků. Tuto informaci přiřadíme prvku input uvnitř formuláře a odešleme.

submit.addEventListener("click", function(){ //událost se zavolá po stisknutí tlačítka
var dataURL = canvas.toDataURL(); //převede data 
inputImage.value = dataURL; //převedená data přiřadíme
formImage.submit(); }); //odešle vyplněný form

Přijmutí dat

Poté, co server přijme snímek v podobě řetězce, musíme nejprve odříznout metadata –⁠ zprávu popisující druh informace. Následně musíme dekódovat nazpět náš snímek tak, aby bylo možné s ním dále pracovat.

@app.route("/detect", methods=['POST', 'GET']) #methods říká jestli cheme data přijímat nebo odesílat
def detection():

    if request.method == 'POST':
        image_b64 = request.values['image'] #vyžádání obrázku z html form jako base64 data
        image_b64 = re.sub('^data:image/.+;base64,', '', image_b64)#nahradí metadata prazdným řetězcem
        image_data = BytesIO(base64.b64decode(image_b64)) #dekódovaní base64 dat

Poté můžeme snímek otevřít v modulu Pillow (PIL), jenž nabízí jednoduchou práci s obrázky.

image = Image.open(image_data)

Budeme ho používat i při vykreslovaní boxů kolem detekovaných objektů. Předtím ale musíme použít náš exportovaný model k získání predikcí o našem snímku.

Práce s exportovaným modelem

Po exportování a následném extrahování .zip souboru uvidíme následující soubory:

Složka s modelem

Soubor object_detection.py obsahuje definici třídy ObjectDetection, kterou dále používají funkce v predict.py k detekci a generování nejpravděpodobnějších značek uvedených v labels.txt. Soubor predict_v1.py je funkčně totožný s predict.py, ale je určen pro starší verze tensorflow 1.x. Proto ho můžeme smazat. Pokud vás zajímá podrobný rozbor toho, co se v souborech děje, najdete ho v tomto článku. Nám ale bude stačit funkce main ze souboru predict.py.

import sys #přidá složku python k prohledávaným složkám
sys.path.insert(1, python)
from predict import main

Ta jako svůj parametr přijme náš snímek a vrátí nám predikce v podobě „seznamu“ (list) „slovníků“ (dictionaries).

      image = Image.open(image_data)
      predictions = main(image_data)
      print("predictions: ", predictions)
      """
      predictions: [{'probability': 0.12775692, 
      'tagId': 0, 
      'tagName': 'Lightning', 
      'boundingBox': {'left': 0.29989651, 'top': 0.65226842, 'width': 
       0.3119592, 'height': 0.37785795}}]
      """

Abychom se dostali k určité hodnotě, musíme specifikovat pořadí detekovaného objektu, jehož je hodnota součástí a klíč hodnoty je: predictions[0]['tagId']

Nyní máme všechny potřebné informace k vykreslení boxů kolem objektů, které model detekoval.

Vykreslování boxů

Jako souřadnice pro nakreslení obdélníku nám poslouží hodnoty ve vnořeném slovníku s klíčem 'boundingBox'.

Ty jsou vyjádřeny v procentuálním poměru k rozměrům původního snímku. Abychom dostali jejich absolutní hodnoty, musíme vynásobit hodnoty ‚left‘ a ‚width‘ šířkou a ‚top‘ a ‚height‘ výškou snímku.

Pro vykreslení použijeme modul ImageDraw, který je stejně jako Image součástí modulu Pillow. Ten pak nabízí metodu rectangle, která přijímá jako souřadnice dvojici bodů (x0, y0) –⁠ levý horní roh a (x1, y1) –⁠ pravý dolní roh. Následně přidáme popisek s názvem předpokládaného objektu. Stejnou proceduru zopakujeme pro každý objekt v seznamu.

from PIL import ImageDraw
def draw_boxes(image, predictions):
      img_width, img_height = image.size #předání šířky výšky
      img = ImageDraw.Draw(image)

      for obj in predictions:
            #výpočet souřadnic
            x0 = obj['boundingBox']['left'] * img_width #horní levý roh
            y0 = obj['boundingBox']['top'] * img_height
            x1 = x0 + (obj['boundingBox']['width'] * img_width) #dolní pravý roh
            y1 = y0 + (obj['boundingBox']['height'] * img_height)
            shape = [(x0, y0), (x1, y1)]

            img.rectangle(shape, outline ="red") #nakreslí box
            img.text((x0 + 10, y0 + 10), obj['tagName'], fill="red") #přidá popisek

      return image
Výsledný obrázek

Upravený snímek uložíme a spolu s jmény dvou nejpravděpodobnějších portů pošleme na další stránku.

      image = draw_boxes(image, predictions)
      image = image.save(os.path.join(app.config['IMAGE_UPLOADS'], '{}.png'.format(predictions[0]['probability']))) #'probability' slouží jako unikatní jméno pro uložení.
    
      return render_template('choice.html', user_image=os.path.join(app.config['IMAGE_UPLOADS'], '{}.png'.format(predictions[0]['probability']))
,port1=predictions[0]['tagName'], port2=predictions[1]['tagName'])

Práce s proměnnými

Jako příklad toho, jak následně pracovat s odeslanými proměnnými, jsem zvolil opět kousek kódu naší aplikace. Flask používá pro proměnné posílané ze serveru na straně HTML symboly „{{ }}“. Ty se po spuštění nahradí proměnnými specifikovanými uvnitř.

<img class= "img" src="{{user_image}}" alt="fotka s oznacenymi boxy" >

<input type="hidden" id="port1" value="{{port1}}">
<input type="hidden" id="port2" value="{{port2}}">

Když teď máme data uložena uvnitř prvků input, můžeme je v JavaScriptu spojit spolu s údajem o požadované délce kabelu zadaným uživatelem a vyhledat řetězec automaticky v prohlížeči.

const port1 = document.getElementById('port1');
const port2 = document.getElementById('port2');
const search_text = document.getElementById('search_text');
const search = document.getElementById('search');

search.addEventListener("click", function(){ searsearch.addEventListener("click", function(){
search_text.value = port1.value + " to " + port2.value + ' ' + length.value + "m";
window.open("https://www.bing.com/search?q="+search_text.value);
});

Závěr

Tímto článkem jsem se snažil vypíchnout to nejdůležitější, co by se mohlo při vytváření webové aplikace s modelem Custom Vision hodit. Mnohé jsem z důvodu délky článku vynechal, a proto znovu doporučuji navštívit GitHub našeho projektu, kde najdete kód kompletní. Další důležitá část vytváření webových aplikací, kterou jsem vynechal, je hostování. Ta naše využívá možnost nasazení z GitHubu přímo do služby Azure Web Apps. O tomto tématu už jeden článek na našem blogu vznikl, proto ho taktéž doporučuji navštívit.

Custom Vision spolu s ostatními kognitivními službami skýtá jistě velký potenciál, jak využít moderní AI systémy bez jakékoliv hluboké expertizy. Stačí tak mít jen ten správný nápad. Doufám, že vám článek posloužil jako návod, jak svůj nápad proměnit s pár řádky kódu v realitu.

Nastavení souborů Cookies

1. Co jsou soubory cookies

Soubory cookies jsou krátké textové soubory, které internetová stránka odešle do vašeho prohlížeče. Umožňují internetové stránce zaznamenat informace o vaší návštěvě, například zvolený jazyk a podobně. Následující návštěva stránek tak pro vás může být snazší a příjemnější. Soubory cookies jsou důležité, neboť bez nich by procházení sítě Internet bylo mnohem složitější. Soubory cookies umožňují lepší využití naší internetové stránky a přizpůsobení jejího obsahu vašim potřebám. Soubory cookies používá téměř každá internetová stránka na světě.

2. Druhy souborů cookies

Relační (tedy dočasné) soubory cookies nám umožňují propojovat vaše jednotlivé aktivity po dobu prohlížení těchto internetových stránek. V okamžiku otevření okna vašeho prohlížeče se tyto soubory vytvoří a po zavření okna vašeho prohlížeče se odstraní.

Trvalé soubory cookies nám pomáhají váš počítač identifikovat, jestliže opětovně navštívíte naši internetovou stránku.

3. Využívání souborů cookies

V souladu s ustanovením § 89 odst. 3 zák. č. 127/2005 Sb., o elektronických komunikacích, v účinném znění, si vás tímto dovolujeme informovat, že naše internetové stránky využívají pro svoji činnost soubory cookies, tedy vaše soubory cookies, včetně trvalých, zpracováváme.

Internetové prohlížeče obvykle obsahují správu souborů cookies. V rámci nastavení vašeho prohlížeče tak pravděpodobně můžete jednotlivé soubory cookies ručně mazat, blokovat či zcela zakázat jejich používání. Pro více informací použijte nápovědu vašeho internetového prohlížeče. Jsou-li cookies povoleny, lze toto nastavení internetového prohlížeče považovat za souhlas se zpracováním osobních údajů.

4. Účel použití souborů cookies

K personalizaci obsahu a reklam, poskytování funkcí sociálních médií a analýze naší návštěvnosti využíváme soubory cookies. Informace o tom, jak náš web používáte, sdílíme se svými partnery působícími v oblasti sociálních médií, inzerce a analýz. Používáním internetových stránek vyjadřujete souhlas propojením následujících služeb: Google Analytics, Google Tag Manager, Facebook Pixel, Microsoft Clarity.

Soubory cookies využíváme, kromě účelu uvedeného v předchozím odstavci, pouze pro měření návštěvnosti webové stránky.

5. Správce osobních údajů

Provozovatelem webové stránky studuj.digital a správcem osobních údajů je společnost: pg-sec s.r.o., sídlem Rybná 716/24, Staré Město, 110 00 Praha 1, identifikační číslo 09580905, zapsaná v obchodním rejstříku Městského soudu v Praze, oddíl C, vložka 338028.

​6. Zásady ochrany osobních údajů

Podrobnější informace o souborech cookies a zpracování tvých osobních údajů najdete v našich Zásadách ochrany osobních údajů.

Nezbytné

Tyto soubory cookie jsou nutné pro základní funkce stránky, a jsou proto vždy povolené. Mezi ně patří soubory cookie, které stránce umožňují si vás zapamatovat při procházení stránky v rámci jedné relace nebo, pokud o ně požádáte, mezi relacemi.

Volitelné

Výkon

Tyto soubory cookie nám pomáhají vylepšovat funkce stránek sledováním využití této webové stránky. V některých případech zrychlují zpracování vašeho požadavku a umožňují nám zapamatovat si vaše vybrané předvolby na stránce. Pokud soubory cookie zakážete, může se tím zhoršit přesnost našich doporučení a zpomalit funkčnost stránek.

Sociální média a relamy

Díky souborům cookies sociálních médií si můžete připojit ke svým sociálním sítím a prostřednictvím sociálních médií sdílet obsah z naší webové stránky. Reklamní soubory cookie (třetích stran) shromažďují informace pro lepší přizpůsobení reklamy tvým zájmům, a to na webových stránkách studuj.digital i mimo ně. V některých případech tyto soubory cookies zpracovávají vaše osobní údaje. Pokud chcete získat více informací o zpracování osobních údajů, přečtěte si naše Zásady ochrany osobních údajů. Pokud zakážete soubory cookies, mohou se zobrazovat reklamy, které méně souvisejí s vašimi zájmy, nebo nebudete moci účinně používat odkazy na Facebook, Instagram či jiné sociální sítě anebo nebudete moci sdílet obsah na sociálnch médiích.