Homomorphic Filter output

左心房为你撑大大i 提交于 2019-12-22 17:44:06

问题


I have written the following code to develop a Homomorphic Filter.

I think (I am not sure though) the color images are being filtered well.

In case of Grayscale images,

Why is the kernel always Green?

Also, the filter was supposed to be sharpening the image. But, its not doing so.

What could have possibly gone wrong?

.

.

Source Code:

Here is the Github repository.

public class HomomorphicFilter
{
    public HomoMorphicKernel Kernel = null;
    public bool IsPadded { get; set; }
    public int Width { get; set; }
    public int Height { get; set; }
    public double RH { get; set; }
    public double RL { get; set; }
    public double Sigma { get; set; }
    public double Slope { get; set; }
    public int PaddedWidth { get; set; }
    public int PaddedHeight { get; set; }
    public Bitmap KernelBitmap
    {
        get
        {
            if (IsPadded)
            {
                return Kernel.PaddedKernelBitmap;
            }
            else
            {
                return Kernel.KernelBitmap;
            }
        }
    }

    #region private methods
    private int[,] Apply8bit(int[,] imageData2d)
    {
        Complex[,] imageData2dShiftFftCplx = FourierShifter.ShiftFft(FourierTransform.ForwardFFT(ImageDataConverter.ToComplex(imageData2d)));

        Complex[,] fftShiftedFiltered = null;

        if (IsPadded)
        {
            fftShiftedFiltered = Tools.Multiply(Kernel.PaddedKernel, imageData2dShiftFftCplx);
        }
        else
        {
            fftShiftedFiltered = Tools.Multiply(Kernel.Kernel, imageData2dShiftFftCplx);
        }

        return ImageDataConverter.ToInteger(FourierTransform.InverseFFT(FourierShifter.RemoveFFTShift(fftShiftedFiltered)));
    }

    private int[, ,] Apply3d(int[, ,] image3d)
    {
        int[, ,] filteredImage3d = new int[image3d.GetLength(0), image3d.GetLength(1), image3d.GetLength(2)];

        int widtH = image3d.GetLength(1);
        int heighT = image3d.GetLength(2);

        int[,] imageData2d = new int[widtH, heighT];
        for (int dimension = 0; dimension < 3; dimension++)
        {
            for (int i = 0; i <= widtH - 1; i++)
            {
                for (int j = 0; j <= heighT - 1; j++)
                {
                    imageData2d[i, j] = image3d[dimension, i, j];
                }
            }

            int[,] filteredImage2d = Apply8bit(imageData2d);

            for (int i = 0; i <= widtH - 1; i++)
            {
                for (int j = 0; j <= heighT - 1; j++)
                {
                    filteredImage3d[dimension, i, j] = filteredImage2d[i, j];
                }
            }
        }

        return filteredImage3d;
    }
    #endregion

    public void Compute()
    {
        if (IsPadded)
        {
            if (Width >= PaddedWidth || Height >= PaddedHeight)
            {
                throw new Exception("PaddedWidth or PaddedHeight must be greater than Width or Height.");
            }
        }

        Kernel = new HomoMorphicKernel();
        Kernel.Width = Width;
        Kernel.Height = Height;
        Kernel.RH = RH;
        Kernel.RL = RL;
        Kernel.Sigma = Sigma;
        Kernel.Slope = Slope;
        Kernel.PaddedWidth = PaddedWidth;
        Kernel.PaddedHeight = PaddedHeight;
        Kernel.Compute();
    }

    public Bitmap Apply8bit(Bitmap image)
    {
        int[,] image2d = ImageDataConverter.ToInteger(image);

        int[,] filtered = Apply8bit(image2d);

        return ImageDataConverter.ToBitmap(filtered);
    }

    public Bitmap Apply32bitColor(Bitmap image)
    {
        int[, ,] image3d = ImageDataConverter.ToInteger3d_32bit(image);

        int[, ,] filtered = Apply3d(image3d);

        return ImageDataConverter.ToBitmap3d_32bit(filtered);
    }
}

回答1:


Why is the kernel always Green?

It is simply because the function which performs the conversion of the integer-valued kernel ImageDataConverter.ToBitmap32bitColor called from HomoMorphicKernel.GetKernelBitmap explicitly only assigns to the green and alpha components of the RGBA word:

for (int i = 0; i < bitmapData.Height; i++)
{
    for (int j = 0; j < bitmapData.Width; j++)
    {
        address[0] = 0;                 //<=== No red
        address[1] = (byte)image[j, i]; //<=== This is the green component
        address[2] = 0;                 //<=== No blue
        address[3] = 255;
        //4 bytes per pixel
        address += 4;
    }//end for j
    //4 bytes per pixel
    address += (bitmapData.Stride - (bitmapData.Width * 4));
}//end for i

If you wanted to show the kernel as intensity on a grey-scale, you could either do this with a 8bit grey-scale image, or assign the same value to the red, green and blue components:

        address[0] = (byte)image[j, i];
        address[1] = (byte)image[j, i];
        address[2] = (byte)image[j, i];
        address[3] = 255;

Also, the filter was supposed to be sharpening the image. But, its not doing so. What could have possibly gone wrong?

That's the more interesting question. In short your high-pass Gaussian kernel conversion in Gaussian.GaussianKernelHPF from low-pass is incorrect. You have to correct general idea to compute a function like 1-f(x) where f(x) is the low-pass kernel, but this applies to the frequency-domain kernel response. In the spatial-domain, the constant term becomes an impulse. With some scaling considerations (to get unitary impulse in the frequency-domain and given your FFT definition, you need the magnitude of the pulse to be Width*Height in the spatial-domain) you should get something such as:

double K = 1 / D1;
double S = Width * Height / (Math.PI * Math.PI * D2 * D2);
for (int i = -halfOfWidth; i < halfOfWidth; i++)
{
    for (int j = -halfOfHeight; j < halfOfHeight; j++)
    {
        int x = halfOfWidth + i;
        int y = halfOfHeight + j;

        if (i == 0 && j == 0)
        {
            GaussianKernel[x, y] = Width * Height + (K / D1 - Kernel[x, y]) * S;
        }
        else
        {
            GaussianKernel[x, y] = -Kernel[x, y] * S;
        }
    }
}

Note that you would also need to shift the kernel such that the peak of your Gaussian kernel is at pixel location (0,0) to avoid getting a circularly shifted result image:

//Swap halves so the peak is at pixel (0,0)
double[,] shifted = new double[Width, Height];
for (int j = 0; j < halfOfHeight; j++)
{
    for (int i = 0; i < halfOfWidth; i++)
    {
        int x = i + halfOfWidth;
        int y = j + halfOfHeight;

        shifted[x, y] = GaussianKernel[i, j];
        shifted[i, j] = GaussianKernel[x, y];
        shifted[x, j] = GaussianKernel[i, y];
        shifted[i, y] = GaussianKernel[x, j];
    }
}
return shifted;

See this pull-request for an implementation of this fix, which also includes a few extra tweaks (e.g. I've modified Sigma to a less aggressive value of 4, some rescalings, displaying the kernel in log-scale, etc.) Feel free to tweak the parameters to whatever values makes sense for your requirements.

With this you should get something that looks like:

I think (I am not sure though) the color images are being filtered well.

This wasn't quite the case. For a sharpening filter I wouldn't expect the colors to be affected so much (referring to the green, yellow and red reflections of the sky on the building). The good news is that the same fix as above also handles that:



来源:https://stackoverflow.com/questions/39427106/homomorphic-filter-output

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!