ANALIZA I PRZETWARZANIE OBRAZÓW
Instalacja pip install opencv-python run pip install opencv-contrib-python
Przydatne Potrzebne importy: import cv2 import numpy as np Odczyt, zapis i wyświetlanie obrazu: img=cv2.imread('cell.jpg') cv2.imwrite('img_binadapt1.jpg', th1) cv2.imshow('bin',img_bin)
Zamiana obrazu na stopnie szarości img_gray=cv2.cvtcolor(img, cv2.color_bgr2gray)
Progowanie globalne Progowanie globalne za wartość progu przyjmuje średnią wartość intensywności wszystkich pikseli w obrazie. Do wykonania progowania globalnego w OpenCV na obrazie img służy funkcja: cv2.threshold(img, THRESH, MAXVALUE, cv2.method) cv2.method dostępne metody progowania i ich efekt na obrazie gradientowy
Progowanie globalne
Progowanie globalne ret,img_bin1 = cv2.threshold(img_gray, 128, 255, cv2.thresh_binary) ret,img_bin2 = cv2.threshold(img_gray,128,255,cv2.thresh_binary_inv) ret,img_bin3 = cv2.threshold(img_gray,128,255,cv2.thresh_trunc) ret,img_bin4 = cv2.threshold(img_gray,128,255,cv2.thresh_tozero) ret,img_bin5 = cv2.threshold(img_gray,128,255,cv2.thresh_tozero_inv)
Progowanie globalne
Progowanie adaptacyjne charakter lokalny: próg binaryzacji zróżnicowany w zależności od regionu obrazu. stosowane jest w przypadku obrazów o rożnym stopniu oświetlenia w różnych rejonach obrazu i daje zdecydowanie lepsze rezultaty niż progowanie globalne: cv2.adaptivethreshold(img, maxvalue, Adaptive Method, BlockSize, C) Adaptive Method służy obliczeniu wartości progu: cv2.adaptive_thresh_mean_c : próg jest średnią intensywności piksela ze zdefiniowanego sąsiedztwa cv2.adaptive_thresh_gaussian_c : próg jest średnią intensywności piksela ze zdefiniowanego sąsiedztwa, zaś współczynniki wagowe odpowiadają filtrowi Gaussa Block Size rozmiar sąsiedztwa C stała odejmowana od średniej lub średniej ważonej (tolerancja progu)
Progowanie adaptacyjne th2 = cv2.adaptivethreshold(img_gray,255,cv2.adaptive_thresh_mean_c,\ cv2.thresh_binary,11,2) th3 = cv2.adaptivethreshold(img_gray,255,cv2.adaptive_thresh_gaussian_c,\ cv2.thresh_binary,11,2)
Binaryzacja Otsu metoda progowania dedykowana obrazom charakteryzującym się bimodalnym histogramem (wylicza optymalną wartość progu w oparciu o analizę histogramu). W OpenCV stosuje się funkcję cv2.threshold() jako metodę progowania wskazując cv2.thresh_binary+ cv2.thresh_otsu. Jako wartość progu należy wpisać 0, co pozwoli algorytmowi na optymalny wybór wartości progu, zwracając go jako drugi argument wejściowy (retval). W metodach innych niż Otsu wartość progu retval jest identyczna jak zastosowanego progu.
Binaryzacja Otsu retval2,threshold2 = cv2.threshold(img_gray,175,255, cv2.thresh_binary+cv2.thresh_otsu)
Przekształcenia geometryczne: skalowanie Skalowanie polega na zmianie rozmiaru obrazu. Implementacja równania: g x, y = f( x M x, y M y ) pozwala skalować obraz (zmniejszać i powiększać) o czynnik M x w osi x oraz M y w osi y. W OpenCV służy do tego funkcja cv2.resize().wynikowy rozmiar obrazu może być zarówno zdefiniowany manualnie lub przy użyciu współczynników skalowanie. Istnieje możliwość zdefiniowania różnych metod interpolacji: cv2.inter_linear (domyślna) cv2.inter_area oraz cv2.inter_cubic (zmniejszanie) cv2.inter_linear (powiększanie)
Przekształcenia geometryczne: skalowanie #pomniejszanie res1 = cv2.resize(img,none,fx=0.5, fy=0.5, interpolation = cv2.inter_cubic) #powiększanie height, width = img.shape[:2] res2 = cv2.resize(img,(2*width, 2*height), interpolation = cv2.inter_cubic)
Przekształcenia geometryczne: przesunięcie Translacja to zmiana położenie obrazu. Jeśli przesunięcie współrzędnych obrazu wzdłuż osi x i y zdefiniujemy jako (t x,t y ), to wówczas macierz transformacji M można zapisać jako: Dla pojedynczego piksela przesunięcie można zdefiniować równaniem: g x, y = f(x + x 0, y + y 0 ) gdzie f(x,y) jest obrazem wejściowym a g(x,y) obrazem wyjściowym. Obraz ulega przesunięciu o wektor długości x 2 0 + y 2 0. Translacja więc jest operacją mapowania przestrzennego. Do wykonania translacji służy funkcja cv2.warpaffine()
Przekształcenia geometryczne: przesunięcie rows,cols = img.shape M = np.float32([[1,0,100],[0,1,50]]) dst = cv2.warpaffine(img,m,(cols,rows))
Przekształcenia geometryczne: rotacja Rotacja obrazu o kąt transformacji M: osiągana jest przez zastosowanie Biblioteka OpenCV zapewnia skalowaną rotację z regulowanym środkiem obrotu, dzięki czemu obraz można obracać w dowolnym miejscu. Zmodyfikowana macierz transformacji: Znajdowaniu modyfikacji macierzy rotacji służy funkcja cv2.getrotationmatrix2d.
Przekształcenia geometryczne: rotacja rows,cols = img.shape M = cv2.getrotationmatrix2d((cols/2,rows/2),45,1) dst = cv2.warpaffine(img,m,(cols,rows))
Zmiana przestrzeni barw 1. Konwersja RGB-> HSV pozwala na implementację wielu algorytmów bazujących na nasyceniu barwy, w tym algorytmów generowania histogramów i wykrywanie obiektów dla których można zdefiniować poziom odbicia światła (co jest szczególnie przydatne w analizie zdjęć fotometrycznych). 2. W bibliotece OpenCV barwy dla systemu RGB zapisywane są w kanałach w odwrotnej kolejności (BGR)
Zmiana przestrzeni barw img_hsv=cv2.cvtcolor(img, cv2.color_bgr2hsv) img_gray = cv2.cvtcolor(img, cv2.color_bgr2gray) RGB GRAY HSV cv2.color_bgr2gray cv2.color_bgr2hsv
Filtr uśredniający cv2.blur() nowa wartość intensywności w pikselu wyjściowym jest średnią wszystkich wartości w zdefiniowanym otoczeniu piksela wejściowego (tzw. jądrze). filtr w kształcie prostokąta (w szczególnej postaci kwadratu), którego wszystkie wartości są równe; w najprostszym przypadku wartości te są równe 1 lub 1/A gdzie A jest rozmiarem filtra (tzw. znormalizowany filtr prostokątny). 1/9 * 1 1 1 1 1 1 1 1 1
Filtr uśredniający blur = cv2.blur(img,(5,5))
Filtr medianowy zmiana wartości każdego piksela na medianę (środkową wartość) piksela w jego zdefiniowanym prostokątnym otoczeniu. Jądro przekształcenia ma charakter nieliniowy, stąd brak możliwości przedstawienia go na rysunku. filtr uśredniający wykazuje dużą podatność na szumy, w szczególności w postaci dużych izolowanych wartości, które finalnie mogą wpływać na wartość średnią (tzw. szum śrutowy)
Filtr medianowy median = cv2.medianblur(img,5)
Filtr Gaussa Obliczenie splotu każdego piksela i znormalizowanego jądra gaussowkiego i zapisanie wyniku w obrazie wyjściowym. Przykład jądra gaussowskiego (na czerwono oznaczono punkt nałożenia maski na piksel centralny): 1/73 * 1 7 1 7 41 7 1 7 1 najskuteczniejszy filtr redukujący szum przy jednoczesnym zachowaniu sygnału spośród metod dostępnych w bibliotece OpenCV (ale gorsze niż np. w przypadku filtru bilateralnego zachowanie krawędzi). powinien być stosowany w przypadku gdy piksele różnią się między sobą tylko nieznacznie, co pozwala na ich wzajemne skorelowanie, podczas gdy szum ma charakter losowy i wpływa na wyraźne różnice między pikselami (brak przestrzennej korelacji szumu). filtr ten ma ograniczoną skuteczność w przypadku zachowania silnie skontrastowanych krawędzi
Filtr Gaussa gaussian = cv2.gaussianblur(img,(5,5),0)
Filtr bilateralny filtracja wygładzającą z zachowaniem krawędzi za cenę wydłużenia czasu filtracji ulepszenie metody filtracji Gaussa, który rozmywa krawędzie, poprzez nadanie większego znaczenia pikselom wzajemnie podobnym a tym samym pozwalając zachować oryginalny przebieg obszarów silnie skontrastowanych. oblicza dla każdego piksela średnią ważoną z jego otoczenia (wraz z uwzględnieniem wartości samego piksela). Dla wyliczenia średniej ważonej wykorzystywane są dwa współczynniki: pierwszy z nich jest identyczny jak w przypadku filtru Gaussa. Drugi współczynnik jest niezależny od odległości pikseli otoczenia od piksela centralnego, jest jednak skorelowany z różnicą w poziomie intensywności pikseli. zapobiega przypadkowej segmentacji przy stosowaniu innych algorytmów przetwarzania obrazu.
Filtr bilateralny bilateral = cv2.bilateralfilter(img,9,75,75)
Wykrywanie krawędzi Detekcja krawędzi opiera się na wyeksponowaniu w obrazie punktów, w których gwałtownie zmienia się poziom intensywności. Przebieg krawędzi w obrazie odzwierciedla ważne z punktu widzenia rozgraniczenia obiektów cechy. Zmiany w poziomie intensywności mogą być spowodowane: zmianą głębokości, zmianą orientacji powierzchni zmianą właściwości materiału (tekstury) zmianą oświetlenia scen (szczególnie istotne w przypadku zdjęć z mikroskopu).
Wykrywanie krawędzi: filtr Sobela kombinacja wygładzenia Gaussa oraz operacji różnicowania, co podnosi odporność metody na obecność szumu Operator ten pozwala na zdefiniowanie przebiegu krawędzi będących przedmiotem zainteresowania (pionowy lub poziomy) odpowiednio poprzez parametry xorder oraz yorder. Dodatkowym parametrem metody jest rozmiar jądra ksize.
Wykrywanie krawędzi: filtr Sobela sobelx = cv2.sobel(img,cv2.cv_64f,1,0,ksize=5) #krawędzie pionowe sobely = cv2.sobel(img,cv2.cv_64f,0,1,ksize=5) #krawędzie poziome sobel = cv2.sobel(img,cv2.cv_64f,1,1,ksize=5)
Wykrywanie krawędzi: filtr Laplace a Operacja ta oblicza laplasian obrazu zgodnie z równaniem: f = δ2 f δx 2 + δ 2 f δy 2 Każda z pochodnych wyliczana jest za pomocą pochodnych Sobela. Jeśli rozmiar sąsiedztwa wynosi1, do filtrowania używane jest następujące jądro:
Wykrywanie krawędzi: filtr Laplace a #filtr Laplace'a laplacian = cv2.laplacian(img,cv2.cv_64f)
Generowanie histogramu Do generowania histogramu w bibliotece OpenCV służy funkcja cv2.calchist(): cv2.calchist(image, channels, mask, histsize, ranges) Parametry funkcji: images - obraz wejściowy (typ danych uint8 lub float32). Argument podawany w [img] channels - liczba kanałów dla których liczony jest histogram. W przypadku obrazów monochromatycznych argument ten przyjmuje wartość 0, dla obrazów RGB może to być [0], [1] lub [2] (odpowiednio dla kanału czerwonego, zielonego i niebieskiego) mask - argument przydatny jeśli chcemy wykonać histogram jedynie fragmentu obrazu (rysunek 11) histsize- reprezentuje liczbę kanałów w histogramie Dla obrazów 8-bitowych jest to 256 kanałów ranges - zakres zmienności poziomów intensywności. Dla obrazów 8-bitowych jest to przedział. [0,255].
Generowanie histogramu
Generowanie histogramu
Generowanie histogramu import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread('cell.jpg') plt.hist(img.ravel(),256,[0,256]); plt.savefig('histogram.png') hist,bins = np.histogram(img.ravel(),256,[0,256]) cv2.imwrite('hist2.jpg',hist) color = ('b','g','r') for i,col in enumerate(color): histr = cv2.calchist([img],[i],none,[256],[0,256]) plt.plot(histr,color = col) plt.xlim([0,256]) plt.savefig('histogram2.png')
Generowanie histogramu Funkcja cv2.calchist() może zostać użyta nie tylko w celu uzyskania histogramu całego obrazu, ale również jego fragmentu wyciętego z pierwotnego obrazu przy użyciu maski (piksele maski służące wycięciu obrazu mają kolor biały, pozostałe czarny)
Generowanie histogramu
import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread('cell.jpg',0) # create a mask mask = np.zeros(img.shape[:2], np.uint8) mask[100:300, 100:400] = 255 masked_img = cv2.bitwise_and(img,img,mask = mask) # Calculate histogram with mask and without mask # Check third argument for mask hist_full = cv2.calchist([img],[0],none,[256],[0,256]) hist_mask = cv2.calchist([img],[0],mask,[256],[0,256]) plt.subplot(221), plt.imshow(img, 'gray') plt.subplot(222), plt.imshow(mask,'gray') plt.subplot(223), plt.imshow(masked_img, 'gray') plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask) plt.xlim([0,256]) plt.savefig('histogram3.png')
Normalizacja histogramu Z matematycznego punktu widzenia normalizacja histogramu polega na odwzorowaniu jednego rozkładu (wyrażonego rozkładem intensywności pikseli w obrazie wejściowym) na inny (szerszy, możliwie jednostajny). Celem normalizacji jest więc równomierne rozciągnięcie rozkładu pierwotnego na docelowy.
Normalizacja histogramu import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread('cell.jpg',0) hist,bins = np.histogram(img.flatten(),256,[0,256]) cdf = hist.cumsum() cdf_normalized = cdf * hist.max()/ cdf.max() plt.plot(cdf_normalized, color = 'b') plt.hist(img.flatten(),256,[0,256], color = 'r') plt.xlim([0,256]) plt.legend(('cdf','histogram'), loc = 'upper left') plt.savefig('hist_eq.png')
Normalizacja histogramu
Normalizacja histogramu Kontrast tła poprawił się po wyrównaniu histogramu, ale część informacji o strukturze krwinki została utracona, zaś skontrastowanie tła wydaje się być zbyt silne i ujawniać obecność zaszumienia. Rozwiązaniem problemu jest wprowadzenie normalizacji adaptacyjnej omówionej w następnym podrozdziale.
Normalizacja histogramu (CLAHE) (ang. Contrast Limited Adaptive Histogram Equalization Adaptacyjna normalizacja histogramu. Obraz zostaje podzielony na małe bloki (kafle, domyślnie 8x8), a każdy z tych bloków jest normalizowany histogramem. Podejście to może wzmocnić szum, stąd dodatkowo stosuje się ograniczenie kontrastu (domyślnie 40) Jeśli jakikolwiek znacznik histogramu przekracza określony limit kontrastu (domyślnie 40 jako liczba zliczeń w kanale), piksele te są przycinane i równomiernie rozdzielane na inne przedziały (przed normalizacją) Po normalizacji usuwane są artefakty powstałe na granicach regionów (kafli), poprzez zastosowanie interpolacji dwuliniowej.
Normalizacja histogramu (CLAHE) (ang. Contrast Limited Adaptive Histogram Equalization import cv2 import numpy as np #from matplotlib import pyplot as plt img = cv2.imread('cell.jpg',0) equ = cv2.equalizehist(img) #zestawienie obok siebie obrazu przed i po normalizacji histogramu res = np.hstack((img,equ)) cv2.imwrite('hist_eq.png',res) # create a CLAHE object (Arguments are optional) #CLAHE (Contrast Limited Adaptive Histogram Equalization) clahe = cv2.createclahe(cliplimit=2.0, tilegridsize=(8,8)) cl1 = clahe.apply(img) res2 = np.hstack((img,cl1)) cv2.imwrite('hist_clahe.jpg',res2)
Normalizacja histogramu (CLAHE)