Zapisz jako PDF
Transkrypt
Zapisz jako PDF
Pracownia EEG / Wstęp do analizy obrazu Spis treści 1 Wstęp 2 Wczytywanie i zapisywanie obrazów 3 Wyświetlanie obrazów 4 Podstawowe manipulacje 5 Częstość w obrazie i dwuwymiarowa transformata Fouriera 5.1 sin 2D 5.2 Transformata Fouriera 2D 6 Filtrowanie liniowe. Korelacja i splot 6.1 Korelacja 6.2 Splot 6.3 Ćwiczenie 7 Filtry nieliniowe 7.1 Odszumianie 8 Operacje morfologiczne 8.1 Element strukturalny 8.2 Erozja 8.3 Dylacja 8.4 Otwarcie 8.5 Domknięcie 8.6 Progowanie 8.7 Szukanie krawędzi 9 Segmentacja 9.1 Segmentacja przez analizę skupień (k-means) 9.2 Segmentacja przez rozrost Wstęp W tym dziale zajmiemy się analizą obrazu za pomocą narzędzi dostępnych w modułach numpy i scipy. Tak więc na potrzeby tego rozdziału uznamy, że obrazy to dwuwymiarowe tablice numpy. Podstawowe operacje macierzowe będziemy czerpać z modułu numpy zaś bardziej specyficzne operacje z modułu scipy.ndimage. W dalszym toku zajęć zapoznamy się też częściowo z bardziej dedykowanymi narzędziami zawartymi w module OpenCV. W poniższych materiałach wykorzystano tutorial: http://scipy-lectures.github.io/advanced/image_processing/#feature-extraction autorstwa: Emmanuelle Gouillart, Gaël Varoquaux. Wczytywanie i zapisywanie obrazów Do wstępnych ćwiczeń posłużymy się standardowym w świecie analizy obrazów zdjęciem znanym jako Lena. Przygotujemy plik w formacie png: from scipy import misc l = misc.lena() misc.imsave('lena.png', l) # tu używany jest niejawnie moduł do podstawowej pracy z plikami graficznymi (PIL - python image library) import matplotlib.pyplot as plt plt.imshow(l) plt.show() Aby wczytać obrazek z pliku graficznego jako tablicę: from scipy import misc lena = misc.imread('lena.png') type(lena) lena.shape, lena.dtype Zwróćmy uwagę na to, że typem dla obrazków jest uint8 (czyli liczby całkowite z zakresu 0-255). Wyświetlanie obrazów W tym celu można zastosować funkcję imshow z matplotlib. Zwróćmy uwagę jak ustawia się mapę kolorów. l = misc.lena() import matplotlib.pyplot as plt plt.imshow(l, cmap=plt.cm.gray) Sterowanie kontrastem za pomocą ustawiania minimalnej i maksymalnej wartości dla skali jasności. plt.imshow(l, cmap=plt.cm.gray, vmin=30, vmax=200) # Remove axes and ticks plt.axis('off') Aby oglądać obraz z dokładnością do piksela należy zmienić domyślną interpolację, porównajmy dwie wersje: plt.subplot(1,2,1) plt.imshow(l[200:220, 200:220], cmap=plt.cm.gray) plt.subplot(1,2,2) plt.imshow(l[200:220, 200:220], cmap=plt.cm.gray, interpolation='nearest') plt.show() Podstawowe manipulacje Konwencja układu współrzędnych stosowana w grafice: Ponieważ traktujemy obraz jako tablicę numpy możemy stosować standardowe operacje: lena = scipy.misc.lena() # adresowanie pojedynczego piksela lena[, 40] # wycinki lena[10:13, 20:23] lena[100:120] = 255 lx, ly = lena.shape X, Y = np.ogrid[:lx, :ly] mask = (X - lx / 2) ** 2 + (Y - ly / 2) ** 2 > lx * ly / 4 # maskowanie lena[mask] = # średnia wartość jasności w obrazie, maksimum i minimum lena.mean() lena.max() lena.min() #przycinanie crop_lena = lena[lx / 4: - lx / 4, ly / 4: - ly / 4] # up <-> down flip flip_ud_lena = np.flipud(lena) # obroty rotate_lena = ndimage.rotate(lena, 45) rotate_lena_noreshape = ndimage.rotate(lena, 45, reshape=False) Częstość w obrazie i dwuwymiarowa transformata Fouriera sin 2D Aby uświadomić sobie związki między jednowymiarowym sygnałem a obrazem — sygnałem 2D — zrobimy następujące ćwiczenie (poniżej prezentowane fragmenty kodu dopisujemy do jednego pliku): Zrobimy sinusa jednowymiarowego o 10 okresach na 100 punktów: # -*- coding: utf-8 -*import matplotlib.pyplot as py import numpy as np X = 100 #pikseli Fs = 1.0/X #czestosc probkowania co jeden piksel dx = 1 x = np.arange(,X,dx) f = 10.0/X # czestosc sinusa -10 okresów na X syg = np.sin(2*np.pi*f*x + np.pi/3); py.figure(1) py.plot(x,syg) py.show() Teraz przekształcimy go w obraz: py.figure(2) # teraz zrobimy z niego obraz 2D Y = X/2 SYG = np.zeros((Y,X)); for y in range(Y): SYG[y,:]=syg py.subplot(2,2,1) py.pcolor(SYG, cmap=py.cm.gray,vmin=-2,vmax = 2) py.show() Te zmieniające intensywność prążki to właśnie obrazek przedstawiający sin w kierunku X. Transformata Fouriera 2D fft2 z sin2D py.subplot(221) py.pcolor(SYG, cmap=py.cm.gray,vmin=-2,vmax = 2) py.subplot(222) SK_X = np.arange(-X/2,X/2,1) SK_Y = np.arange(-Y/2,Y/2,1) S = np.fft.fft2(SYG) modS = np.abs(np.fft.fftshift(S)) py.pcolor(SK_X,SK_Y, modS, cmap=py.cm.gray) sprawdźmy jak widoczne jest dodanie stałej wartości do obrazu: py.subplot(223) py.pcolor(SYG+1, cmap=py.cm.gray, vmin=-2,vmax = 2) py.subplot(224) S = np.fft.fft2(SYG+1) py.pcolor(SK_X,SK_Y,np.abs(np.fft.fftshift(S)), cmap=py.cm.gray) py.show() Filtrowanie liniowe. Korelacja i splot Sygnały jednowymiarowe, np. jeden kanał sygnału EEG, filtrowaliśmy poprzez splatanie sygnału z funkcją odpowiedzi impulsowej filtru. Podobnie można myśleć o filtrowanie obrazów, traktowanych jako sygnały dwuwymiarowe. Trzeba tylko określić jak liczyć splot w dwóch wymiarach. Zobaczmy jak to się robi praktycznie. Korelacja Przyjmijmy, że: obrazem jest duża macierz; jądrem splotu/korelacji jest mniejsza macierz. Załóżmy, że mamy obraz A: [[17, 24, 1, 8, 15], [23, 5, 7, 14, 16], [ 4, 6, 13, 20, 22], [10, 12, 19, 21, 3], [11, 18, 25, 2, 9]] i jądro korelacji h: [[8, 1, 6], [3, 5, 7], [4, 9, 2]]) wówczas operacja korelacji dla elementu A[1,3] jest następująca: 1. kładziemy macierz h elementem środkowym (5) na elemencie A[1,3]; 2. wymnażamy przez siebie wszystkie pokrywające się elementy i sumujemy iloczyny. Wtedy dla tego elementu otrzymujemy: D[1,3] =1*8 + 8*1 +15*6 + 7*3 + 14*5 + 16*7+ 13*4 + 20*9 +22*2 = 585 Taką korelację mamy zaimplementowaną w ndimage.correlate (w dokumentacji można doczytać różne opcje dotyczące traktowania brzegów, kiedy macierz h "wystaje" poza macierz A) : A = np.array([[17, 24, 1, 8, 15],[23, 5, 7, 14, 16 ],[4,6,13,20,22],[10,12,19,21,3],[11,18,25,2,9] ]) h = np.array([[8,1,6],[3,5,7],[4,9,2]]) D = ndimage.correlate(A,h) Splot Bardzo podobna jest operacja dwuwymiarowego splotu. Jedyna różnica jest taka, że przed operacją mnożenia i dodawania jądro jest obracane o 180°, implementacja w ndimage.convolve. Ćwiczenie Na obrazie lena proszę sprawdzić działanie kilku podstawowych, często stosowanych filtrów: filtr uśredniający: h = np.array([[1./9, 1./9, 1./9], [1./9, 1./9, 1./9], [1./9, 1./9, 1./9]]) filtr wyostrzający: h = np.array([[ , -1, ], [ -1, 5, -1], [ , -1, ]]) filtr uwypuklający: h = np.array([[-1, [ -1, [ -1, [ -1, [ , -1, -1, -1, , 1, -1, -1, , 1, 1, -1, ], , 1], 1, 1], 1, 1], 1, 1]]) Filtry nieliniowe Koncepcję filtrowania można rozszerzyć na inne operacje wykonywane na pikselach otaczających aktualnie rozważany. Może to być obliczenie dowolnej funkcji z wartości pikseli w sąsiedztwie, np.: mediany, maximum, minimum, konkretnego kwantyla, odchylenia standardowego, zakresu zmienności itd. Dla przykładu rozważmy odszumianie obrazu. Odszumianie Szum w obrazie polegający na tym, że wartość każdego piksela jest zaburzona przez wartość losową ma głównie wysokie częstości przestrzenne. W pierwszym podejściu można próbować usunąć je przez filtr uśredniający. Będzie on niestety rozmywał krawędzie. Zwykle lepsze rezultaty daje filtr medianowy (do elementu centralnego przypisywana jest wartość mediany z sąsiedztwa o zadanym promieniu). Proszę porównać: from scipy import misc l = misc.lena() l = l[230:310, 210:350] noisy = l + 0.4 * l.std() * np.random.random(l.shape) # dodajmy jeszcze "zagniecenie" zdjęcia noisy[range(80),range(80)]=255 #filtr gaussowski gauss_denoised = ndimage.gaussian_filter(noisy, 2) #filtr medianowy med_denoised = ndimage.median_filter(noisy, 3) Operacje morfologiczne Szczególnym przypadkiem filtrów nieliniowych sa operacje morfologiczne. Polegają one na tym, że obraz jest "próbkowany" pewnym prostym kształtem tzw. elementem strukturalnym. Obraz wyjściowy jest modyfikowany w zależności od tego jak taki element "pasuje" do poszczególnych miejsc obrazu wejściowego. Dalej zajmiemy się operacjami morfologicznymi dla obrazów binarnych. Element strukturalny Element strukturalny to binarna maska przykładana do oryginalnego obrazka. Erozja Erozja powoduje zamianę aktualnego piksela na wartość minimalną zawartą w części wspólnej elementu strukturalnego o środku w aktualnym pikselu i oryginalnego obrazka. # -*- coding: utf-8 -*import numpy as np import matplotlib.pyplot as py from scipy import ndimage from scipy import misc def element(): el = np.array([[, 1, ],[1,1,1],[,1,]], dtype=bool) #el = np.array([[0, 0, 0],[1,1,1],[0,0,0]], dtype=bool) return el def dodaj_kwadrat(ob, x,y, dx,dy): '''dodaje kwadrat wypełniony 1 do obrazu binarnego ob. x, y - lewy górny róg dx,dy - szerokosć i wysokość ''' ob[x:x+dx,y:y+dy] = 1 return ob def dodaj_kolo(ob, x,y, r): '''dodaje koło wypełnione 1 do obrazu binarnego ob. x, y - środek r - promień ''' r2=r**2 for xi in range(ob.shape[]): for yi in range(ob.shape[1]): if (xi-x)**2+(yi-y)**2<=r2: ob[xi,yi]=1 return ob NX=10 NY=10 py.figure('erozja 1') ob = np.zeros((NX,NY)) py.subplot(1,3,1) el = element() py.imshow(el,interpolation = 'nearest',cmap=py.cm.gray) py.xlim([-0.5,NX]) py.ylim([-0.5,NY]) py.axis('off') py.title('element strukturalny') py.subplot(1,3,2) ob = dodaj_kwadrat(ob,2,2,6,6) py.imshow(ob, interpolation = 'nearest',cmap=py.cm.gray) py.xlim([-0.5,NX]) py.ylim([-0.5,NY]) py.axis('off') py.title(u'obraz wejściowy') py.subplot(1,3,3) ob_wyj = ndimage.binary_erosion(ob, structure = el).astype(ob.dtype) py.imshow(ob_wyj, interpolation = 'nearest',cmap=py.cm.gray) py.xlim([-0.5,NX]) py.ylim([-0.5,NY]) py.axis('off') py.title(u'obraz wyjściowy') py.show() py.figure('erozja 2') ob = np.zeros((NX,NY)) py.subplot(1,3,1) el = element() py.imshow(el,interpolation = 'nearest',cmap=py.cm.gray) py.xlim([-0.5,NX]) py.ylim([-0.5,NY]) py.axis('off') py.title('element strukturalny') py.subplot(1,3,2) #ob = dodaj_kwadrat(ob,2,2,6,6) ob = dodaj_kolo(ob,5,5,3) py.imshow(ob, interpolation = 'nearest',cmap=py.cm.gray) py.xlim([-0.5,NX]) py.ylim([-0.5,NY]) py.axis('off') py.title(u'obraz wejściowy') py.subplot(1,3,3) ob_wyj = ndimage.binary_erosion(ob, structure = el).astype(ob.dtype) py.imshow(ob_wyj, interpolation = 'nearest',cmap=py.cm.gray) py.xlim([-0.5,NX]) py.ylim([-0.5,NY]) py.axis('off') py.title(u'obraz wyjściowy') py.show() Dylacja Dylacja powoduje zamianę aktualnego piksela na wartość maksymalną zawartą w części wspólnej elementu strukturalnego o środku w aktualnym pikselu i oryginalnego obrazka. Implementacja: ndimage.binary_dilation. Proszę wypróbować tą operację modyfikując kod przykładowy od Erozji. Otwarcie Otwarcie to złożenie operacji erozji i dylacji. Operacja otwarcia zachowuje rozmiary obiektów obrazu przy ich jednoczesnym wygładzeniu — usunięciu wszystkich „wystających” elementów. Zwiększanie rozmiaru elementu strukturalnego B skutkuje usuwaniem coraz większych detali obrazu oraz upodabnianiem powstałych obszarów do elemetu strukturalnego. Implementacja: ndimage.binary_opening. Proszę wypróbować tą operację: # -*- coding: utf-8 -*import numpy as np import matplotlib.pyplot as py from scipy import ndimage from scipy import misc def element(): #el = np.array([[0, 1, 0],[1,1,1],[0,1,0]], dtype=bool) el = np.zeros((3,3)) el = dodaj_kolo(el,1.5,1.5,1.5) return el def dodaj_kwadrat(ob, x,y, dx,dy): '''dodaje kwadrat wypełniony 1 do obrazu binarnego ob. x, y - lewy górny róg dx,dy - szerokosć i wysokość ''' ob[x:x+dx,y:y+dy] = 1 return ob def dodaj_kolo(ob, x,y, r): '''dodaje koło wypełnione 1 do obrazu binarnego ob. x, y - środek r - promień ''' r2=r**2 for xi in range(ob.shape[]): for yi in range(ob.shape[1]): if (xi-x)**2+(yi-y)**2<=r2: ob[xi,yi]=1 return ob def dodaj_szum(ob,ile): tmp = np.random.rand(ob.shape[],ob.shape[1]) idx = np.where(tmp<ile) ob[idx] = np.abs(ob[idx]-1) return ob py.close('all') NX=100 NY=100 #spróbujmy progowania na różnym poziomie py.figure('erozja 1') ob = np.zeros((NX,NY)) py.subplot(1,3,1) el = element() py.imshow(el,interpolation = 'nearest',cmap=py.cm.gray) py.xlim([-0.5,NX]) py.ylim([-0.5,NY]) py.axis('off') py.title('element strukturalny') py.subplot(1,3,2) ob = dodaj_kwadrat(ob,20,20,60,60) ob = dodaj_szum(ob,0.2) py.imshow(ob, interpolation = 'nearest',cmap=py.cm.gray) py.xlim([-0.5,NX]) py.ylim([-0.5,NY]) py.axis('off') py.title(u'obraz wejściowy') py.subplot(1,3,3) ob_wyj = ndimage.binary_opening(ob, structure = el).astype(ob.dtype) py.imshow(ob_wyj, interpolation = 'nearest',cmap=py.cm.gray) py.xlim([-0.5,NX]) py.ylim([-0.5,NY]) py.axis('off') py.title(u'obraz wyjściowy') py.show() py.figure('erozja 2') ob = np.zeros((NX,NY)) py.subplot(1,3,1) el = element() py.imshow(el,interpolation = 'nearest',cmap=py.cm.gray) py.xlim([-0.5,NX]) py.ylim([-0.5,NY]) py.axis('off') py.title('element strukturalny') py.subplot(1,3,2) #ob = dodaj_kwadrat(ob,2,2,6,6) ob = dodaj_kolo(ob,50,50,30) ob = dodaj_szum(ob,0.2) py.imshow(ob, interpolation = 'nearest',cmap=py.cm.gray) py.xlim([-0.5,NX]) py.ylim([-0.5,NY]) py.axis('off') py.title(u'obraz wejściowy') py.subplot(1,3,3) ob_wyj = ndimage.binary_opening(ob, structure = el).astype(ob.dtype) py.imshow(ob_wyj, interpolation = 'nearest',cmap=py.cm.gray) py.xlim([-0.5,NX]) py.ylim([-0.5,NY]) py.axis('off') py.title(u'obraz wyjściowy') py.show() Domknięcie Domknięcie to złożenie operacji dylacji i erozji. Domknięcie usuwa z obrazu wszelkie „dziury” oraz wklęsłości mniejsze od elementu strukturalnego. Może skutkować „połączeniem się” blisko położonych detali. Zwiększanie wielkości elementu strukturalnego powoduje wypełnianie coraz większych „dziur” oraz wklęsłości, upodabnianiem powstałych obszarów do elementu strukturalnego i łączeniem coraz dalej położonych detali.Implementacja: ndimage.binary_closing. Proszę wypróbować tą operację modyfikując poprzedni skrypt. Progowanie Aby uzyskać obraz binarny z obrazu w skali szarości należy obraz wejściowy sprogować. Proszę obejrzeć histogram szarości Leny i zobaczyć efekty progowania dla kilku wybranych wartości: # -*- coding: utf-8 -*import numpy as np import matplotlib.pyplot as py from scipy import ndimage from scipy import misc def proguj(ob, prog): ob_wyj = np.zeros(ob.shape) ob_wyj[np.where(ob>prog)]=1 return ob_wyj py.close('all') l = misc.lena() py.figure('histogram szarosci') py.hist(l.ravel(),255) # narysujmy histogram odcieni py.show() #spróbujmy progowania na różnym poziomie py.figure('progowanie') py.subplot(1,2,1) py.imshow(l,interpolation = 'nearest',cmap=py.cm.gray) py.axis('off') py.subplot(1,2,2) prog = 110 l_prog = proguj(l,prog) py.imshow(l_prog, interpolation = 'nearest',cmap=py.cm.gray) py.axis('off') py.show() Szukanie krawędzi Złożenie operacji morfologicznych erozji i dylacji można zastosować do detekcji krawędzi. # -*- coding: utf-8 -*import numpy as np import matplotlib.pyplot as py from scipy import ndimage from scipy import misc def proguj(ob, prog): ob_wyj = np.zeros(ob.shape) ob_wyj[np.where(ob>prog)]=1 return ob_wyj def element(): #el = np.array([[0, 1, 0],[1,1,1],[0,1,0]], dtype=bool) el = np.zeros((6,6)) el = dodaj_kolo(el,3,3,3) return el def dodaj_kwadrat(ob, x,y, dx,dy): '''dodaje kwadrat wypełniony 1 do obrazu binarnego ob. x, y - lewy górny róg dx,dy - szerokosć i wysokość ''' ob[x:x+dx,y:y+dy] = 1 return ob def dodaj_kolo(ob, x,y, r): '''dodaje koło wypełnione 1 do obrazu binarnego ob. x, y - środek r - promień ''' r2=r**2 for xi in range(ob.shape[]): for yi in range(ob.shape[1]): if (xi-x)**2+(yi-y)**2<=r2: ob[xi,yi]=1 return ob py.close('all') l = misc.lena() py.figure('operacje') py.subplot(3,3,1) py.imshow(l,interpolation = 'nearest',cmap=py.cm.gray) py.axis('off') py.title(u'oryginał') py.subplot(3,3,2) prog = 110 l_prog = proguj(l,prog) py.imshow(l_prog, interpolation = 'nearest',cmap=py.cm.gray) py.axis('off') py.title('progowany') py.subplot(3,3,3) el=element() l_wyj1 = ndimage.binary_erosion(l_prog, structure = el).astype(l_prog.dtype) py.imshow(l_wyj1, interpolation = 'nearest',cmap=py.cm.gray) py.axis('off') py.title('erozja') py.subplot(3,3,6) el=element() l_wyj2 = ndimage.binary_dilation(l_prog, structure = el).astype(l_prog.dtype) py.imshow(l_wyj2, interpolation = 'nearest',cmap=py.cm.gray) py.axis('off') py.title('dylacja') py.subplot(3,3,9) l_wyj3 = l_wyj2-l_wyj1 py.imshow(l_wyj3, interpolation = 'nearest',cmap=py.cm.gray) py.axis('off') py.title('erozja-dylacja') py.subplot(3,3,8) el = np.array([[, 1, ],[1,1,1],[,1,]], dtype=bool) l_wyj3 = ndimage.binary_erosion(l_prog, structure = el).astype(l_prog.dtype)ndimage.binary_dilation(l_prog, structure = el).astype(l_prog.dtype) py.imshow(l_wyj3, interpolation = 'nearest',cmap=py.cm.gray) py.axis('off') py.title(u'erozja-dylacja,mały element') py.subplot(3,3,4) l_wyj4 = ndimage.binary_closing(l_prog, structure = el).astype(l_prog.dtype) py.imshow(l_wyj4, interpolation = 'nearest',cmap=py.cm.gray) py.axis('off') py.title(u'domknięcie') py.subplot(3,3,5) l_wyj5 = ndimage.binary_opening(l_prog, structure = el).astype(l_prog.dtype) py.imshow(l_wyj5, interpolation = 'nearest',cmap=py.cm.gray) py.axis('off') py.title(u'otwarcie') py.show() Segmentacja Na początek, do treningu wytworzymy obrazek zawierający kilka obiektów. # -*- coding: utf-8 -*import numpy as np import matplotlib.pyplot as py from scipy import ndimage from scipy import misc np.random.seed(1) n = 10 l = 256 # generujemy pusty obrazek im = np.zeros((l, l)) # w losowych miejscach dorzucamy n punktów points = l*np.random.random((2, n**2)) im[(points[]).astype(np.int), (points[1]).astype(np.int)] = 1 #rozmywamy punkty filtrem gaussowskim im = ndimage.gaussian_filter(im, sigma=l/(4.*n)) #progujemy maskę na połowie wartości średniej obrazka mask = (im > im.mean()).astype(np.float) # wytwarzamy obrazek złożony z zaszumionej maski img = mask + 0.2*np.random.randn(*mask.shape) #progujemy zszumiony obrazek binary_img = img > 0.5 hist, bin_edges = np.histogram(img, bins=60) bin_centers = 0.5*(bin_edges[:-1] + bin_edges[1:]) py.figure(figsize=(11,4)) py.subplot(131) py.imshow(img) py.axis('off') py.subplot(132) #py.plot(bin_centers, hist, lw=2) py.hist(img.ravel(),60) py.axvline(0.5, color='r', ls='--', lw=2) py.yticks([]) py.subplot(133) py.imshow(binary_img, cmap=py.cm.gray, interpolation='nearest') py.axis('off') py.show() Segmentacja za pomocą funkcji ndimage.label # odszumiamy # usuwamy białe kropki open_img = ndimage.binary_opening(binary_img) py.subplot(234) py.imshow(open_img) py.axis('off') # usuwamy małe czarne dziury close_img = ndimage.binary_closing(open_img) py.subplot(235) py.imshow(close_img) py.axis('off') # segmentujemy: img_labeled,N_objects = ndimage.label(close_img) py.subplot(236) py.imshow(img_labeled.astype(np.float),interpolation='nearest') py.colorbar() py.axis('off') py.title(str(N_objects)+u' obiektów') py.show() Segmentacja przez analizę skupień (k-means) # -*- coding: utf-8 -*import numpy as np import matplotlib.pyplot as py from scipy import ndimage from scipy import misc #segmentacja przez analizę skupień l=misc.lena() from scipy.cluster.vq import kmeans,vq K_opt = 8 centroids,_ = kmeans(l.ravel(),K_opt) # przypisujemy klasę idx,_ = vq(l.ravel(),centroids) idx.shape = l.shape py.figure(2) py.subplot(1,2,1) py.imshow(idx)#, cmap=py.cm.gray) py.colorbar() py.axis('off') py.subplot(1,2,2) tmp_obj = np.zeros((l.shape[],l.shape[1])) tmp_obj[np.where(idx==4)] = 1 py.imshow(tmp_obj)#, cmap=py.cm.gray) py.axis('off') py.show() Segmentacja przez rozrost # -*- coding: utf-8 -*import numpy as np import matplotlib.pyplot as py from scipy import ndimage from scipy import misc import sys #segmentacja przez rozrost def testuj(reg_id, x,y, do_sprawdzenia, prog = 10): global im_org, im_marked, reg_wsp, reg_wart while len(do_sprawdzenia)>: tmp_x,tmp_y = do_sprawdzenia.pop() #print 'testuje: ', tmp_x,tmp_y if tmp_x>= and tmp_y>= and tmp_x<im_org.shape[] and tmp_y<im_org.shape[1]: # jeśli punkt wewnątrz obrazu if im_marked[tmp_x,tmp_y]==-1: # jeśli punkt nie należy do żadnego regionu if im_marked[tmp_x,tmp_y] != reg_id: #jeśli punkt nie należy do aktualnego regionu if np.abs(im_org[tmp_x,tmp_y]-np.mean(reg_wart[reg_id])) < prog: #jeśli punkt ma wartość bliższą do średniej z obszaru niż próg #to dodajemy punkt do listy bieżącego regionu, odhaczamy go na mapie regionów i dodajemy jego sąsiadów do listy, którą trzeba sprawdzić reg_wsp[reg_id].append((tmp_x,tmp_y)) reg_wart[reg_id].append(im_org[tmp_x,tmp_y]) im_marked[tmp_x,tmp_y]=reg_id do_sprawdzenia.append((tmp_x-1,tmp_y)) do_sprawdzenia.append((tmp_x ,tmp_y+1)) do_sprawdzenia.append((tmp_x, tmp_y-1)) do_sprawdzenia.append((tmp_x+1,tmp_y)) return do_sprawdzenia # wczytujemy lenę i wybieramy fragment z twarzą l=misc.lena() l = np.copy(l[200:400,200:400]) global im_org, im_marked, reg_wsp, reg_wart im_marked = -1*np.ones((l.shape[],l.shape[1]),dtype='int') im_org = l # próg na odchylenie dołączanego punktu od średniej prog = 35 reg_wsp = [] reg_wart =[] reg_id = while np.sum(im_marked==-1)>: # współrzędne punktu startowego bierzemy jako pierwsze z brzegu nie przypisane jeszcze do żadnego regionu x=np.where(im_marked==-1)[][] y=np.where(im_marked==-1)[1][] # dodajemy miejsce na listę przechowującą współrzedne punktów należących do regionów i na ich wartości reg_wsp.append([]) reg_wsp[reg_id].append((x,y)) reg_wart.append([]) reg_wart[reg_id].append(im_org[x,y]) # odchaczamy na mapie regionów bieżący punkt jako przypisany do reg_id im_marked[x,y] = reg_id # inicjujemy listę punktów, które trzeba sprawdzić, czy nie należą do bieżącego regionu do_sprawdzenia = [(x-1,y),(x,y+1),(x,y-1),(x+1,y)] do_sprawdzenia = testuj(reg_id,x,y,do_sprawdzenia,prog) print reg_id reg_id +=1 # rysowanie wyników py.figure(2) py.subplot(1,3,1) py.imshow(l, interpolation='nearest', cmap=py.cm.gray) py.axis('off') py.title(u'oryginał') py.subplot(1,3,2) py.imshow(im_marked, interpolation='nearest')#, cmap=py.cm.gray) py.axis('off') py.title('po segmentacji') py.subplot(1,3,3) S = np.array([ len(reg_wart[i]) for i in range(len(reg_wart))]) id = np.where((S>5000))[][] py.imshow(im_marked==id, interpolation='nearest')#, cmap=py.cm.gray) py.axis('off') py.title('obiekt '+str(id)) py.show()