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
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.
a-jelű kép
b-jelű kép
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.

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)