程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Python big white opencv learning lesson from scratch -8 Frequency domain image filtering (Part 1)

編輯:Python

Finish today :【OpenCV routine 100 piece 】100. Adaptive local noise reduction filter
Celebrate Jiageng : Python Big white starts from scratch OpenCV Learning lessons -8. Frequency domain image filtering ( On )

Welcome to your attention 『OpenCV routine 200 piece 100 piece 』 series , Ongoing update
Welcome to your attention 『Python Small white OpenCV Learning lessons 』 series , Ongoing update


Python Big white starts from scratch OpenCV Learning lessons -8. Frequency domain image filtering ( On )

This series is for Python The small white , Start from scratch OpenCV Project practice .
Image filtering is to suppress the noise of the target image while preserving the detailed features of the image as much as possible , It is a common image preprocessing operation .
In frequency domain image processing, the image is first Fourier transformed , Then it is processed in the transform domain , Finally, the inverse Fourier transform is carried out and converted back to the spatial domain .
Frequency domain filtering is the multiplication of the filter transfer function and the corresponding pixel of the Fourier transform of the input image . The concept of filtering in frequency domain is more intuitive , Filter design is also easier . Using fast Fourier transform algorithm , Much faster than spatial convolution .
This paper provides the complete routines and running results of the above algorithms , An application case is used to demonstrate the comprehensive use of a variety of image enhancement methods .



1. The Fourier transform

Filtering usually refers to filtering or suppressing the components of a specific frequency in the image . Image filtering is to suppress the noise of the target image while preserving the detailed features of the image as much as possible , It is a common image preprocessing operation .

Image filtering can be carried out not only in spatial domain, but also in frequency domain . Spatial filtering is the combination of image and various spatial filters ( Templates 、 nucleus ) Convolution of , The Fourier transform of spatial convolution is the product of the corresponding transform in the frequency domain , Therefore, frequency domain filtering is a frequency domain filter ( Transfer function ) Multiplied by the Fourier transform of the image .

Frequency domain image filtering , First, Fourier transform the image , Then it is processed in the transform domain , Finally, the inverse Fourier transform is carried out and converted back to the spatial domain .

The spatial domain filter and the frequency domain filter form a Fourier transform pair :
f ( x , y ) ⊗ h ( x , y ) ⇔ F ( u , v ) H ( u , v ) f ( x , y ) h ( x , y ) ⇔ F ( u , v ) ⊗ H ( u , v ) f(x,y) \otimes h(x,y) \Leftrightarrow F(u,v)H(u,v) \\f(x,y) h(x,y) \Leftrightarrow F(u,v) \otimes H(u,v) f(x,y)⊗h(x,y)⇔F(u,v)H(u,v)f(x,y)h(x,y)⇔F(u,v)⊗H(u,v)
in other words , Spatial domain filter and frequency domain filter are actually corresponding to each other , It is more convenient for some spatial domain filters to be realized by Fourier transform in frequency domain 、 Faster . for example , Laplace filter in space domain is high pass filter in frequency domain .


1.1 Fourier series and Fourier transform

Fourier series (Fourier series) In number theory 、 Combinatorial mathematics 、 signal processing 、 probability theory 、 statistical 、 cryptography 、 acoustics 、 Optics and other fields have a wide range of applications .

The Fourier series formula indicates that , Any periodic function can be expressed as a sine function of different frequencies and / Or the weighted sum of cosine functions :
f ( t ) = A 0 + ∑ n = 1 ∞ A n s i n ( n ω t + ψ n ) = A 0 + ∑ n = 1 ∞ [ a n c o s ( n ω t ) + b n s i n ( n ω t ) ] \begin{aligned} f(t) &= A_0 + \sum^{\infty}_{n=1} A_n sin(n \omega t + \psi _n)\\ &= A_0 + \sum^{\infty}_{n=1} [a_n cos(n \omega t) + b_n sin(n \omega t)] \end{aligned} f(t)​=A0​+n=1∑∞​An​sin(nωt+ψn​)=A0​+n=1∑∞​[an​cos(nωt)+bn​sin(nωt)]​
This sum is Fourier series .

Further , Any aperiodic function can also be expressed as sinusoidal functions of different frequencies and / Or the integral of the cosine function multiplied by the weighting function :
F ( ω ) = ∫ − ∞ + ∞ f ( t ) e − j ω t d t f ( t ) = 1 2 π ∫ − ∞ + ∞ F ( ω ) e j ω t d ω \begin{aligned} F(\omega) &= \int_{-\infty}^{+\infty} f(t) e^{-j\omega t} dt\\ f(t) &= \frac{1}{2 \pi} \int_{-\infty}^{+\infty} F(\omega) e^{j\omega t} d \omega \end{aligned} F(ω)f(t)​=∫−∞+∞​f(t)e−jωtdt=2π1​∫−∞+∞​F(ω)ejωtdω​
This formula is Fourier transform (Fourier transform ) And inverse transformation .

* The sufficient condition for the existence of Fourier transform is :f(t) The integral of the absolute value of is finite , In signal processing 、 In the field of image processing, this condition can be met .


routine 8.1: Continuous Fourier series of periodic signals

 # 8.1: Continuous Fourier series of periodic signals 
from scipy import integrate
nf = 30
T = 10
tao = 1.0
y = 1
k = np.arange(0, nf)
an = np.zeros(nf)
bn = np.zeros(nf)
amp = np.zeros(nf)
pha = np.zeros(nf)
half0, err0 = integrate.quad(lambda t: y, -tao/2, tao/2)
an[0] = 2 * half0 / T
for n in range(1, nf):
half1, err1 = integrate.quad(lambda t: 2*y * np.cos(2.0/T * np.pi * n * t), -tao/2, tao/2)
an[n] = half1 / 10
half2, err2 = integrate.quad(lambda t: 2*y * np.sin(2.0/T * np.pi * n * t), -tao/2, tao/2)
bn[n] = half2 / 10
amp[n] = np.sqrt(an[n]**2 + bn[n]**2)
for i in range(0, nf):
pha[i] = 0.0 if an[i]>=0 else np.pi
plt.figure(figsize=(9, 6))
plt.subplot(211), plt.title("Amplitude spectrum"), plt.stem(k, amp)
plt.subplot(212), plt.title("Phase spectrum"), plt.stem(k, pha)
plt.show()


