我编写了一些例程来使用3x3内核来锐化灰度图像,
-1 -1 -1
-1 9 -1
-1 -1 -1以下代码在非FFT(空域)卷积的情况下工作良好,但不工作于基于FFT的(频域)卷积。。
输出图像似乎模糊了。
我有几个问题:
(1)此例程无法生成所需的结果。它还冻结了应用程序。
public static Bitmap ApplyWithPadding(Bitmap image, Bitmap mask)
{
if(image.PixelFormat == PixelFormat.Format8bppIndexed)
{
Bitmap imageClone = (Bitmap)image.Clone();
Bitmap maskClone = (Bitmap)mask.Clone();
/////////////////////////////////////////////////////////////////
Complex[,] cPaddedLena = ImageDataConverter.ToComplex(imageClone);
Complex[,] cPaddedMask = ImageDataConverter.ToComplex(maskClone);
Complex[,] cConvolved = Convolution.Convolve(cPaddedLena, cPaddedMask);
return ImageDataConverter.ToBitmap(cConvolved);
}
else
{
throw new Exception("not a grascale");
}
}(2)这个例程取得了很好的效果。但是,太慢了。
public static Bitmap Apply(Bitmap sourceBitmap)
{
Sharpen filter = new Sharpen();
BitmapData sourceData = sourceBitmap.LockBits(new Rectangle(0, 0,
sourceBitmap.Width, sourceBitmap.Height),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte[] pixelBuffer = new byte[sourceData.Stride * sourceData.Height];
byte[] resultBuffer = new byte[sourceData.Stride * sourceData.Height];
Marshal.Copy(sourceData.Scan0, pixelBuffer, 0, pixelBuffer.Length);
sourceBitmap.UnlockBits(sourceData);
double blue = 0.0;
double green = 0.0;
double red = 0.0;
int filterWidth = filter.FilterMatrix.GetLength(1);
int filterHeight = filter.FilterMatrix.GetLength(0);
int filterOffset = (filterWidth - 1) / 2;
int calcOffset = 0;
int byteOffset = 0;
for (int offsetY = filterOffset; offsetY < sourceBitmap.Height - filterOffset; offsetY++)
{
for (int offsetX = filterOffset; offsetX <
sourceBitmap.Width - filterOffset; offsetX++)
{
blue = 0;
green = 0;
red = 0;
byteOffset = offsetY *
sourceData.Stride +
offsetX * 4;
for (int filterY = -filterOffset;
filterY <= filterOffset; filterY++)
{
for (int filterX = -filterOffset;
filterX <= filterOffset; filterX++)
{
calcOffset = byteOffset +
(filterX * 4) +
(filterY * sourceData.Stride);
blue += (double)(pixelBuffer[calcOffset]) *
filter.FilterMatrix[filterY + filterOffset,
filterX + filterOffset];
green += (double)(pixelBuffer[calcOffset + 1]) *
filter.FilterMatrix[filterY + filterOffset,
filterX + filterOffset];
red += (double)(pixelBuffer[calcOffset + 2]) *
filter.FilterMatrix[filterY + filterOffset,
filterX + filterOffset];
}
}
blue = filter.Factor * blue + filter.Bias;
green = filter.Factor * green + filter.Bias;
red = filter.Factor * red + filter.Bias;
if (blue > 255)
{ blue = 255; }
else if (blue < 0)
{ blue = 0; }
if (green > 255)
{ green = 255; }
else if (green < 0)
{ green = 0; }
if (red > 255)
{ red = 255; }
else if (red < 0)
{ red = 0; }
resultBuffer[byteOffset] = (byte)(blue);
resultBuffer[byteOffset + 1] = (byte)(green);
resultBuffer[byteOffset + 2] = (byte)(red);
resultBuffer[byteOffset + 3] = 255;
}
}
Bitmap resultBitmap = new Bitmap(sourceBitmap.Width, sourceBitmap.Height);
BitmapData resultData = resultBitmap.LockBits(new Rectangle(0, 0,
resultBitmap.Width, resultBitmap.Height),
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
Marshal.Copy(resultBuffer, 0, resultData.Scan0, resultBuffer.Length);
resultBitmap.UnlockBits(resultData);
return resultBitmap;
}(3)下面是我的GUI代码。如果我使用图像作为掩码,SharpenFilter.ApplyWithPadding()就能正常工作。但是,如果我使用3x3内核,则无法工作。
string path = @"E:\lena.png";
string path2 = @"E:\mask.png";
Bitmap _inputImage;
Bitmap _maskImage;
private void LoadImages_Click(object sender, EventArgs e)
{
_inputImage = Grayscale.ToGrayscale(Bitmap.FromFile(path) as Bitmap);
/*
_maskImage = Grayscale.ToGrayscale(Bitmap.FromFile(path2) as Bitmap);
*/
SharpenFilter filter = new SharpenFilter();
double[,] mask = new double[,] { { -1, -1, -1, },
{ -1, 9, -1, },
{ -1, -1, -1, }, };
_maskImage = ImageDataConverter.ToBitmap(mask);
inputImagePictureBox.Image = _inputImage;
maskPictureBox.Image = _maskImage;
}
Bitmap _paddedImage;
Bitmap _paddedMask;
private void padButton_Click(object sender, EventArgs e)
{
Bitmap lena = Grayscale.ToGrayscale(_inputImage);
Bitmap mask = Grayscale.ToGrayscale(_maskImage);
////Not working...
//int maxWidth = (int)Math.Max(lena.Width, mask.Width);
//int maxHeight = (int)Math.Max(lena.Height, mask.Height);
////This is working correctly in case if I use a png image as a mask.
int maxWidth = (int)Tools.ToNextPow2(Convert.ToUInt32(lena.Width + mask.Width));
int maxHeight = (int)Tools.ToNextPow2(Convert.ToUInt32(lena.Height + mask.Height));
_paddedImage = ImagePadder.Pad(lena, maxWidth, maxHeight);
_paddedMask = ImagePadder.Pad(mask, maxWidth, maxHeight);
paddedImagePictureBox.Image = _paddedImage;
paddedMaskPictureBox.Image = _paddedMask;
}
private void filterButton_Click(object sender, EventArgs e)
{
// Not working properly.
// Freezes the application.
Bitmap sharp = SharpenFilter.ApplyWithPadding(_paddedImage, _paddedMask);
////Works well. But, very slow.
//Bitmap sharp = SharpenFilter.Apply(_paddedImage);
filteredPictureBox.Image = sharp as Bitmap;
}输出:

源代码:

发布于 2016-08-30 02:58:18
主要问题似乎是将内核解释为由无符号字节值组成的图像。因此,将-1值转换为255,有效地计算了与内核的卷积。
255 255 255
255 9 255
255 255 255这可以立即从“卷积核”图像的白色区域观察到。因此,产生的核心是低通滤波器,产生相应的模糊效应.
可能是处理这一问题的最佳方法,即将内核读入有符号值的矩阵,而不是图像。
如果您仍然喜欢将内核作为映像处理,则需要将图像转换回有符号的值。我认为实现这一结果的最简单方法是创建一个修改过的ImageDataConverter.ToInteger(Bitmap)版本,将字节映射到有符号的值:
public static Complex[,] Unwrap(Bitmap bitmap)
{
int Width = bitmap.Width;
int Height = bitmap.Height;
Complex[,] array2D = new Complex[bitmap.Width, bitmap.Height];
...
else// If there is only one channel:
{
iii = (int)(*address);
if (iii >= 128)
{
iii -= 256;
}
}
Complex tempComp = new Complex((double)iii, 0.0);
array2D[x, y] = tempComp;然后,您就可以使用以下方法在SharpenFilter.ApplyWithPadding中转换您的图像:
Complex[,] cPaddedMask = ImageDataConverter.Unwrap(maskClone);然后,这将给出以下结果:

虽然这提高了图像的清晰度,但您应该立即注意到,图像比原始图像暗得多。这是由于Convolution.Rescale函数,它根据图像的最小值和最大值动态地重新调整图像的标度。这可以方便地显示图像的最大动态范围,但可能导致一个不同的整体缩放比标准卷积。要实现此标准缩放(基于FFT实现的缩放),可以使用以下实现:
//Rescale values between 0 and 255.
private static void Rescale(Complex[,] convolve)
{
int imageWidth = convolve.GetLength(0);
int imageHeight = convolve.GetLength(1);
double scale = imageWidth * imageHeight;
for (int j = 0; j < imageHeight; j++)
{
for (int i = 0; i < imageWidth; i++)
{
double re = Math.Max(0, Math.Min(convolve[i, j].Real * scale, 255.0));
double im = Math.Max(0, Math.Min(convolve[i, j].Imaginary * scale, 255.0));
convolve[i, j] = new Complex(re, im);
}
}
}这将为您提供一个亮度级别更合适的图像:

最后,对于滤波操作,通常期望结果与原始图像大小相匹配(不像包含尾部的卷积)。在SharpenFilter.ApplyWithPadding中将结果裁剪为:
...
// -3 terms are due to kernel size
// +5 vertical offset term is due to vertical reflection & offset in SetPixel
Rectangle rect = new Rectangle((cPaddedLena.GetLength(0) / 2 - 3) / 2,
(cPaddedLena.GetLength(1) / 2 - 3) / 2 + 5,
cPaddedLena.GetLength(0) / 2,
cPaddedLena.GetLength(1) / 2);
return ImageDataConverter.ToBitmap(cConvolved).Clone(rect, PixelFormat.Format8bppIndexed);应该给你:

为了更容易地进行视觉比较,这里再次显示了原始图像:

https://stackoverflow.com/questions/39114265
复制相似问题