Pixelosztályozás kísérlet

Pixel elemzés RGB értékek alapján

A következő képelemző kísérletet beadandó feladatként készítettem az egyetemen. Bemutatására elsősorban azért kerítek sort, mivel a feladat megoldásához Python kódokat használtam fel. Célom tehát, hogy bemutassam a gondolatmenetet és a kódok felhasználását. Annyit még hozzáfűznék ehhez, hogy a feladat csupán egy kísérlet, és elkészítésének elsődleges célja nem egy azonnal alkalmazható módszer létrehozása, hanem a problémamegoldás szemléltetése volt.

A kísérlet kiinduló kérdése, hogy miképpen lehet elkészíteni egy olyan program alapját, amely a pixelek RGB értékei szerint azonosítja a képen látható tárgyakat. A feladatban gyomnövények azonosításáról volt szó, ami tulajdonképpen egy valós probléma a precíziós mezőgazdaságban. A kísérlettel egyidőben a hiperspektrális képelemzés MI megoldásairól írtam dolgozatot, és abból a feltételezésből indultam ki, hogy egy lényegesen kisebb, három sávos RGB vizsgálat is alkalmas lehet bizonyos típusú problémák (pl. növények felismerése) megoldására. Persze ebben az esetben, már nem a spektrumok száma, hanem a különböző magasságokban történő felvételezések, változó időjárási körülmények, eltérő természetes fényviszonyok határoznák meg az adatmennyiséget.

A következőkben a probléma megoldását szemléltetem, az elemzett képek illetve az elemzést segítő kódok ismertetésével. A kísérletet nem mutatom be teljes részletességgel, ahogy az a dolgozatban szerepelt, ezért a hangsúly inkább a megoldás logikáján illetve a Python programnyelv alkalmazásán lesz.

1.

Mintapixel kiválasztása

Az alapul vett ortogonális felvétel
m1 és m2 kiválasztása

A mintaképekeket (m1 és m2) egy drónnal, 10 méteres magasságból készült ortogonális képről választottam ki az alapján, hogy a képeken jól láthatóan gyomnövények vannak. Ezután úgy állapítottam meg a mintapixelt, hogy vettem a két mintakép RGB átlagát.

A def get_rgb(): kóddal a kiválasztott képen pixelről, pixelre (w*h) nyertem ki az RGB értékeket, melyeket Excelben, egy színmegjelenítő VBA szubrutin segítségével tettem láthatóvá. Ezzel elsősorban az volt a célom, hogy a színek közti különbség és a képen látható különböző objektumok (ez esetben növény – nem növény) közötti esetleges összefüggések jobban észrevehetők legyenek illetve az, hogy részletesebb dokumentáció készüljön a kísérlethez.

1. Python függvény: get_rgb():

def get_rgb(fajl):

im = Image.open(fajl)
w, h = im.size
#r,g,b
ch = ["R=red","G=green","B=blue"]
channel_range = [0,1,2]

for rgb in channel_range:
print channel_range[rgb],ch[rgb],'csatorna ertekei:'

for i in range(w):
for j in range(h):
#pixel felvetel
pixel = im.getpixel((i, j))
print pixel[rgb]

return

print get_rgb(sample)

1. Python függvény: get_rgb():

def get_rgb(fajl):

im = Image.open(fajl)
w, h = im.size
#r,g,b
ch = ["R=red","G=green","B=blue"]
channel_range = [0,1,2]

for rgb in channel_range:
print channel_range[rgb],ch[rgb],'csatorna ertekei:'

for i in range(w):
for j in range(h):
#pixel felvetel
pixel = im.getpixel((i, j))
print pixel[rgb]

return

print get_rgb(sample)

Miután az m1 és m2 mintakép pixeleit és a színűket is – az előző bekezdésben említett módon – dokumentáltam, kiszámítottam az RGB átlagukat. Ezzel tehát kaptam egy ideális RGB értéket vagyis egy mintapixelt.

Az átlagpixel kiszámításához nem saját függvényt használtam, hanem az alább látható kódot.

2. Python függvény: average_colour():

#fajl neve/minta-kep(m1/m2.png);
img = 'm1.png'
sample = os.path.join('c:\\temp\\',img)