routine 8.2: Discontinuous Fourier coefficients

 # 8.2: Discontinuous Fourier coefficients 
dx = 0.001
x = np.pi * np.arange(-1+dx, 1+dx, dx)
n = len(x)
nquart = int(np.floor(n/4))
f = np.zeros_like(x)
f[nquart: 2*nquart] = (4/n) * np.arange(1, nquart+1)
f[2*nquart: 3*nquart] = np.ones(nquart) - (4/n) * np.arange(0, nquart)
plt.figure(figsize=(9, 6))
plt.title("Fourier series of hat function")
plt.plot(x, f, '-', color='k',label="Original")
# Compute Fourier series
A0 = np.sum(f * np.ones_like(x)) * dx
fFS = A0 / 2
orders = 3
A = np.zeros(orders)
B = np.zeros(orders)
for k in range(orders):
A[k] = np.sum(f * np.cos(np.pi * (k+1) * x / np.pi)) * dx # Inner product
B[k] = np.sum(f * np.sin(np.pi * (k+1) * x / np.pi)) * dx
fFS = fFS + A[k] * np.cos((k + 1) * np.pi * x / np.pi) + B[k] * np.sin((k + 1) * np.pi * x / np.pi)
plt.plot(x, fFS, '-', label="{} order".format(k))
print('Line ', k, ': A = ', A[k], ' B = ', B[k], fFS.shape)
plt.legend(loc="upper right")
plt.show()


routine 8.3: Fourier transform of one-dimensional continuous function ( Box functions )

Take one-dimensional box function as an example , Its Fourier transform is :

F ( ω ) = ∫ − ∞ + ∞ f ( t ) e − j ω t d t = ∫ − π π f ( t ) e − j ω t d t = s i n ( ω ) ω \begin{aligned} F(\omega) &= \int_{-\infty}^{+\infty} f(t) e^{-j\omega t} dt\\ &= \int_{-\pi}^{\pi} f(t) e^{-j\omega t} dt\\ &= \frac{sin(\omega)}{\omega} \end{aligned} F(ω)​=∫−∞+∞​f(t)e−jωtdt=∫−ππ​f(t)e−jωtdt=ωsin(ω)​​

 # 8.3: Fourier transform of one-dimensional continuous function ( Box functions )
# Box functions (Box_function)
x = np.arange(-5, 5, 0.1)
w = 2 * np.pi
halfW = w / 2
y = np.where(x, x > -halfW, 0)
y = np.where(x < halfW, y, 0)
plt.figure(figsize=(9, 4))
plt.subplot(131), plt.title("Box_function")
plt.plot(x, y, '-b')
plt.subplot(132), plt.title("Fourier transform")
fu = np.sinc(x)
plt.plot(x, fu, '-b')
plt.subplot(133), plt.title("Spectrum of box_func")
fu = abs(fu)
plt.plot(x, fu, '-b')
plt.show()


1.2 Sampling of continuous functions

Continuous functions must be sampled and quantized into discrete functions , Can be processed by computer .

Consider a continuous function f ( t ) f(t) f(t), In terms of independent variables t Evenly spaced Δ T \Delta T ΔT Sample functions , Any sampling value in the sampling sequence is :

f k = ∫ − ∞ + ∞ f ( t ) δ ( t − k Δ T ) d t = f ( k Δ T ) f_k = \int_{-\infty}^{+\infty} f(t) \delta (t-k \Delta T) dt = f(k \Delta T) fk​=∫−∞+∞​f(t)δ(t−kΔT)dt=f(kΔT)

The Fourier transform of the sampled function is :

F ~ ( μ ) = ( F ⋆ S ) ( μ ) = ∫ − ∞ + ∞ F ( τ ) S ( μ − τ ) d τ = 1 Δ T ∑ n = − ∞ ∞ F ( μ − n Δ T ) \begin{aligned} \tilde{F}(\mu) &= (F \star S) (\mu) \\ &= \int_{-\infty}^{+\infty} F(\tau) S(\mu-\tau) d \tau\\ &= \frac{1}{\Delta T} \sum_{n=-\infty}^{\infty}F(\mu-\frac{n}{\Delta T}) \end{aligned} F~(μ)​=(F⋆S)(μ)=∫−∞+∞​F(τ)S(μ−τ)dτ=ΔT1​n=−∞∑∞​F(μ−ΔTn​)​

Shannon (Shannon) The sampling theorem states , For a continuous signal , Use a frequency greater than the maximum frequency of the signal 2 Times the sampling rate , The valid information of the signal will not be lost . Or say , With 1 / Δ T 1/\Delta T 1/ΔT What is the maximum frequency of the sampling rate to the signal sampling point μ m a x = 1 / 2 Δ T \mu_{max}=1/2\Delta T μmax​=1/2ΔT .


routine 8.4: Sampling of continuous functions

 # 8.4: Sampling of continuous functions 
