FFT Convolution - действительно низкий PSNR - PullRequest
2 голосов
/ 23 августа 2011

Я сворачиваю изображение (512 * 512) с FFT-фильтром (kernelsize = 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 float

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

Вот некоторые фрагменты из функции Convolution

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

#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

Input: image

Выход: image

Вывод из нормальной свертки: image

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

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;
}

Ответы [ 2 ]

2 голосов
/ 06 сентября 2011

Фонон имел правильную идею. Ваши изображения смещены. Если вы сдвинете ваше изображение на (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]
0 голосов
/ 29 августа 2013

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

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));

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

...