Fiquei me perguntando se existe uma maneira de determinar se uma imagem está embaçada ou não, analisando os dados da imagem.
Fiquei me perguntando se existe uma maneira de determinar se uma imagem está embaçada ou não, analisando os dados da imagem.
Respostas:
Sim, ele é. Calcule a Transformada rápida de Fourier e analise o resultado. A transformação de Fourier informa quais frequências estão presentes na imagem. Se houver uma quantidade baixa de altas frequências, a imagem está tremida.
A definição dos termos 'baixo' e 'alto' depende de você.
Editar :
Conforme declarado nos comentários, se você quiser um único flutuador representando a desfocagem de uma determinada imagem, precisará elaborar uma métrica adequada.
a resposta da nikie fornece essa métrica. Convolve a imagem com um kernel da Lapônia:
1
1 -4 1
1
E use uma métrica máxima robusta na saída para obter um número que você pode usar para limiar. Tente evitar suavizar demais as imagens antes de calcular o Laplaciano, porque você descobrirá apenas 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 (por exemplo, escolher o contraste N-mais alto em vez do contraste mais alto.) Se você espera um brilho variável da imagem, também deve incluir 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 essa no Mathematica, e tentei em algumas imagens de teste:
O primeiro teste desfoca as imagens de teste usando um filtro Gaussiano com um tamanho variável do kernel, calcula a FFT da imagem desfocada e calcula a média das frequências mais altas 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}]
Resultar em um gráfico logarítmico:
As 5 linhas representam as 5 imagens de teste, o eixo X representa o raio do filtro gaussiano. Os gráficos estão diminuindo, portanto a FFT é uma boa medida de nitidez.
Este é o código para o estimador de desfocagem "LoG mais alto": ele simplesmente aplica um filtro LoG e retorna o pixel mais brilhante no resultado do filtro:
testLaplacian[img_] := Table[
(
blurred = GaussianFilter[img, r];
Max[Flatten[ImageData[LaplacianGaussianFilter[blurred, 1]]]];
), {r, 0, 10, 0.5}]
Resultar em um gráfico logarítmico:
A propagação 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 a FFT é essencialmente uma média sobre toda a imagem. As funções também estão diminuindo mais rapidamente, portanto, pode ser mais fácil definir um limite "embaçado".
Durante algum trabalho com uma lente de foco automático, deparei-me com um conjunto muito útil de algoritmos para detectar o foco da imagem . É implementado no MATLAB, mas a maioria das funções é bastante fácil de portar para o OpenCV com o filter2D .
É basicamente uma implementação de pesquisa de muitos algoritmos de medição de foco. Se você quiser ler os artigos originais, as 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 a forma a partir do foco (SFF) fornece uma ótima revisão de todas essas medidas, bem como de seu desempenho (tanto em termos de velocidade quanto de precisão, conforme aplicado ao SFF).
EDIT: Adicionado código MATLAB, caso o link morra.
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)
[M N] = 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(FM<Th)=0;
FM = sum(FM(:))/sum(sum(FM~=0));
case 'GRAS' % Squared gradient (Eskicioglu95)
Ix = diff(Image, 1, 2);
FM = Ix.^2;
FM = mean2(FM);
case 'HELM' %Helmli's mean method (Helmli2001)
MEANF = fspecial('average',[WSize WSize]);
U = imfilter(Image, MEANF, 'replicate');
R1 = U./Image;
R1(Image==0)=1;
index = (U>Image);
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)
[M N] = 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_<double>(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 de que essas medidas sejam ou não a melhor escolha para o seu problema, mas se você localizar os documentos associados a essas medidas, eles poderão fornecer mais informações. Espero que você ache o código útil! Eu sei que sim.
Aproveitando a resposta da Nike. É simples implementar o método baseado em laplaciano com 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 nos meus testes em amostras do mundo real, é um bom indicador de se a câmera está focada ou não. Não é de surpreender que os valores normais dependam 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 vim com uma solução totalmente diferente. Eu precisava analisar os quadros estáticos de vídeo para encontrar o mais nítido em todos os quadros (X). Dessa forma, eu detectaria desfoque de movimento e / ou imagens desfocadas.
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 da nikie, tive problemas com vídeos VHS digitalizados e vídeos entrelaçados pesados).
Otimizei o desempenho definindo uma região de interesse (ROI) na imagem original.
Usando o EmguCV:
//Convert image using Canny
using (Image<Gray, byte> 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, pela ótima sugestão de Laplace. Os documentos do OpenCV me apontaram 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)))
resultado está entre 0-255. Descobri que algo acima de 200 está muito focado e, aos 100, está visivelmente embaçado. o máximo nunca fica muito abaixo dos 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 fica atrás de um paywall, mas já vi algumas cópias gratuitas por aí. Basicamente, eles localizam bordas verticais em uma imagem e medem a largura dessas bordas. A média da largura fornece o resultado final da estimativa de desfoque para a imagem. Bordas mais amplas correspondem a imagens tremidas e vice-versa.
Esse problema pertence ao campo de estimativa da qualidade da imagem sem referência . Se você procurar no Google Scholar, terá muitas referências úteis.
EDITAR
Aqui está um gráfico das estimativas de desfoque obtidas para as 5 imagens no post da nikie. Valores mais altos correspondem a maior desfocagem. Usei um filtro Gaussiano de tamanho fixo 11x11 e variei o desvio padrão (usando o convert
comando imagemagick para obter as imagens borradas).
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 amplas.
Finalmente, um problema significativo é distinguir entre desfoque artístico e desfoque indesejado (causado por falta de foco, compressão, movimento relativo do objeto para a câmera), mas isso está além de abordagens simples como esta. Para um exemplo de desfoque artístico, dê uma olhada na imagem de Lenna: o reflexo de Lenna no espelho está embaçado, mas seu rosto está perfeitamente focado. Isso contribui para uma estimativa de desfoque mais alta da imagem Lenna.
Eu tentei solução com base no filtro Laplacian deste post. Isso não me ajudou. Então, 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))
Uma imagem menos desfocada tem um sum
valor máximo !
Você também pode ajustar a velocidade e a precisão alterando a etapa, por exemplo
esta parte
for x in range(width - 1):
você pode substituir por este
for x in range(0, width - 1, 10):
As respostas acima elucidaram muitas coisas, mas acho que é útil fazer uma distinção conceitual.
E se você tirar uma foto perfeitamente focada de uma imagem borrada?
O problema de detecção de desfoque só é bem colocado quando você tem uma referência . Se você precisar projetar, por exemplo, um sistema de foco automático, compare uma sequência de imagens tiradas com diferentes graus de desfoque ou suavização e tente encontrar o ponto de desfoque mínimo nesse 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 imagem com o maior conteúdo de alta frequência).
O código Matlab de dois métodos que foram publicados em periódicos conceituados (IEEE Transactions on Image Processing) estão disponíveis aqui: https://ivulab.asu.edu/software
verifique os algoritmos CPBDM e JNBM. Se você verificar o código, não é muito difícil ser portado e, aliás, é baseado no método de Marzialiano como recurso básico.
Eu o implementei use fft no matlab e verifique o histograma da média de computação fft e std, mas também a função 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(:));
É o que 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];