Skip navigation

Mesterséges zajterhelés

Additív zaj egycsatornás szürkeárnyalatos képhez adása OpenCV-ben

A zajt önálló képként állítjuk elő, képpontonként megfelelő eloszlású és paraméterű véletlelen számként.

Nagyon figyeljünk arra, hogy a zaj kép esetén pozitív és negatív értékek is előfordulhatnak! Ehhez megfelelő előjeles egész, vagy lebegőpontos típust kell választanunk! További probléma, hogy a véletlenszám-generáló függvények nem veszik figyelembe a csatornák számát, ha több is van, akkor is csak a legelsőt töltik ki! Emiatt eleve célszerű lehet a zajkép mátrix méretét egycsatornásként létrehozni. Például:

noise = np.zeros(img.shape[:2], np.int16)

Egyenletes eloszlású zaj képet a cv2.randu() függvénnyel készíthetünk, megadott minimum és maximum értékek között (a példában [-10, 10]). Első paraméterként át kell adni egy már létező képmátrixot.

cv2.randu(noise, -10.0, 10.0)

Normál eloszlású zajt pedig a cv2.randn() függvénnyel, várható érték és szórás megadásával. A várható érték rendszerint 0.0, a szórás értékkel tudjuk szabályozni a zajterhelés erősségét. Itt is szükséges a képmátrix előzetes létrehozása.

cv2.randn(noise, 0.0, 20.0)

Additív zajt a képhez a mátrixok összeadásával adhatunk. Itt is fontos az eredmény típusának pontos megválasztása!

imnoise1 = cv2.add(img, noise, dtype=cv2.CV_8UC1)

vagy

imnoise2 = cv2.add(img, noise, dtype=cv2.CV_16SC1)

Fontos!

Lényeges különbség van az imnoise1 és az imnoise2 képmátrixok között! Az első esetben az eredmény típusa eleve 8 bites, előjel nélküli egész, így a nullánál kisebb, és a 255-nél nagyobb intenzitásértékek csonkolódnak 0 és 255 értékekre. Az imnoise2 típusa 16 bites előjeles egész, vagyis egy [0, 255] intenzitástartományú kép esetén a zajterheléssel negatív, valamint 255-nél nagyobb intenzitásértékek is ki fognak alakulni.

Melyik megközelítést válasszuk?

Gondoljuk át, az adott probléma esetén melyik közelíti jobban a valós zajterhelést?

Egyszerűbb a munka, és a valóságot is jól közelíti az első megközelítés: a mért, folytonos szenzor értékek [0, 255] értékre kvantálódnak, a mérési tartományon kívül értéket nem is biztos, hogy a szenzor egyáltalán tudna adni. Eleve jogos elvárás lehet, hogy a tökéletesen mért, zajjal nem terhelt képpontok értéke ne változzon, ami így biztosított.

Ha valamiért mégis a 16 bites előjeles vagy lebegőpontos eredményt választjuk, akkor ezekből a reprezentációkból célszerű visszatérni a 8 bitesre. Ezt megtehetjük normalizálva és csonkolva.

A normalizálás esetén a képmátrix minimális eleme az általunk megadott minimális értékbe, a mátrix maximális eleme a megadott maximálisba képződik, közöttük pedig a beosztás lineáris. Ezzel találkoztunk már korábban is.

imnoise2 = cv2.add(img, noise, dtype=cv2.CV_16SC1)
imnoisenorm = cv2.normalize(imnoise2, None, 0, 255, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8UC1)

Csonkolás esetén az ábrázolható tartományon kívüli értékek az ábrázolható minimális és maximális értékkel helyettesítődnek. Ez megoldható a típuskonverzió előtt végrehajtott vágás művelettel, ahogyan korábban láttuk. Így az első megközelítéssel egyező eredményt kapunk.

06_01_a_add_noise.py

import cv2
import numpy as np

img = cv2.imread('OpenCV-logo.png', cv2.IMREAD_GRAYSCALE)

noise = np.zeros(img.shape[:2], np.int16)
cv2.randn(noise, 0.0, 20.0)

imnoise1 = cv2.add(img, noise, dtype=cv2.CV_8UC1)
cv2.imshow('add', imnoise1)

imnoise2 = cv2.add(img, noise, dtype=cv2.CV_16SC1)
imnoisenorm = cv2.normalize(imnoise2, None, 0, 255, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8UC1)
cv2.imshow('norm', imnoisenorm)

cv2.waitKey(0)

cv2.destroyAllWindows()

A 06_01_b_add_noise_tb.py példaprogram változat csúszkával módosítható erősségű additív zajt ad a képhez.