#fajl megnyitasa
im = Image.open(sample)

def average_colour(image):

colour_tuple = [None, None, None]

for channel in range(3):
pixels = image.getdata(band=channel)
values = []

for pixel in pixels:
values.append(pixel)

colour_tuple[channel] = sum(values) / len(values)

return tuple(colour_tuple)

print average_colour(im)

2. Python függvény: average_colour():

#fajl neve/minta-kep(m1/m2.png);
img = 'm1.png'
sample = os.path.join('c:\\temp\\',img)

#fajl megnyitasa
im = Image.open(sample)

def average_colour(image):

colour_tuple = [None, None, None]

for channel in range(3):
pixels = image.getdata(band=channel)
values = []

for pixel in pixels:
values.append(pixel)

colour_tuple[channel] = sum(values) / len(values)

return tuple(colour_tuple)

print average_colour(im)

2.

Osztályozás

Az osztályozáshoz tehát egy ideális RGB-re volt szükség, amivel az alapkép más részletei is kiértékelhetőek, abból a szempontból, hogy milyen százalékos arányban reprezentálják ezt a mintát. (lásd a-b-c-jelű képek) Az ideális, mintapixeltől való eltéréséket egy egyszerű távolságmérő függvénnyel, az euklideszi távolsággal vizsgáltam. A következő kód az euklideszi távolságmérés két lehetséges implementációját mutatja be.

3. Python függvény: euclidean():

#euklideszi_0
def euclidean(p1,p2,p3,q1,q2,q3):

dist=math.sqrt((p1 - q1)**2 + (p2 - q2)**2 + (p3 - q3)**2)

return dist

#euklideszi_1
def euclidean(p,q):

res, channel = 0, 3 #RGB channel = 3

for i in range(channel):
res = res + (p[i]-q[i])**2

return math.sqrt (res)

3. Python függvény: euclidean():

#euklideszi_0
def euclidean(p1,p2,p3,q1,q2,q3):

dist=math.sqrt((p1 - q1)**2 + (p2 - q2)**2 + (p3 - q3)**2)

return dist

#euklideszi_1
def euclidean(p,q):

res, channel = 0, 3 #RGB channel = 3

for i in range(channel):
res = res + (p[i]-q[i])**2

return math.sqrt (res)

