Skip navigation

Szűrés frekvenciatérben

Aluláteresztő ideális szűrő a frekvenciatérben

13_03_a_dft_ideal_lpf.py példaprogram

Az aluláteresztő szűrés azt jelenti, hogy a képet frekvenciatérbe alakítva, ott az alacsony frekvenciákért felelős komponenseket tartjuk meg, a magasakat kinullázzuk. Visszatérve a képtérbe egy simító hatást láthatunk. Az ideális szó jelentése arra vonatkozik, hogy egy komponenst vagy teljesen megtartunk, vagy kinullázuk, a kettő határán az átmenet éles.

Importálás.

import cv2
import numpy as np

Kör alakú szűrő sugara.

filter_radius = 40

Frekvenciakép megjelenítése.

def display_freq_log_magnitude(dft_in, wnd_title='Frequency magnitude'):
    # Displaying log normalized magnitude of the frequency spectrum
    spectrum_magnitude = np.log(1.0 + cv2.magnitude(dft_in[:, :, 0], dft_in[:, :, 1]))
    magnitude_norm = cv2.normalize(spectrum_magnitude, None, 0, 1, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_32FC1)
    cv2.imshow(wnd_title, magnitude_norm)

Segédfüggvények a DFT síknegyedek cseréjére.

def do_fft_shift(dft_in):
    dft_np_shift = np.fft.fftshift(dft_in)
    return cv2.merge([dft_np_shift[:, :, 1], dft_np_shift[:, :, 0]])

 

def do_ifft_shift(dft_in):
    dft_np_shift = np.fft.ifftshift(dft_in)
    return cv2.merge([dft_np_shift[:, :, 1], dft_np_shift[:, :, 0]])

Bementi kép beolvasása és méretének feljegyzése.

img = cv2.imread('GolyoAlszik_rs.jpg', cv2.IMREAD_GRAYSCALE)
orig_width = img_in.shape[1]
orig_height = img_in.shape[0]

Az optimális képméret számítása és alkalmazása, lebegőpontos képre áttérés.

print('Proposed padding of the original image sizes:')
padded_width = cv2.getOptimalDFTSize(img_in.shape[1])
padded_height = cv2.getOptimalDFTSize(img_in.shape[0])
print('Width:', img_in.shape[1], '->', cv2.getOptimalDFTSize(img_in.shape[1]))
print('Height:', img_in.shape[0], '->', cv2.getOptimalDFTSize(img_in.shape[0]))
img = np.zeros((padded_height, padded_width), np.uint8)
img[:img_in.shape[0], :img_in.shape[1]] = img_in

img_float = np.float32(img) / 255
cv2.imshow('img_float', img_float)

Diszkrét Fourier transzformáció végrehajtása. Az eredmény komplex számokból álló (kétcsatornás) kép lesz! A képnegyedeket átrendezzük, hogy a bal felső sarok a kép középpontjába kerüljön.

dft = cv2.dft(imgflt, flags = cv2.DFT_COMPLEX_OUTPUT)
dft_shift = do_fft_shift(dft)

Megjelenítjük a Fourier eredményt. Komplex számokból kiszámítjuk a magnitúdójukat. Mivel a magnitúdókban nagyon nagy eltérések mutatkoznak, a jobb megjelenítés miatt célszerű logaritmikus skálán tenni ezt.

display_freq_log_magnitude(dft_shift, 'dft log magnitude')

Az ideális aluláteresztő szűrő egy kör a kép középpontjában, 1.0 értékekkel a valós és képzetes síkon egyaránt. Először a kört rajzoljuk meg, majd ebből állítjuk elő a szűrőt.

circle_img = np.ndarray(img.shape, np.float32)
circle_img[:, :] = 0
cv2.circle(circle_img, (int(circle_img.shape[1] / 2), int(circle_img.shape[0] / 2)), filter_radius, 1, -1)


# Ideal filter should be complex
ideal_filter = np.ndarray(dft.shape, np.float32)

ideal_filter[:, :, 0] = circle_img
ideal_filter[:, :, 1] = 0

cv2.imshow('circle', circle_img)

A szűrés végrehajtását a Fourier transzformált kép és a szűrő elemenkénti szorzásával állíthatjuk elő. Ezt végzi el az OpenCV mulSpectrums() függvénye.

filtered = cv2.mulSpectrums(dft_shift, ideal_filter, 0)

A megjelenítést az előzővel megegyezően a logaritmikus normalizált magnitúdóval végezzük.

display_freq_log_magnitude(filtered, 'filtered log magnitude')
cv2.waitKey(0)

Frekvenciatérből az inverz Fourier transzformációval térünk vissza a képtérbe. Előzetesen szükséges a képnegyedek visszarendezése, hogy az origó a bal felső sarokban legyen újra. Ennek az eredménye is komplex lesz! Ebből a valós rész jelenti a szűrt eredmény képet, ennek a normalizált változatát jelenítjük meg

filtered_shift = do_ifft_shift(filtered)
idft_cplx = cv2.idft(filtered_shift, flags=cv2.DFT_SCALE)

print(idft_cplx.shape)

# Take the real part of the result
idft = idft_cplx[:, :, 0]
filtered_img = np.uint8(np.clip(idft, 0, 1) * 255)[:orig_height, :orig_width]

cv2.imshow('idft', magnitude_norm)

cv2.waitKey(0)

Interaktív alul- és felüláteresztő szűrő

13_03_b_dft_ideal_lpf_interactive.py példaprogramban csúszkák segítségével választhatunk szűrő méretet, illetve hogy alul- vagy felüláteresztő szűrőt szeretnénk. A változást rögtön láthatjuk az eredményképen.

Aluláteresztő Butterworth szűrő

Az ideális szűrő esetén a simító hatás mellett az eredeti élekkel párhuzamosan gyűrűsödést figyelhetünk meg. Ezt a hatást csökkenthetjük, ha a szűrő átmenet nem éles, hanem fokozatosan csökken a komponensek súlya. Ilyen szűrők a Gauss és a Butterworth.

Egy egyszerű kör rajzolás helyett például az alábbi függvénnyel állíthatunk elő D sugarú, n-ed rendű aluláteresztő Butterworth szűrőt.

# create a 2-channel butterworth low-pass filter with radius D, order n
# (assumes pre-allocated filter_img array)
def create_butterworth_lowpass_filter(filter_img, D, n):

center_x = filter_img.shape[1] / 2
center_y = filter_img.shape[0] / 2
for y in range(0, filter_img.shape[0]):

for x in range(0, filter_img.shape[1]):
radius = math.sqrt(math.pow(x - center_x, 2) + math.pow(y - center_y, 2))
filter_img[y, x] = 1.0 / (1 + math.pow(radius / D, (2 * n)))

A filter_img eredményt egy kétcsatornás komplex kép mindkét csatornájára másoljuk be.

A teljes példát a 13_4_dft_butterworth_lpf.py példaprogram mutat.