Na początku zajmijmy się opracowaniem prostej metody, która będzie miała na celu zalezienie w podanej przez nas palecie koloru najbliższego dla wartości piksela, który aktualnie analizujemy. Naszą funkcję będziemy wstępnie opracowywać dla danych testowych, zakładamy, że nasze dane są dostarczane w formacie float \(<0,1>\). Na początek zadeklarujmy sobie naszą wstępną paletę zawierającą \(N\) wartości:

    paleta = np.linspace(0,1,N).reshape(N,1)
    print(paleta) #można usunać

Po wywołaniu tego fragmentu kodu mamy dostępny kolumnowy wektor N-elementowy, zawierający wartości z zakresu naszego koloru. Jeżeli chcecie zmienić zakres na uint8 to trzeba przesunąć górną granicę zmiennej oraz zrzutować całość na uint8. Na początek ustawmy sobie \(N=3\). Jakie mamy wartości w naszej palecie?

Zacznijmy od zadeklarowania naszej funkcji:

def colorFit(pixel,Pallet):
        pass

Nasza funkcja nie będzie potrzebowała do działania żadnych pętli i będzie działała niezależnie od ilości kolorów (tak długo, jak ilość kolorów pikseli i palety będzie taka sama). Zacznijmy od omówienia podejścia:

Zakładamy, że traktujemy nasze kolory jako punkty w przestrzeni. Żeby policzyć, który z tych punktów z palety jest najbliżej naszego piksela, potrzebujemy odległości między nimi. Odległość ta jest długością wektora pomiędzy nimi. Z matematyki powinniście wiedzieć, że aby stworzyć wektor pomiędzy dwoma punktami i od razu przesunąć jego początek do centrum osi współrzędnych, to należy odjąć jeden punkt od drugiego. Nasze środowisko NumPy daje nam możliwość wykonania tej operacji od razu dla całej tablicy. Co dostajemy poniżej:

print(Pallet-pixel)

Dla przypadku jednowymiarowego, czyli palety dla obrazu w skali odcieni szarości mamy już dostateczną ilość informacji, ale nasza funkcja ma działać w sposób kompleksowy, czyli dla wszystkich ilości kolorów. Dlatego potrzebujemy wyliczyć odległość pomiędzy naszymi kolorami do tego celu najlepiej wykorzystać funkcję liczącą odległość euklidesową dla naszego wektora:

np.linalg.norm(?,axis=1)

Na tej podstawie bez względu ile mamy kolorów (skala odcieni szarości, RGB, CMYK itd.) na tym etapie będziemy dysponować wektorem wartości. Teraz musimy znaleźć najmniejszą wartość w naszym wektorze. Do tego celu sugeruję wykorzystać poniższą funkcję.

np.argmin()

Jeżeli wykorzystacie powyższe funkcje, to cała funkcja może składać się z 1 linii (maksymalnie 3) i działać dla dowolnej ilości kolorów (o ile ilość kolumn będzie równa ilości warstw). Poniżej kilka przykładów działania:

paleta = np.linspace(0,1,3).reshape(3,1)
print(colorFit(0.43,paleta)) # 0.5
print(colorFit(0.66,paleta)) # ?
print(colorFit(0.8,paleta)) # ?

Przedefiniowane palety kolorów

Paleta 8 kolorów ([R, G, B]):

pallet8 = np.array([
        [0.0, 0.0, 0.0,],
        [0.0, 0.0, 1.0,],
        [0.0, 1.0, 0.0,],
        [0.0, 1.0, 1.0,],
        [1.0, 0.0, 0.0,],
        [1.0, 0.0, 1.0,],
        [1.0, 1.0, 0.0,],
        [1.0, 1.0, 1.0,],
])

Paleta 16 kolorów [web safe] ([R, G, B]):

pallet16 =  np.array([
        [0.0, 0.0, 0.0,], 
        [0.0, 1.0, 1.0,],
        [0.0, 0.0, 1.0,],
        [1.0, 0.0, 1.0,],
        [0.0, 0.5, 0.0,], 
        [0.5, 0.5, 0.5,],
        [0.0, 1.0, 0.0,],
        [0.5, 0.0, 0.0,],
        [0.0, 0.0, 0.5,],
        [0.5, 0.5, 0.0,],
        [0.5, 0.0, 0.5,],
        [1.0, 0.0, 0.0,],
        [0.75, 0.75, 0.75,],
        [0.0, 0.5, 0.5,],
        [1.0, 1.0, 1.0,], 
        [1.0, 1.0, 0.0,]
])

Co tu powinno się zwrócić?

print(colorFit(np.array([0.25,0.25,0.5]),pallet8))
print(colorFit(np.array([0.25,0.25,0.5]),pallet16))

Wykorzystanie funkcji colorFit do kwantyzacji obrazu

Do wykorzystania funkcji colorFit do kwantyzacji obrazu potrzebujemy dwie pętle po wierszach i po kolumnach. Dzięki właściwościom Python i NumPy nie potrzeba dodawać 3 pętli po warstwach, gdyż domyślnie zostaną one przekazane jako tablica.

def kwant_colorFit(img,Pallet):
        out_img = img.copy()
        for w in range():
                for k in range():
                        out_img[w,k]=colorFit(img[w,k],Pallet)
        return out_img