Az alábbi kód, a pixelek átlagtól való euklideszi eltérését vagy távolságát vizsgálja, három intervallumban. Ezek a [0;25] valamint [26;50] és végül [50; [ . A kísérlet során dokumentáltam a nagyobb euklidészi távolságokat is, de csak az első intervallum alapján vizsgáltam, hiszen ezek fejezték ki a legkisebb távolságokat, azaz ezek mutatták a legnagyobb hasonlóságot.

4. Python függvény: dist():

def dist(b,item):

for i in range(item):
print format(b[i],'.2f')

return

#szamitas

for i in range(w):
for j in range(h):
pixel = im.getpixel((i, j))
if euclidean(average_colour(im),pixel) <= 25:
b1[item_b1] = euclidean(average_colour(im),pixel)
item_b1 = item_b1 + 1
elif euclidean(average_colour(im),pixel) > 50:
b3[item_b3] = euclidean(average_colour(im),pixel)
item_b3 = item_b3 + 1
else:
b2[item_b2] = euclidean(average_colour(im),pixel)
item_b2 = item_b2 + 1

print '\nK1: <=25\n'
dist(b1,item_b1)
print '\nK2: 26-50\n'
dist(b2,item_b2)
print '\nK3: 50<\n'
dist(b3,item_b3)

4. Python függvény: dist():

def dist(b,item):

for i in range(item):
print format(b[i],'.2f')

return

#szamitas

for i in range(w):
for j in range(h):
pixel = im.getpixel((i, j))
if euclidean(average_colour(im),pixel) <= 25:
b1[item_b1] = euclidean(average_colour(im),pixel)
item_b1 = item_b1 + 1
elif euclidean(average_colour(im),pixel) > 50:
b3[item_b3] = euclidean(average_colour(im),pixel)
item_b3 = item_b3 + 1
else:
b2[item_b2] = euclidean(average_colour(im),pixel)
item_b2 = item_b2 + 1

print '\nK1: <=25\n'
dist(b1,item_b1)
print '\nK2: 26-50\n'
dist(b2,item_b2)
print '\nK3: 50<\n'
dist(b3,item_b3)

Ezek után a kiválasztott képen, három mintaképet vetettem vizsgálat alá. Az a-jelű képet, amely szemmel láthatóan nem reprezentálja a gyomnövényt, a b-jelűt, amely részlegesen reprezentálja illetve a c-jelűt, amely a legalkalmasabb a reprezentációra.

abc_img
Kiválasztott kép
py_a

a-jelű kép

py_b

b-jelű kép

py_c

c-jelű kép

Majd ezeken a képeken vizsgáltam az ideális pixel (átlagtól való <=25 euklideszi távolságú) %-os előfordulási arányát. A próba eredményeképp, a c-jelű képen 40%-os reprezentációt kaptam, amiből végül arra a következtettem, hogy egy valóban működő módszer kidolgozásához még további kísérleteket kell elvégezni.

Az osztályozás eredményét reprezentáló ábra szintén Python függvénnyel készült.

5. Python függvény: ideal_p():

im = Image.open('c:\\temp\\c3.png') #tesztkep valasztasa
pixelMap = im.load()
Ir, Ig, Ib = 165,158,80

def ideal_p(sampleR, sampleG, sampleB):

r, g, b = 0,1,2
ideal, non_ideal = 0,0
pixelsNew = im.load()

for i in range((im.size[0]):
for j in range((im.size[1]):
if euclidean(sampleR, sampleG, sampleB,pixelMap[i,j] [r],pixelMap[i,j] [g],pixelMap[i,j][b]) <= 25:
pixelMap[i,j] = (Ir,Ig,Ib,255)
ideal = ideal + 1
else:
pixelMap[i,j] = (255,204,0,255)
non_ideal = non_ideal + 1

return ideal, non_ideal, im.show()

print ideal_p(Ir, Ig, Ib)

5. Python függvény: ideal_p():

im = Image.open('c:\\temp\\c3.png') #tesztkep valasztasa
pixelMap = im.load()
Ir, Ig, Ib = 165,158,80

def ideal_p(sampleR, sampleG, sampleB):

r, g, b = 0,1,2
ideal, non_ideal = 0,0
pixelsNew = im.load()

for i in range((im.size[0]):
for j in range((im.size[1]):
if euclidean(sampleR, sampleG, sampleB,pixelMap[i,j [r],pixelMap[i,j] [g],pixelMap[i,j][b]) <= 25:
pixelMap[i,j] = (Ir,Ig,Ib,255)
ideal = ideal + 1
else:
pixelMap[i,j] = (255,204,0,255)
non_ideal = non_ideal + 1

return ideal, non_ideal, im.show()

print ideal_p(Ir, Ig, Ib)

Az ábrán jól látható, hogy az ideális pixelek a meghatározott RGB értékük alapján kapják színűket, míg a nem ideális pixelek jelölésére egy sárgás színt választottam.

Az osztályozás eredményei

Végül egy olyan függvény következik, melyet nem használtam fel a kísérletben, de hasznos lehet bármilyen pixelelemző feladathoz. Ez a függvény egy tetszőleges kép pixeleinek koordinátáit listázza a képernyőre.

6. Python függvény: coord():

from PIL import Image
import os.path

#fajl neve/minta-kep(m1/m2.png)
img = 'm1.png'
sample = os.path.join('c:\\temp\\',img)

def coord(fajl):
#fajl megnyitasa
im = Image.open(fajl)
w, h = im.size

for i in range(w):
for j in range(h):
print i,j

return

print coord(sample)

6. Python függvény: coord():

from PIL import Image
import os.path

#fajl neve/minta-kep(m1/m2.png)
img = 'm1.png'
sample = os.path.join('c:\\temp\\',img)

def coord(fajl):
#fajl megnyitasa
im = Image.open(fajl)
w, h = im.size

for i in range(w):
for j in range(h):
print i,j

return

print coord(sample)