# Defined function , Used to calculate the frequency of all basis vectors 
def gen_freq(N, fs):
k = np.arange(0, np.floor(N/2) + 1, 1)
return (k * fs) / N
T = 100
# Define multiple fundamental frequency signals with different frequencies 
fk = [2/T, 5/T, 12/T] # frequency 
A = [7, 3, 2] # The amplitude 
phi = [np.pi, 2, 2*np.pi] # Initial phase 
n = np.arange(T)
s0 = A[0] * np.sin(2 * np.pi * fk[0] * n + phi[0])
s1 = A[1] * np.sin(2 * np.pi * fk[1] * n + phi[1])
s2 = A[2] * np.sin(2 * np.pi * fk[2] * n + phi[2])
s = s0 + s1 + s2 # Superimpose to generate mixed signal 
g = np.fft.rfft(s) # The Fourier transform 
plt.figure(figsize=(8, 6))
plt.subplot(311)
plt.plot(n, s0, n, s1, n, s2, ':', marker='+', alpha=0.5)
plt.plot(n, s, 'r-', lw=2)
plt.title("Sampling of continuous functions")
plt.subplot(312)
fs = 1 # The sampling interval is 1
freq = gen_freq(T, fs=fs) # Calculate the frequency sequence 
ck = np.abs(g) / T # Calculate the amplitude 
plt.plot(freq, ck, '.') # frequency - Amplitude diagram 
for f in fk:
ck0 = round(ck[np.where(freq==f*fs)][0], 1)
plt.annotate('$({},{})$'.format(f*fs, ck0), xy=(f*fs, ck0), xytext=(5, -10), textcoords='offset points')
plt.subplot(313)
fs = 10 # The sampling interval is 10
freq = gen_freq(T, fs=fs) # Calculate the frequency sequence 
ck = np.abs(g) / T # Calculate the amplitude 
plt.plot(freq, ck, '.') # frequency - Amplitude diagram 
for f in fk:
ck0 = round(ck[np.where(freq==f*fs)][0], 1)
plt.annotate('$({},{})$'.format(f*fs, ck0), xy=(f*fs, ck0), xytext=(5, -10), textcoords='offset points')
plt.show()


1.3 One dimensional discrete Fourier transform

Both digital signal and digital image are discrete variables obtained by sampling .

The data after sampling the transformation of the original function

F ~ ( μ ) = ∫ − ∞ + ∞ f ~ ( t ) e − j 2 π μ t d t = ∫ − ∞ + ∞ ∑ n = − ∞ ∞ f ( t ) δ ( t − n Δ T ) e − j 2 π μ t d t = ∑ n = − ∞ ∞ ∫ − ∞ + ∞ f ( t ) δ ( t − n Δ T ) e − j 2 π μ t d t = ∑ n = − ∞ ∞ f n e − j 2 π μ n Δ T \begin{aligned} \tilde{F}(\mu) &= \int_{-\infty}^{+\infty} \tilde{f}(t) e^{-j 2 \pi \mu t} dt\\ &= \int_{-\infty}^{+\infty} \sum_{n=-\infty}^{\infty} f(t) \delta (t-n{\Delta T}) e^{-j 2 \pi \mu t} dt\\ &= \sum_{n=-\infty}^{\infty} \int_{-\infty}^{+\infty} f(t) \delta (t-n{\Delta T}) e^{-j 2 \pi \mu t} dt\\ &= \sum_{n=-\infty}^{\infty} f_n e^{-j 2 \pi \mu n{\Delta T}}\\ \end{aligned} F~(μ)​=∫−∞+∞​f~​(t)e−j2πμtdt=∫−∞+∞​n=−∞∑∞​f(t)δ(t−nΔT)e−j2πμtdt=n=−∞∑∞​∫−∞+∞​f(t)δ(t−nΔT)e−j2πμtdt=n=−∞∑∞​fn​e−j2πμnΔT​

When the sampling frequency is μ = m / M Δ T \mu = m/M \Delta T μ=m/MΔT when , The discrete Fourier transform can be obtained (DFT) And the inverse transformation formula is :

F m = ∑ n = 0 M − 1 f n e − j   2 π μ m n / M , m = 0 , . . . M − 1 f n = 1 M ∑ m = 0 M − 1 F m e j   2 π μ m n / M , n = 0 , . . . M − 1 \begin{aligned} F_m &= \sum_{n=0}^{M-1} f_n e^{-j\ 2\pi \mu mn/M}, &m=0,...M-1\\ f_n &= \frac{1}{M} \sum_{m=0}^{M-1} F_m e^{j\ 2\pi \mu mn/M}, &n=0,...M-1 \end{aligned} Fm​fn​​=n=0∑M−1​fn​e−j 2πμmn/M,=M1​m=0∑M−1​Fm​ej 2πμmn/M,​m=0,...M−1n=0,...M−1​

Because any periodic or aperiodic function can be expressed as the sum of sine function and cosine function of different frequencies , Therefore, the function characteristics expressed by Fourier transform can be reconstructed by inverse Fourier transform , And you won't lose any information . This is the mathematical basis of frequency domain image processing .

Discrete Fourier transform Is to decompose the discrete signal into a series of discrete trigonometric function components , Each trigonometric function component has a corresponding amplitude A、 frequency f And phase φ \varphi φ. All the original discrete components can be superimposed .

In image processing , Usually use x , y x, y x,y Represents a discrete space coordinate variable , use u , v u,v u,v Represents a discrete variable in the frequency domain . Therefore, the one-dimensional discrete Fourier transform is expressed as :

F ( u ) = ∑ x = 0 M − 1 f ( x ) e − j   2 π u x / M , u = 0 , . . . M − 1 f ( x ) = 1 M ∑ u = 0 M − 1 F ( u ) e j   2 π u x / M , x = 0 , . . . M − 1 \begin{aligned} F(u) &= \sum_{x=0}^{M-1} f(x) e^{-j\ 2\pi u x/M}, &u=0,...M-1\\ f(x) &= \frac{1}{M} \sum_{u=0}^{M-1} F(u) e^{j\ 2\pi u x/M}, &x=0,...M-1 \end{aligned} F(u)f(x)​=x=0∑M−1​f(x)e−j 2πux/M,=M1​u=0∑M−1​F(u)ej 2πux/M,​u=0,...M−1x=0,...M−1​


routine 8.6: One dimensional discrete Fourier transform

 # 8.6: One dimensional discrete Fourier transform 
