Macierz współwystępowania jest w pewnym sensie podobna do histogramu, jednak, zamiast zliczać wystąpienia, zlicza relacje pomiędzy poszczególnymi pikselami. Podobnie jak w przypadku histogramu mamy do czynienia z podziałem wartości pikseli obrazu na \(N\) przedziałów jasności. Dlatego nasza macierz współwystępowania będzie macierzą kwadratową o rozmiarze \(NxN\). Nasza relacja pomiędzy pikselami może zachodzić w dowolnym kierunku, jak i z dowolną odległością.
Funkcja powinna zatem prezentować się w poniższy sposób:
def fCoocurence( img, N ): # wersja podstawowa
def fCoocurence( img, N , dir ): # wersja zaawansowana
W przypadku wykonywania wersji zaawansowanej zarówno odległość, jak i kierunek można przekazać jako offset (przesunięcie) zarówno w ilości wierszy, jak i kolumn.
Sam algorytm nie jest skomplikowany. Zacznijmy od “obrazu” źródłowego dla \(N=5\), czyli wartości pikseli znajdują się w przedziale \(<0,4>\), a nasza macierz współwystępowania ma rozmiar \(5x5\). Rozważymy relację o jeden piksel w prawo. Na starcie nasz obraz i macierz współwystępowania wyglądają następująco:
Obraz: \(\begin{bmatrix} 0 & & 0 & & 1 & & 1 & & 2 \\ 2 & & 1 & & 3 & & 4 & & 1 \\ 2 & & 3 & & 4 & & 2 & & 2 \\ 1 & & 3 & & 1 & & 3 & & 3 \\ 2 & & 4 & & 4 & & 1 & & 4 \\ 3 & & 1 & & 3 & & 1 & & 2 \end{bmatrix}\) Macierz współwystępowania: \(\begin{bmatrix} 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 \end{bmatrix}\).
Zacznijmy od pierwszej pary pikseli w relacji w naszym przypadku jest to relacja \(0 \to 0\), czyli zwiększamy wartość macierzy współwystępowania w miejscu odpowiadającym tej relacji. W celu uproszczenia załóżmy, że indeks wiersza odpowiada wartości piksela z
, którego wychodzi relacja, indeks kolumny to wartość piksela na
, który relacja jest skierowana. Po pierwszej iteracji stan prezentuje się następująco:
Obraz: \(\begin{bmatrix} 0 & \to & 0 & & 1 & & 1 & & 2 \\ 2 & & 1 & & 3 & & 4 & & 1 \\ 2 & & 3 & & 4 & & 2 & & 2 \\ 1 & & 3 & & 1 & & 3 & & 3 \\ 2 & & 4 & & 4 & & 1 & & 4 \\ 3 & & 1 & & 3 & & 1 & & 2 \end{bmatrix}\) Macierz współwystępowania: \(\begin{bmatrix} 1 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 \end{bmatrix}\).
Kontynuując do końca pierwszego wiersza, sytuacja powinna wyglądać następująco:
Obraz: \(\begin{bmatrix} 0 &\to& 0 &\to& 1 &\to& 1 &\to& 2 \\ 2 & & 1 & & 3 & & 4 & & 1 \\ 2 & & 1 & & 4& & 2 & & 2 \\ 1 & & 3 & & 1 & & 3 & & 3 \\ 2 & & 4 & & 4 & & 1 & & 4 \\ 3 & & 1 & & 3 & & 1 & & 2 \end{bmatrix}\) Macierz współwystępowania: \(\begin{bmatrix} 1 & 1 & 0 & 0 & 0 \\ 0 & 1 & 1 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 \end{bmatrix}\).
Następnie kolejny wiersz:
Obraz: \(\begin{bmatrix} 0 &\to& 0 &\to& 1 &\to& 1 &\to& 2 \\ 2 &\to& 1 &\to& 3 &\to& 4 &\to& 1 \\ 2 & & 3 & & 4 & & 2 & & 2 \\ 1 & & 3 & & 1 & & 3 & & 3 \\ 2 & & 4 & & 4 & & 1 & & 4 \\ 3 & & 1 & & 3 & & 1 & & 2 \end{bmatrix}\) Macierz współwystępowania: \(\begin{bmatrix} 1 & 1 & 0 & 0 & 0 \\ 0 & 1 & 1 & 1 & 0 \\ 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 1 \\ 0 & 1 & 0 & 0 & 0 \end{bmatrix}\).
Następnie kolejny wiersz:
Obraz: \(\begin{bmatrix} 0 &\to& 0 &\to& 1 &\to& 1 &\to& 2 \\ 2 &\to& 1 &\to& 3 &\to& 4 &\to& 1 \\ 2 &\to& 3 &\to& 4 &\to& 2 &\to& 2 \\ 1 & & 3 & & 1 & & 3 & & 3 \\ 2 & & 4 & & 4 & & 1 & & 4 \\ 3 & & 1 & & 3 & & 1 & & 2 \end{bmatrix}\) Macierz współwystępowania: \(\begin{bmatrix} 1 & 1 & 0 & 0 & 0 \\ 0 & 1 & 1 & 1 & 0 \\ 0 & 1 & 1 & 1 & 0 \\ 0 & 0 & 0 & 0 & 2 \\ 0 & 1 & 1 & 0 & 0 \end{bmatrix}\).
Następnie kolejny wiersz:
Obraz: \(\begin{bmatrix} 0 &\to& 0 &\to& 1 &\to& 1 &\to& 2 \\ 2 &\to& 1 &\to& 3 &\to& 4 &\to& 1 \\ 2 &\to& 3 &\to& 4 &\to& 2 &\to& 2 \\ 1 &\to& 3 &\to& 1 &\to& 3 &\to& 3 \\ 2 & & 4 & & 4 & & 1 & & 4 \\ 3 & & 1 & & 3 & & 1 & & 2 \end{bmatrix}\) Macierz współwystępowania: \(\begin{bmatrix} 1 & 1 & 0 & 0 & 0 \\ 0 & 1 & 1 & 3 & 0 \\ 0 & 1 & 1 & 1 & 0 \\ 0 & 1 & 0 & 1 & 2 \\ 0 & 1 & 1 & 0 & 0 \end{bmatrix}\).
Następnie kolejny wiersz:
Obraz: \(\begin{bmatrix} 0 &\to& 0 &\to& 1 &\to& 1 &\to& 2 \\ 2 &\to& 1 &\to& 3 &\to& 4 &\to& 1 \\ 2 &\to& 3 &\to& 4 &\to& 2 &\to& 2 \\ 1 &\to& 3 &\to& 1 &\to& 3 &\to& 3 \\ 2 &\to& 4 &\to& 4 &\to& 1 &\to& 4 \\ 3 & & 1 & & 3 & & 1 & & 2 \end{bmatrix}\) Macierz współwystępowania: \(\begin{bmatrix} 1 & 1 & 0 & 0 & 0 \\ 0 & 1 & 1 & 3 & 1 \\ 0 & 1 & 1 & 1 & 1 \\ 0 & 1 & 0 & 1 & 2 \\ 0 & 2 & 1 & 0 & 1 \end{bmatrix}\).
Na koniec sytuacja prezentuje się w następujący sposób:
Obraz: \(\begin{bmatrix} 0 &\to& 0 &\to& 1 &\to& 1 &\to& 2 \\ 2 &\to& 1 &\to& 3 &\to& 4 &\to& 1 \\ 2 &\to& 3 &\to& 4 &\to& 2 &\to& 2 \\ 1 &\to& 3 &\to& 1 &\to& 3 &\to& 3 \\ 2 &\to& 4 &\to& 4 &\to& 1 &\to& 4 \\ 3 &\to& 1 &\to& 3 &\to& 1 &\to& 2 \end{bmatrix}\) Macierz współwystępowania: \(\begin{bmatrix} 1 & 1 & 0 & 0 & 0 \\ 0 & 1 & 2 & 4 & 1 \\ 0 & 1 & 1 & 1 & 1 \\ 0 & 3 & 0 & 1 & 2 \\ 0 & 2 & 1 & 0 & 1 \end{bmatrix}\).
Oczywiście nasza macierz współwystępowania może zostać znormalizowana na dwa sposoby:
- Ułatwiający wyświetlanie — czyli żeby zakres wartości był pomiędzy \(<0,1>\)
- Poprawny (wyświetli się dobrze przy automatycznym skalowaniu) - czyli wartości niezależne od rozmiaru obrazu/ilości, czyli \(\dfrac{C}{\sum{C}}\)
Jak do tego podejść programistycznie?
Po pierwsze, żeby wygodniej wyodrębnić sobie dwie macierze, które pozwolą nam w łatwiejszy sposób analizować nasze relacje pomiędzy pikselami. Nazwiemy je Z
i DO
lub jakoś podobnie. O ile kierunek relacji nie jest odwrócony (któraś ze współrzędnych relacji jest \(<0\)), w żadnym wymiarze, to ich wyznaczenie nie powinno być trudne. Jeżeli jednak macie odwrócenie relacji to trzeba zamienić część współrzędnych pomiędzy macierzami. Macierz Z
powinna zawierać wszystkie elementy macierzy, z których relacja wychodzi, a macierz DO
wszystkie elementy będących celem relacji. W praktyce macierz Z
powinna zaczynać się w pozycji [0,0]
, a macierz DO
powinna kończyć się w ostatnim elemencie macierzy. Pozostałe współrzędne zależą od naszego kierunku relacji dir
. Przykładowo, jeżeli mamy przesunięcie dir=[2,2]
to macierz Z
będzie się kończyć na współrzędnej [w-2,k-2]
, a macierz DO
powinna zaczynać się w miejscu [2,2]
.
Dlaczego to robimy? Ponieważ w tym momencie każda relacja odbywa się pomiędzy wartościami pod tym samym adresem w obu macierzach, czyli przykładowo relacja odbywa się pomiędzy pikselami Z[x,y]
i DO[x,y]
.
Teraz jak podjeść do wypełniania tej macierzy? Mamy dwie możliwości albo przechodzić po wszystkich pikselach i inkrementować naszą macierz, albo szukać wszystkich relacji po kolei i je zliczać.
Przykład dla podejścia 1:
for w in range(Z.shape[0]):
for k in range(Z.shape[1]):
int(Z[w,k]),int(DO[w,k])]+=1 CO[
Przykład dla podejścia 2:
for w in range(CO.shape[0]):
for k in range(CO.shape[1]):
=np.sum( np.logical_and( Z==w,DO==k ) ) CO[w,k]