Existe-t-il un moyen de détecter si une image est floue?

Je me demandais s’il existe un moyen de déterminer si une image est floue ou non en analysant les données d’image.

Oui, ça l’est. calculer le fft et parsingr le résultat. La transformée de Fourier vous indique quelles fréquences sont présentes dans l’image. S’il y a peu de hautes fréquences, l’image est floue.

Définir les termes «faible» et «élevé» dépend de vous.

edit : comme indiqué dans les commentaires, si vous voulez un seul float représentant le flou d’une image donnée, vous devez élaborer une mésortingque appropriée.

La réponse de Nikie fournit une telle mesure. Transformez l’image avec un kernel laplacien:

1 1 -4 1 1 

Et utilisez une mésortingque maximale robuste sur la sortie pour obtenir un nombre que vous pouvez utiliser pour le seuillage. Essayez d’éviter de trop lisser les images avant de calculer le laplacien, car vous découvrirez qu’une image lissée est floue :-).

Un autre moyen très simple d’estimer la netteté d’une image consiste à utiliser un filtre Laplace (ou LoG) et à choisir simplement la valeur maximale. Utiliser une mesure robuste comme un quantile à 99,9% est probablement préférable si vous prévoyez du bruit (c.-à-d. Choisir le contraste le plus élevé au lieu du contraste le plus élevé). contraste (eg égalisation de l’histogramme).

J’ai implémenté la suggestion de Simon et celle-ci dans Mathematica, et je l’ai essayée sur quelques images de test:

images de test

Le premier test brouille les images de test à l’aide d’un filtre gaussien avec une taille de kernel variable, puis calcule la FFT de l’image floue et prend la moyenne des fréquences les plus élevées de 90%:

 testFft[img_] := Table[ ( blurred = GaussianFilter[img, r]; fft = Fourier[ImageData[blurred]]; {w, h} = Dimensions[fft]; windowSize = Round[w/2.1]; Mean[Flatten[(Abs[ fft[[w/2 - windowSize ;; w/2 + windowSize, h/2 - windowSize ;; h/2 + windowSize]]])]] ), {r, 0, 10, 0.5}] 

Résultat dans un tracé logarithmique:

résultat fft

Les 5 lignes représentent les 5 images de test, l’axe X représente le rayon du filtre gaussien. Les graphiques diminuent, donc la FFT est une bonne mesure de la netteté.

Ceci est le code de l’estimateur de flou “LoG” le plus élevé: il applique simplement un filtre LoG et renvoie le pixel le plus shiny dans le résultat du filtre:

 testLaplacian[img_] := Table[ ( blurred = GaussianFilter[img, r]; Max[Flatten[ImageData[LaplacianGaussianFilter[blurred, 1]]]]; ), {r, 0, 10, 0.5}] 

Résultat dans un tracé logarithmique:

résultat de laplace

L’écart pour les images non floues est un peu mieux ici (2.5 vs 3.3), principalement parce que cette méthode n’utilise que le contraste le plus fort de l’image, tandis que la FFT est essentiellement un moyen sur l’image entière. Les fonctions diminuent également plus rapidement, il est donc plus facile de définir un seuil “flou”.

Lors de certains travaux avec un objective à mise au point automatique, je suis tombé sur cet ensemble d’algorithmes très utiles pour détecter le focus de l’image . Il est implémenté dans MATLAB, mais la plupart des fonctions sont assez faciles à transférer sur OpenCV avec filter2D .

Il s’agit essentiellement d’une mise en œuvre d’enquête de nombreux algorithmes de mesure de la focalisation. Si vous souhaitez lire les documents originaux, des références aux auteurs des algorithmes sont fournies dans le code. L’article de 2012 de Pertuz et al. L’parsing des opérateurs de mesure de focalisation pour la forme à partir du focus (SFF) donne un excellent aperçu de toutes ces mesures, ainsi que de leurs performances (tant en termes de rapidité que de précision, en termes de SFF).