# Generate square wave signal 
N = 200
t = np.linspace(-10, 10, N)
dt = t[1] - t[0]
sint = np.sin(t)
sig = np.sign(sint)
fig = plt.figure(figsize=(10, 4))
plt.subplot(131), plt.title("source"), plt.xticks([]), plt.yticks([])
plt.plot(t, sig)
# Discrete Fourier transform 
fft = np.fft.fft(sig, N) # Discrete Fourier transform 
fftshift = np.fft.fftshift(fft) # Symmetrical translation 
amp = abs(fftshift) / len(fft) # amplitude 
pha = np.angle(fftshift) # phase 
fre = np.fft.fftshift(np.fft.fftfreq(d=dt, n=N)) # frequency 
plt.subplot(132), plt.title("DFT"), plt.xticks([]), plt.yticks([])
plt.plot(t, fft)
# Signal recovery 
recover = np.zeros(N)
for a, p, f in zip(amp, pha, fre):
sigCos = a * np.cos(2 * np.pi * f * np.arange(N) * dt + p) # According to the amplitude , phase , Frequency construction trigonometric function 
recover += sigCos # Add up all these trigonometric functions 
plt.subplot(133), plt.title("recover"), plt.xticks([]), plt.yticks([])
plt.plot(t, recover)
plt.show()


2. Fourier transform of two-dimensional function

2.1 Two dimensional continuous Fourier transform

set up f ( t , z ) f(t,z) f(t,z) Is a two-dimensional continuous variable t , z t, z t,z The continuous function of , Then the two-dimensional continuous Fourier transform and inverse transform are :

F ( μ , ν ) = ∫ − ∞ + ∞ ∫ − ∞ + ∞ f ( t , z ) e − j 2 π ( μ t + ν z ) d t   d z f ( t , z ) = ∫ − ∞ + ∞ ∫ − ∞ + ∞ F ( μ , ν ) e j 2 π ( μ t + ν z ) d μ   d ν \begin{aligned} F(\mu,\nu) &= \int_{-\infty}^{+\infty} \int_{-\infty}^{+\infty} f(t,z) e^{-j 2\pi (\mu t + \nu z)} dt \ dz\\ f(t,z) &= \int_{-\infty}^{+\infty} \int_{-\infty}^{+\infty} F(\mu,\nu) e^{j 2\pi (\mu t + \nu z)} d\mu \ d\nu \end{aligned} F(μ,ν)f(t,z)​=∫−∞+∞​∫−∞+∞​f(t,z)e−j2π(μt+νz)dt dz=∫−∞+∞​∫−∞+∞​F(μ,ν)ej2π(μt+νz)dμ dν​
For image processing , Type in the t , z t, z t,z Represents a continuous spatial variable , μ , ν \mu, \nu μ,ν Represents a continuous frequency variable .


routine 8.7: Fourier transform of two-dimensional continuous function ( Two dimensional box function )

Take box functions as an example , Its Fourier transform is :

F ( μ , ν ) = ∫ − ∞ + ∞ ∫ − ∞ + ∞ f ( t , z ) e − j 2 π ( μ t + ν z ) d t   d z = ∫ − T / 2 + T / 2 ∫ − Z / 2 + Z / 2 A e − j 2 π ( μ t + ν z ) d t   d z = A T Z [ s i n ( π μ T ) π μ T ] [ s i n ( π ν Z ) π ν Z ] \begin{aligned} F(\mu,\nu) &= \int_{-\infty}^{+\infty} \int_{-\infty}^{+\infty} f(t,z) e^{-j 2\pi (\mu t + \nu z)} dt \ dz\\ &= \int_{-T/2}^{+T/2} \int_{-Z/2}^{+Z/2} A e^{-j 2\pi (\mu t + \nu z)} dt \ dz\\ &= ATZ[\frac{sin(\pi \mu T)}{\pi \mu T}][\frac{sin(\pi \nu Z)}{\pi \nu Z}] \end{aligned} F(μ,ν)​=∫−∞+∞​∫−∞+∞​f(t,z)e−j2π(μt+νz)dt dz=∫−T/2+T/2​∫−Z/2+Z/2​Ae−j2π(μt+νz)dt dz=ATZ[πμTsin(πμT)​][πνZsin(πνZ)​]​

 # 8.7: Fourier transform of two-dimensional continuous function ( Two dimensional box function )
# Two dimensional box function (2D-Box_function)
t = np.arange(-5, 5, 0.1) # start, end, step
z = np.arange(-5, 5, 0.1)
height, width = len(t), len(z)
tt, zz = np.meshgrid(t, z) # One dimensional arrays xnew, ynew Convert to grid point set ( Two dimensional array )
f = np.zeros([len(t), len(z)]) # initialization , Zeroing 
f[30:70, 30:70] = 1 # Two dimensional box function 
fu = np.sinc(t)
fv = np.sinc(z)
fuv = np.outer(np.sinc(t), np.sinc(z)) # Calculate the Continuous Fourier transform from the formula 
print(fu.shape, fv.shape, fuv.shape)
fig = plt.figure(figsize=(10, 6))
ax1 = plt.subplot(121, projection='3d')
ax1.set_title("2D-Box function")
ax1.plot_wireframe(tt, zz, f, rstride=2, cstride=2, linewidth=0.5, color='c')
ax1.set_xticks([]), ax1.set_yticks([]), ax1.set_zticks([])
ax2 = plt.subplot(122, projection='3d')
ax2.set_title("2D-Fourier transform")
ax2.plot_wireframe(tt, zz, fuv, rstride=2, cstride=2, linewidth=0.5, color='c')
ax2.set_xticks([]), ax2.set_yticks([]), ax2.set_zticks([])
plt.show()



routine 8.8: Fourier transform of two-dimensional continuous function ( Box functions with different parameters )

 # 8.8: Fourier transform of two-dimensional continuous function ( Box functions with different parameters )
