Свертка БПФ - действительно низкий PSNR

Я сворачиваю изображение (512 * 512) с помощью фильтра БПФ (размер ядра = 10), выглядит хорошо.

Но когда я сравниваю его с изображением, которое я свернул обычным образом, результат был ужасен.
PSNR составляет около 35.

67,187 / 262,144 Значения пикселей имеют разницу в 1 или более (максимум ~ 8) (с максимальным значением 255 пикселей).

Мой вопрос: это нормально при свертке в частотном пространстве или может быть проблема с моими функциями свертки / преобразования? . Потому что странно то, что я должен получить лучшие результаты при использовании double в качестве типа данных. Но он остается ПОЛНОСТЬЮ прежним.

Когда я преобразую изображение в частотное пространство, НЕ сворачиваю его, а затем преобразовываю обратно, все в порядке, и PSNR составляет около 140 при использовании float.

Кроме того, из-за того, что разница в пикселях составляет всего 1-10, я думаю, что могу исключить ошибки масштабирования.

РЕДАКТИРОВАТЬ: дополнительные сведения для скучающих заинтересованных лиц

Я использую библиотеку kissFFT с открытым исходным кодом. С реальным двумерным вводом (kiss_fftndr.h)

Тип данных моего изображения - PixelMatrix. Просто матрица с альфа, красным, зеленым и синим значениями от 0,0 до 1,0 с плавающей точкой.

Мое ядро ​​тоже PixelMatrix.

Вот несколько отрывков из функции свертки

Используемые типы данных:

#define kiss_fft_scalar float
#define kiss_fft_cpx struct {
    kiss_fft_scalar r;
    kiss_fft_scalar i,
}

Конфигурация БПФ:

//parameters to kiss_fftndr_alloc:
//1st param = array with the size of the 2 dimensions (in my case dim={width, height})
//2nd param = count of the dimensions (in my case 2)
//3rd param = 0 or 1 (forward or inverse FFT)
//4th and 5th params are not relevant

kiss_fftndr_cfg stf = kiss_fftndr_alloc(dim, 2, 0, 0, 0);
kiss_fftndr_cfg sti = kiss_fftndr_alloc(dim, 2, 1, 0, 0);

Заполнение и преобразование ядра:

I make a new array:

kiss_fft_scalar kernel[width*height];

I fill it with 0 in a loop.

Then I fill the middle of this array with the kernel I want to use.
So if I would use a 2*2 kernel with values 1/4, 1/4, 1/4 and 1/4 it would look like

0 0 0 0 0 0
0 1/4 1/4 0
0 1/4 1/4 0
0 0 0 0 0 0

The zeros are padded until they reach the size of the image.

Then I swap the quadrants of the image diagonally. It looks like:

1/4 0 0 1/4
 0  0 0  0
 0  0 0  0
1/4 0 0 1/4

now I transform it: kiss_fftndr(stf, floatKernel, outkernel);

outkernel is declarated as 
kiss_fft_cpx outkernel= new kiss_fft_cpx[width*height]

Получение цветов в массивы:

kiss_fft_scalar *red = new kiss_fft_scalar[width*height];
kiss_fft_scalar *green = new kiss_fft_scalar[width*height];
kiss_fft-scalar *blue = new kiss_fft_scalar[width*height];

for(int i=0; i<height; i++) {
 for(int j=0; i<width; j++) {
  red[i*height+j] = input.get(j,i).getRed();  //input is the input image pixel matrix
  green[i*height+j] = input.get(j,i).getGreen();
  blue{i*height+j] = input.get(j,i).getBlue();
 }
}

Then I transform the arrays:

kiss_fftndr(stf, red, outred);
kiss_fftndr(stf, green, outgreen);
kiss_fftndr(stf, blue, outblue);      //the out-arrays are type kiss_fft_cpx*

Свертка:

Что имеем сейчас:

  • 3 преобразованных цветовых массива из типа kiss_fft_cpx *
  • 1 преобразованный массив ядра из типа kiss_fft_cpx *