EDIT: Ajout du code MATLAB au cas où le lien mourrait.

 function FM = fmeasure(Image, Measure, ROI) %This function measures the relative degree of focus of %an image. It may be invoked as: % % FM = fmeasure(Image, Method, ROI) % %Where % Image, is a grayscale image and FM is the computed % focus value. % Method, is the focus measure algorithm as a ssortingng. % see 'operators.txt' for a list of focus % measure methods. % ROI, Image ROI as a rectangle [xo yo width heigth]. % if an empty argument is passed, the whole % image is processed. % % Said Pertuz % Abr/2010 if ~isempty(ROI) Image = imcrop(Image, ROI); end WSize = 15; % Size of local window (only some operators) switch upper(Measure) case 'ACMO' % Absolute Central Moment (Shirvaikar2004) if ~isinteger(Image), Image = im2uint8(Image); end FM = AcMomentum(Image); case 'BREN' % Brenner's (Santos97) [MN] = size(Image); DH = Image; DV = Image; DH(1:M-2,:) = diff(Image,2,1); DV(:,1:N-2) = diff(Image,2,2); FM = max(DH, DV); FM = FM.^2; FM = mean2(FM); case 'CONT' % Image contrast (Nanda2001) ImContrast = inline('sum(abs(x(:)-x(5)))'); FM = nlfilter(Image, [3 3], ImContrast); FM = mean2(FM); case 'CURV' % Image Curvature (Helmli2001) if ~isinteger(Image), Image = im2uint8(Image); end M1 = [-1 0 1;-1 0 1;-1 0 1]; M2 = [1 0 1;1 0 1;1 0 1]; P0 = imfilter(Image, M1, 'replicate', 'conv')/6; P1 = imfilter(Image, M1', 'replicate', 'conv')/6; P2 = 3*imfilter(Image, M2, 'replicate', 'conv')/10 ... -imfilter(Image, M2', 'replicate', 'conv')/5; P3 = -imfilter(Image, M2, 'replicate', 'conv')/5 ... +3*imfilter(Image, M2, 'replicate', 'conv')/10; FM = abs(P0) + abs(P1) + abs(P2) + abs(P3); FM = mean2(FM); case 'DCTE' % DCT energy ratio (Shen2006) FM = nlfilter(Image, [8 8], @DctRatio); FM = mean2(FM); case 'DCTR' % DCT reduced energy ratio (Lee2009) FM = nlfilter(Image, [8 8], @ReRatio); FM = mean2(FM); case 'GDER' % Gaussian derivative (Geusebroek2000) N = floor(WSize/2); sig = N/2.5; [x,y] = meshgrid(-N:N, -N:N); G = exp(-(x.^2+y.^2)/(2*sig^2))/(2*pi*sig); Gx = -x.*G/(sig^2);Gx = Gx/sum(Gx(:)); Gy = -y.*G/(sig^2);Gy = Gy/sum(Gy(:)); Rx = imfilter(double(Image), Gx, 'conv', 'replicate'); Ry = imfilter(double(Image), Gy, 'conv', 'replicate'); FM = Rx.^2+Ry.^2; FM = mean2(FM); case 'GLVA' % Graylevel variance (Krotkov86) FM = std2(Image); case 'GLLV' %Graylevel local variance (Pech2000) LVar = stdfilt(Image, ones(WSize,WSize)).^2; FM = std2(LVar)^2; case 'GLVN' % Normalized GLV (Santos97) FM = std2(Image)^2/mean2(Image); case 'GRAE' % Energy of gradient (Subbarao92a) Ix = Image; Iy = Image; Iy(1:end-1,:) = diff(Image, 1, 1); Ix(:,1:end-1) = diff(Image, 1, 2); FM = Ix.^2 + Iy.^2; FM = mean2(FM); case 'GRAT' % Thresholded gradient (Snatos97) Th = 0; %Threshold Ix = Image; Iy = Image; Iy(1:end-1,:) = diff(Image, 1, 1); Ix(:,1:end-1) = diff(Image, 1, 2); FM = max(abs(Ix), abs(Iy)); FM(FMImage); FM = 1./R1; FM(index) = R1(index); FM = mean2(FM); case 'HISE' % Histogram entropy (Krotkov86) FM = entropy(Image); case 'HISR' % Histogram range (Firestone91) FM = max(Image(:))-min(Image(:)); case 'LAPE' % Energy of laplacian (Subbarao92a) LAP = fspecial('laplacian'); FM = imfilter(Image, LAP, 'replicate', 'conv'); FM = mean2(FM.^2); case 'LAPM' % Modified Laplacian (Nayar89) M = [-1 2 -1]; Lx = imfilter(Image, M, 'replicate', 'conv'); Ly = imfilter(Image, M', 'replicate', 'conv'); FM = abs(Lx) + abs(Ly); FM = mean2(FM); case 'LAPV' % Variance of laplacian (Pech2000) LAP = fspecial('laplacian'); ILAP = imfilter(Image, LAP, 'replicate', 'conv'); FM = std2(ILAP)^2; case 'LAPD' % Diagonal laplacian (Thelen2009) M1 = [-1 2 -1]; M2 = [0 0 -1;0 2 0;-1 0 0]/sqrt(2); M3 = [-1 0 0;0 2 0;0 0 -1]/sqrt(2); F1 = imfilter(Image, M1, 'replicate', 'conv'); F2 = imfilter(Image, M2, 'replicate', 'conv'); F3 = imfilter(Image, M3, 'replicate', 'conv'); F4 = imfilter(Image, M1', 'replicate', 'conv'); FM = abs(F1) + abs(F2) + abs(F3) + abs(F4); FM = mean2(FM); case 'SFIL' %Steerable filters (Minhas2009) % Angles = [0 45 90 135 180 225 270 315]; N = floor(WSize/2); sig = N/2.5; [x,y] = meshgrid(-N:N, -N:N); G = exp(-(x.^2+y.^2)/(2*sig^2))/(2*pi*sig); Gx = -x.*G/(sig^2);Gx = Gx/sum(Gx(:)); Gy = -y.*G/(sig^2);Gy = Gy/sum(Gy(:)); R(:,:,1) = imfilter(double(Image), Gx, 'conv', 'replicate'); R(:,:,2) = imfilter(double(Image), Gy, 'conv', 'replicate'); R(:,:,3) = cosd(45)*R(:,:,1)+sind(45)*R(:,:,2); R(:,:,4) = cosd(135)*R(:,:,1)+sind(135)*R(:,:,2); R(:,:,5) = cosd(180)*R(:,:,1)+sind(180)*R(:,:,2); R(:,:,6) = cosd(225)*R(:,:,1)+sind(225)*R(:,:,2); R(:,:,7) = cosd(270)*R(:,:,1)+sind(270)*R(:,:,2); R(:,:,7) = cosd(315)*R(:,:,1)+sind(315)*R(:,:,2); FM = max(R,[],3); FM = mean2(FM); case 'SFRQ' % Spatial frequency (Eskicioglu95) Ix = Image; Iy = Image; Ix(:,1:end-1) = diff(Image, 1, 2); Iy(1:end-1,:) = diff(Image, 1, 1); FM = mean2(sqrt(double(Iy.^2+Ix.^2))); case 'TENG'% Tenengrad (Krotkov86) Sx = fspecial('sobel'); Gx = imfilter(double(Image), Sx, 'replicate', 'conv'); Gy = imfilter(double(Image), Sx', 'replicate', 'conv'); FM = Gx.^2 + Gy.^2; FM = mean2(FM); case 'TENV' % Tenengrad variance (Pech2000) Sx = fspecial('sobel'); Gx = imfilter(double(Image), Sx, 'replicate', 'conv'); Gy = imfilter(double(Image), Sx', 'replicate', 'conv'); G = Gx.^2 + Gy.^2; FM = std2(G)^2; case 'VOLA' % Vollath's correlation (Santos97) Image = double(Image); I1 = Image; I1(1:end-1,:) = Image(2:end,:); I2 = Image; I2(1:end-2,:) = Image(3:end,:); Image = Image.*(I1-I2); FM = mean2(Image); case 'WAVS' %Sum of Wavelet coeffs (Yang2003) [C,S] = wavedec2(Image, 1, 'db6'); H = wrcoef2('h', C, S, 'db6', 1); V = wrcoef2('v', C, S, 'db6', 1); D = wrcoef2('d', C, S, 'db6', 1); FM = abs(H) + abs(V) + abs(D); FM = mean2(FM); case 'WAVV' %Variance of Wav...(Yang2003) [C,S] = wavedec2(Image, 1, 'db6'); H = abs(wrcoef2('h', C, S, 'db6', 1)); V = abs(wrcoef2('v', C, S, 'db6', 1)); D = abs(wrcoef2('d', C, S, 'db6', 1)); FM = std2(H)^2+std2(V)+std2(D); case 'WAVR' [C,S] = wavedec2(Image, 3, 'db6'); H = abs(wrcoef2('h', C, S, 'db6', 1)); V = abs(wrcoef2('v', C, S, 'db6', 1)); D = abs(wrcoef2('d', C, S, 'db6', 1)); A1 = abs(wrcoef2('a', C, S, 'db6', 1)); A2 = abs(wrcoef2('a', C, S, 'db6', 2)); A3 = abs(wrcoef2('a', C, S, 'db6', 3)); A = A1 + A2 + A3; WH = H.^2 + V.^2 + D.^2; WH = mean2(WH); WL = mean2(A); FM = WH/WL; otherwise error('Unknown measure %s',upper(Measure)) end end %************************************************************************ function fm = AcMomentum(Image) [MN] = size(Image); Hist = imhist(Image)/(M*N); Hist = abs((0:255)-255*mean2(Image))'.*Hist; fm = sum(Hist); end %****************************************************************** function fm = DctRatio(M) MT = dct2(M).^2; fm = (sum(MT(:))-MT(1,1))/MT(1,1); end %************************************************************************ function fm = ReRatio(M) M = dct2(M); fm = (M(1,2)^2+M(1,3)^2+M(2,1)^2+M(2,2)^2+M(3,1)^2)/(M(1,1)^2); end %****************************************************************** 

Quelques exemples de versions OpenCV:

 // OpenCV port of 'LAPM' algorithm (Nayar89) double modifiedLaplacian(const cv::Mat& src) { cv::Mat M = (Mat_(3, 1) < < -1, 2, -1); cv::Mat G = cv::getGaussianKernel(3, -1, CV_64F); cv::Mat Lx; cv::sepFilter2D(src, Lx, CV_64F, M, G); cv::Mat Ly; cv::sepFilter2D(src, Ly, CV_64F, G, M); cv::Mat FM = cv::abs(Lx) + cv::abs(Ly); double focusMeasure = cv::mean(FM).val[0]; return focusMeasure; } // OpenCV port of 'LAPV' algorithm (Pech2000) double varianceOfLaplacian(const cv::Mat& src) { cv::Mat lap; cv::Laplacian(src, lap, CV_64F); cv::Scalar mu, sigma; cv::meanStdDev(lap, mu, sigma); double focusMeasure = sigma.val[0]*sigma.val[0]; return focusMeasure; } // OpenCV port of 'TENG' algorithm (Krotkov86) double tenengrad(const cv::Mat& src, int ksize) { cv::Mat Gx, Gy; cv::Sobel(src, Gx, CV_64F, 1, 0, ksize); cv::Sobel(src, Gy, CV_64F, 0, 1, ksize); cv::Mat FM = Gx.mul(Gx) + Gy.mul(Gy); double focusMeasure = cv::mean(FM).val[0]; return focusMeasure; } // OpenCV port of 'GLVN' algorithm (Santos97) double normalizedGraylevelVariance(const cv::Mat& src) { cv::Scalar mu, sigma; cv::meanStdDev(src, mu, sigma); double focusMeasure = (sigma.val[0]*sigma.val[0]) / mu.val[0]; return focusMeasure; } 

Aucune garantie quant à savoir si ces mesures sont le meilleur choix pour votre problème, mais si vous suivez les documents associés à ces mesures, elles vous donneront peut-être une meilleure idée. J'espère que vous trouverez le code utile! Je sais que je l'ai fait.

S’appuyant sur la réponse de Nike. Il est simple de mettre en œuvre la méthode basée sur le laplacian avec opencv:

 short GetSharpness(char* data, unsigned int width, unsigned int height) { // assumes that your image is already in planner yuv or 8 bit greyscale IplImage* in = cvCreateImage(cvSize(width,height),IPL_DEPTH_8U,1); IplImage* out = cvCreateImage(cvSize(width,height),IPL_DEPTH_16S,1); memcpy(in->imageData,data,width*height); // aperture size of 1 corresponds to the correct masortingx cvLaplace(in, out, 1); short maxLap = -32767; short* imgData = (short*)out->imageData; for(int i =0;i< (out->imageSize/2);i++) { if(imgData[i] > maxLap) maxLap = imgData[i]; } cvReleaseImage(&in); cvReleaseImage(&out); return maxLap; } 

Reviendra un court indiquant que la netteté maximale détectée, qui est basée sur mes tests sur des échantillons réels, est un très bon indicateur de la mise au point ou non d’une caméra. Sans surprise, les valeurs normales dépendent de la scène, mais beaucoup moins que la méthode FFT qui doit présenter un taux de faux positifs élevé pour être utile dans mon application.

J’ai trouvé une solution totalement différente. J’avais besoin d’parsingr des images fixes vidéo pour trouver la plus nette dans chaque image (X). De cette façon, je détecterais les images floues et / ou floues.

J’ai fini par utiliser la détection de Canny Edge et j’ai obtenu de très bons résultats avec presque tous les types de vidéo (avec la méthode de Nikie, j’avais des problèmes avec les vidéos VHS numérisées et les vidéos entrelacées lourdes).

J’ai optimisé les performances en définissant une région d’intérêt (ROI) sur l’image d’origine.

En utilisant EmguCV:

 //Convert image using Canny using (Image imgCanny = imgOrig.Canny(225, 175)) { //Count the number of pixel representing an edge int nCountCanny = imgCanny.CountNonzero()[0]; //Compute a sharpness grade: //< 1.5 = blurred, in movement //de 1.5 à 6 = acceptable //> 6 =stable, sharp double dSharpness = (nCountCanny * 1000.0 / (imgCanny.Cols * imgCanny.Rows)); } 

Merci Nikie pour cette excellente suggestion de Laplace. Les documents OpenCV m’ont orienté dans le même sens: utiliser python, cv2 (opencv 2.4.10) et numpy …

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) numpy.max(cv2.convertScaleAbs(cv2.Laplacian(gray_image,3)))

le résultat est entre 0 et 255. J’ai trouvé que quelque chose de plus de 200 ans était très concentré, et à 100 ans, c’est nettement flou. le maximum ne devient jamais vraiment inférieur à 20, même s’il est complètement flou.

Une des méthodes que j’utilise actuellement mesure la propagation des contours dans l’image. Recherchez ce papier:

 @ARTICLE{Marziliano04perceptualblur, author = {Pina Marziliano and Frederic Dufaux and Stefan Winkler and Touradj Ebrahimi}, title = {Perceptual blur and ringing mesortingcs: Application to JPEG2000,” Signal Process}, journal = {Image Commun}, year = {2004}, pages = {163--172} } 

C’est généralement derrière un paywall mais j’ai vu des copies gratuites. Fondamentalement, ils localisent des arêtes verticales dans une image, puis mesurent la largeur de ces arêtes. La moyenne de la largeur donne le résultat final de l’estimation du flou pour l’image. Les bords plus larges correspondent à des images floues et inversement.

Ce problème appartient au domaine de l’ estimation de la qualité d’image sans référence . Si vous recherchez Google Scholar, vous obtiendrez de nombreuses références utiles.

MODIFIER

Voici un graphique des estimations de flou obtenues pour les 5 images dans le post de Nikie. Des valeurs plus élevées correspondent à un plus grand flou. J’ai utilisé un filtre Gaussien 11×11 de taille fixe et fait varier l’écart type (en utilisant la commande convert imagemagick pour obtenir les images floues).

entrer la description de l'image ici

Si vous comparez des images de tailles différentes, n’oubliez pas de normaliser selon la largeur de l’image, car les images plus grandes auront des bords plus larges.

Enfin, la distinction entre le flou artistique et le flou indésirable (provoqué par la focalisation, la compression, le mouvement relatif du sujet par rapport à la caméra) est un problème important, mais cela dépasse les approches simples comme celle-ci. Pour un exemple de flou artistique, regardez l’image Lenna: le reflet de Lenna dans le miroir est flou, mais son visage est parfaitement net. Cela consortingbue à une estimation plus élevée du flou pour l’image Lenna.

Les réponses ci-dessus ont élucidé beaucoup de choses, mais je pense qu’il est utile de faire une distinction conceptuelle.

Que faire si vous prenez une photo parfaitement nette d’une image floue?

Le problème de détection de flou n’est bien posé que lorsque vous avez une référence . Si vous devez concevoir, par exemple, un système de mise au point automatique, vous comparez une séquence d’images sockets avec différents degrés de flou ou de lissage, et vous essayez de trouver le sharepoint flou minimum dans cet ensemble. En d’autres termes, vous devez recouper les différentes images en utilisant l’une des techniques illustrées ci-dessus (essentiellement – avec différents niveaux de raffinement possibles dans l’approche – à la recherche de l’image avec le contenu haute fréquence le plus élevé).

J’ai essayé une solution basée sur le filtre Laplacian de cet article. Cela ne m’a pas aidé. Donc, j’ai essayé la solution à partir de cet article et c’était bon pour mon cas (mais c’est lent):

 import cv2 image = cv2.imread("test.jpeg") height, width = image.shape[:2] gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) def px(x, y): return int(gray[y, x]) sum = 0 for x in range(width-1): for y in range(height): sum += abs(px(x, y) - px(x+1, y)) 

