Existe uma maneira de detectar se uma imagem está desfocada?

Eu queria saber se existe uma maneira de determinar se uma imagem está desfocada ou não, analisando os dados da imagem.

    sim. calcule o fft e analise o resultado. A transformada de Fourier informa quais freqüências estão presentes na imagem. Se houver uma baixa quantidade de altas freqüências, a imagem ficará desfocada.

    Definir os termos “baixo” e “alto” depende de você.

    editar : como indicado nos comentários, se você quiser um único float representando a nitidez de uma determinada imagem, você precisa calcular uma métrica adequada.

    A resposta de Nikie fornece tal métrica. Convolve a imagem com um kernel laplaciano:

    1 1 -4 1 1 

    E use uma métrica máxima robusta na saída para obter um número que você possa usar para o limite. Tente evitar suavizar demais as imagens antes de computar o Laplacian, porque você só descobrirá que uma imagem suavizada está realmente borrada :-).

    Outra maneira muito simples de estimar a nitidez de uma imagem é usar um filtro Laplace (ou LoG) e simplesmente escolher o valor máximo. Usar uma medida robusta como um quantil de 99,9% é provavelmente melhor se você espera ruído (ou seja, escolher o segundo maior contraste em vez do mais alto). Se você espera variação de brilho da imagem, inclua também uma etapa de pré-processamento para normalizar o brilho da imagem. contraste (por exemplo, equalização do histograma).

    Eu implementei a sugestão de Simon e esta no Mathematica, e tentei em algumas imagens de teste:

    imagens de teste

    O primeiro teste desfoca as imagens de teste usando um filtro gaussiano com um tamanho de kernel variável, calcula a FFT da imagem borrada e calcula a média das freqüências 90% mais altas:

     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}] 

    Resultado em uma plotagem logarítmica:

    resultado fft

    As 5 linhas representam as 5 imagens de teste, o eixo X representa o raio do filtro Gaussiano. Os charts estão diminuindo, portanto, a FFT é uma boa medida para nitidez.

    Este é o código para o estimador de obscurecimento “LoG mais alto”: ele simplesmente aplica um filtro LoG e retorna o pixel mais shiny no resultado do filtro:

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

    Resultado em uma plotagem logarítmica:

    laplace resultar

    O spread para as imagens não desfocadas é um pouco melhor aqui (2.5 vs 3.3), principalmente porque esse método usa apenas o contraste mais forte na imagem, enquanto o FFT é essencialmente uma média sobre a imagem inteira. As funções também estão diminuindo mais rapidamente, então pode ser mais fácil definir um limite “desfocado”.

    Durante algum trabalho com uma lente de foco automático, encontrei este conjunto muito útil de algoritmos para detectar o foco da imagem . Ele é implementado no MATLAB, mas a maioria das funções é muito fácil de ser portada para o OpenCV com o filter2D .

    É basicamente uma implementação de levantamento de muitos algoritmos de medição de foco. Se você quiser ler os artigos originais, referências aos autores dos algoritmos são fornecidas no código. O artigo de 2012 de Pertuz et al. A análise dos operadores de medida de foco para forma a partir do foco (SFF) fornece uma excelente revisão de todas essas medidas, bem como de seu desempenho (tanto em termos de velocidade quanto de precisão aplicadas à SFF).

    EDIT: Adicionado código MATLAB apenas no caso do link morre.

     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 string. % 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 %****************************************************************** 

    Alguns exemplos de versões do 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; } 

    Não há garantias sobre se essas medidas são ou não a melhor escolha para o seu problema, mas se você rastrear os documentos associados a essas medidas, eles podem fornecer mais informações. Espero que você ache o código útil! Eu sei que fiz.

    Construindo fora da resposta da Nike. É simples implementar o método baseado na laplacian com o 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 matrix 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; } 

    Retornará um breve, indicando a nitidez máxima detectada, que, com base em meus testes em amostras do mundo real, é um bom indicador de se uma câmera está em foco ou não. Não surpreendentemente, os valores normais dependem da cena, mas muito menos do que o método FFT, que tem uma taxa alta de falsos positivos para ser útil na minha aplicação.

    Eu criei uma solução totalmente diferente. Eu precisava analisar frameworks fixos de vídeo para encontrar o mais nítido em todos os frameworks (X). Dessa forma, eu detectaria borrões de movimento e / ou imagens fora de foco.

    Eu acabei usando a detecção do Canny Edge e obtive resultados MUITO MUITO bons com quase todo tipo de vídeo (com o método do nikie, eu tive problemas com vídeos VHS digitalizados e vídeos entrelaçados pesados).

    Eu otimizei o desempenho definindo uma região de interesse (ROI) na imagem original.

    Usando o 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)); } 

    Obrigado nikie por essa grande sugestão de Laplace. OpenCV docs apontou-me na mesma direção: usando python, cv2 (opencv 2.4.10) e numpy …

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

    O resultado está entre 0 e 255. Eu achei que qualquer coisa acima de 200ish está muito em foco, e por 100, é notavelmente embaçada. o max nunca recebe muito menos de 20, mesmo que esteja completamente desfocado.

    Uma maneira que estou usando atualmente mede a propagação de bordas na imagem. Procure este artigo:

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

    É geralmente atrás de um paywall, mas eu vi algumas cópias gratuitas por aí. Basicamente, eles localizam bordas verticais em uma imagem e, em seguida, medem a largura dessas bordas. Averaging a largura dá o resultado final da estimativa do borrão para a imagem. Bordas mais largas correspondem a imagens borradas e vice-versa.

    Esse problema pertence ao campo de estimativa de qualidade de imagem sem referência . Se você pesquisar no Google Acadêmico, receberá muitas referências úteis.

    EDITAR

    Aqui está um gráfico das estimativas de borrão obtidas para as 5 imagens no post do nikie. Valores mais altos correspondem a um maior desfoque. Eu usei um filtro gaussiano 11×11 de tamanho fixo e variei o desvio padrão (usando o comando convert do imagemagick para obter as imagens borradas).

    insira a descrição da imagem aqui

    Se você comparar imagens de tamanhos diferentes, não se esqueça de normalizar pela largura da imagem, pois as imagens maiores terão bordas mais largas.

    Finalmente, um problema significativo é distinguir entre desfoque artístico e desfoque indesejado (causado por falta de foco, compression, movimento relativo do object para a câmera), mas isso está além de abordagens simples como essa. Para um exemplo de borrão artístico, dê uma olhada na imagem de Lenna: o reflexo de Lenna no espelho está embaçado, mas o rosto dela está perfeitamente em foco. Isso contribui para uma maior estimativa de desfoque para a imagem de Lenna.

    As respostas acima elucidaram muitas coisas, mas acho que é útil fazer uma distinção conceitual.

    E se você tirar uma foto perfeitamente focalizada de uma imagem borrada?

    O problema de detecção de desfoque só é bem posicionado quando você tem uma referência . Se você precisa projetar, por exemplo, um sistema de foco automático, compara uma sequência de imagens tiradas com diferentes graus de desfoque ou suavização e tenta encontrar o ponto de mínima desfocagem dentro desse conjunto. Em outras palavras, você precisa fazer referência cruzada das várias imagens usando uma das técnicas ilustradas acima (basicamente – com vários níveis possíveis de refinamento na abordagem – procurando a única imagem com o conteúdo de alta frequência mais alto).

    Eu tentei solução com base no filtro Laplacian deste post. Isso não me ajudou. Então, eu tentei a solução deste post e foi bom para o meu caso (mas é lento):

     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)) 

    Imagem menos desfocada tem valor máximo de sum !

    Você também pode ajustar velocidade e precisão mudando passo, por exemplo

    esta parte

     for x in range(width - 1): 

    você pode replace por este

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

    O código Matlab de dois methods que foram publicados em periódicos conceituados (IEEE Transactions on Image Processing) está disponível aqui: https://ivulab.asu.edu/software

    verifique os algoritmos CPBDM e JNBM. Se você verificar o código não é muito difícil de ser portado e, aliás, é baseado no método do Marzialiano como recurso básico.

    Eu implementei isso usando fft no matlab e verifiquei o histograma do meio de computação fft e std mas também a function de ajuste pode ser feita

     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(:)); 

    Isso é o que eu faço no Opencv para detectar a qualidade do foco em uma região:

     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];