Они оба являются сложными массивами

А теперь свертка:

for(int m=0; m<til; m++) {
 for(int n=0; n<til; n++) {
  kiss_fft_scalar real = outcolor[m*til+n].r;      //I do that for all 3 arrys in my code!
  kiss_fft_scalar imag = outcolor[m*til+n].i;      //so I have realred, realgreen, realblue
  kiss_fft_scalar realMask = outkernel[m*til+n].r; // and imagred, imaggreen, etc.
  kiss_fft_scalar imagMask = outkernel[m*til+n].i;

  outcolor[m*til+n].r = real * realMask - imag * imagMask; //Same thing here in my code i
  outcolor[m*til+n].i = real * imagMask + imag * realMask; //do it with all 3 colors
 }
}

Теперь трансформирую их обратно:

kiss_fftndri(sti, outred, red);
kiss_fftndri(sti, outgreen, green);
kiss_fftndri(sti, outblue, blue);

and I create a new Pixel Matrix with the values from the color-arrays

PixelMatrix output;

for(int i=0; i<height; i++) {
 for(int j=0; j<width; j++) {
  Pixel p = new Pixel();
  p.setRed( red[i*height+j] / (width*height) ); //I divide through (width*height) because of the scaling happening in the FFT;
  p.setGreen( green[i*height+j] );
  p.setBlue( blue[i*height+j] );
  output.set(j , i , p);
 }
}

Примечания:

  • Я уже заранее позаботился о том, чтобы изображение имело размер в степени 2 (256 * 256), (512 * 512) и т. Д.

Примеры:

размер ядра: 10

Ввод:

Вывод:

Вывод обычной свертки:

моя консоль говорит:

142519 out of 262144 Pixels have a difference of 1 or more (maxRGB = 255)

PSNR: 32.006027221679688
MSE: 44.116752624511719

хотя для моих глаз они выглядят одинаково °. °

Может быть, одному человеку скучно и он перебирает код. Это не срочно, но это своего рода проблема, я просто хочу знать, что, черт возьми, я сделал не так ^^

И последнее, но не менее важное, моя функция PSNR, хотя я не думаю, что это проблема: D

void calculateThePSNR(const PixelMatrix first, const PixelMatrix second, float* avgpsnr, float* avgmse) {

int height = first.getHeight();
int width = first.getWidth();

BMP firstOutput;
BMP secondOutput;

firstOutput.SetSize(width, height);
secondOutput.SetSize(width, height);

double rsum=0.0, gsum=0.0, bsum=0.0;
int count = 0;
int total = 0;
for(int i=0; i<height; i++) {
    for(int j=0; j<width; j++) {
        Pixel pixOne = first.get(j,i);
        Pixel pixTwo = second.get(j,i);

        double redOne = pixOne.getRed()*255;
        double greenOne = pixOne.getGreen()*255;
        double blueOne = pixOne.getBlue()*255;

        double redTwo = pixTwo.getRed()*255;
        double greenTwo = pixTwo.getGreen()*255;
        double blueTwo = pixTwo.getBlue()*255;

        firstOutput(j,i)->Red = redOne;
        firstOutput(j,i)->Green = greenOne;
        firstOutput(j,i)->Blue = blueOne;

        secondOutput(j,i)->Red = redTwo;
        secondOutput(j,i)->Green = greenTwo;
        secondOutput(j,i)->Blue = blueTwo;

        if((redOne-redTwo) > 1.0 || (redOne-redTwo) < -1.0) {
            count++;
        }
        total++;

        rsum += (redOne - redTwo) * (redOne - redTwo);
        gsum += (greenOne - greenTwo) * (greenOne - greenTwo);
        bsum += (blueOne - blueTwo) * (blueOne - blueTwo);

    }
}
fprintf(stderr, "%d out of %d Pixels have a difference of 1 or more (maxRGB = 255)", count, total);
double rmse = rsum/(height*width);
double gmse = gsum/(height*width);
double bmse = bsum/(height*width);

double rpsnr = 20 * log10(255/sqrt(rmse));
double gpsnr = 20 * log10(255/sqrt(gmse));
double bpsnr = 20 * log10(255/sqrt(bmse));

firstOutput.WriteToFile("test.bmp");
secondOutput.WriteToFile("test2.bmp");

system("display test.bmp");
system("display test2.bmp");

*avgmse = (rmse + gmse + bmse)/3;
*avgpsnr = (rpsnr + gpsnr + bpsnr)/3;
}