# Two dimensional box function (2D-Box_function)
height, width = 128, 128
m = int((height - 1) / 2)
n = int((width - 1) / 2)
fig = plt.figure(figsize=(9, 6))
T = [5, 10, 20]
print(len(T))
for i in range(len(T)):
f = np.zeros([height, width]) # initialization , Zeroing 
f[m-T[i]:m+T[i], n-T[i]:n+T[i]] = 1 # Box functions of different sizes 
fft = np.fft.fft2(f)
shift_fft = np.fft.fftshift(fft)
amp = np.log(1 + np.abs(shift_fft))
plt.subplot(2,len(T),i+1), plt.xticks([]), plt.yticks([])
plt.imshow(f, "gray"), plt.title("Box Filter (T={})".format(T[i]))
#
plt.subplot(2,len(T),len(T)+i+1), plt.xticks([]), plt.yticks([])
plt.imshow(amp, "gray"), plt.title("FFT Spectrum")
plt.tight_layout()
plt.show()



2.2 Resampled and aliased images

Because a function cannot be sampled indefinitely , Therefore, aliasing always occurs in digital images .

Aliasing is divided into spatial aliasing and temporal aliasing . Spatial aliasing is caused by under sampling , It is more obvious in unfamiliar and repeated images . Time aliasing is related to the time interval of images in dynamic image sequence , As if the spokes were reversed “ Wheel effect ”.

The main problem of spatial aliasing is the introduction of artifacts , That is, the line sawtooth that does not appear in the original image 、 False highlights and frequency patterns .

Aliasing will be introduced when scaling the image . Image magnification can be considered oversampling , Use bilinear 、 Bicubic interpolation can reduce aliasing in image magnification . Image reduction can be regarded as under sampling , Aliasing is usually more severe .

To reduce aliasing , Before resampling, a low-pass filter should be used to smooth , To attenuate the high-frequency component of the image , It can effectively suppress aliasing , But the sharpness of the image has also decreased .


routine 8.9: Anti aliasing of images

 # 8.9: Anti aliasing of images 
imgGray = cv2.imread("../images/Fig0417a.tif", flags=0) # flags=0 Read as grayscale image 
height, width = imgGray.shape[:2] # The height and width of the picture 
# First reduce the image , Then enlarge the image 
imgZoom1 = cv2.resize(imgGray, (int(0.33*width), int(0.33*height)))
imgZoom2 = cv2.resize(imgZoom1, None, fx=3, fy=3, interpolation=cv2.INTER_AREA)
# First, the original image 5x5 Low pass filtering , Zoom out again , Then enlarge the image 
kSize = (5, 5)
imgBoxFilter = cv2.boxFilter(imgGray, -1, kSize) # cv2.boxFilter Method 
imgZoom3 = cv2.resize(imgBoxFilter, (int(0.33*width), int(0.33*height)))
imgZoom4 = cv2.resize(imgZoom3, None, fx=3, fy=3, interpolation=cv2.INTER_AREA)
fig = plt.figure(figsize=(9,5))
plt.subplot(131), plt.axis('off'), plt.title("Origin")
plt.imshow(imgGray, cmap='gray')
plt.subplot(132), plt.axis('off'), plt.title("Resample")
plt.imshow(imgZoom2, cmap='gray')
plt.subplot(133), plt.axis('off'), plt.title("Box Resample")
plt.imshow(imgZoom4, cmap='gray')
plt.tight_layout()
plt.show()



2.3 Two dimensional discrete Fourier transform (DFT)

For two-dimensional image processing , Usually use x , y x, y x,y Represents a discrete space coordinate variable , use u , v u,v u,v Represents a discrete variable in the frequency domain . Two dimensional discrete Fourier transform (DFT) And inverse transformation (IDFT) by :

F ( u , v ) = ∑ x = 0 M − 1 ∑ y = 0 N − 1 f ( x , y ) e − j 2 π ( u x / M + v y / N ) f ( x , y ) = 1 M N ∑ u = 0 M − 1 ∑ v = 0 N − 1 F ( u , v ) e j 2 π ( u x / M + v y / N ) \begin{aligned} F(u,v) &= \sum_{x=0}^{M-1} \sum_{y=0}^{N-1} f(x,y) e^{-j 2\pi (ux/M+vy/N)}\\ f(x,y) &= \frac{1}{MN} \sum_{u=0}^{M-1} \sum_{v=0}^{N-1} F(u,v) e^{j 2\pi (ux/M+vy/N)} \end{aligned} F(u,v)f(x,y)​=x=0∑M−1​y=0∑N−1​f(x,y)e−j2π(ux/M+vy/N)=MN1​u=0∑M−1​v=0∑N−1​F(u,v)ej2π(ux/M+vy/N)​
Two dimensional discrete Fourier transform can also be expressed in polar coordinates :
F ( u , v ) = R ( u , v ) + j I ( u , v ) = ∣ F ( u , v ) ∣ e j ϕ ( u , v ) F(u,v) = R(u,v) + j I(u,v) = |F(u,v)| e^{j \phi (u,v)} F(u,v)=R(u,v)+jI(u,v)=∣F(u,v)∣ejϕ(u,v)
Fourier spectrum (Fourier spectrum) by :
∣ F ( u , v ) ∣ = [ R 2 ( u , v ) + I 2 ( u , v ) ] 1 / 2 |F(u,v)| = [R^2(u,v) + I^2(u,v)]^{1/2} ∣F(u,v)∣=[R2(u,v)+I2(u,v)]1/2
Fourier phase spectrum (Fourier phase spectrum) by :
ϕ ( u , v ) = a r c t a n [ I ( u , v ) / R ( u , v ) ] \phi (u,v) = arctan[I(u,v)/R(u,v)] ϕ(u,v)=arctan[I(u,v)/R(u,v)]
Fourier power spectrum (Fourier power spectrum) by :
P ( u , v ) = ∣ F ( u , v ) ∣ 2 = R 2 ( u , v ) + I 2 ( u , v ) P(u,v) = |F(u,v)|^2 = R^2(u,v) + I^2(u,v) P(u,v)=∣F(u,v)∣2=R2(u,v)+I2(u,v)

