Traitement d'image dans OpenCV - Transformée de Fourier + correspondance de modèle

Traitement d'image dans OpenCV - Transformée de Fourier + correspondance de modèle

Maintenant, cela s'approfondit progressivement et j'espère progresser avec tout le monde et devenir de plus en plus fort

1. Transformée de Fourier

Les deux concepts les plus importants de la transformée de Fourier : le domaine temporel et le domaine fréquentiel. La méthode d'observation du monde dynamique avec le temps comme référence s'appelle l'analyse du domaine temporel, et qu'est-ce que le domaine fréquentiel ? Il s'agit d'un système de coordonnées utilisé pour décrire les caractéristiques d'un signal en termes de fréquence. Le diagramme du domaine fréquentiel montre quantité de signal dans chaque bande de fréquence donnée dans la plage. L'une des méthodes qui traverse le domaine temporel et le domaine fréquentiel est la fameuse analyse de Fourier , qui peut être divisée en série de Fourier et transformée de Fourier.La transformée de Fourier est ce dont nous allons parler dans cette partie

La transformée de Fourier est un outil puissant pour analyser les systèmes linéaires. Elle nous dit que toute fonction périodique peut être considérée comme une superposition d'ondes sinusoïdales d'amplitudes et de phases différentes . Au sens mathématique, la transformée de Fourier décompose une fonction périodique arbitraire sous la forme de la somme de fonctions sinusoïdales infinies ; en termes d'effets physiques, la transformée de Fourier réalise la transformation du signal du domaine spatial au domaine fréquentiel