Une image moins floue a une valeur de sum maximale!

Vous pouvez également régler la vitesse et la précision en changeant d’étape, par ex.

cette partie

 for x in range(width - 1): 

vous pouvez remplacer par celui-ci

 for x in range(0, width - 1, 10): 

Le code Matlab de deux méthodes publiées dans des revues de haut niveau (Transactions IEEE on Image Processing) est disponible ici: https://ivulab.asu.edu/software

vérifier les algorithmes CPBDM et JNBM. Si vous vérifiez le code, il n’est pas très difficile d’être porté et, incidemment, il est basé sur la méthode de base de Marzialiano.

je l’ai mis en œuvre en utilisant fft dans matlab et vérifier l’histogramme de la moyenne de calcul fct et std mais aussi la fonction d’ajustement peut être fait

 fa = abs(fftshift(fft(sharp_img))); fb = abs(fftshift(fft(blured_img))); f1=20*log10(0.001+fa); f2=20*log10(0.001+fb); figure,imagesc(f1);title('org') figure,imagesc(f2);title('blur') figure,hist(f1(:),100);title('org') figure,hist(f2(:),100);title('blur') mf1=mean(f1(:)); mf2=mean(f2(:)); mfd1=median(f1(:)); mfd2=median(f2(:)); sf1=std(f1(:)); sf2=std(f2(:)); 

C’est ce que je fais dans Opencv pour détecter la qualité de la mise au point dans une région:

 Mat grad; int scale = 1; int delta = 0; int ddepth = CV_8U; Mat grad_x, grad_y; Mat abs_grad_x, abs_grad_y; /// Gradient X Sobel(matFromSensor, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT); /// Gradient Y Sobel(matFromSensor, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT); convertScaleAbs(grad_x, abs_grad_x); convertScaleAbs(grad_y, abs_grad_y); addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad); cv::Scalar mu, sigma; cv::meanStdDev(grad, /* mean */ mu, /*stdev*/ sigma); focusMeasure = mu.val[0] * mu.val[0];