同态滤波器输出

时间:2016-09-10 14:37:14

标签: c# image-processing filter fft convolution

我编写了以下代码来开发同态滤波器。

我认为(我不确定)彩色图像是否过滤得很好。

enter image description here

如果是灰度图像,

enter image description here

为什么内核总是绿色?

此外,过滤器应该是锐化图像。但是,它没有这样做。

可能出现什么问题?

源代码:

enter image description here

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 个答案:

答案 0 :(得分:1)

  

为什么内核总是绿色?

这只是因为执行从ImageDataConverter.ToBitmap32bitColor调用的整数值内核HomoMorphicKernel.GetKernelBitmap的转换的函数明确地仅分配给RGBA字的绿色和alpha分量:

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

如果你想在灰度级上显示核心强度,你可以使用8位灰度图像执行此操作,或者为红色,绿色和蓝色分量指定相同的值:

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

此外,过滤器应该是锐化图像。但是,它没有这样做。什么可能出错?

这是一个更有趣的问题。简而言之,Gaussian.GaussianKernelHPF中低通的高通高斯内核转换是不正确的。您必须更正一般的想法来计算像1-f(x)这样的函数,其中f(x)是低通内核,但这适用于频域内核响应。在空间域中,常数项成为冲动。通过一些扩展考虑(为了获得频域中的单一脉冲并给出FFT定义,您需要在空间域中将脉冲的大小设置为Width*Height),您应该获得如下内容:

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

请注意,您还需要移动内核,使高斯内核的峰值位于像素位置(0,0),以避免获得循环移位的结果图像:

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

有关此修复程序的实现,请参阅this pull-request,其中还包括一些额外的调整(例如,我将Sigma修改为较不激进的值4,一些rescalings,在日志中显示内核-scale等)随意将参数调整为符合您要求的任何值。

有了这个,你应该得到一些看起来像: enter image description here

  

我认为(我不确定)彩色图像是否过滤得很好。

事实并非如此。对于锐化滤镜,我不希望颜色受到太大影响(指的是建筑物上天空的绿色,黄色和红色反射)。好消息是,上述同样的解决方案也可以处理:

enter image description here