Istnieje wiele możliwości celowego obniżenia jakości obrazów. W ramach tego materiału mamy do wyboru kilka rodzajów modyfikacji:

  • Kompresja stratna JPEG,
  • Rozmywanie obrazu,
  • Zaszumienie obrazu.

Jak modyfikować obrazy

Wszystkie modyfikacje będziemy nanosić przy wykorzystaniu biblioteki OpenCV wewnątrz skryptu w Pythonie. Starać się tak dobierać parametry, aby według was różnice były według was widoczne, ale nie oczywiste. Czyli testowany powinien być w stanie rozróżniać obrazy, ale nie tak, żeby wszystkie były albo maksymalnie zniekształcone, albo nieróżniące się od oryginału. Wszystkie zmodyfikowane obrazy można zapisywać do plików bezstratnych np. .png, aby je potem wczytywać podczas dalszych badań.

Kompresja JPEG

Efekt kompresji JPEG możemy osiągnąć za pomocą kodowania wewnątrz pamięci, co umożliwia zarówno zapisanie, jak i wyświetlenie zmodyfikowanego obrazu. Parametrem, jakim możemy sterować jakością naszej kompresji, jest jakość kompresji JPEG. Jest ona wartością od \(0-100\) (\(%\)). Domyślna wartość to \(95\), więc najlepiej testować zakres jakości w przedziale \(5-80\) (najlepiej nawet \(5-65\)), bo jak sami wiecie z zajęć z JPGE nawet przy \(50%\) jakości za bardzo nie widać straty jakości na pierwszy rzut oka.

img=cv2.imread('0000.png')
encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 50]
result, encimg = cv2.imencode('.jpg', img, encode_param)
decimg = cv2.imdecode(encimg, 1)

fig, axs = plt.subplots(1, 2 , sharey=True   )
axs[0].imshow(img)
axs[1].imshow(decimg)

cv2.imwrite('0000_1.png',decimg)

Rozmywanie obrazu

Jeżeli chodzi o rozmywanie obrazu mamy do dyspozycji różne zestawy funkcji i rodzajów rozmyć. Każde z nich dysponuje innymi parametrami. Mamy do dyspozycji między innymi:

  • Filtrację uśredniającą (cv2.blur) - jako parametr przyjmuje rozmiar okna filtru uśredniającego,
  • rozmycie gaussowskie (cv2.GaussianBlur) - dwa parametry rozmiar filtru, jak również parametr sigma dla osi X, jak również dla osi Y,
  • filtr medianowy (cv2.medianBlur) - jako parametr podaje się tutaj pojedynczą wartość jako rozmiar maski filtru,
  • filtr bilateralny (cv2.bilateralFilter) - tutaj mamy więcej parametrów rozmiar oraz dwie sigma (jak w filtrze Gaussa) dla Koloru i przestrzeni.

Tutaj można zarówno badać różnice pomiędzy różnymi filtrami, jak i również można wybrać jeden z filtrów i przetestować go dla różnych parametrów. Więcej informacji na temat filtrów można znaleźć w tutorialu i dokumentacji

kernal_size = (5,5) # rozmiar rdzenia obliczeń 
blur = cv2.blur(img,kernal_size) # tu sterujecie tylko rozmiarem rdzenia
blur = cv2.GaussianBlur(img,kernal_size,sigmaX,SigmaY) # Tu można streować zrówno rozmiarem rdzenia jak i sigmami w obu wymiarach 
median = cv2.medianBlur(img,5) # tu można sterować rozmiarem ale tylko w ograniczonym zakresie 
blur = cv2.bilateralFilter(img,5,sigmaColor,sigmaSpace) # tu mozna sterować zarówno rozamiarem jak i sigmami dla koloru i przestrzeni 

Zaszumienie obrazu

Dodawanie szumu do obrazu polega na odchylaniu wartości pikseli obrazu, zaburzając go w sposób losowy. Możemy generować szum za pomocą różnych generatorów liczb losowych. Dwie informacje w ramach przypomnienia: Pamiętajcie, że nie wszystkie generatory dają wartości wycentrowane na 0, dlatego czasem trzeba je wyśrodkować. Drugie przypomnienie to fakt, że jeżeli przekroczymy wartości skrajne uint8, to wartości będą zastępowane w sposób cykliczny, czyli \(255+5=0+5\) oraz \(0-55=255-55\), co może być niepożądanym efektem. Tutaj również można porównywać kilka różnych metod albo sprawdzić jedną metodę dla różnych parametrów. Stopień zaszumienia możemy regulować za pomocą jakiegoś parametru alpha.

alpha=0.5 # 0-1

sigma = 25 # tym można sterować
gauss = np.random.normal(0,sigma,(img.shape))
noisy1 = (img + alpha * gauss).clip(0,255).astype(np.uint8)


beta=0.01    # małe wartoci                    
vals = len(np.unique(img))
vals =  beta * 2 ** np.ceil(np.log2(vals))
noisy2 = (np.random.poisson(img * vals) / float(vals)).clip(0,255).astype(np.uint8)


Noise_range=[-25,25] #zakres szumu 
rand = (Noise_range[1]-Noise_range[0])*np.random.random((img.shape))+Noise_range[0]
noisy3 = (img + alpha * rand).clip(0,255).astype(np.uint8)

Inne generatory liczb losowych, które mogą wam się przydać (doczytać w dokumentacji):

I na koniec parametryczny generator szumu sól i pieprz:

def noise_SnP(img,S=255,P=0,rnd=(333,9999)):
    r , c = img.shape
    number_of_pixels = random.randint(rnd[0], rnd[1])
    for i in range(number_of_pixels):
        y=random.randint(0, r - 1)
        x=random.randint(0, c - 1)
        img[y][x] = S
    number_of_pixels = random.randint(rnd[0], rnd[1])
    for i in range(number_of_pixels):
        y=random.randint(0, r - 1)
        x=random.randint(0, c - 1)
        img[y][x] = P
    return img