Мне было интересно, если есть способ определить, является ли изображение размытым или нет, анализируя данные изображения.
Мне было интересно, если есть способ определить, является ли изображение размытым или нет, анализируя данные изображения.
Ответы:
Да, это так. Вычислите быстрое преобразование Фурье и проанализируйте результат. Преобразование Фурье говорит вам, какие частоты присутствуют на изображении. Если высоких частот мало, изображение размытое.
Определение терминов «низкий» и «высокий» зависит от вас.
Редактировать :
Как указано в комментариях, если вы хотите, чтобы один float представлял размытость данного изображения, вы должны выработать подходящую метрику.
В ответе ники предоставьте такую метрику. Сверните изображение с лапласовым ядром:
1
1 -4 1
1
И используйте надежную максимальную метрику на выходе, чтобы получить число, которое вы можете использовать для определения порога. Старайтесь избегать слишком сглаживания изображений перед вычислением лапласиана, потому что вы только обнаружите, что сглаженное изображение действительно размыто :-).
Другой очень простой способ оценить резкость изображения - использовать фильтр Лапласа (или LoG) и просто выбрать максимальное значение. Использование надежного измерения, такого как квантиль 99,9%, вероятно, лучше, если вы ожидаете шума (то есть выбираете N-й самый высокий контраст вместо самого высокого контраста.) Если вы ожидаете, что переменная яркость изображения будет изменена, вам также следует включить этап предварительной обработки для нормализации яркости изображения / контраст (например, выравнивание гистограммы).
Я реализовал предложение Саймона и это в Mathematica и попробовал его на нескольких тестовых изображениях:
Первый тест размывает тестовые изображения с использованием фильтра Гаусса с изменяющимся размером ядра, затем вычисляет БПФ размытого изображения и принимает среднее значение 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 представляет радиус фильтра Гаусса. Графики уменьшаются, поэтому БПФ является хорошим показателем резкости.
Это код для оценки размытости с «самым высоким уровнем LoG»: он просто применяет фильтр LoG и возвращает самый яркий пиксель в результате фильтра:
testLaplacian[img_] := Table[
(
blurred = GaussianFilter[img, r];
Max[Flatten[ImageData[LaplacianGaussianFilter[blurred, 1]]]];
), {r, 0, 10, 0.5}]
Результат в логарифмическом графике:
Разброс для не размытых изображений здесь немного лучше (2,5 против 3,3), главным образом потому, что этот метод использует только самый сильный контраст в изображении, в то время как БПФ, по сути, представляет собой среднее значение по всему изображению. Функции также уменьшаются быстрее, поэтому может быть проще установить «размытый» порог.
Во время некоторой работы с объективом с автофокусом я наткнулся на этот очень полезный набор алгоритмов для определения фокусировки изображения . Он реализован в MATLAB, но большинство функций довольно легко портировать на OpenCV с помощью filter2D. .
Это в основном обзорная реализация многих алгоритмов измерения фокуса. Если вы хотите прочитать оригинальные статьи, ссылки на авторов алгоритмов приведены в коде. Бумага 2012 года Pertuz, et al. Анализ операторов измерения фокуса для формы от фокуса (SFF) дает большой обзор всех этих показателей, а также их производительности (как с точки зрения скорости и точности применительно к 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;
}
Нет гарантий того, являются ли эти меры лучшим выбором для вашей проблемы, но если вы отследите документы, связанные с этими мерами, они могут дать вам более глубокое понимание. Надеюсь, вы найдете код полезным! Я знаю, что сделал.
Построение ответа Nike. Реализовать метод на основе лапласиана с помощью 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;
}
Возвращает короткое замыкание, указывающее максимальную обнаруженную резкость, которая, основываясь на моих тестах на реальных образцах, является довольно хорошим показателем того, находится ли камера в фокусе или нет. Неудивительно, что нормальные значения зависят от сцены, но намного меньше, чем метод 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 указали мне в одном направлении: использование python, cv2 (opencv 2.4.10) и numpy ...
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
numpy.max(cv2.convertScaleAbs(cv2.Laplacian(gray_image,3)))
результат между 0-255. Я обнаружил, что все, что выше 200, очень в фокусе, а к 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, вы получите много полезных ссылок.
РЕДАКТИРОВАТЬ
Вот график оценок размытия, полученных для 5 изображений в посте Ники. Более высокие значения соответствуют большей размытости. Я использовал фильтр Гаусса с фиксированным размером 11x11 и изменил стандартное отклонение (используя convert
команду imagemagick для получения размытых изображений).
Если вы сравниваете изображения разных размеров, не забудьте выполнить нормирование по ширине изображения, так как у больших изображений будут более широкие края.
Наконец, серьезной проблемой является различие между художественным размытием и нежелательным размытием (вызванным отсутствием фокуса, сжатием, относительным перемещением объекта к камере), но это выходит за рамки простых подходов, подобных этому. В качестве примера художественного размытия взгляните на изображение Ленны: отражение Ленны в зеркале размыто, но ее лицо идеально сфокусировано. Это способствует более высокой оценке размытия изображения Ленны.
Я попробовал решение на основе фильтра Лапласа из этого поста. Это не помогло мне. Итак, я попробовал решение из этого поста, и это было хорошо для моего случая (но медленно):
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))
Менее размытое изображение имеет максимум sum
значение!
Вы также можете настроить скорость и точность, изменив шаг, например
эта часть
for x in range(width - 1):
Вы можете заменить этим
for x in range(0, width - 1, 10):
Ответы выше объяснили многие вещи, но я думаю, что было бы полезно провести концептуальное различие.
Что если вы сделаете идеально сфокусированное изображение размытого изображения?
Проблема обнаружения размытия хорошо ставится, только если у вас есть ссылка . Если вам необходимо спроектировать, например, систему автоматической фокусировки, вы сравниваете последовательность изображений, полученных с разной степенью размытия или сглаживания, и пытаетесь найти точку минимального размытия в этом наборе. Другими словами, вам нужно сопоставить различные изображения, используя один из методов, показанных выше (в основном - с различными возможными уровнями детализации в подходе - поиск одного изображения с самым высоким высокочастотным контентом).
Код Matlab двух методов, которые были опубликованы в авторитетных журналах (транзакции IEEE по обработке изображений), доступен здесь: https://ivulab.asu.edu/software.
проверьте алгоритмы CPBDM и JNBM. Если вы проверите код, его не очень сложно перенести, и, кстати, он основан на методе Марциалиано в качестве базовой функции.
Я реализовал это, используя FFT в Matlab и проверить гистограмму FFT вычислить среднее значение и STD, но также можно сделать функцию подгонки
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(:));
Вот что я делаю в Opencv для определения качества фокусировки в регионе:
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];