{"id":542,"date":"2014-10-30T20:14:13","date_gmt":"2014-10-30T20:14:13","guid":{"rendered":"http:\/\/dronesonen.hibu.no\/?p=542"},"modified":"2014-10-30T20:18:38","modified_gmt":"2014-10-30T20:18:38","slug":"bildegjenkjenning-2-0","status":"publish","type":"post","link":"https:\/\/dronesonen.usn.no\/?p=542","title":{"rendered":"Bildegjenkjenning 2.0"},"content":{"rendered":"<p>Bildegjenkjenning for Firebot Vi bestemte oss tidlig for at bildegjenkjenning skulle v\u00e6re fremgangsm\u00e5ten vi bruker for \u00e5 finne branner (tente lys) i arbeidsomr\u00e5det. Vi vurderte to alternative plasseringer av kamera: Enten fastmontert med utsyn over hele arbeidsomr\u00e5det, eller montert p\u00e5 kj\u00f8ret\u00f8yet. Begge alternativer ble utpr\u00f8vd, og valget falt p\u00e5 kj\u00f8ret\u00f8ymontering. Vi kan dermed montere kameraet sammen med slukke\/tenne-utstyret. Neste sp\u00f8rsm\u00e5l ble da: Hvilken kameral\u00f8sning skal vi velge? Alternativene var mange, inkludert:<\/p>\n<ul>\n<li>GoPro kamera: Lite og hendig, men vanskelig \u00e5 hente video-str\u00f8m fra.<\/li>\n<li>Web-kamera: Njaa \u2026<\/li>\n<li>Pixy CMUCam: Kameramodul med innebygget bildegjenkjenning. Hadde sikkert v\u00e6rt midt i blinken, men vi har ikke denne modulen tilgjengelig. Dessuten er det ikke mye l\u00e6ring i \u00e5 bruke ferdigtygd vare \u2026<\/li>\n<li>Raspberry Pi kamera-modul: Denne l\u00f8sningen viste seg \u00e5 v\u00e6re et naturlig valg, siden vi uansett hadde beregnet \u00e5 bruke Raspberry Pi (RasPi) til bildegjenkjenningsbiten. I tillegg viste det seg at skolen hadde Raspi-cam tilgjengelig.<\/li>\n<\/ul>\n<p>Bildegjenkjenning er en komplisert prosess, og tiln\u00e6rmet umulig \u00e5 gjennomf\u00f8re p\u00e5 den tiden vi har til r\u00e5dighet dersom man ikke bruker et eksisterende bibliotek. Etter en del leting fant vi, som tidligere nevnt OpenCV. OpenCV (Open Source Computer Vision Library: <a href=\"http:\/\/opencv.org\">http:\/\/opencv.org<\/a>) er et anerkjent bildebehandlingsbibliotek med mye dokumentasjon og mange artikler og kodeeksempler tilgjengelig p\u00e5 Internett. En annen forutsetning for valg av bibliotek var at det m\u00e5tte kunne kj\u00f8re p\u00e5 RasPi. Vi fant fort ut at dette skulle v\u00e6re greit. Det viste seg ogs\u00e5 at SD-kortet som fulgte med Pi\u2019en ikke var stort nok for alt vi m\u00e5tte installere. Et nytt SD-kort ble kj\u00f8pt inn, og Raspbian ble installert. Deretter lastet vi ned OpenCV, og satte i gang prosessen med \u00e5 bygge dette for RasPi. Biblioteket er p\u00e5 totalt 3.8 GB, og Make-prosessen skulle vise seg \u00e5 ta 13-14 timer, s\u00e5 dette var greit \u00e5 gj\u00f8re hjemme \u2026 Vi bruker raspicam for \u00e5 kontrollere kameraet fra C++. Dette biblioteket m\u00e5tte ogs\u00e5 lastes ned og bygges, men det gikk kjappere. Dette biblioteket er basert p\u00e5 C, s\u00e5 ting m\u00e5 gj\u00f8res p\u00e5 en litt annen m\u00e5te enn en die-hard OO-stakkar er vant til. Dette var imidlertid ikke s\u00e5 veldig store utfordringen, siden interaksjonen mot dette biblioteket er relativt liten. For \u00e5 kommunisere med det Arduino-baserte styresystemet til roboten, bestemte vi oss for \u00e5 bruke en I2C-buss. Denne bussen bruker et minimum av ledninger mellom nodene, samtidig som den har kapasitet til \u00e5 h\u00e5ndtere mange noder samtidig. I v\u00e5rt tilfelle trenger vi ikke mer enn \u00e9n server og \u00e9n klient, men I2C er likevel godt egnet. Vi bruker biblioteket WiringPi til \u00e5 hjelpe oss med denne kommunikasjonen. En spesiell utfordring ved kommunikasjon mellom Arduino og RasPi er at RasPi ikke t\u00e5ler mer enn 3.3V p\u00e5 inngangene, mens Arduino gjerne trykker ut 5V. Av denne grunn valgte vi \u00e5 sette opp RasPien som master, og Arduinoen som slave. Dermed er det alltid RasPien som tilf\u00f8rer spenning til bussen, s\u00e5 den er trygg. Eneste betingelse er at Arduinoen aldri setter sine pinner \u00abh\u00f8ye\u00bb. Hensikten med bildegjenkjenningsfunksjonen er \u00e5 finne branner i arbeidsomr\u00e5det til roboten, og deretter sende koordinatene tilbake til Arduinoen. Dette gj\u00f8res ved \u00e5 lete etter omr\u00e5der i kameraets synsfelt som er lysere enn et gitt niv\u00e5. Vi pr\u00f8vde f\u00f8rst \u00e5 lete kun etter omr\u00e5der med flammens farge og intensitet, men dette viste seg \u00e5 bli for snevert. Selv om denne fremgangsm\u00e5ten ga lite st\u00f8y i bildet, ble det rett og slett for f\u00e5 piksler i kameraets synsfelt til at vi kunne se lyset p\u00e5 litt avstand. L\u00f8sningen vi endte opp med var \u00e5 lete etter lys i alle spekterets farger, med lav metning og h\u00f8y intensitet. Dette matcher flammen og det n\u00e6rmeste omr\u00e5det av lyset bra, men medf\u00f8rer ogs\u00e5 en del st\u00f8y i form av gjenskinn fra vinduer eller kunstig lys. RaspiCam kan levere videostr\u00f8m eller stillbilder etter behov. Vi har valgt \u00e5 behandle stillbilder s\u00e5 fort som plattformen tillater. Dette vil si at et nytt bilde blir tatt s\u00e5 snart det forrige er prosessert. Denne fremgangsm\u00e5ten gir oss en oppdateringsfrekvens p\u00e5 anslagsvis 4-6 bilder i sekundet, n\u00e5r programmet ikke bruker ressurser p\u00e5 \u00e5 vise bilder til skjerm. Bildeprosesseringen foreg\u00e5r slik:<\/p>\n<ul>\n<li>Et frame tas ved hjelp av en kommando som sendes til raspicam. Dette bildet lagres i et objekt av Mat-klassen fra OpenCV. I dette objektet lagres hver fargekanal i sin egen matrise.<\/li>\n<li>Funksjonen cvtColor() brukes for \u00e5 konvertere bildets farger fra RBG (r\u00f8d-bl\u00e5-gr\u00f8nn, kartesisk format) til HSV (hue-saturation-value, sylindrisk koordinatsystem). I HSV-omr\u00e5det beskrives farger ved hjelp av fargehjulet med en ekstra akse, slik at det utvides til en sylinder. Hue (nyanse) angir farge som en verdi mellom 0 og 179, og angir \u00abvinkelen\u00bb i forhold til startpunktet i hjulet. Saturation (metning) angir \u00abhvor ren\u00bb fargen er, angitt ved hvor langt fra sentrum vi er. Lavere saturation (n\u00e6rmere sentrum) betyr st\u00f8rre grad av blanding av farger. Value angir intensiteten, og angis ved h\u00f8yden p\u00e5 sylinderen.<\/li>\n<li>En \u00abthreshold\u00bb-funksjon benyttes for \u00e5 selektere piksler med intensitet over \u00f8nsket niv\u00e5. Resultatet av denne funksjonen lagres i en ny matrise i form av bin\u00e6re verdier, dvs. svarte eller hvite piksler.<\/li>\n<li>Deretter g\u00e5r vi i gang med en filtreringsprosess for \u00e5 fjerne mest mulig st\u00f8y f\u00f8r vi pr\u00f8ver \u00e5 finne konturer. Denne prosessen best\u00e5r av \u00aberoding\u00bb og \u00abdilation\u00bb, hvor dilation fors\u00f8ker \u00e5 binde sammen n\u00e6rst\u00e5ende enkeltpiksler til st\u00f8rre samlinger (redusere ujevnheter i kantene av interessante omr\u00e5der), mens eroding har til hensikt \u00e5 fjerne enkeltpiksler som st\u00e5r for seg selv (st\u00f8y).<\/li>\n<li>OpenCV fors\u00f8ker deretter \u00e5 finne konturer i bildet ved \u00e5 se etter omr\u00e5der med sammenhengende piksler, og lagrer hvert av disse i en vector-liste. For hvert omr\u00e5de som finnes beregnes arealet (antall piksler i omr\u00e5det), samt tyngdepunktet (senteret av omr\u00e5det).<\/li>\n<li>Vi pr\u00f8ver \u00e5 eliminere en del st\u00f8y ved \u00e5 bare vurdere omr\u00e5der med minimum 10 piksler. Dersom det finnes flere slike omr\u00e5der i bildet, velger vi det omr\u00e5det med sentrum n\u00e6rmest det punktet som ble valgt i forrige frame. Dermed unng\u00e5r vi en del tilfeller av at forskjellige konturer velges fra frame til frame.<\/li>\n<li>N\u00e5 sender vi dette punktet til Arduinoen via I2C-bussen som et punkt med verdier mellom (0, 0) og (255,255) hvor (0,0) er \u00f8vre venstre hj\u00f8rne av synsfeltet og (255,255) er nedre h\u00f8yre hj\u00f8rne. Vi har valgt \u00e5 lage en protokoll der vi f\u00f8rst sender 3 kjente bytes for \u00e5 indikere starten av en melding, f\u00f8r x og y-koordinatene sendes til slutt. Dette hindrer at vi f\u00e5r byttet om p\u00e5 rekkef\u00f8lgen dersom en melding ikke kommer frem som den skal.<\/li>\n<\/ul>\n<p><a href=\"http:\/\/dronesonen.usn.no\/wp-content\/uploads\/2014\/10\/ImgProcess.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone  wp-image-548\" src=\"http:\/\/dronesonen.usn.no\/wp-content\/uploads\/2014\/10\/ImgProcess-300x109.png\" alt=\"ImgProcess\" width=\"617\" height=\"224\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2014\/10\/ImgProcess-300x109.png 300w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2014\/10\/ImgProcess-1024x372.png 1024w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2014\/10\/ImgProcess.png 1273w\" sizes=\"auto, (max-width: 617px) 100vw, 617px\" \/><\/a> \u00a0 <span style=\"color: #44546a\"><i>Illustration 1: Debug-bilde til venstre viser treff p\u00e5 flammen. H\u00f8yre side viser omr\u00e5det som matcher niv\u00e5et vi \u00f8nsker.<\/i><\/span><\/p>\n<p>Det har v\u00e6rt nok av utfordringer i prosessen med \u00e5 f\u00e5 til bildeprosesseringen. Det har ikke v\u00e6rt bare enkelt \u00e5 f\u00e5 alle biblioteker og annen software opp og g\u00e5 p\u00e5 Raspberry Pi, men det gikk til slutt. Da vi endelig hadde f\u00e5tt det meste opp og g\u00e5, sa SD-kortet takk for seg. Masse \u00abbad disk-block\u00bb ved boot\u2026 Dette er spesielt morsomt n\u00e5r det tar 13-14 timer \u00e5 bygge bare det ene biblioteket.\uf04c Jeg plugget kortet inn i en Windows-maskin for \u00e5 se om det var mulig \u00e5 kopiere det vha. et backup-program som leser blokker direkte fra disken. Dette gikk ikke; \u00abdisken er korrupt\u00bb. Kortet ble imidlertid st\u00e5ende i maskinen en stund mens jeg bannet og svor. Etter ca. en halv time fant jeg ut at jeg skulle pr\u00f8ve igjen, og da fikk jeg kopiert det uten feilmeldinger. Kanskje det var oppvarmingen som hjalp? Kortet ble byttet p\u00e5 garanti, men f\u00f8r jeg tok det nye kortet i bruk, leste jeg en del p\u00e5 nett om andre som hadde hatt lignende opplevelser. RasPi er ganske n\u00f8ye p\u00e5 hvilke SD-kort den liker eller ikke. <a href=\"http:\/\/elinux.org\/RPi_SD_cards\">http:\/\/elinux.org\/RPi_SD_cards<\/a> lister opp en mengde spesifikke SD-kort med informasjon om RasPi liker dem eller ikke. Det f\u00f8rste kortet var imidlertid en av de godkjente typene, s\u00e5 jeg fortsatte \u00e5 lete. En annen ting som mange nevnte var at det ofte oppstod problemer med SD-kort p\u00e5 RasPi dersom str\u00f8mforsyningen ikke leverte nok spenning. Det er tydeligvis vitalt at spenningen (m\u00e5lt under drift) mellom TP1 og TP2 p\u00e5 RasPi ikke er under 4.75 eller over 5.25 volt. <a href=\"http:\/\/elinux.org\/RPi_Hardware\">http:\/\/elinux.org\/RPi_Hardware<\/a> (under avsnittet \u00abPower Supply Problems\u00bb) forklarer hvordan spenningen skal m\u00e5les under drift. <a href=\"http:\/\/dronesonen.usn.no\/wp-content\/uploads\/2014\/10\/File-RPI_Test_Points.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone  wp-image-546\" src=\"http:\/\/dronesonen.usn.no\/wp-content\/uploads\/2014\/10\/File-RPI_Test_Points-300x278.jpg\" alt=\"File-RPI_Test_Points\" width=\"366\" height=\"339\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2014\/10\/File-RPI_Test_Points-300x278.jpg 300w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2014\/10\/File-RPI_Test_Points.jpg 400w\" sizes=\"auto, (max-width: 366px) 100vw, 366px\" \/><\/a> <a href=\"http:\/\/dronesonen.usn.no\/wp-content\/uploads\/2014\/10\/File-Voltmeter.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone  wp-image-547\" src=\"http:\/\/dronesonen.usn.no\/wp-content\/uploads\/2014\/10\/File-Voltmeter-200x300.jpg\" alt=\"File-Voltmeter\" width=\"233\" height=\"350\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2014\/10\/File-Voltmeter-200x300.jpg 200w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2014\/10\/File-Voltmeter.jpg 250w\" sizes=\"auto, (max-width: 233px) 100vw, 233px\" \/><\/a> Jeg m\u00e5lte kun 4.6 volt n\u00e5r jeg brukte USB-porten p\u00e5 pc-en som str\u00f8mkilde, s\u00e5 jeg regner med at det var en medvirkende \u00e5rsak til at kortet ble \u00f8delagt. Etter dette brukte vi kun en ekstern str\u00f8mforsyning for \u00e5 v\u00e6re sikker p\u00e5 at det var nok spenning. Etter \u00e5 ha lagt tilbake sikkerhetskopien fra det f\u00f8rste kortet, virket alt som det skulle. Jeg har imidlertid blitt mer n\u00f8ye p\u00e5 \u00e5 ta sikkerhetskopier med ujevne mellomrom \u2026 N\u00e5r RasPi-en blir montert p\u00e5 kj\u00f8ret\u00f8yet, vil vi ikke ha skjerm og tastatur tilgjengelig for \u00e5 kunne starte bildeprosesseringsprosessen manuelt. Det m\u00e5 derfor startes automatisk n\u00e5r RasPi-en booter opp. Dette er tilsynelatende enkelt nok, og kan gj\u00f8res p\u00e5 mange forskjellige m\u00e5ter og ved forskjellige tidspunkter i boot-prosessen. Det som er viktig \u00e5 tenke p\u00e5 er at prosessen ikke startes for tidlig, dvs. f\u00f8r alle n\u00f8dvendige ressurser er tilgjengelig. Vi m\u00e5 ogs\u00e5 huske p\u00e5 \u00e5 legge inn en mulighet for \u00e5 avslutte programmet, f.eks. via en tastekombinasjon p\u00e5 tastaturet. Om man glemmer dette, vil man ikke kunne f\u00e5 tilgang til operativsystem-shellet i det hele tatt. Dooh\u2026 Vi endte opp med \u00e5 legge inn en linje i .bashrc i hjemmekatalogen til brukeren \u00abpi\u00bb for \u00e5 starte opp programmet. P\u00e5 denne m\u00e5ten ble det enkelt \u00e5 debugge oppstarten, siden dette skriptet kj\u00f8res hver gang man \u00e5pner et nytt terminalvindu (inkludert det f\u00f8rste som operativsystemet \u00e5pner ved fullf\u00f8rt boot). <a name=\"_GoBack\"><\/a>Vi m\u00e5tte som tidligere nevnt fors\u00f8ke en del forskjellige fremgangsm\u00e5ter for \u00e5 finne et tent lys p\u00e5 litt avstand, men mener n\u00e5 at dette er i orden. Kommunikasjonen mellom RasPi og Arduino fungerer fint, og vi er n\u00e5 klare til \u00e5 implementere dette p\u00e5 kj\u00f8ret\u00f8yet. <a href=\"http:\/\/dronesonen.usn.no\/wp-content\/uploads\/2014\/10\/2014-10-24-10.08.15.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone  wp-image-544\" src=\"http:\/\/dronesonen.usn.no\/wp-content\/uploads\/2014\/10\/2014-10-24-10.08.15-300x168.jpg\" alt=\"2014-10-24 10.08.15\" width=\"616\" height=\"345\" \/><\/a><a href=\"http:\/\/dronesonen.usn.no\/wp-content\/uploads\/2014\/10\/2014-10-24-10.06.40.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-543\" src=\"http:\/\/dronesonen.usn.no\/wp-content\/uploads\/2014\/10\/2014-10-24-10.06.40-300x168.jpg\" alt=\"2014-10-24 10.06.40\" width=\"300\" height=\"168\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2014\/10\/2014-10-24-10.06.40-300x168.jpg 300w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2014\/10\/2014-10-24-10.06.40-1024x576.jpg 1024w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><a href=\"http:\/\/dronesonen.usn.no\/wp-content\/uploads\/2014\/10\/2014-10-24-10.08.28.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-medium wp-image-545\" src=\"http:\/\/dronesonen.usn.no\/wp-content\/uploads\/2014\/10\/2014-10-24-10.08.28-300x168.jpg\" alt=\"2014-10-24 10.08.28\" width=\"300\" height=\"168\" srcset=\"https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2014\/10\/2014-10-24-10.08.28-300x168.jpg 300w, https:\/\/dronesonen.usn.no\/wp-content\/uploads\/2014\/10\/2014-10-24-10.08.28-1024x576.jpg 1024w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Bildegjenkjenning for Firebot Vi bestemte oss tidlig for at bildegjenkjenning skulle v\u00e6re fremgangsm\u00e5ten vi bruker for \u00e5 finne branner (tente lys) i arbeidsomr\u00e5det. Vi vurderte to alternative plasseringer av kamera: Enten fastmontert med utsyn over hele arbeidsomr\u00e5det, eller montert p\u00e5 kj\u00f8ret\u00f8yet. Begge alternativer ble utpr\u00f8vd, og valget falt p\u00e5 kj\u00f8ret\u00f8ymontering. Vi kan dermed montere kameraet [&hellip;]<\/p>\n","protected":false},"author":28,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[15],"tags":[],"class_list":["post-542","post","type-post","status-publish","format-standard","hentry","category-smart-systems-2014-fire-fighters"],"_links":{"self":[{"href":"https:\/\/dronesonen.usn.no\/index.php?rest_route=\/wp\/v2\/posts\/542","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dronesonen.usn.no\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dronesonen.usn.no\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dronesonen.usn.no\/index.php?rest_route=\/wp\/v2\/users\/28"}],"replies":[{"embeddable":true,"href":"https:\/\/dronesonen.usn.no\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=542"}],"version-history":[{"count":4,"href":"https:\/\/dronesonen.usn.no\/index.php?rest_route=\/wp\/v2\/posts\/542\/revisions"}],"predecessor-version":[{"id":552,"href":"https:\/\/dronesonen.usn.no\/index.php?rest_route=\/wp\/v2\/posts\/542\/revisions\/552"}],"wp:attachment":[{"href":"https:\/\/dronesonen.usn.no\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=542"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dronesonen.usn.no\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=542"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dronesonen.usn.no\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=542"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}