Search code examples
javascriptopencvimage-processingfftlowpass-filter

Fourier Ideal Lowpass filter result is bit weird


I'm trying to implement a Fourier ideal lowpass filter. The result I got is kind of correct (I guess?), but there are some weird unprocessed pixels around the image.

50 is the cutoff radius.

Here is the picture of original image, grayscale, Fourier transform + filtered, and reverse transform back to original image.

result

Here is my code:

function idealLowHighPass(img,par,d0){
  let height=img.rows;
  let width=img.cols;
  let prettypls=img.clone();
  let temp=createArray(height,width);
  let tmp = fastFourier(img);
  var tempD;

  let fraction = 0.3;
  let filter = 1;
  var x = width/2;
  var y = height/2;
  var state = -1;
  for(i=0;i<height;i++){
    for(j=0;j<width;j++){

      if(i > y && j > x){
        state = 3;
      }
      else if( i > y){
        state = 1;
      }
      else if (j > x){
        state = 2;
      }
      else{
        state = 0;
      }

      switch(state){
        case 0:
            tempD = (i * i + j * j);
            tempD = Math.sqrt(tempD);
            break;
        case 1:
            tempD = ((height - i) * (height - i) + j * j);
            tempD = Math.sqrt(tempD);
            break;
        case 2:
            tempD = (i * i + (width - j) * (width - j));
            tempD = Math.sqrt(tempD);
            break;
        case 3:
            tempD = ((height - i) * (height - i) + (width - j) * (width - j));
            tempD = Math.sqrt(tempD);
            break;
        default:
            break;
      }

      if(par == 'ideallowpass'){

        if(tempD <= d0){
            tempD = 1;
        }
        else{
            tempD = 0;
        }
      }
      tmp[i][j].re*=tempD;
      tmp[i][j].im*=tempD; 
    }
  }

  //HANDLE FFT
  //take the magnitudes
  for(i=0;i<height;i++){
    for(j=0;j<width;j++){
      temp[i][j]=Math.round(tmp[i][j].re);
    }
  }

  temp=logTransform(temp,height,width);
  for(i=0;i<height;i++){
    for(j=0;j<width;j++){
      let pixel = prettypls.ucharPtr(i,j);
      pixel[0]=Math.round(temp[i][j]);
    }
  }

  // rearrange the quadrants of Fourier image
  // so that the origin is at the image center
  let cx = prettypls.cols / 2;
  let cy = prettypls.rows / 2;
  let tmp2 = new cv.Mat();

  let rect0 = new cv.Rect(0, 0, cx, cy);
  let rect1 = new cv.Rect(cx, 0, cx, cy);
  let rect2 = new cv.Rect(0, cy, cx, cy);
  let rect3 = new cv.Rect(cx, cy, cx, cy);

  let q0 = prettypls.roi(rect0);
  let q1 = prettypls.roi(rect1);
  let q2 = prettypls.roi(rect2);
  let q3 = prettypls.roi(rect3);

  // exchange 1 and 4 quadrants
  q0.copyTo(tmp2);
  q3.copyTo(q0);
  tmp2.copyTo(q3);

  // exchange 2 and 3 quadrants
  q1.copyTo(tmp2);
  q2.copyTo(q1);
  tmp2.copyTo(q2);

  cv.imshow('fourierTransform', prettypls);

  //HANDLE IFFT
  let tmp1 = reverseFastFourier(tmp,height,width);
  //take the magnitudes
  for(i=0;i<height;i++){
    for(j=0;j<width;j++){
      temp[i][j]=Math.round(tmp1[i][j].re);
    }
  }

  for(i=0;i<height;i++){
    for(j=0;j<width;j++){
      let pixel = prettypls.ucharPtr(i,j);
      pixel[0] = Math.max(0, Math.min(255, temp[i][j]));
    }
  }
  cv.imshow('reverseFourier', prettypls);
  temp=[];tmp=[];prettypls.delete();
}

Is there something I missed or something wrong with my code?


Solution

  • I guess your problem is in this line:

    pixel[0]=Math.round(temp[i][j]);
    

    Here you copy a floating-point value, resulting from the real component of the inverse Fourier transform, into an unsigned 8-bit integer (I presume from what I can see in the code).

    If one of these values is slightly above 255, which can happen with an "ideal" low-pass filter because of the ringing artefacts, then the conversion to 8-bit unsigned integer will cause the value to wrap around.

    What you need to do is clamp the value to the [0,255] range. Replace that line with:

    pixel[0] = Math.max(0, Math.min(255, temp[i][j]));
    

    Note: I'm guessing that this syntax will work based on some quick Googling, but it is the concept that you need to look at...

    The Math.round call is not necessary, as temp already contains rounded values.