person Marco    schedule 23.08.2011    source источник
comment
Свертка в пространстве изображений математически эквивалентна умножению в частотном пространстве, поэтому они должны быть одинаковыми. Не могли бы вы опубликовать примеры изображений и / или кода?   -  person WebMonster    schedule 23.08.2011
comment
Из-за барьера точности с типами данных должна быть какая-то разница, но не такая уж большая, как я думал. Да, я опубликую несколько примеров изображений / кода, как только смогу   -  person Marco    schedule 23.08.2011
comment
Doess целует окно ваши данные для вас? Кроме того, почему бы вам не построить карту ошибок, чтобы увидеть, где пиксели имеют наибольшую ошибку.   -  person koan    schedule 23.08.2011
comment
Что означает оконная обработка данных? Да, спасибо за комментарий, я сделаю это сейчас.   -  person Marco    schedule 24.08.2011
comment
Я еще не проверил ваш код, но у меня есть сильное подозрение, что два ваших результирующих изображения как-то смещены. Убедитесь, что они идеально выровнены, вычислив их коэффициенты взаимной корреляции.   -  person Phonon    schedule 26.08.2011


Ответы (2)


У Phonon была правильная идея. Ваши изображения сдвинуты. Если вы сместите изображение на (1,1), то MSE будет приблизительно равным нулю (при условии, что вы соответствующим образом замаскируете или обрежете изображения). Я подтвердил это, используя приведенный ниже код (Python + OpenCV).

import cv
import sys
import math

def main():
    fname1, fname2 = sys.argv[1:]
    im1 = cv.LoadImage(fname1)
    im2 = cv.LoadImage(fname2)

    tmp = cv.CreateImage(cv.GetSize(im1), cv.IPL_DEPTH_8U, im1.nChannels)
    cv.AbsDiff(im1, im2, tmp)
    cv.Mul(tmp, tmp, tmp)
    mse = cv.Avg(tmp)
    print 'MSE:', mse

    psnr = [ 10*math.log(255**2/m, 10) for m in mse[:-1] ]
    print 'PSNR:', psnr

if __name__ == '__main__':
    main()

Вывод:

MSE: (0.027584912741602553, 0.026742391458366047, 0.028147870144492403, 0.0)
PSNR: [63.724087463606452, 63.858801190963192, 63.636348220531396]
person mpenkov    schedule 06.09.2011
comment
Мне не удалось получить MSE, но я подтвердил, что смещение изображения не оставило ничего, кроме случайного одноуровневого шума в разнице между предоставленными выходными изображениями. - person Mark Ransom; 06.09.2011
comment
Да, вы правы, ровно ноль не будет. Я обновил свой пост, чтобы получить фактические значения для каждого канала. - person mpenkov; 06.09.2011
comment
Спасибо. Сам уже разобрался, просто забыл выложить сюда. Это действительно был такой простой x.x .. Я также теперь использую double вместо float для БПФ, что дает мне бесконечный psnr, поэтому изображения в значительной степени равны - person Marco; 10.09.2011

Мой совет вам попробовать реализовать следующий код:

A=double(inputS(1:10:length(inputS))); %segmentation 
A(:)=-A(:);
%process the image or signal by fast fourior transformation and inverse fft
fresult=fft(inputS);
fresult(1:round(length(inputS)*2/fs))=0;
fresult(end-round(length(fresult)*2/fs):end)=0;
Y=real(ifft(fresult));

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

person 3asheq els7ab    schedule 28.08.2013