이미지 데이터를 분석하여 이미지가 흐릿한 지 여부를 결정하는 방법이 있는지 궁금합니다.
답변
그렇습니다. 고속 푸리에 변환을 계산하고 결과를 분석하십시오. 푸리에 변환은 이미지에 어떤 주파수가 있는지 알려줍니다. 고주파수가 적은 경우 이미지가 흐릿합니다.
‘낮음’과 ‘높음’이라는 용어를 정의하는 것은 당신에게 달려 있습니다.
편집 :
주석에서 언급했듯이 주어진 이미지 의 흐릿함 을 나타내는 단일 플로트를 원하면 적절한 메트릭을 계산해야합니다.
nikie의 답변 은 이러한 지표를 제공합니다. Laplacian 커널로 이미지를 관련 시키십시오.
1
1 -4 1
1
그리고 출력에서 강력한 최대 메트릭을 사용하여 임계 값에 사용할 수있는 숫자를 얻습니다. 라플라시안을 계산하기 전에 이미지가 너무 매끄러 워지지 않도록하십시오. 매끄럽게 된 이미지가 실제로 흐릿하다는 것을 알기 때문입니다. 🙂
답변
이미지의 선명도를 추정하는 또 다른 매우 간단한 방법은 Laplace (또는 LoG) 필터를 사용하여 간단히 최대 값을 선택하는 것입니다. 노이즈가 예상되는 경우 99.9 % 분위수와 같은 강력한 측정 값을 사용하는 것이 좋습니다 (예 : 가장 높은 대비 대신 N 번째로 높은 대비 선택). 이미지 밝기가 변할 것으로 예상되는 경우 이미지 밝기를 정규화하기위한 전처리 단계도 포함해야합니다. 대비 (예 : 히스토그램 등화).
나는 Mathematica에서 Simon의 제안과 이것을 제안했으며 몇 가지 테스트 이미지에서 시도했습니다.
첫 번째 테스트는 다양한 커널 크기의 가우시안 필터를 사용하여 테스트 이미지를 흐리게 한 다음 흐릿한 이미지의 FFT를 계산하고 평균 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}]
로그 플롯 결과 :
5 개의 선은 5 개의 테스트 이미지를 나타내고 X 축은 가우스 필터 반경을 나타냅니다. 그래프가 감소하고 있으므로 FFT는 선명도를 측정하는 데 유용합니다.
다음은 “최고 LoG”흐림 추정기의 코드입니다. LoG 필터를 적용하고 필터 결과에서 가장 밝은 픽셀을 반환합니다.
testLaplacian[img_] := Table[
(
blurred = GaussianFilter[img, r];
Max[Flatten[ImageData[LaplacianGaussianFilter[blurred, 1]]]];
), {r, 0, 10, 0.5}]
로그 플롯 결과 :
흐릿하지 않은 이미지의 스프레드는 여기에서 약간 더 좋습니다 (2.5 vs 3.3). 주로이 방법은 이미지에서 가장 강한 대비 만 사용하고 FFT는 기본적으로 전체 이미지에 대한 평균이기 때문입니다. 기능도 더 빨리 감소하므로 “흐리게”임계 값을 설정하는 것이 더 쉬울 수 있습니다.
답변
자동 초점 렌즈로 작업하는 동안 이미지 초점 을 감지하는 매우 유용한 알고리즘을 발견했습니다 . MATLAB으로 구현되었지만 대부분의 기능은 filter2D 로 OpenCV로 이식하기가 매우 쉽습니다 .
기본적으로 많은 초점 측정 알고리즘의 설문 조사 구현입니다. 원본 논문을 읽으려면 알고리즘 작성자에 대한 참조가 코드에 제공됩니다. Pertuz 등의 2012 논문. SFF ( shape from focus)에 대한 초점 측정 연산자 분석을 통해 이러한 측정뿐만 아니라 성능 (SFF에 적용되는 속도 및 정확도 측면)을 모두 검토 할 수 있습니다.
편집 : 링크가 죽는 경우를 대비하여 MATLAB 코드가 추가되었습니다.
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
%******************************************************************
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;
}
이러한 조치가 문제에 가장 적합한 지 여부를 보장하지는 않지만 이러한 조치와 관련된 논문을 추적하면 더 많은 통찰력을 얻을 수 있습니다. 코드가 유용하기를 바랍니다. 나도 알아
답변
나이키의 답을 바탕으로합니다. opencv로 laplacian 기반 메소드를 구현하는 것은 간단합니다.
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;
}
실제 샘플에 대한 테스트를 기반으로 감지 된 최대 선명도를 나타내는 짧은 값을 반환하면 카메라 초점이 맞았는지 여부를 나타내는 좋은 지표입니다. 놀랍지 않게도, 정상 값은 장면에 따라 다르지만 FFT 방법보다 훨씬 적습니다 .FFT 방법은 내 응용 프로그램에 유용하기 위해 오 탐율이 높아야합니다.
답변
나는 완전히 다른 해결책을 생각해 냈습니다. 모든 (X) 프레임에서 가장 선명한 프레임을 찾기 위해 비디오 스틸 프레임을 분석해야했습니다. 이런 식으로 모션 블러 및 / 또는 초점이 맞지 않는 이미지를 감지합니다.
Canny Edge 감지를 사용하여 거의 모든 종류의 비디오에서 매우 좋은 결과를 얻었습니다 (nikie의 방법으로 디지털 VHS 비디오 및 무거운 인터레이스 비디오에 문제가 있음).
원본 이미지에서 관심 영역 (ROI)을 설정하여 성능을 최적화했습니다.
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));
}
답변
멋진 라플라스 제안에 감사드립니다.
OpenCV 문서 는 파이썬, cv2 (opencv 2.4.10) 및 numpy를 사용하여 같은 방향으로 나를 지적했습니다.
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
numpy.max(cv2.convertScaleAbs(cv2.Laplacian(gray_image,3)))
결과는 0-255 사이입니다. 200ish가 넘는 것은 매우 초점이 맞았고 100에 의해 눈에 띄게 흐릿합니다. 최대 값이 완전히 흐려져도 최대 값은 20 미만이되지 않습니다.
답변
현재 사용중인 한 가지 방법은 이미지의 가장자리 확산을 측정합니다. 이 논문을 찾으십시오 :
@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} }
일반적으로 월페이퍼 뒤에 있지만 무료 사본이 있습니다. 기본적으로 이미지에서 세로 가장자리를 찾은 다음 해당 가장자리의 너비를 측정합니다. 너비를 평균하면 이미지에 대한 최종 흐림 추정 결과가 제공됩니다. 더 넓은 가장자리는 흐릿한 이미지에 해당하며 그 반대도 마찬가지입니다.
이 문제는 비 참조 이미지 품질 추정 분야에 속한다 . Google Scholar에서 찾아 보면 유용한 참고 자료를 많이 얻을 수 있습니다.
편집하다
다음은 nikie의 게시물에서 5 개의 이미지에 대해 얻은 흐림 추정치의 도표입니다. 값이 클수록 흐림이 커집니다. 고정 크기 11×11 Gaussian 필터를 사용하고 표준 편차를 변경했습니다 (imagemagick의 convert
명령을 사용하여 이미지가 흐려짐 ).
크기가 다른 이미지를 비교하는 경우 이미지 너비가 커질수록 이미지 너비가 정규화되는 것을 잊지 마십시오.
마지막으로 중요한 문제는 예술적 흐림 효과와 원하지 않는 흐림 효과 (초점 미스, 압축, 카메라에 대한 피사체의 상대적 움직임으로 인한)를 구별하는 것입니다. 예술적 흐림 효과의 예를 보려면 Lenna 이미지를 살펴보십시오. 거울에서 Lenna의 반사가 흐릿하지만 얼굴의 초점이 완벽합니다. 이는 Lenna 이미지에 대한 더 높은 흐림 추정치에 기여합니다.