En vision par ordinateur, la transformée de Fourier est utilisée pour analyser les caractéristiques de fréquence de divers filtres. Pour les images, la transformée de Fourier discrète 2D (DFT) est utilisée pour trouver le domaine fréquentiel (il existe également un algorithme rapide appelé la transformée de Fourier rapide ( FFT ). C'est un morceau de texte pas facile à comprendre, car il implique trop de choses ésotériques, et la transformée de Fourier elle-même est un point relativement difficile. Je n'entrerai pas dans les détails ici. Parlons juste de notre Comment l'utiliser , étudiants qui veulent en savoir plus, jetez un œil à cet article : Expliquer la transformée de Fourier de manière simple (vraiment facile à comprendre)

Pour un signal sinusoïdal, si l'amplitude change relativement rapidement dans un court laps de temps, on peut dire qu'il s'agit d'un signal haute fréquence, et s'il change lentement, c'est un signal basse fréquence, on peut étendre la même idée aux images , où l'amplitude change brusquement dans l'image ? Bien sûr, c'est aux points de bord ou au bruit, par conséquent, on peut dire que le bord et le bruit sont le contenu haute fréquence de l'image

1.1 Numpy implémente la transformée de Fourier

Numpy fournit le package FFT pour trouver la transformée de Fourier, np.fft.fft2() nous fournit la transformée de fréquence, ce sera un tableau complexe, son premier paramètre est l'image d'entrée (image en niveaux de gris), Le deuxième paramètre est facultatif et détermine la taille du tableau de sortie. Si elle est supérieure à la taille de l'image d'entrée, remplissez l'image d'entrée avec des zéros avant de calculer la FFT. Si elle est plus petite que l'image d'entrée, l'image d'entrée sera recadrée. Si aucun paramètre n'est passé, la taille du tableau de sortie sera la même que la taille de l'entrée, mais la composante de fréquence nulle (composante DC) du résultat obtenu maintenant sera dans le coin supérieur gauche. , nous devons le centrer, et le processus de centrage est lié à la fonction np.fft.fftshift()

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

img = cv.imread(r'E:\image\test16.png', 0)
f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)
magnitude_spectrum = 20 * np.log(np.abs(fshift))
plt.subplot(121), plt.imshow(img, cmap='gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(magnitude_spectrum, cmap='gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()

insérez la description de l'image ici

Nous pouvons voir qu'il y a plus de zones blanches au centre du spectre de magnitude, indiquant que l'image a plus de contenu basse fréquence. Après avoir trouvé le spectre d'amplitude, peut-on effectuer quelques opérations dans le domaine fréquentiel ? Par exemple, le filtrage passe-haut et la reconstruction d'image, l'essentiel est de trouver la DFT inverse, nous utilisons d'abord un masque de fenêtre rectangulaire d'une taille de 60*60 pour compenser le signal basse fréquence, puis utilisons np.fft.ifftshift () pour appliquer le décalage inverse afin que la composante CC réapparaisse dans le coin supérieur gauche. Utilisez ensuite la fonction np.ifft2 () pour trouver la FFT inverse, le résultat est également un nombre complexe

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

img = cv.imread(r'E:\image\test15.png', 0)
f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)
magnitude_spectrum = 20 * np.log(np.abs(fshift))
rows, cols = img.shape
crow, ccol = rows//2, cols//2
fshift[crow - 30:crow + 31, ccol - 30:ccol + 31] = 0
f_ishift = np.fft.ifftshift(fshift)
img_back = np.fft.ifft2(f_ishift)
img_back = np.abs(img_back)
plt.subplot(131), plt.imshow(img, cmap='gray'),
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(132), plt.imshow(magnitude_spectrum, cmap='gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.subplot(133), plt.imshow(img_back)
plt.title('Result in JET'), plt.xticks([]), plt.yticks([])
plt.show()

insérez la description de l'image ici

1.2 OpenCV implémente la transformée de Fourier

OpenCV fournit les fonctions cv.dft () et cv.idft () à cet effet. Il renvoie le même résultat que le précédent, mais avec deux canaux. Le premier canal est la partie réelle du résultat et le second canal est la partie imaginaire du résultat. L'image d'entrée doit d'abord être convertie en np.float32

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

img = cv.imread(r'E:\image\test17.png', 0)
dft = cv.dft(np.float32(img), flags=cv.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)
magnitude_spectrum = 20 * np.log(cv.magnitude(dft_shift[:, :, 0], dft_shift[:, :, 1]))
plt.subplot(121), plt.imshow(img, cmap='gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(magnitude_spectrum, cmap='gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()

insérez la description de l'image ici

Il y a quelques endroits difficiles dans ce bout de code, ce n'est pas grave, analysons-le : la première fois que vous regarderez ce code, vous aurez quelques questions, comment passer les paramètres du cv.dft() fonction? Comment la fonction cv.magnitude() est-elle utilisée ?

La fonction de la fonction cv.dft() est d'effectuer une transformée de Fourier discrète directe ou inverse sur un tableau de nombres à virgule flottante unidimensionnel ou bidimensionnel, qui comprend 4 paramètres, le premier est l'image source et le second paramètre est le type OutputArray dst, le résultat de l'opération renvoyé après l'appel de la fonction existe ici, sa taille et son type dépendent de l'identifiant de conversion des drapeaux du troisième paramètre, sa valeur par défaut est 0 (voir : opencv : dft() explication détaillée de la fonction )

La fonction cv.magnitude() est utilisée pour calculer la magnitude d'un vecteur bidimensionnel, qui comprend 3 paramètres. Le premier est x de type InputArray, qui représente la valeur de la coordonnée X à virgule flottante du vecteur, qui est la partie réelle. Le deuxième paramètre It est y de type InputArray, indiquant la valeur de la coordonnée Y à virgule flottante du vecteur, c'est-à-dire la partie imaginaire, et le troisième paramètre est l'amplitude de sortie

Ensuite, nous devons faire la transformation inverse de DFT dans OpenCV. Dans la section précédente, nous avons utilisé le filtre passe-haut HPF. Dans cette partie, nous appliquerons le filtre passe-bas LPF à l'image.

Remarque : En général, les fonctions OpenCV cv.dft() et cv.idft() sont plus rapides que les fonctions Numpy, environ 3 fois plus rapides, mais les fonctions Numpy sont plus faciles à utiliser

Nous mettons cette partie du code plus tard, et elle est plus facile à comprendre avec l'optimisation des performances de DFT

1.3 Optimisation des performances de DFT

Le DFT calcule mieux pour certaines tailles de tableau, par exemple le plus rapide lorsque la taille du tableau est une puissance de 2, et peut également être traité très efficacement pour les tableaux qui sont le produit des tailles 2, 3 et 5, à propos du problème de performance du code, nous peut modifier la taille du tableau à n'importe quelle taille optimale (en remplissant des zéros) avant de trouver le DFT, pour OpenCV nous devons remplir les zéros manuellement, mais pour Numpy, spécifiez la nouvelle taille pour le calcul FFT et ce sera automatiquement vous remplissez zéro

Concernant la recherche de la taille optimale, OpenCV fournit une fonction pour cela : cv.getOptimalDFTSize()

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread(r'E:\image\test17.png', 0)
rows, cols = img.shape
print(rows, cols)

# 计算DFT效率最佳的尺寸
nrows = cv2.getOptimalDFTSize(rows)
ncols = cv2.getOptimalDFTSize(cols)
print(nrows, ncols)

nimg = np.zeros((nrows, ncols))
nimg[:rows, :cols] = img
img = nimg

# OpenCV计算快速傅里叶变换,输入图像应首先转换为np.float32,然后使用函数cv2.dft()和cv2.idft()。
# 返回结果与Numpy相同,但有两个通道。第一个通道为有结果的实部,第二个通道为有结果的虚部。
dft = cv2.dft(np.float32(img), flags=cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)

magnitude_spectrum = 20 * np.log(cv2.magnitude(dft_shift[:, :, 0], dft_shift[:, :, 1]))

plt.subplot(121), plt.imshow(img, cmap='gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(magnitude_spectrum, cmap='gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()

rows, cols = img.shape
crow, ccol = rows // 2, cols // 2

# 首先创建一个mask,中心正方形为1,其他均为0
# 如何删除图像中的高频内容,即我们将LPF应用于图像。它实际上模糊了图像。
# 为此首先创建一个在低频时具有高值的掩码,即传递LF内容,在HF区域为0。
mask = np.zeros((rows, cols, 2), np.uint8)
mask[crow - 30:crow + 30, ccol - 30:ccol + 30] = 1

# 应用掩码Mask和求逆DTF
fshift = dft_shift * mask
f_ishift = np.fft.ifftshift(fshift)
img_back = cv2.idft(f_ishift)
img_back = cv2.magnitude(img_back[:, :, 0], img_back[:, :, 1])

plt.subplot(121), plt.imshow(img, cmap='gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(img_back, cmap='gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()

insérez la description de l'image ici

Avis de droit d'auteur : Le code source provient de l'article CSDN Transformation d'image dans OpenCV - Transformation de Fourier

2. Correspondance des modèles

La correspondance de modèle est une méthode pour rechercher et trouver l'emplacement d'une image de modèle dans une image plus grande. Pour cela, OpenCV est livré avec une fonction cv.matchTemplate (), qui fait simplement glisser l'image du modèle sur l'image d'entrée (comme dans la convolution 2D), puis compare les mosaïques d'image du modèle et de l'image d'entrée sous l'image du modèle, qui renvoie une image en niveaux de gris où chaque pixel représente à quel point le voisinage de ce pixel correspond au modèle. Mais dans la connotation de cette méthode, quelle méthode est utilisée pour faire le matching ? Ceci est déterminé par l'un des paramètres de la fonction

Si la taille de l'image d'entrée est (W * H) et la taille de l'image modèle est (w * h), alors la taille de l'image de sortie sera (Ww + 1, Hh + 1) et après nous obtenons le résultat, nous pouvons utiliser cv La fonction .minMaxLoc () trouve où se trouve le max/min, le prend comme le coin supérieur gauche du rectangle, et prend (w, h) comme largeur et hauteur du rectangle

La méthode de correspondance (méthode de comparaison) est liée aux paramètres de cv.matchTemplate(). Examinons les paramètres et appliquons différents paramètres pour avoir des effets différents. Le premier paramètre de cette fonction est l'image source, le deuxième paramètre est l'image modèle, le troisième paramètre est l'image résultat correspondante et le quatrième paramètre est la méthode utilisée pour spécifier la comparaison.

  • cv::TM_SQDIFF : cette méthode utilise la différence carrée pour la correspondance, donc le meilleur résultat de correspondance est au résultat de 0, et plus la valeur est grande, plus le résultat de correspondance est mauvais
  • cv::TM_SQDIFF_NORMED : cette méthode utilise la différence quadratique normalisée pour correspondre, et la meilleure correspondance est également là où le résultat est 0
  • cv::TM_CCORR : méthode de correspondance de corrélation, qui utilise le résultat de convolution de l'image source et de l'image modèle pour correspondre, par conséquent, la meilleure position de correspondance est à la valeur maximale, et plus la valeur est petite, plus le résultat de correspondance est mauvais
  • cv :: TM_CCORR_NORMED : méthode d'appariement de corrélation normalisée, similaire à la méthode d'appariement de corrélation, la meilleure position d'appariement est également à la valeur maximale
  • cv::TM_CCOEFF : Méthode d'appariement du coefficient de corrélation, qui utilise la corrélation entre la différence entre l'image source et sa valeur moyenne et la différence entre le modèle et sa valeur moyenne pour l'appariement. Le meilleur résultat d'appariement est à une valeur égale à 1, et le plus mauvais résultat d'appariement Lorsque la valeur est égale à -1, la valeur égale à 0 indique directement que les deux ne sont pas liés
  • cv::TM_CCOEFF_NORMED : méthode d'appariement du coefficient de corrélation normalisé. Une valeur positive indique un meilleur résultat d'appariement, et une valeur négative indique un effet d'appariement plus faible. Plus la valeur est élevée, meilleur est l'effet d'appariement

Données extraites de : [OpenCV3] template matching - explication détaillée de cv :: matchTemplate()

2.1 Correspondance de modèle pour un seul objet

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

img = cv.imread(r'E:\image\test15.png', 0)
img2 = img.copy()
template = cv.imread(r'E:\image\temple.png', 0)
w, h = template.shape[::-1]
# 列表中所有的6种比较方法
methods = ['cv.TM_CCOEFF', 'cv.TM_CCOEFF_NORMED', 'cv.TM_CCORR',
           'cv.TM_CCORR_NORMED', 'cv.TM_SQDIFF', 'cv.TM_SQDIFF_NORMED']
for meth in methods:
    img = img2.copy()
    method = eval(meth)
    # 应用模板匹配
    res = cv.matchTemplate(img, template, method)
    min_val, max_val, min_loc, max_loc = cv.minMaxLoc(res)
    # 如果方法是TM_SQDIFF或TM_SQDIFF_NORMED,则取最小值
    if method in [cv.TM_SQDIFF, cv.TM_SQDIFF_NORMED]:
        top_left = min_loc
    else:
        top_left = max_loc
    bottom_right = (top_left[0] + w, top_left[1] + h)
    cv.rectangle(img, top_left, bottom_right, [255, 0, 0], 2)
    plt.subplot(121), plt.imshow(res, cmap='gray')
    plt.title('Matching Result'), plt.xticks([]), plt.yticks([])
    plt.subplot(122), plt.imshow(img, cmap='gray')
    plt.title('Detected Point'), plt.xticks([]), plt.yticks([])
    plt.suptitle(meth)
    plt.show()

insérez la description de l'image ici

2.2 Correspondance de modèles pour plusieurs objets

Dans la section précédente, nous avons fait correspondre le visage de M. Mei, mais que se passe-t-il s'il existe de nombreux modèles dans l'image qui remplissent les conditions de correspondance ? Pour le moment, cv.minMaxLoc() ne nous fournira pas tous les emplacements, nous utiliserons le seuillage

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

img_rgb = cv.imread(r'E:\image\num.png')
img_gray = cv.cvtColor(img_rgb, cv.COLOR_BGR2GRAY)
template = cv.imread(r'E:\image\temple2.png', 0)
w, h = template.shape[::-1]
res = cv.matchTemplate(img_gray, template, cv.TM_CCOEFF_NORMED)
threshold = 0.8
loc = np.where(res >= threshold)
for pt in zip(*loc[::-1]):
    cv.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 2)
cv.imshow('res.png', img_rgb)
cv.waitKey(0)
cv.destroyWindow()

insérez la description de l'image ici

Analyse de code : la fonction shape() à la ligne 8 est une fonction dans numpy.core.fromnumeric, sa fonction est de vérifier la dimension de la matrice ou du tableau, et le locloc à la ligne 11 est le pixel qui satisfait "res >= threshold " Collection d'index, la fonction zip() à la ligne 12 utilise un objet itérable comme paramètre, regroupe les éléments correspondants dans l'objet en tuples, puis renvoie une liste composée de ces tuples


(Note : Pour le contenu de l'article, reportez-vous au document officiel chinois d'OpenCV4.1)
Si l'article vous est utile, pensez à le soutenir en un clic et trois liens

おすすめ

転載: blog.csdn.net/qq_50587771/article/details/124543040
おすすめ