Spatial sampling and frequency interval correspond to each other , The interval between discrete variables corresponding to the frequency domain is : Δ u = 1 / M Δ T , Δ v = 1 / N Δ Z \Delta u = 1/M \Delta T,\Delta v = 1/N \Delta Z Δu=1/MΔT,Δv=1/NΔZ. namely : The interval between samples in the frequency domain , It is inversely proportional to the product of the interval between spatial samples and the number of samples .

Space domain filter and frequency domain filter also correspond to each other , The two-dimensional convolution theorem is the link to establish the equivalent relationship between spatial domain and frequency domain filtering :
( f ⋆ h ) ( x , y ) ⇔ ( F ⋅ H ) ( u , v ) (f \star h)(x,y) \Leftrightarrow (F \cdot H)(u,v) (f⋆h)(x,y)⇔(F⋅H)(u,v)
This shows that F and H Namely f and h The Fourier transform of ;f and h Fourier transform of spatial convolution , Is the product of their transformations .

therefore , Calculate the spatial convolution of two functions , It can be calculated directly in the spatial domain , It can also be calculated in the frequency domain : First calculate the Fourier transform of each function , Multiply the two transformations , Finally, the inverse Fourier transform is carried out and converted back to the spatial domain .

in other words , Spatial domain filter and frequency domain filter are actually corresponding to each other , It is more convenient for some spatial domain filters to be realized by Fourier transform in frequency domain 、 Faster .


2.4 Numpy Realize image Fourier transform

Numpy Medium np.fft.fft2() Function can realize the Fourier transform of the image .

Function description :

 numpy.fft.fft2(a, s=None, axes=(- 2, - 1), norm=None) → out

Parameter description :

  • a: Input array , One dimensional or multidimensional arrays , It can be plural
  • s: The shape of the output array ( The length of each transformation axis ), Corresponding to fft(x,n) Medium n, optional
  • out: The output array , A one-dimensional or multi-dimensional array in a complex form (complex ndarray)

matters needing attention :

  1. When used for Fourier transform of two-dimensional image , Input array a yes numpy Two dimensional array ( Grayscale image ) Or a three-dimensional array ( Color images ).
  2. The output is in the plural form ( R e a l + j ∗ I m a g ) (Real + j * Imag) (Real+j∗Imag) Array of , Cannot be used directly to display images . In order to display the image of the Fourier transform result , You need to adjust the value of the array to [0,255] In the gray space .

after np.fft.fft2() Function to realize the picture spectrum information obtained by Fourier transform , The maximum value of the amplitude spectrum ( Low frequency component ) In the upper left corner (0,0) It's about . For the convenience of observation , Usually use np.fft.fftshift() The function moves the low-frequency component to the center of the frequency-domain image .

Function description :

 numpy.fft.fftshift(x, axes=None) → y

Parameter description :

  • x: Input array , One dimensional or multidimensional arrays
  • axes: Integers , Or enter the tuple of the array shape , Used to specify the axis of movement , optional
  • y: The output array

routine 8.10: Discrete Fourier transform of two-dimensional image (Numpy)

 # 8.10:Numpy Realize two-dimensional discrete Fourier transform 
normalize = lambda x: (x - x.min()) / (x.max() - x.min() + 1e-6)
imgGray = cv2.imread("../images/Fig0424a.tif", flags=0) # flags=0 Read as grayscale image 
# imgGray = cv2.imread("../images/imgBall.png", flags=1) # flags=0 Read as grayscale image 
# The Fourier transform 
# fft = np.fft.fft2(imgGray.astype(np.float32))
fft = np.fft.fft2(imgGray) # np.fft.fft2 Fourier transform 
# Decentralization , Calculate the amplitude spectrum and phase spectrum 
ampSpectrum = np.sqrt(np.power(fft.real, 2) + np.power(fft.imag, 2)) # Amplitude spectrum 
print("ampSpectrum max={}, min={}".format(ampSpectrum.max(), ampSpectrum.min()))
# phase = np.arctan2(fft.imag, fft.real) # Calculate the phase angle ( Radian system )
# phiSpectrum = phase / np.pi*180 # Convert the phase angle to [-180, 180]
phiSpectrum = np.angle(fft)
# Centralization , Move the low-frequency component to the center of the frequency domain image 
fftShift = np.fft.fftshift(fft) # Move the low-frequency component to the center of the frequency domain image 
# The amplitude spectrum after centralization 
ampSpeShift = np.sqrt(np.power(fftShift.real, 2) + np.power(fftShift.imag, 2))
ampShiftNorm = np.uint8(normalize(ampSpeShift)*255) # Owned by one becomes [0,255]
# Logarithmic transformation of amplitude spectrum 
ampSpeLog = np.log(1 + ampSpeShift) # The amplitude spectrum is logarithmically transformed for easy display 
ampSpeLog = np.uint8(normalize(ampSpeLog)*255) # Owned by one becomes [0,255]
# np.fft.ifft2 Realize the inverse Fourier transform of the image 
invShift = np.fft.ifftshift(fftShift) # Switch the low-frequency reversal back to the four corners of the image 
imgIfft = np.fft.ifft2(invShift) # Inverse Fourier transform , The return value is a complex array 
imgRebuild = np.abs(imgIfft) # Adjust the complex array to gray space 
plt.figure(figsize=(9, 6))
plt.subplot(231), plt.title("Original image"), plt.axis('off')
plt.imshow(imgGray, cmap='gray')
plt.subplot(232), plt.title("FFT phase spectrum"), plt.axis('off')
plt.imshow(phiSpectrum, cmap='gray')
plt.subplot(233), plt.title("Rebuild image with IFFT"), plt.axis('off')
plt.imshow(imgRebuild, cmap='gray')
plt.subplot(234), plt.title("FFT amplitude spectrum"), plt.axis('off')
plt.imshow(ampSpectrum, cmap='gray')
plt.subplot(235), plt.title("FFT-shift amplitude"), plt.axis('off')
plt.imshow(ampSpeShift, cmap='gray')
plt.subplot(236), plt.title("Log-trans of FFT amp"), plt.axis('off')
plt.imshow(ampSpeLog, cmap='gray')
plt.tight_layout()
plt.show()


