问题
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