Rozpoznávání a klasifikace fotografií pivních přepravek Seminární práce z předmětu Neuronové sítě v aplikacích: Rozpoznávání obrazu Ondřej Veselý, N-II Inženýrská informatika, Automatizace řízení a informatika
Úvod Rozpoznávání obrazu je důležitou úlohou oboru umělé inteligence. Ke klasifikaci obrazových dat se nejčastěji používá fuzzy rozhodování a neuronové sítě. Tato práce se bude zabývat klasifikací obrazů za použití klasické vícevrstvé neuronové sítě.
Popis použitých technologií ● ● ● ●
Nástroj Convert z balíku Imagemagick pro změnu vzorkování rastrových dat Bash shell pro řízení preprocessingu Python pro implementaci algoritmu na detekci hran Jazyk C a Fast Artificial Neural Network Library pro implementaci neuronové sítě
Metodika Z důvodu snadnějšího a rychlejšího zpracování obrazu jsem se rozhodl nepoužívat převod na vektory, ale po celou dobu pracovat s rastrovými daty. Proto v prvním kroku předzpracování provádím škálování obrazu na nižší rozlišení pomocí nástroje Convert. Vzhledem k tomu, že zdrojové obrazy byly různě nasvětleny, považoval jsem za nejdůležitější součást předzpracování obrazu detekci hran. Protože taková implementace je poměrně triviální, použil jsem několikařádkové řešení v Pythonu (operátor Sobel využívající konvolučních masek). Tento skript rovnou převádí obrázek na číselný vektor, přičemž se zahazuje informace o barvách a škála šedé se redukuje pouze na šestnáct odstínů. Celý preprocessing je řízen shellovým skriptem, který parsuje názvy vstupních souborů, vytváří z nich další údaje potřebné pro učení a ty spolu s číselným vektorem ukládá do tzv. prepo souboru, který už je ve formátu kompatibilním s metodami knihovny FANN. Testovací sadu jsem vytvořil vždy z prvního obrázku v sadě; na ostatních se síť učila. Vlastní učení jednouchý program využívající vysokoúrovňových metod zmíněné knihovny. Naučená síť je uložena jako soubor. Testování dat je implementování opět Cčkovým programem. Ten načte neuronovu síť ze souboru; pak testovací sadu předzpracovanou shellovým skriptem a vypíše pro každý testovací záznam dva údaje – nejpravděpodobnější index vzorku a míru jistoty. Síť jsem testoval na standardním vzorku třiceti ořezaných fotek přepravek; průběh experimentu popisuji dále. Podobnou procentuální úspěšnost síť vykazovala i na dvojnásobném vzorku lépe předpřipravených fotek.
Vlastní experiment Jako výkonnostně snesitelné se ještě ukázala být neuronová síť s řádově jednotky tisíc vstupních neuronů; proto jsem se rozhodl pro rozměr 60 x 60 pixelů, tedy 3600 vstupních neuronů. Parametry použité neuronové sítě
Aktivační funkce:
sigmoidální, symetrická
Počet vstupních neuronů:
3600
Počet výstupních neuronů:
30
Počet vrstev:
3
Počet neuronů ve skryté vrstvě:
90
Počet expoch:
25
Trvání jednotlivých částí experimentu akce
čas (s)
Preprocessing tréninkových dat
55
Učení
12
Preprocessing testovacích dat
22
Testování testovacích dat
1
Výsledky název sady bakalarlogo bakalar bazantt bazant birell branik cernahora dudak gambrinus gorgon horden hostan chotebor ch klaster koruna most ostravar passau pelhrimov plzen popper postrizinske primator primus purkmistr rr
číslo sady 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
rozpoznaná přepravka 22 2 3 11 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
Míra jistoty (-1 až 1) -0,44 0,01 0,87 -0,17 0,81 0,72 0,90 0,70 0,98 0,86 0,42 0,63 0,21 -0,37 0,85 0,39 0,32 -0,55 -0,40 0,81 -0,71 0,79 -0,19 -0,21 0,97 0,60 0,87
!
!
!
! ! ! ! !
určeno správně ne ano ano ne ano ano ano ano ano ano ano ano ano ano ano ano ano ano ano ano ano ano ano ano ano ano ano
schoffer steiger urpiner
28 29 30
28 29 30
-0,28 0,70 0,48
!
ano ano ano
Úspěšnost sítě v tomto experimentu je tedy přibližně 93%.
Diskuze Síť měla problémy nejčastěji s přepravkou bazant. Následující série obrázků znázorňuje 1. přepravku bazant 2. přepravku bazant tak jak je posílána na vstup neuronové síti a 3. přepravku horden, která je nejčastěji chybně určována místo přepravky bazant.
1)
2)
3)
V tomto případě se neúspěch podobností loga, které nebylo zachyceno v dostatečném rozlišení. Vzhledem k tomu, že jsem neprováděl rozsáhlé množstí experimentů, které by vedly k doladění parametrů, má tato síť pravěpodobně skrytý potenciál jak v oblasti preprocessingu tak při samotném učení. Preprocessing bych doporučil vylepšit tak, aby generoval hned dva obrazy ke zpracování – hrubý rastr 12 x 12 pixelů celé přepravky a pak o něco větší výřez detekovaného loga (zde se ovšem objevuje nutnost detekce polohy loga).
Závěr I přes zmíněné kvalitatvní nedostatky úspěšnost sítě předčila mé očekávání, přesto že podávala dobré výsledky rovnou bez jakéhokoli ladění. Především je velice rychlá. Celá implementace je navíc provedena relativně nízkoúrovňově a dá se snadno přizpůsobit dalším úlohám, nebo snadno zaintegrovat např. do automatizovaného serverového řešení.
Příloha 1: Kód skriptu řídící preprocessing #!/bin/sh # skript vytvori soubor prepo-$1 pro nacteni neuronovou siti # pracuje se sadou obrazku v adresari bedny # author:
[email protected] # nastavime parametry podle toho jaka data chceme predzpracovat if [ "all" = "$1" ] then maximalni_sada=30 hlavicka="100 3600 30" echo "Generuji ucici sadu pro vsechna data" fi if [ "vsekromenuly" = "$1" ] then maximalni_sada=30 hlavicka="70 3600 30" echo "Generuji ucici sadu pro ne-prvni obrazky ze sady" fi if [ "pouzenuly" = "$1" ] then maximalni_sada=30 hlavicka="30 3600 30" echo "Generuji ucici sadu pro prvni obrazky ze sady" fi if [ "" = "$1" ] || [ ! "$hlavicka" ] then echo "nespravne parametry" exit 1 fi echo $hlavicka > prepro.data sadanum=0 for fname in `ls bedny/*.png | sort`; do # parse filenames to get metadata sada=`echo $fname | cut -f1 -d'0' | cut -f2 -d'/'` obrazek=`echo $fname | cut -f2 -d'0' | cut -f1 -d'.'` if [ $obrazek -eq 1 ] then sadanum=$((sadanum+1)) fi # pokud nechceme zahrnout testovaci obrazky, vynechame cyklus if [ "vsekromenuly" = "$1" ] && [ $obrazek -eq 1 ] then continue fi # pokud ucime pouze testovaci obrazky, vynechame cyklus if [ "pouzenuly" = "$1" ] && [ ! $obrazek -eq 1 ] then continue fi echo "Processing: sada $sada number $sadanum, obrazek $obrazek..." convert -resize 62x62! $fname tmp.png ./s.py tmp.png >> prepro.data
# zmena velikosti # normalizace dat v obrazku
# pozadovany vystup z neuronky for i in `seq 1 $maximalni_sada`; do if [ $i -eq $sadanum ]; then echo -n "1 " >> prepro.data else echo -n "-1 " >> prepro.data fi done echo >> prepro.data done rm tmp.png rm tmp.png.png mv prepro.data prepo-$1.data echo "Normalised data saved to prepo-$1.data"
Příloha 2: Normalizace vstupních dat v jednom obrázku #!/usr/bin/python # # # #
Provede prevod do sede skaly a detekci hran; zapise je do obrazku. Ten ulozi jako $1.png Na vystup vypise radu cisel odpovidajici tmavosti pixelu. prevzato a prizpusobeno z http://lu-vision-dock.blogspot.com/2009/04/sobel-edge-detection.html
import import import import
sys Image math opencv
def sobel(img): if img.mode != "RGB": img = img.convert("RGB") out_image = Image.new(img.mode, img.size, None) imgdata = img.load() outdata = out_image.load() gx = [[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]] gy = [[-1, -2, -1], [0, 0, 0], [1, 2, 1]] p = 0 for row in xrange(1, img.size[0]-1): for col in xrange(1, img.size[1]-1): pixel_gx = pixel_gy = 0 pxval = sum(imgdata[row,col])/3 for i in range(-1, 2): for j in range(-1, 2): val = sum(imgdata[row+i,col+j])/3 pixel_gx += gx[i+1][j+1] * val pixel_gy += gy[i+1][j+1] * val newpixel = math.sqrt(pixel_gx * pixel_gx + pixel_gy * pixel_gy) newpixel = 255 - int(newpixel) outdata[row, col] = (newpixel, newpixel, newpixel) print (newpixel / 16), return out_image img = Image.open(sys.argv[1]) out = sobel(img) out.save(sys.argv[1] + ".png")