Program description :

The uncentralized amplitude spectrum in the figure (FFT amp spe) It's not completely black , There are tiny bright areas at the four corners of the image , But it's hard to see , This is also the centralization of the amplitude spectrum (fftShift) Why .


2.5 OpenCV Realize image Fourier transform (cv.dft)

Use OpenCV Medium cv.dft() Function can also realize the Fourier transform of the image ,cv.idft() Function to realize the inverse Fourier transform of image .

Function description :

 cv.dft(src[, dst[, flags[, nonzeroRows]]]) → dst
cv.idft(src[, dst[, flags[, nonzeroRows]]]) → dst

Parameter description :

  • src: The input image , Single channel gray image , Use np.float32 Format
  • dst: Output image , Image size and src identical , The data type consists of flag decision
  • flag: Conversion identifier
    • cv.DFT_INVERSE: Replace the default forward transform with one-dimensional or two-dimensional inverse transform
    • cv.DFT_SCALE: Zoom scale identification , Calculate the scaling result according to the number of elements , Often with DFT_INVERSE Use it with
    • cv.DFT_ROWS: Forward or reverse Fourier transform is performed on each row of the input matrix , It is often used for complex operations such as 3D or high-dimensional transformation
    • cv.DFT_COMPLEX_OUTPUT: Forward transform one-dimensional or two-dimensional real arrays , The default method , The result is 2 A complex array of channels , The first channel is the real part , The second channel is the imaginary part
    • cv.DFT_REAL_OUTPUT: Inverse transformation of one-dimensional or two-dimensional complex arrays , The result is usually a complex matrix of the same size

matters needing attention :

  1. The input image src yes np.float32 Format , Such as image use np.uint8 The format must be converted first np.float32 Format .
  2. The default method cv.DFT_COMPLEX_OUTPUT when , Input src yes np.float32 Format of single channel two-dimensional array , Output dst yes 2 A two-dimensional array of channels , The first channel dft[:,:,0] It's the real part , The second channel dft[:,:,1] It's the imaginary part .
  3. Cannot be used directly to display images . have access to cv.magnitude() Function converts the result of Fourier transform to gray scale [0,255].
  4. idft(src, dst, flags) Equivalent to dft(src, dst, flags=DFT_INVERSE).
  5. OpenCV Fourier transform , Calculate the speed ratio Numpy faster .

Convert the identifier to cv.DFT_COMPLEX_OUTPUT when ,cv.dft() The output of the function is 2 A two-dimensional array of channels , Use cv.magnitude() Function can calculate the amplitude of two-dimensional vector .

Function description :

 cv.magnitude(x, y[, magnitude]) → dst

Parameter description :

  • x: One dimensional or multidimensional arrays , Also represents the real part of a complex number , floating-point
  • y: One dimensional or multidimensional arrays , It also represents the imaginary part of the complex number , floating-point , Array size must match x identical
  • dst: The output array , The array size and data type are the same as x identical , The operation formula is :

d s t ( I ) = x ( I ) 2 + y ( I ) 2 dst(I) = \sqrt{x(I)^2 + y(I)^2} dst(I)=x(I)2+y(I)2

The value range of Fourier transform and related operations may not be suitable for image display , It needs to be normalized . OpenCV Medium cv.normalize() Function can realize image normalization .

Function description :

 cv.normalize(src, dst[, alpha[, beta[, norm_type[, dtype[, mask]]]]]) → dst

Parameter description :

  • src: The input image
  • dst: Output results , Same size and type as the input image
  • alpha: Normalized minimum , optional , The default value is 0
  • beta: Normalized maximum , optional , The default value is 1
  • norm_type: Normalization type
    • NORM_INF:Linf norm ( The maximum absolute value )
    • NORM_L1:L1 norm ( The sum of absolute values )
    • NORM_L2:L2 norm ( Euclid distance ), Default type
    • NORM_MINMAX: Linear scaling , Common types
  • dtype: optional , The default value is -1, Indicates that the output matrix is the same as the input image type
  • mask: Mask mask , optional , The default is no mask

In theory, Fourier transform needs O ( M N ) 2 O(MN)^2 O(MN)2 Times operation , It's very time consuming ; The fast Fourier transform only needs O ( M N l o g ( M N ) ) O(MN log (MN)) O(MNlog(MN)) One operation can complete .

OpenCV Fourier transform function in cv.dft() The number of rows and columns can be decomposed into 2 p ∗ 3 q ∗ 5 r 2^p * 3^q * 5^r 2p∗3q∗5r The computational performance of the matrix is the best . In order to improve the performance of computation , You can complement the right and bottom of the original matrix 0, To satisfy this decomposition condition .OpenCV Medium cv.getOptimalDFTSize() Function can achieve the optimal image DFT Size expansion , Apply to cv.dft() and np.fft.fft2().

Function description :

 cv.getOptimalDFTSize(versize) → retval

Parameter description :

  • versize: Array size
  • retval:DFT Extended optimal array size

routine 8.11: Discrete Fourier transform of two-dimensional image (OpenCV)

 # 8.11:OpenCV Realize the discrete Fourier transform of two-dimensional image 