Additív zaj eredmények

Az OpenCV-logo.png kép szürkeárnyalatos változatához adunk normál eloszlású additív zajt, 0 várható értékkel és 20 szórással.

Az első megközelítést célszerű választani, mert az nem módosít a zajjal nem terhelt képpontok fényességén, és eleve ezt a legegyszerűbb használni.

A második egyértelműen rossz, mert a zaj modellezés hatására kialakuló negatív értékek normalizáláskori skála módosító hatása miatt sötétebb tónusú képet kapunk.

Összeadás, 8 bites egész típusú eredményként Előjeles eredmény normalizálva

Megközelítések színes képek esetén

Színes, többcsatornás képek esetén többféle megközelítés is szóba jöhet.

Az egyik lehetőség, hogy mindhárom színcsatornához külön additív zajt adunk. Vigyázzunk arra, hogy a cv2.randu() és cv2.randn() függvények többcsatornás képmátrix átadása esetén is csak a [0] indexűt töltik ki, a többi konstans 0 marad! Vagyis három különböző, egycsatornás zajképet kell generálnunk, amiket csatornánként külön kell hozzáadnunk a képhez. Az igazi probléma ezzel a megközelítéssel az, hogy a képalkotó szenzorok nem külön RGB csatornák szerint mérnek, így a valóságot ezzel nem túl jól modellezzük.

A zajhatás modellezését elvégezhetjük a fényerő csatornán, a kromatikus információt megtartva. Ehhez a korábban látott módon YUV színtérbe kell áttérni, ott az Y csatornához adni a zajterhelést, és visszatérni BGR színtérbe. Ez sem tökéletes, mert valós esetben valószínűleg a szín információk is változnak a mérési pontatlanság hatására.

Választhatunk más színteret is, ahol a színértékek közötti Euklideszi távolság jobban közelíti az emberi színérzetet (pl. HSV), és ott végezzük a módosítást csatornánként. Az RGB ebben nem igazán jó.

Természetesen a zaj szűrésekor is ismernünk kell a zaj pontos megjelenési módját, és annak megfelelően dolgozni.

Ahogyan a fejezet bevezetőjében láttuk, a valós helyzet jobb közelítéséhez az lenne szükséges, hogy ismerjük a képalkotó szenzor pontosabb működését.

Só-bors zaj képhez adása OpenCV-ben

Külön függvény erre a célra nincs, de könnyen készíthetünk sajátot. Erre példát a 06_02_add_saltpepper_noise.py példaprogram mutat.

Só-bors zaj esetén a zaj paraméterét rendszerint úgy adjunk meg, hogy az intenzitásértékek hány százaléka legyen só, illetve bors típusú. A programunk a kiszámított darabszámszor véletlenszerűen pozíciót választ a képen, és oda beírja a zaj értéket. Mivel az nem kerül ellenőrzésre, hogy egy zaj helyre ne eshessen újabb, így a megoldás nem tökéletes, de első közelítésben megfelelő lesz a számunkra.

Ebben az esetben jóval egyszerűbb a színes képek kezelése: a kiválasztott képpontokban a megfelelő (fekete vagy fehér) szín írható a képmátrixba.

06_02_add_saltpepper_noise.py

import cv2
import numpy as np


def add_point_noise(img_in, percentage, value):
noise = np.copy(img_in)
n = int(img_in.shape[0] * img_in.shape[1] * percentage)
print(n)

for k in range(1, n):
i = np.random.randint(0, img_in.shape[1])
j = np.random.randint(0, img_in.shape[0])

if img_in.ndim == 2:
noise[j, i] = value

if img_in.ndim == 3:
noise[j, i] = [value, value, value]

return noise


def add_salt_and_pepper_noise(img_in, percentage1, percentage2):
n = add_point_noise(img_in, percentage1, 255) # Só
n2 = add_point_noise(n, percentage2, 0) # Bors

return n2



img = cv2.imread('OpenCV-logo.png', cv2.IMREAD_GRAYSCALE)
# img = cv2.imread('OpenCV-logo.png', cv2.IMREAD_COLOR)
noise = add_salt_and_pepper_noise(img, 0.01, 0.01)

cv2.imshow('SaltNPepper', noise)
cv2.waitKey(0)

cv2.destroyAllWindows()

Feladat

Teszteljük többféle, színes és szürkeárnyalatos képeken is a zajterheléseket!

Egészítsük ki a példaprogramokat úgy, hogy a zajterhelés mértékét csúszka segítségével interaktívan változtathassuk!