Мені було цікаво, чи є спосіб визначити, чи розмите зображення, чи не аналізуючи дані зображення.
Мені було цікаво, чи є спосіб визначити, чи розмите зображення, чи не аналізуючи дані зображення.
Відповіді:
Так. Обчисліть Швидке перетворення Фур'є та проаналізуйте результат. Перетворення Фур'є повідомляє вам, які частоти є в зображенні. Якщо низька кількість високих частот, то зображення розмито.
Визначення термінів "низький" та "високий" залежить від вас.
Редагувати :
Як зазначено в коментарях, якщо ви хочете, щоб один поплавок представляв розмитість даного зображення, ви повинні розробити відповідний показник.
відповідь nikie надає таку метрику. Оберніть зображення лапласіанським ядром:
1
1 -4 1
1
І використовуйте надійний максимальний показник на виході, щоб отримати число, яке ви можете використовувати для визначення порогу. Постарайтеся уникати занадто згладжування зображень перед тим, як обчислити лапласіанців, адже ви лише дізнаєтесь, що згладжене зображення справді розмите :-).
Ще один дуже простий спосіб оцінити чіткість зображення - використовувати фільтр Лапласа (або LoG) і просто вибрати максимальне значення. Використовувати надійну міру, наприклад, 99,9% -ний квантил, ймовірно, краще, якщо ви очікуєте шуму (тобто вибору найнижчого контрасту замість найбільшого контрасту). Якщо ви очікуєте різної яскравості зображення, вам слід також включити крок попередньої обробки для нормалізації яскравості зображення / контраст (наприклад, вирівнювання гістограми).
Я реалізував пропозицію Саймона та цю у Mathematica і спробував її на кількох тестових зображеннях:
Перший тест розмиває тестові зображення, використовуючи фільтр Гаусса з різним розміром ядра, потім обчислює 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 проти 3,3), головним чином тому, що цей метод використовує лише найсильніший контраст у зображенні, тоді як FFT по суті є середнім для всього зображення. Функції також зменшуються швидше, тому може бути простіше встановити "розмитий" поріг.
Під час роботи з лінзою з автоматичним фокусуванням я натрапив на цей дуже корисний набір алгоритмів для виявлення фокусу зображення . Він реалізований в MATLAB, але більшість функцій досить легко перенести на OpenCV з filter2D .
Це в основному опитування впровадження багатьох алгоритмів вимірювання фокусу. Якщо ви хочете прочитати оригінальні статті, в коді наведено посилання на авторів алгоритмів. Документ Pertuz 2012 та ін. Аналіз операторів вимірювання фокусу для форми від фокуса (SFF) дає чудовий огляд усіх цих заходів, а також їхніх характеристик (як з точки зору швидкості та точності, що застосовуються до SFF).
EDIT: Додано код 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, і я отримав ДУЖЕ ДУЖЕ хороші результати майже з усіма видами відео (за методом Нікіє, у мене були проблеми з оцифрованими відео 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 Академія, ви отримаєте багато корисних посилань.
EDIT
Ось сюжет оцінок розмитості, отриманих для 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 Transaction on Processing Image), доступний тут: https://ivulab.asu.edu/software
перевірити алгоритми CPBDM та JNBM. Якщо ви перевірите код, його перенести не дуже важко, і, до речі, він заснований на методі Марціаліано як основній функції.
Я реалізував це використовувати Fft в matlab і перевірити гістограму середнього обчислення fft і std, але також функцію fit можна зробити
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];