imgGray = cv2.imread("../images/Fig0424a.tif", flags=0) # flags=0 Read as grayscale image 
# cv2.dft Realize Fourier transform of image 
imgFloat32 = np.float32(imgGray) # Convert the image to float32
dft = cv2.dft(imgFloat32, flags=cv2.DFT_COMPLEX_OUTPUT) # The Fourier transform 
dftShift = np.fft.fftshift(dft) # Move the low-frequency component to the center of the frequency domain image 
# Amplitude spectrum 
# ampSpe = np.sqrt(np.power(dft[:,:,0], 2) + np.power(dftShift[:,:,1], 2))
dftAmp = cv2.magnitude(dft[:,:,0], dft[:,:,1]) # Amplitude spectrum , Not centralized 
dftShiftAmp = cv2.magnitude(dftShift[:,:,0], dftShift[:,:,1]) # Amplitude spectrum , Centralization 
dftAmpLog = np.log(1 + dftShiftAmp) # Logarithmic transformation of amplitude spectrum , In order to display 
# Phase spectrum 
phase = np.arctan2(dftShift[:,:,1], dftShift[:,:,0]) # Calculate the phase angle ( Radian system )
dftPhi = phase / np.pi*180 # Convert the phase angle to [-180, 180]
print("dftMag max={}, min={}".format(dftAmp.max(), dftAmp.min()))
print("dftPhi max={}, min={}".format(dftPhi.max(), dftPhi.min()))
print("dftAmpLog max={}, min={}".format(dftAmpLog.max(), dftAmpLog.min()))
# cv2.idft Realize the inverse Fourier transform of the image 
invShift = np.fft.ifftshift(dftShift) # Switch the low-frequency reversal back to the four corners of the image 
imgIdft = cv2.idft(invShift) # Inverse Fourier transform 
imgRebuild = cv2.magnitude(imgIdft[:,:,0], imgIdft[:,:,1]) # Reconstruction image 
plt.figure(figsize=(9, 6))
plt.subplot(231), plt.title("Original image"), plt.axis('off')
plt.imshow(imgGray, cmap='gray')
plt.subplot(232), plt.title("DFT Phase"), plt.axis('off')
plt.imshow(dftPhi, cmap='gray')
plt.subplot(233), plt.title("Rebuild image with IDFT"), plt.axis('off')
plt.imshow(imgRebuild, cmap='gray')
plt.subplot(234), plt.title("DFT amplitude spectrum"), plt.axis('off')
plt.imshow(dftAmp, cmap='gray')
plt.subplot(235), plt.title("DFT-shift amplitude"), plt.axis('off')
plt.imshow(dftShiftAmp, cmap='gray')
plt.subplot(236), plt.title("Log-trans of DFT amp"), plt.axis('off')
plt.imshow(dftAmpLog, cmap='gray')
plt.tight_layout()
plt.show()



routine 8.12:OpenCV The fast Fourier transform

 # 8.12:OpenCV The fast Fourier transform 
imgGray = cv2.imread("../images/Fig0429a.tif", flags=0) # flags=0 Read as grayscale image 
rows,cols = imgGray.shape[:2] # Row of image ( Height )/ Column ( Width )
# The fast Fourier transform ( To expand the matrix of the original image )
rPad = cv2.getOptimalDFTSize(rows) # The optimal DFT Extended size 
cPad = cv2.getOptimalDFTSize(cols) # For fast Fourier transform 
imgEx = np.zeros((rPad,cPad,2),np.float32) # Expand the edge of the original image 
imgEx[:rows,:cols,0] = imgGray # Edge expansion , The lower and right side are patched 0
dftImgEx = cv2.dft(imgEx, cv2.DFT_COMPLEX_OUTPUT) # The fast Fourier transform 
# Inverse Fourier transform 
idftImg = cv2.idft(dftImgEx) # Inverse Fourier transform 
idftMag = cv2.magnitude(idftImg[:,:,0], idftImg[:,:,1]) # Inverse Fourier transform 
# Matrix clipping , Get the restored image 
idftMagNorm = np.uint8(cv2.normalize(idftMag, None, 0, 255, cv2.NORM_MINMAX)) # Owned by one becomes [0,255]
imgRebuild = np.copy(idftMagNorm[:rows, :cols])
print("max(imgGray-imgRebuild) = ", np.max(imgGray-imgRebuild))
print("imgGray:{}, dftImg:{}, idftImg:{}, imgRebuild:{}".
format(imgGray.shape, dftImgEx.shape, idftImg.shape, imgRebuild.shape))
plt.figure(figsize=(9, 6))
plt.subplot(131), plt.title("Original image"), plt.axis('off')
plt.imshow(imgGray, cmap='gray')
plt.subplot(132), plt.title("Log-trans of DFT amp"), plt.axis('off')
dftAmp = cv2.magnitude(dftImgEx[:,:,0], dftImgEx[:,:,1]) # Amplitude spectrum , Centralization 
dftAmpLog = np.log(1 + dftAmp) # Logarithmic transformation of amplitude spectrum , In order to display 
plt.imshow(cv2.normalize(dftAmpLog, None, 0, 255, cv2.NORM_MINMAX), cmap='gray')
plt.subplot(133), plt.title("Rebuild image with IDFT"), plt.axis('off')
plt.imshow(imgRebuild, cmap='gray')
plt.tight_layout()
plt.show()



Copyright notice :

Welcome to your attention 『Python Xiaobai starts from scratch OpenCV Learning lessons @ youcans』 Original works

Some of the original images in this article are from Rafael C. Gonzalez “Digital Image Processing, 4th.Ed.”, Hereby I thank you. .

Original works , Reprint must be marked with the original link :https://blog.csdn.net/youcans/article/details/122917951

Copyright 2022 youcans, XUPT

Crated:2022-2-14


Welcome to your attention 『OpenCV routine 200 piece 100 piece 』 series , Ongoing update
Welcome to your attention 『Python Small white OpenCV Learning lessons 』 series , Ongoing update

Python Big white starts from scratch OpenCV Learning lessons -1. Installation and environment configuration
Python Big white starts from scratch OpenCV Learning lessons -2. Image reading and display
Python Big white starts from scratch OpenCV Learning lessons -3. Image creation and modification
Python Big white starts from scratch OpenCV Learning lessons -4. Image overlay and blending
Python Big white starts from scratch OpenCV Learning lessons -5. Geometric transformation of images
Python Big white starts from scratch OpenCV Learning lessons -6. Gray transformation and histogram processing
Python Big white starts from scratch OpenCV Learning lessons -7. Spatial domain image filtering
Python Big white starts from scratch OpenCV Learning lessons -8. Frequency domain image filtering ( On )


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved