Göm menyn

5A. Introduktion till OpenCV, Git och listbyggare

5.1 Inledning

Syftet med denna laborationen är att introducera bildhanteringsbiblioteket OpenCV. Med OpenCV kan man visa och manipulera bilder.

I denna del kommer ett stort fokus ligga på versionshantering med verktyget Git. Med Git kan du hantera och hålla koll på kod, som till exempel att återställa till en gammal version av koden om du skulle ångra dig. Dessutom övas konceptet listbyggare, och hur det kan knytas samman med praktisk tillämpning som bildhantering.

Studiematerial

Innan denna labb påbörjas ska följande studiematerial ha lästs:

5.2 Datastrukturen i OpenCV

För att komma åt individuella pixlar i bilden används []-operatorn. Precis som för vanliga Python-listor kan man alltså på detta sätt hämta ett värde eller ändra ett värde. Indexet är en koordinat för en individuell pixel och värdet är färgen som denna pixel har, vilket i sin tur är en slags lista.

Vill man komma åt färgvärdet i en pixel med koordinaterna (x, y) används [y, x] vilket ger en lista på formatet [b, g, r], alltså inte (r, g, b) som det brukar vara i en del andra sammanhang. Man måste hålla tungan rätt i munnen och se till att alla parametrar kommer i rätt ordning! Här är ett exempel på hur man laddar in en bild, tittar på enskilda pixlars färgvärden och ändrar dem:

>>> import cv2 # Använd OpenCV >>> img = cv2.imread('plane.jpg') # Läs in en bild från en fil >>> img[0,0] # Visa värdet av pixeln i ena hörnet array([139, 80, 48], dtype=uint8) >>> print(img[0,0]) # Skriv ut värdet av pixeln lite mer läsbart [139 80 48] >>> img[0,0][2] = 0 # Förändra värdet av r-komponenten

Övning 501 Skriv en funktion som givet en bild tar bort all blåhet. Detta kan åstadkommas genom att gå igenom varje pixel och få tag i dess färgvärde. Sätt sedan det färgvärdet som representerar blått till 0. Hur tar du reda på hur stor en bild är? Du har väl läst studiematerialet om bildbehandling?

Uppgift 5A1 - Konvertering av datastrukturen i OpenCV

Målet med uppgiften är att du ska kunna sätta dig in i en större mängd redan färdig programkod och utöka den med nya möjligheter, utan att nödvändigtvis ha förstått alla detaljer.

OpenCV sparar sina bilder i en egen datastruktur som är optimerad för OpenCV:s interna operationer. I exemplet ovan syns lite av hur den datastrukturen ser ut. I den här labben vill vi dock manipulera vanliga Python-listor för att öva på det. För att kunna göra det måste vi skriva en funktion för att konvertera OpenCV:s datastruktur till en vanlig Python-lista.

Din uppgift är att skriva en funktion cvimg_to_list som tar en OpenCV-bild och returnerar en lista med BGR-tupler. Funktionen ska fungera så här:

>>> img = cv2.imread('image.jpg') >>> cvimg_to_list(img) [(232, 24, 42), (42, 32, 0), (3, 21, 2), ...]

En funktion för konvertering åt andra hållet finns redan i den medskickade koden i filen cvlib.py i Git-repot. Den kan användas för att verifiera att konverteringen fungerar som den ska. Observera att funktionen antar att tuplerna är skrivna i ordningen (b, g, r), d v s ni ska inte ändra den inbördes färgordningen i bilden. Den bild som visas upp av följande körexempel ska vara identisk med originalbilden:

>>> img = cv2.imread('image.jpg') >>> list_img = cvimg_to_list(img) >>> converted_img = rgblist_to_cvimg(list_img, img.shape[0], img.shape[1]) # Bildens dimensioner >>> cv2.imshow("converted", converted_img) >>> cv2.waitKey(0)

5.3 Listbyggare

Innan vi fortsätter med bildbehandlingen ska vi titta på listbyggare. Listbyggare (eng. list comprehensions) är ett snabbt sätt att skapa en lista utifrån någon viss egenskap, till exempel genom att behandla alla element i en gammal lista. Detta behandlas i studiematerialet och du kan läsa mer om det i dokumentationen för Python 3.

Övning 502 Skapa en funktion double_elements som tar en lista med tal och dubblerar alla element i listan. Använd listbyggare.

>>> double_elements([1, 2, 3, 4]) [2, 4, 6, 8]

Övning 503 Skapa en funktion all_pairs_ordered som tar ett tal n och som med hjälp av listbyggare skapar alla par av element från 0 till n.

>>> all_pairs_ordered(2) [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]

Övning 504 Skapa en funktion distribute som tar ett element och lägger in det i samtliga dellistor i en lista. Använd listbyggare. Koden inuti funktionen ska inte behöva bli mer än en rad.

>>> distribute('k', [['o'], [0, 1, 2], ['o','o']]) [['o', 'k'], [0, 1, 2, 'k'], ['o', 'o', 'k']]

Övning 505 I Pascals triangel är varje tal summan av de två tal som står ovanför, förutom i kanterna där vi har ettor. Utöver att vara ett snyggt arrangemang av siffror är Pascals triangel ett visuellt uttryck för binomialkoefficienter, även kända som n över k som vi snubblade över i uppgift 3B.

1 1 1 1 2 1 1 3 3 1 1 4 6 4 1

Skriv en funktion pascal som givet ett tal n genererar de n första raderna i Pascals triangel till och med en viss rad. Svaret ska representeras som listor i listor, och du ska använda listbyggare för att lösa uppgiften.

>>> pascal(4) [[1], [1, 1], [1, 2, 1], [1, 3, 3, 1]]

Du har tidigare skrivit en användbar funktion som kan användas för att generera talen i triangeln.

Uppgift 5A2 - Skärpa bild

Målet med uppgiften är att du ska kunna lösa matematiska uppgifter med hjälp av programmering.

Den här uppgiften innehåller en hel del matematik, eftersom det är en förutsättning för att kunna arbeta med bildbehandling. Det är dock inte viktigt att förstå alla detaljer i formlerna för att kunna göra uppgiften. Det räcker att kunna översätta matematiken till Python-kod.

Listbyggare kan användas till flera saker inom bildbehandling med OpenCV. Till exempel kan man nyttja listbyggare är när man vill öka skärpan på en bild. Då kan man använda sig av en så kallad oskarp mask (eng. unsharp mask). Det låter kanske konstigt att det heter unsharp masking när vi ska skärpa till bilden, men det ska heta så. Om du är nyfiken kan du läsa mer om unsharp masking.

I OpenCV kan man filtrera med en specifik 2D-array (alltså en lista av listor som motsvarare innehållet i en tvådimensionell matris) för att få den önskade skärpökningen. Varje element i denna array använder sig av negativ gaussisk blur. Formeln ges enligt figuren nedan:

Negativ gaussisk blur

Negativ gaussisk blur

I vårt fall är s en konstant, medan x och y beror på dess position i relation till origo. Hur formeln appliceras på varje element visas enligt figuren nedan:

Negativ gaussisk blur som faltningskärna

Negativ gaussisk blur som faltningskärna

Denna array ska vara lika många rader som kolumner, d v s det är en NxN-array. Ett sista steg för att fullfölja denna oskarpa mask är att ersätta värdet position (0, 0) med ett konstant värde.

Din uppgift är att med hjälp av listbyggare konstruera en funktion unsharp_mask som tar en parameter N som indata. Funktionen ska returnera en 2D-lista som vi kan använda som indata till arraykonvertering. (Se körexemplet nedan för en illustration av vad en 2D-lista är.) N är storleken i båda leden. För att veta hur de olika positionerna för x och y beror av N, se nedanstående figur. Observera att man utökar med negativt index först, följt av ett positivt index för ökande N.

Negativ gaussisk blur-positioner

Negativ gaussisk blur-positioner

Varje element i 2D-listan ska ha negativ gaussisk blur för respektive koordinat, men för position (0, 0) ska värdet vara 1.5. Använd s = 4.5 i alla beräkningar. Följande exempel illustrerar hur det ska fungera:

>>> unsharp_mask(3)
[[-0.007480807217716918, -0.007667817778372781, -0.007480807217716918],
[-0.007667817778372781, 1.5, -0.007667817778372781], 
[-0.007480807217716918, -0.007667817778372781, -0.007480807217716918]]
>>> unsharp_mask(1)
[[1.5]]

När funktionen unsharp_mask är definierad kan vi använda den för att öka skärpan i en bild. I följande exempel använder vi N = 11. Observera att vi också måste omvandla utdata från unsharp_mask till en numpy.array för att OpenCV ska kunna arbeta med informationen. Bilden som visas i slutet av exemplet ska, förhoppningsvis, vara lite skarpare än originalbilden. Och det "enda" som ni behövde göra var att definiera unsharp_mask.

>>> img = cv2.imread('image.jpg') >>> kernel = numpy.array(unsharp_mask(11)) >>> filtered_img = cv2.filter2D(img, -1, kernel) >>> cv2.imshow("filtered", filtered_img) >>> cv2.waitKey(0)


Sidansvarig: Peter Dalenius
Senast uppdaterad: 2023-10-09