Stavanger, 1. februar 2017 Det teknisknaturvitenskapelige fakultet ELE610 Prosjekter i robotteknikk, vår 2017. Bildefangst-del, oppgave 1. Hensikten med denne øvingen er å finne ut hvordan det kamera en alltid har tilgjengelig kan brukes for å ta bilder som skal brukes for bildebehandling for å gi relevant informasjon om et motiv. Det er bruken som avgjør hva som er relevant eller ikke. Den normale bruk (for mennesker) er å ønske et bilde som er godt å se på, som framkaller minner (gjenkjennelse) stemninger og følelser. I en teknisk bruk sier en gjerne heller at det er interessant å finne hvilke objekter som er i bilde, hvor de er i bilde og relativt til kamera eller andre objekter i bilde, hvilken orientering de har, eventuelt også hastighet. Spesielle egenskaper ved objektet kan også være ønskelig å finne: størrelse, form, material og farge. Dette skal gjerne finnes ved å (automatisk) behandle bilde i en datamaskin. Et mobilkamera er laget for normal (mennesklig) bruk. Her vil vi teste hvor egnet det er for teknisk bruk. 1 Bildefangst med smarttelefon Oppgavene i denne øvinga kan gjøres på laboratorium E468, men det er egentlig ingenting som gjør det nødvendig. Dere trenger deres egen mobiltelefon med kamera, et motiv med fast kjent form, for eksempel et sjakkbrett eller et ruteark, og en (laptop) PC med Matlab. 1.1 Finne egenskaper for kamera som dere bruker Det første dere skal gjøre er å finne mest mulig informasjon om kamera i deres mobiltelefon. Merk at det som er lettest å finne er det som gjerne kalles salgsinformasjon, det som skal gi kjøperen den informasjon han mener er nødvendig for å ønske å kjøpe dette kamera (framfor et annet kamera). Kameratester er nesten alltid orientert mot den mennesklige bruk og gjerne rettet mot vanlige forbrukere og kunder, men kan også være rettet mot (nesten) profesjonelle fotografer. Noe teknisk informasjon er imidlertid alltid tilgjengelig. Karl Skretting, Institutt for data- og elektroteknikk (IDE), Universitetet i Stavanger (UiS), 4036 Stavanger. Sentralbord 51 83 10 00. Direkte 51 83 20 16. E-post: karl.skretting@uis.no.
Prøv å finne hva hvert av stikkordene nedenfor betyr generelt, og spesielt om du kan si noe om ditt eget kamera for dette punktet. Oppgavebesvarelsen (rapporten) skal inneholde litt forklaring om hvert punkt og eventuelt hvor ditt kamera er i forhold til dette. De fleste stikkorda er hentet fra tabell med tekniske data for et industrikamera, så det kan godt være at flere ikke er relevante for et mobilkamera. 1. Oppløsning (resolution) og total antall megapixel 2. Oppløsning for hvert pixel (bit for ADC for hver farge) 3. (Chroma) farger 4. Bilderate (Frame rate) 5. Sensor type og størrelse (og eventuelt navn, modell). Det vanlige er CMOS eller CCD. 6. Pixel størrelse 7. Lukkertype (shutter) og lukkertider (exposure range) 8. Ekstra lys, blitz (eksternt eller innebygd eller ingenting) 9. Linse, eller type feste for linse 10. Fokallengde eller område 11. Lysåpning 12. Følsomhet (sensitivity, or for old/film type cameras: film speed, ISO number), 13. (Quantum efficiency) for ulike bølgelengder er gjerne omtrent tilsvarende som følsomhet? 14. Minne (memory, storage) 15. Stabilisering 16. Bildebehandling, komrimering og lagringsformat 17. Overføring, grensesnitt (interface) 18. Strømforsyning, effekt 19. Vekt og størrelse (dimensjon) 20. Temperatur og fuktighetskrav (innkapsling) ved bruk og lagring 2
Selv om en kan finne opplysninger om mange av punkta ovenfor, og sammenligne disse for ulike kamera, er det ikke helt enkelt å si hvor godt egnet et kamera er for ulike teknisk bruk (målinger/beslutninger ut fra automatisk bildebehandling) eller hvilket kamera som er best. 1.2 Ta et testbilde Ta et testbilde av et sjakkbrett eller et ark med sjakkbrettmønster, sorte og hvite ruter. Hold motiv og kamera i ro i en fast avstand, ca 50 cm, og ta bilde. Overfør bilde til Matlab, eller til et annet program der du kan se på hele bildet, på utvalgte deler av bilde, og vise verdi for hvert pixel. Svar på følgende: 1. Beskriv prosedyren for å overføre bilde fra kamera til Matlab. Ta bilde med i rapporten. 2. Hvilke metainformasjon er tilgjengelig om bildet du har tatt. 3. Se på ulike pixel som tilhører sorte ruter, er alle representert med samme verdier? Hvor stort spenn er det? 4. Se på ulike pixel som tilhører hvite ruter, er alle representert med samme verdier? Hvor stort spenn er det? 5. Se på kantene mellom sort og hvitt. Hvor skarpt er bilde? Hvor mange pixel er det mellom et (helt) sort pixel og et (helt) hvitt pixel? Hvordan skulle det vært i et helt sylskarpt bilde? 6. Varierer skarphet mellom ulike deler av bilde. 7. Er (alle) rette linjer i motivet også rette linjer i bildet? 8. Hvor lang [mm] er en kant i motivet? 9. Hvor lang er en kant, altså avstand mellom hjørner, i antall pixel? 10. Er det forskjell mellom vertikale og horisontale kanter, eller mellom kanter midt i bilde og kanter nærmere kanten av bilde? Prøv å forklare hvorfor. 11. Hvor stor avstand tilsvarer en pixelkant, og hvor stor vinkel danner pixelkanten? Er dette det samme i midten av bilde som nærmere kantene. Prøv å forklare hvorfor det er slik. 3
Figur 1: Bilde som tatt med med Iphone 4, jpg-bilde (RGB) med bredde 3264 og høyde 2448 piksler. 1.3 Enkel bildebehandling Automatisk bildebehandling er generelt ikke helt enkelt. Første utfordring er gjerne at en ikke har kontroll pa bilde som ble tatt, motiv og lys og kameraposisjon og orientering og mer, og at en derfor ma gjøre noe manuell forbehandling av data (preprocessing). Nedenfor er det beskrevet hva jeg har gjort for et bilde. Dere skal ta deres eget bilde og gjøre tilsvarende bildebehandling som her og presenter resultatene med tilsvarende figurer/bilder. Eksempelbilde jeg tok viser i figur 1. Her velger jeg a beskjære bilde, finne ROI (region of interest), og a gjøre det om fra fargebilde til gra tonebilde, siden det er minimalt med interessant fargeinformasjon i dette bilde. Bilde viser i figur 2. I Matlab ble dette gjort med: A = imread( ruter.jpg ); % Finner del av bilde som er interessant na, ROI % Tallene for hjørnepunktene er funnet ved a se pa bildet. ul = [271,441]; % (y,x) lr = [2320, 2170]; % 4
Figur 2: Beska ret bilde gjort om til gra tonebilde, med bredde 1730 og høyde 2050 piksler. A = rgb2gray( A(ul(1):lr(1), ul(2):lr(2), :) ); imwrite(a, rutergray.png ); Na r en skal gjøre bildebehandling ma en vite nøyaktig hva en er interessert i, og hvordan en ønsker a presentere resultatet. Alt som er gjort hittel er a finne interessant del av bilde, og det ble gjort manuelt. La oss si at oppgaven her er a finne 8 horisontale kanter og 7 vertikale kanter fra bilde i figur 2 og plotte de horisontale kantene bla og de vertikale kantene røde i samme bilde. Hver av de 21 sorte kvadratene har egentlig 4 kanter, men vi ser her at de de kan kobles sammen til lengre linjer og det er disse lengre linjene vi vil finne. For a finne kanter er det flere muligheter i Matlab. Det vanligste er Hough transformasjon eller Matlab edge() funksjonen med Canny parameter. Glatting kan integreres i edge() funksjonen. Ofte vil en ogsa gjøre bildet om til et binærbilde (sort-hvitt), det gjøres med terskling i gra tonebilde, hvor terskling bør gjøres kan ofte finnes ut fra histogrammet. Her har jeg valgt en annen ma te. 5
Jeg velger her å se på vertikale og horisontale kanter hver for seg, og bruker følgende Matlab-kode for å finne kantbilder. [I,J] = size(a); % Filtrerer for å finne vertikale og horisontale kanter. % (her er edge-funksjonen et alternativ) h = fspecial( sobel ); % bruke abs for ikke å skille høgrekant og venstrekant Eh = abs(filter2(h,a)); % horisontale kanter Ev = abs(filter2(h,a)); % vertikale kanter % Filtrering her antar 0-ere (svart) utenfor kantene av bildet og derfor % får en kraftige kanter på to av sidene for hvert av disse kantbildene. % Denne kanten er kunstig. Eh([1,I],:) = 0; Ev(:,[1,J]) = 0; % figure(1); imagesc(eh); colormap(gray); % axis equal; title( Horisontale kanter, bilde Eh ); Selv om kant-pixel nå viser tydelig på kantbildene Eh og Ev så er ikke de lange linjene identifisert, det vil si endepunktene er ikke lokalisert. Alternativ til endepunkt for ei linje er å finne senterpunkt og vinkel. Radon transformenn projeksjerer bilde ned på et linjestykke som har en gitt vinkel. Dette linjestykket akkumulerer opp (verdiene for) alle bildepixel. Linjestykket går gjennom sentrum av bilde og har vinkel θ (i grader) til x-akse i bilde (mot høyre). Matlab eksempel: A = zeros(15); A(:,3) = 1; [R, xp] = radon(a, 0); % horisontal linje gjennom A(8,8). R(xp==-5) skal være stor, de andre verdier i R små. På grunn av litt glatting blir noe av verdiene fordelt til R(xp==-4) og R(xp==-6). Merk at sum(r) == sum(a(:)). I kantbildene Eh og Ev er det kun noen få vinkeler som er interessante å bruke i Radon-transformen. For horisontale linjer er det gjerne naturlig å bruke 80 θ 100 og for vertikale linjer er det gjerne naturlig å bruke 10 θ 10 men jeg begrenser vinklene enda mer nedenfor theta = 88:0.1:91; [R, xp] = radon(eh, theta); ph = houghpeaks(floor(r), 8, NHoodSize, [101,31],... Threshold, floor(0.1*max(r(:)))); ph = sortrows(ph); ph = [xp(ph(:,1)), theta(ph(:,2)) ]; figure(2); clf; colormap(jet); imagesc(xp, theta, log10(max(r,1e4))); %#ok<udim> title(sprintf( log10 av Radon transform av Eh. )); xlabel( x-position ); ylabel( Vinkel theta ); Og tilsvarende for vertikale kanter 6
theta = -2:0.1:1; [R, xp] = radon(ev, theta); pv = houghpeaks(floor(r), 7, NHoodSize, [101,31],... Threshold, floor(0.1*max(r(:)))); pv = sortrows(pv); pv = [xp(pv(:,1)), theta(pv(:,2)) ]; figure(3); clf; colormap(jet); imagesc(xp, theta, log10(max(r,1e4))); %#ok<udim> title(sprintf( log10 av Radon transform av Ev. )); xlabel( x-position ); ylabel( Vinkel theta ); Nå vil toppunkt i R indikere de mest markerte kantene i bildet. Det er heller ikke helt trivielt å finne topper i R, en kan se på bildene og zoome inn på spesielle områder og finne topper. Men ovenfor så prøver jeg å bruke Matlab-funksjonen houghpeaks, en må være oppmerksom på at denne bruker heltallsargument, en må også gi enn et tilstrekkelig stort naboområde som skal ekskluderes etter hvert valg for å unngå linjer så nær hverandre at det egentlig er samme linje, og en må gi inn lav nok terskelverdi for å få med svakere linjer også. Nå er ph noen toppunkt i radon-transformasjonen for horisontale linjer og pv noen toppunkt i radon-transformasjonen for vertikale linjer. Jeg lager en liten funkson for å finne der ei horisontal og ei vertikal linje krysser hverandre, funksjonen kan kalles med: p = this linjekryss(ph(i,:), pv(j,:));. Koden for denne funksjonen kan være function p = this_linjekryss(no1, no2) % finner punktet der to linjer krysser hverandre % x-akse er mot høyre og y-akse nedover (som for bilder) % vinkel th er i forhold til x-akse (mot klokka) % xp er linja sin avstand fra origo xp1 = no1(1); th1 = no1(2); xp2 = no2(1); th2 = no2(2); p1 = [-sind(th1); cosd(th1)]*xp1; p2 = [-sind(th2); cosd(th2)]*xp2; v1 = [cosd(th1); sind(th1)]; v2 = [cosd(th2); sind(th2)]; a = [v1, -v2]\(p2-p1); p = p1+a(1)*v1; return Da skulle alt være på plass for å plotte linjer i figuren. Når dette gjøres så blir øverste venstre del av resultatbilde som i figur 3. 7
sort-hvitt-bilde og eventuelt også nedsample slik at en får mindre oppløsning. Begge deler kunne blitt gjort her, men vi velger å kun gjøre bilde om til sorthvitt-bilde der hvitt er 1 og sort er 0. Vi tar også en lukking og åpning av bildet for å rense bort eventuelle små områder med hvite pixel i sorte områder og små områder med sorte pixel i hvite områder. bwareaopen(.) er en alternativ funksjon for tilsvarende operasjoner. Se gjerne på bildet både før og etter de morfologiske operasjonene er gjort på bildet: figure(1); imagesc(b); level = graythresh(a); % A er gråtonebilde B = im2bw(a,level); % B er sort-hvitt B = bwmorph(bwmorph(b, close,4), open,4); Ut fra sort-hvitt-bildet funnet over er det mange muligheter å finne hjørnene. I et ideelt bilde ville hjørnet av en sort rute vært et sort pixel med to hvite naboer og to sorte naboer, og det stemmer nok stor sett, men en vil da også finne mange andre pixel. Image Processing Toolbox inneholder en funksjon corner(.) for å finne hjørner. Denne kan kanskje brukes og jeg prøver: C = corner(b); figure(1); imshow(b); hold on; plot(c(:,1), C(:,2), r* ); Denne fant for mange hjørner, men kanskje kan en justere dette ved å sette noen parametere. En har 21 sorte ruter og dermed skulle en forvente å finne 84 hjørner, og kan da bruke C = corner(b,84);, imidlertid blir ikke alle ønska hjørnene funnet nå heller, og en finner noen uønska hjørner. Det er flere alternativer en nå kan prøve, enten å forbedre bildet slik at corner(.) funksjonen virker som ønskelig, eller å prøve å finne en annen måte å lokalisere hjørnene på. Her skal vi prøve å lage et eget hjørnefilter. Når de lange linjene er rimelig rette (loddrette og vannrette) slik som her er det ganske enkelt å definere et filter som finner (gir stor verdi) for (først) øverste venstre hjørne av ei sort rute. n=5; % filteret er (2*n+1)^2 h = [zeros(n), ones(n,n+1); ones(n+1,n), -ones(n+1)]; ul = filter2(h,b) + (n+1)^2; % 0 <= ul(i,j) <= (3n+1)(n+1) % figure(1); clf; imagesc(ul); colormap(gray); Minste mulig verdi ut fra filteret er (n + 1) 2 som legges til for å unngå negative verdier i ul. For et ideelt hjørne vil filteret gi verdi 2n(n + 1) som er maksimum verdi ut av filteret (for et binært bilde inn), og ul vil ha maxverdien. Men det er ikke nødvendigvis slik for alle 21 hjørnene som vi ønsker 9
å finne her. Ved å se på resultatet ser en at alle de 21 hjørnene vi ønsker å finne har store verdier. Det kan være at en grenseverdi kan brukes for å finne hjørnene, men en mer robust måte er å bruke houghpeaks(.) siden en her har mulighet til å nulstille naboer til en maksimums verdi. Hjørnene en finner ønsker en å sortere linje for linje fra venstre mot høyre. Dette gjøres med: peaksul = houghpeaks(ul, 21, NHoodSize, [2*n+7,2*n+7], Threshold, floor(0.8*max(ul(:)))); [temp,i]= sort(10*peaksul(:,1)+peaksul(:,2)); peaksul = peaksul(i,:); % figure(1); clf; imshow(b); hold on; % plot(peaksul(:,2), peaksul(:,1), ro ); Tilsvarende kan gjøres for å finne peaksur, peaksll og peakslr. De nye hjørnefiltrene kan finnes ved å rotere 90 grader: h = rot90(h);. Når så alle hjørnene er funnet så kan en vise resultatet ved å tegne en grønn boks rundt alle sorte ruter. Vi regner også ut lengden for alle kanter. figure(1); clf; imshow(b); hold on; kantlengde = zeros(4*21,1); for i = 1:21 x = [peaksul(i,2), peaksur(i,2), peakslr(i,2),... peaksll(i,2), peaksul(i,2)]; y = [peaksul(i,1), peaksur(i,1), peakslr(i,1),... peaksll(i,1), peaksul(i,1)]; kantlengde((4*i-3):(4*i)) = sqrt((x(2:5)-x(1:4)).^2 +... (y(2:5)-y(1:4)).^2); plot(x, y, g- ); text(x(1)+20,y(1)-20,sprintf( %6.1f,... mean(kantlengde((4*i-3):(4*i)))), FontSize,12); end xlabel( Over hver rute står gjennomsnitts kantlengde. ); title( Binært bilde (B) med kanter rundt hver sort rute. ); fprintf( Minimum kantlengde er %5.1f\n, min(kantlengde)); fprintf( Maksimum kantlengde er %5.1f\n, max(kantlengde)); fprintf( Gjennomsnitt kantlengde er %5.1f\n, mean(kantlengde)); fprintf( Standardavvik er %5.1f\n, std(kantlengde)); For bilde jeg brukte ser de grønne firkantene ut til å treffe rutene svært bra. Jeg så også at kantlengdene kunne variere med 5-6 prosent, de lengste kantene er øverst i bildet. Dette kan forklares med at denne delen av motivet har vært nærmere linsa enn den nederste delen av motivet. 10
1.5 Installere Python og OpenCV Det kan være lurt å installere Python (ver 2.7) og OpenCV siden det vil brukes senere. CV står for Computer Vision og pakken heter cv2 også når det er versjon 3. For enkel bildebehandling er Python Image Library (PIL) (eller pillow) enklere. Men ut fra det jeg har forstått er OpenCV mer i tråd med våre ønsker. Fra nettet sakser jeg: Pillow is an image manipulation/processing library while OpenCV is a computer vision library. While there is certainly a lot of overlap (i.e. OpenCV contains a fair bit of image processing functionality) they are vastly different in scope. To make an extreme simplification, you use Pillow if you want to cut and resize images, and maybe do a bit of filtering, and you use OpenCV when you are building a robot that is trying to seethings. Personally, as someone who use OpenCV more or less daily, but have never touched Pillow, I would probably use OpenCV for things Pillow does, simply because I am familiar with it. However, unless you have specific reasons so to use OpenCV, I would stay away since it can be extremely anoying and over complicated at times. Det er flere installasjonsveiledninger ute på netttet, noen eldre og noen nyere, noen som kanskje virker bra og noen som kanskje ikke virker fullt så bra. Det ser ut som om det fortsatt er enklest å installere OpenCV på Python 2.7, selv om det også skal gå med Python 3.x versjoner. Mye av det som kommer opp når jeg søker på OpenCV og Python 3.x er imidlertid spørsmål (og svar) på ulike problemer en har fått ved (forsøk på) å innstallere dette. Derfor velger jeg det som ser mest velprøvd ut, og som forhåpentligvis gir minst problem. Først prøvde jeg en installasjonsoppskrift for Windows fra OpenCV nettside. Men dessverre passet ikke versjonene av numpy og opencv sammen. Python, numpy og matplotlib lenkene der peker til versjoner 2.7.5, 1.7.1 og 1.3.0 henholdsvis, mens OpenCV versjonen må velges selv fra en lang liste av muligheter og der versjon 3.2 (nyeste nå) ikke virker sammen med versjonen av numpy. Til slutt kom jeg fram til at Anaconda er enkelt å installere og at det tilsynelatende virker bra. En ulempe er at det trenger stor plass (2.15 GB) men til gjengeld er mange pakker og biblioteker inkludert og jeg regner med at disse passer sammen med hverandre, selv om de skulle være ulike versjoner. De sier på nettsida si: Installing Python from scratch is no joy. Many scientific packages require a specific version of Python or R computer language along with many dependencies. It s hard to keep packages from interacting with each other, and harder to keep them all updated. Anaconda makes getting and maintaining all these packages quick and easy. Dette kan jeg nå si meg helt enig i. Fra Anaconda nettside lastet jeg ned 64 bit installer av Python 2.7 versjonen, Anaconda2-4.2.0-Windows-x86 64.exe. Når jeg kjørte dette installasjonspro- 11
grammet ble alt installert (på c:/programfiler/anaconda2 katalogen). Deretter lastet jeg siste OpenCV utgivelse fra sourceforge som jeg pakka opp direkte til katalogen c:/programfiler/anaconda2/lib/site-packages. Til slutt kopierte jeg fila c:/programfiler/anaconda2/lib/site-packages/- opencv/build/python/2.7/x64/cv2.pyd til c:/programfiler/anaconda2/- Lib/site-packages/cv2.pyd. Til slutt ble installasjon sjekket med å starte python, dobbelklikke på fila c:/programfiler/anaconda2/python.exe i filbehandler. Fra python viste jeg versjonsnummer med: >>> import sys >>> print(sys.version) 2.7.12 Anaconda 4.2.0 (64-bit)... >>> import numpy as np >>> print(np. version ) 1.11.1 >>> import matplotlib as mpl >>> print(mpl. version ) 1.5.3 >>> import cv2 >>> print(cv2. version ) 3.2.0 Nyttige lenker: Tutorial Python 2.7 Numpy for Matlab users OpenCV tutorial, egentlig mange dokument samlet i en trestruktur. OpenCV-Python tutorials. OpenCV cheatsheet, ark med kompakt oversikt over OpenCV. 12