Есть ли способ определить, размыто ли изображение? - PullRequest
182 голосов
/ 14 октября 2011

Мне было интересно, есть ли способ определить, является ли изображение размытым или нет, анализируя данные изображения.

Ответы [ 12 ]

148 голосов
/ 14 октября 2011

Еще один очень простой способ оценить резкость изображения - использовать фильтр Лапласа (или LoG) и просто выбрать максимальное значение. Использование надежного измерения, такого как квантиль 99,9%, вероятно, будет лучше, если вы ожидаете шума (то есть выбираете N-й самый высокий контраст вместо самого высокого контраста.) Если вы ожидаете варьирования яркости изображения, вы должны также включить шаг предварительной обработки для нормализации яркости изображения / контраст (например, выравнивание гистограммы).

Я реализовал предложение Саймона и это в Mathematica и попробовал его на нескольких тестовых изображениях:

test images

Первый тест размывает тестовые изображения с использованием фильтра Гаусса с переменным размером ядра, затем вычисляет БПФ размытого изображения и принимает среднее значение 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}]

Результат на логарифмическом графике:

fft result

5 линий представляют 5 тестовых изображений, ось X представляет радиус фильтра Гаусса. Графики уменьшаются, поэтому БПФ является хорошим показателем резкости.

Это код для оценки размытости «наивысшего уровня потерь»: он просто применяет фильтр LoG и возвращает самый яркий пиксель в результате фильтрации:

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

Результат на логарифмическом участке:

laplace result

Разброс для не размытых изображений здесь немного лучше (2,5 против 3,3), главным образом потому, что в этом методе используется только самый сильный контраст в изображении, в то время как БПФ представляет собой среднее значение по всему изображению. Функции также уменьшаются быстрее, поэтому может быть проще установить «размытый» порог.

119 голосов
/ 14 октября 2011

Да, это так. Вычислите быстрое преобразование Фурье и проанализируйте результат. Преобразование Фурье говорит вам, какие частоты присутствуют на изображении. Если высоких частот мало, изображение размытое.

Определение терминов «низкий» и «высокий» зависит от вас.

Редактировать

Как указано в комментариях, если вы хотите, чтобы один поплавок представлял размытость данного изображения, вы должны выработать подходящую метрику.

ответ Ники предоставьте такую ​​метрику. Свернуть изображение с лапласовым ядром:

   1
1 -4  1
   1

И используйте надежную максимальную метрику на выходе, чтобы получить число, которое вы можете использовать для определения порога. Старайтесь избегать слишком сглаживания изображений перед вычислением лапласиана, потому что вы только обнаружите, что сглаженное изображение действительно размыто: -).

75 голосов
/ 14 октября 2011

Во время некоторой работы с объективом с автофокусировкой я наткнулся на этот очень полезный набор алгоритмов для обнаружения фокусировки изображения . Он реализован в 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;
}

Нет гарантий того, являются ли эти меры лучшим выбором для вашей проблемы, но если вы отследите документы, связанные с этими мерами, они могут дать вам более глубокое понимание. Надеюсь, вы найдете код полезным! Я знаю, что сделал.

31 голосов
/ 01 августа 2012

Построение ответа 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, который должен иметь высокий уровень ложных срабатываний, чтобы быть полезным в моем приложении.

21 голосов
/ 25 ноября 2013

Я придумал совершенно другое решение. Мне нужно было проанализировать видеокадры, чтобы найти самый четкий кадр в каждом (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));
}
14 голосов
/ 26 марта 2015

Спасибо Ники за это замечательное предложение Лапласа. OpenCV docs указал мне в том же направлении: используя 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, даже если он полностью размыт.

9 голосов
/ 16 октября 2011

Один из способов, который я сейчас использую, измеряет разброс по краям изображения.Поищите эту статью:

@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 и изменил стандартное отклонение (используя команду imagemagick convert для получения размытых изображений).

enter image description here

Если сравнивать изображения разных размеровне забудьте нормализовать по ширине изображения, так как у больших изображений будут более широкие края.

Наконец, существенной проблемой является различие между художественным размытием и нежелательным размытием (вызванным отсутствием фокуса, сжатием, относительным движением объекта к камере), но это выходит за рамки простых подходов, подобных этому.В качестве примера художественного размытия взгляните на изображение Ленны: отражение Ленны в зеркале размыто, но ее лицо идеально сфокусировано.Это способствует более высокой оценке размытия изображения Ленны.

4 голосов
/ 12 марта 2015

Ответы выше объяснили многие вещи, но я думаю, что было бы полезно провести концептуальное различие.

Что, если вы сделаете идеально сфокусированное изображение размытого изображения?

Проблема обнаружения размытия хорошо ставится, только если у вас есть ссылка .Если вам необходимо спроектировать, например, систему автоматической фокусировки, вы сравниваете последовательность изображений, полученных с разной степенью размытия или сглаживания, и пытаетесь найти точку минимального размытия в этом наборе.Другими словами, вам нужно перекрестно ссылаться на различные изображения, используя один из методов, показанных выше (в основном - с различными возможными уровнями уточнения в подходе - поиск одного изображения с самым высоким высокочастотным контентом).

3 голосов
/ 11 октября 2016

Я пробовал решение на основе фильтра Лапласа из этого поста. Это не помогло мне. Итак, я попробовал решение из этого поста, и это было хорошо для моего случая (но медленно):

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):
1 голос
/ 03 июля 2014

я реализовал это, используя 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(:));
...