Kod
library(magick)
frink <- image_read("https://jeroen.github.io/images/frink.png")
frink
Obrazy cyfrowe mogą zostać poddane różnego rodzaju transformacjom. Oto kilka najważniejszych z nich:
Skalowanie: zmiana rozmiaru obrazu;
Obracanie: obrót obrazu o określony kąt;
Przesunięcie: przesunięcie obrazu w kierunku ustalonego wektora;
Progowanie: przekształcenie obrazu binarnego, gdzie piksele o wartościach powyżej określonego progu są przekształcane na białe, a pozostałe na czarne;
Kompresja: zmniejszanie rozmiaru pliku obrazu poprzez redukcję jego jakości;
Filtrowanie: zastosowanie filtrów, takich jak filtr Gaussa, filtr medianowy, filtr Laplasjan itp.
Skalowanie obrazu to proces zmiany rozmiaru obrazu. Może to być zwiększenie lub zmniejszenie wielkości obrazu. Skalowanie może być wykonywane przy użyciu różnych algorytmów, takich jak skalowanie proporcjonalne, skalowanie nieproporcjonalne, skalowanie bilinearne, skalowanie bikubiczne itp. Skalowanie proporcjonalne zachowuje proporcje pikseli i jakość obrazu, natomiast skalowanie nieproporcjonalne nie zachowuje proporcji i może prowadzić do zniekształceń obrazu. Skalowanie bilinearne i bikubiczne są bardziej zaawansowanymi metodami skalowania, które pozwalają na uzyskanie lepszej jakości obrazu po skalowaniu.
Jak widać z powyższych przykładów interpolacja obrazów jest kluczową techniką w wizji komputerowej, szczególnie przy operacjach takich jak obrót i skalowanie obrazów. Pozwala na wygładzenie obrazu i zminimalizowanie efektów zniekształceń. Dwie powszechnie stosowane metody interpolacji, które zostaną bliżej opisane, to interpolacja bilinearna i bikubiczna. Obie metody różnią się złożonością obliczeniową i jakością wynikowego obrazu.
Skalowanie bilinearne to metoda skalowania obrazu, która polega na interpolacji między pikselami. W tym procesie, dla każdego nowego piksela wynikowego obrazu, algorytm oblicza wartość piksela poprzez interpolację między czterema najbliższymi pikselami z oryginalnego obrazu. Konkretniej, dla każdego nowego piksela, algorytm określa współrzędne na oryginalnym obrazie, i następnie interpoluje wartości pikseli z czterech najbliższych sąsiednich pikseli w oryginalnym obrazie. Interpolacja ta jest wykonywana przez przemnożenie każdej z wartości pikseli przez odpowiedni współczynnik interpolacji i następnie sumowanie ich. W ten sposób, skalowanie bilinearne pozwala na uzyskanie obrazu o wyższej jakości niż skalowanie prostopadłe, ponieważ interpoluje wartości pikseli między pikselami oryginalnego obrazu, zamiast przydzielać nowym pikselom wartości pikseli z oryginalnego obrazu.
Załóżmy, że chcemy obliczyć wartość piksela \(P(x,y)\) w obrazie docelowym. Niech \(Q_{11}, Q_{12}, Q_{21}, i Q_{22}\) będą wartościami czterech najbliższych pikseli w obrazie źródłowym, położonymi odpowiednio w lewym górnym, prawym górnym, lewym dolnym i prawym dolnym rogu kwadratu, w którym znajduje się punkt \(P\). Wówczas wartość \(P\) można obliczyć jako:
\[ P(x,y) = \frac{1}{(x_2-x_1)(y_2-y_1)} \left[(x_2-x)(y_2-y)Q_{11} + (x-x_1)(y_2-y)Q_{21} + (x_2-x)(y-y_1)Q_{12} + (x-x_1)(y-y_1)Q_{22}\right] \]
gdzie \(x_1, x_2, y_1, y_2\) to odpowiednio współrzędne poziome i pionowe granic kwadratu zawierającego punkt \(P\).
Skalowanie bikubiczne jest bardziej zaawansowaną metodą skalowania obrazów niż skalowanie bilinearne. Podobnie jak skalowanie bilinearne, skalowanie bikubiczne również polega na interpolacji między pikselami, jednak jest bardziej złożone i dokładniejsze. W skalowaniu bikubicznym, dla każdego nowego piksela, algorytm określa współrzędne na oryginalnym obrazie, a następnie interpoluje wartości pikseli z szesnastu najbliższych sąsiednich pikseli w oryginalnym obrazie, zamiast czterech jak w skalowaniu bilinearnym. Interpolacja ta jest wykonywana za pomocą funkcji bikubicznej, która jest bardziej złożona niż funkcja liniowa używana w skalowaniu bilinearnym. Wartość każdego nowego piksela jest obliczana przez wielokrotne mnożenie wartości pikseli z oryginalnego obrazu przez odpowiednie współczynniki interpolacji i sumowanie ich. Dzięki zastosowaniu bardziej złożonej funkcji interpolacji i większej liczby pikseli z oryginalnego obrazu, skalowanie bikubiczne pozwala na uzyskanie jeszcze lepszej jakości obrazu po skalowaniu, niż skalowanie bilinearne.
Wartość piksela \(P(x,y)\) w interpolacji bikubicznej obliczana jest za pomocą wzoru:
\[ P(x,y) = \sum_{i=-1}^{2} \sum_{j=-1}^{2} a_{ij} \cdot (x-x_1)^i \cdot (y-y_1)^j \]
gdzie \(a_{ij}\) są współczynnikami uzyskanymi z warunków brzegowych i charakterystyk interpolowanych punktów, a \(x_1\) i \(y_1\) są współrzędnymi punktu w obrazie źródłowym, najbliższego lewemu górnemu rogowi obszaru interpolacji.
Obie metody mają swoje zastosowania w zależności od wymagań dotyczących jakości i wydajności. Interpolacja bilinearna jest często stosowana w przeglądarkach internetowych i aplikacjach graficznych, gdzie szybkość jest kluczowa, natomiast interpolacja bikubiczna znajduje zastosowanie w bardziej zaawansowanych edytorach grafiki, gdzie priorytetem jest jakość obrazu.
Obrót obrazu polega na przekształceniu obrazu przez obrócenie go o określony kąt względem pewnego punktu, zwykle środka obrazu. Ten proces polega na transformacji współrzędnych pikseli na obrazie i może być wykonywany za pomocą różnych algorytmów. Jednym z najczęściej stosowanych algorytmów jest algorytm obrotu o kąt \(\theta\), który polega na przekształceniu każdego piksela \((x, y)\) na \((x', y')\) za pomocą następujących równań:
\[ \begin{align} x' &= x \cos(\theta) - y \sin(\theta)\\ y' &= x \sin(\theta) + y \cos(\theta), \end{align} \]
gdzie \(\theta\) jest kątem obrotu w radianach.
Jeśli obraz jest skalowany podczas obrotu, to mogą pojawić się brakujące lub zbędne piksele w obrazie wynikowym, w związku z tym trzeba zastosować odpowiednie metody interpolacji takie jak skalowanie bilinearne czy bikubiczne.
Obrót obrazu jest przydatny w wielu aplikacjach, takich jak analiza obrazów, przetwarzanie obrazów i uczenie maszynowe, ponieważ pozwala na zmianę orientacji obrazu, co może być konieczne do prawidłowego przetwarzania i analizy.
Przesunięcie obrazu o wektor to proces przemieszczenia obrazu o określoną odległość w pionie i/lub poziomie. W tym procesie, każdy piksel w obrazie jest przesunięty o taki sam wektor \((dx, dy)\) w pionie i poziomie. Wektor przesunięcia może być dodatni lub ujemny, co oznacza przesunięcie obrazu w górę lub w dół oraz w lewo lub w prawo. Przesunięcie obrazu o wektor może być wykonywane za pomocą różnych algorytmów, ale jednym z najprostszych jest przekształcenie każdego piksela \((x, y)\) na \((x+dx, y+dy)\).
Jeśli przesunięcie prowadzi do utraty części obrazu, to trzeba zastosować odpowiednie metody interpolacji, takie jak skalowanie bilinearne lub bikubiczne, aby uzupełnić brakujące piksele. Przesunięcia są również ważne w kontekście uczenia maszynowego.
Przekształcenie afiniczne (afiniczne transformacje obrazów) to rodzaj przekształcenia obrazu, które polega na zmianie rozmiaru, kształtu i położenia obrazu za pomocą macierzy afinicznej. Macierz afiniczna jest to macierz 3x3, która zawiera informacje o skali, obrocie, translacji i odkształceniu obrazu.
Przekształcenie afiniczne pozwala na wykonywanie operacji takich jak:
Przekształcenie afiniczne jest często używane w przetwarzaniu obrazów do uzyskania lepszego dopasowania obrazów do siebie, np. w celu wykonania mapowania międzyobrazowego lub w celu korygowania perspektywy obrazów.
Progowanie obrazu polega na zmianie poziomów jasności pikseli w obrazie na podstawie określonego progu. Próg jest wartością graniczną, po której piksele o niższej jasności są zmieniane na czarne, a piksele o jasności powyżej progu są zmieniane na białe. W ten sposób obraz jest konwertowany na obraz czarno-biały, co może ułatwić dalsze operacje na nim, takie jak analiza obrazu.
Filtracja obrazów to proces przetwarzania obrazów polegający na przepuszczeniu obrazu przez filtr, który modyfikuje wartości pikseli. Cel filtracji może być różny, np. usunięcie szumów, wzmocnienie krawędzi, rozmycie obrazu.
Istnieje wiele różnych rodzajów filtrów. Wśród nich można wymienić:
Filtracja jest ważnym krokiem w przetwarzaniu obrazów, ponieważ pozwala na usunięcie niepożądanych cech obrazu lub wzmocnienie istotnych cech obrazu, co ułatwia dalsze analizy.
Translacja (przesunięcie) i rotacja nie zmieniają rozmiarów i orientacji obrazu, natomiast skalowanie zmienia rozmiar obrazu ale zachowuje orientację i kąty. Przekształcenia afiniczne zachowują jedynie równoległość par prostych. Projekcje są przekształceniami polegającymi na zmianie perspektywy patrzenia na obraz dlatego jedyną własnością, która jest zachowana w transformowanym obrazie to linie proste.
library(magick)
frink <- image_read("https://jeroen.github.io/images/frink.png")
frink
image_scale(frink, "250%x250%")
image_scale(frink, "300x100!")
image_rotate(frink, 90)
Obroty o inne kąty niż pełne wielokrotności 90\(\degree\) powodują pewne problemy, ponieważ po rotacji powstają piksele, które nie pokrywają żadnej wartości z oryginalnego obrazu, a część z nich zawiera kilka wartości (Rysunek 4.1). Podobne zjawisko może powstać w sytuacji zmiany rozmiaru obrazów. W tych sytuacjach konieczna jest interpolacja pikseli zarówno “pustych”, jak i “wypełnionych”.
library(imager)
library(keras)
data <- dataset_mnist()
im <- t(data$train$x[2023,,])
im <- as.cimg(im)
im_list <- map_il(0:6, ~imrotate(im, # Nearest Neighbour interp.
angle = 15*.x,
interpolation = 0))
row1 <- imappend(im_list[1:4], axis = "x")
row2 <- imappend(im_list[5:7], axis = "x")
imappend(list(row1,row2), "y") |>
plot(interp=F) # antialiasing off
im_list <- map_il(0:6, ~imrotate(im, # linear interpolation
angle = 15*.x,
interpolation = 1))
row1 <- imappend(im_list[1:4], axis = "x")
row2 <- imappend(im_list[5:7], axis = "x")
imappend(list(row1,row2), "y") |>
plot(interp=F)
im_list <- map_il(0:6, ~imrotate(im, # cubic interpolation
angle = 15*.x,
interpolation = 2))
row1 <- imappend(im_list[1:4], axis = "x")
row2 <- imappend(im_list[5:7], axis = "x")
imappend(list(row1,row2), "y") |>
plot(interp=F)
Jak widać z powyższych wykresów rotacja wpływa na jakość obrazu, dlatego zaleca się nie stosować kilku występujących po sobie rotacji o kąty niebędące wielokrotnościami \(90\degree\).
W przypadku gdy zmieniamy rozmiar obrazu w proporcji 0.5, 2, 3 funkcja imresize
korzysta z algorytmu opisanego na stronie http://www.scale2x.it/algorithm.html.
W innych przypadkach wykorzystuje jedną z 7 opcji interpolacji:
boundary_conditions
(opcja 0);