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:

  1. Ułatwiający wyświetlanie — czyli żeby zakres wartości był pomiędzy \(<0,1>\)
  2. 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]):
        CO[int(Z[w,k]),int(DO[w,k])]+=1

Przykład dla podejścia 2:

for w in range(CO.shape[0]):
    for k in range(CO.shape[1]):
        CO[w,k]=np.sum( np.logical_and( Z==w,DO==k ) )