C# 从高斯模糊图像中去除黑边界
我正试图为我的一个库实现一种高斯模糊方法,该库基于 由于某些原因,我在生成的图像周围不断得到一个黑色边框,它侵犯了原始图像,如下所示 边界厚度随着高斯核长度的增加而增加。此图像是使用15x15的矩阵生成的 有人能解释一下可能发生的事情吗 我的代码;对长度表示歉意: 处理方法。C# 从高斯模糊图像中去除黑边界,c#,gdi+,gaussian,C#,Gdi+,Gaussian,我正试图为我的一个库实现一种高斯模糊方法,该库基于 由于某些原因,我在生成的图像周围不断得到一个黑色边框,它侵犯了原始图像,如下所示 边界厚度随着高斯核长度的增加而增加。此图像是使用15x15的矩阵生成的 有人能解释一下可能发生的事情吗 我的代码;对长度表示歉意: 处理方法。 /// <summary> /// Processes the image. /// </summary> /// <param name="factory">The the curr
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">The the current instance of the
/// <see cref="T:ImageProcessor.ImageFactory" /> class containing
/// the image to process.</param>
/// <returns>
/// The processed image from the current instance of the
/// <see cref="T:ImageProcessor.ImageFactory" /> class.
/// </returns>
public Image ProcessImage(ImageFactory factory)
{
Bitmap newImage = null;
Bitmap image = (Bitmap)factory.Image;
try
{
double[,] filterMatrix = this.Calculate((int)this.DynamicParameter, 10);
// We could implement factor and bias here as parameters if
// we move this to a separate method.
const double Factor = 1;
const double Bias = 0;
BitmapData sourceData = image.LockBits(
new Rectangle(0, 0, image.Width, image.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);
image.UnlockBits(sourceData);
int filterWidth = filterMatrix.GetLength(1);
int filterHeight = filterMatrix.GetLength(0);
int filterOffsetWidth = (filterWidth - 1) / 2;
int filterOffsetHeight = (filterHeight - 1) / 2;
for (int offsetY = filterOffsetHeight; offsetY < image.Height - filterOffsetHeight; offsetY++)
{
for (int offsetX = filterOffsetWidth; offsetX < image.Width - filterOffsetWidth; offsetX++)
{
double blue = 0;
double green = 0;
double red = 0;
int byteOffset = (offsetY * sourceData.Stride) + (offsetX * 4);
for (int filterY = -filterOffsetWidth; filterY <= filterOffsetWidth; filterY++)
{
for (int filterX = -filterOffsetWidth; filterX <= filterOffsetWidth; filterX++)
{
int calcOffset = byteOffset + (filterX * 4) + (filterY * sourceData.Stride);
blue += pixelBuffer[calcOffset]
* filterMatrix[filterY + filterOffsetWidth, filterX + filterOffsetWidth];
green += pixelBuffer[calcOffset + 1]
* filterMatrix[filterY + filterOffsetWidth, filterX + filterOffsetWidth];
red += pixelBuffer[calcOffset + 2]
* filterMatrix[filterY + filterOffsetWidth, filterX + filterOffsetWidth];
}
}
blue = (Factor * blue) + Bias;
green = (Factor * green) + Bias;
red = (Factor * red) + Bias;
blue = blue > 255 ? 255 : (blue < 0 ? 0 : blue);
green = green > 255 ? 255 : (green < 0 ? 0 : green);
red = red > 255 ? 255 : (red < 0 ? 0 : red);
resultBuffer[byteOffset] = (byte)blue;
resultBuffer[byteOffset + 1] = (byte)green;
resultBuffer[byteOffset + 2] = (byte)red;
resultBuffer[byteOffset + 3] = 255;
}
}
// ReSharper disable once UseObjectOrCollectionInitializer
newImage = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppArgb);
newImage.Tag = image.Tag;
BitmapData resultData = newImage.LockBits(
new Rectangle(0, 0, newImage.Width, newImage.Height),
ImageLockMode.WriteOnly,
PixelFormat.Format32bppArgb);
Marshal.Copy(resultBuffer, 0, resultData.Scan0, resultBuffer.Length);
newImage.UnlockBits(resultData);
image.Dispose();
image = newImage;
// Save the image to ensure that nothing else is going on downstream.
newImage.Save("C:\\Users\\James\\Desktop\\image.jpg", ImageFormat.Jpeg);
}
catch
{
if (newImage != null)
{
newImage.Dispose();
}
}
return image;
}
/// <summary>
/// Calculates a Gaussian kernel with the given .
/// </summary>
/// <param name="length">
/// The length.
/// </param>
/// of the kernel to produce
/// <param name="weight">
/// The weight of the kernel.
/// </param>
/// <returns>
/// The <see><cref>double[,]</cref></see> containing the Gaussian kernel.
/// </returns>
private double[,] Calculate(int length, double weight)
{
double[,] kernel = new double[length, length];
double sumTotal = 0;
int kernelRadius = length / 2;
double calculatedEuler = 1.0 /
(2.0 * Math.PI * Math.Pow(weight, 2));
for (int filterY = -kernelRadius;
filterY <= kernelRadius; filterY++)
{
for (int filterX = -kernelRadius; filterX <= kernelRadius; filterX++)
{
double distance = ((filterX * filterX) + (filterY * filterY)) / (2 * (weight * weight));
kernel[filterY + kernelRadius,
filterX + kernelRadius] =
calculatedEuler * Math.Exp(-distance);
sumTotal += kernel[filterY + kernelRadius,
filterX + kernelRadius];
}
}
for (int y = 0; y < length; y++)
{
for (int x = 0; x < length; x++)
{
kernel[y, x] = kernel[y, x] *
(1.0 / sumTotal);
}
}
return kernel;
}
//
///处理图像。
///
///的当前实例
///类包含
///要处理的图像。
///
///来自当前实例的已处理图像
///班级。
///
公共图像处理图像(图像工厂)
{
位图newImage=null;
位图图像=(位图)工厂图像;
尝试
{
double[,]filterMatrix=this.Calculate((int)this.DynamicParameter,10);
//我们可以在这里实现因子和偏差作为参数,如果
//我们将其转移到一个单独的方法。
常数双因子=1;
常数双偏压=0;
BitmapData sourceData=image.LockBits(
新矩形(0,0,image.Width,image.Height),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
byte[]pixelBuffer=新字节[sourceData.Stride*sourceData.Height];
byte[]resultBuffer=新字节[sourceData.Stride*sourceData.Height];
复制(sourceData.Scan0,pixelBuffer,0,pixelBuffer.Length);
图像。解锁位(源数据);
int filterWidth=filterMatrix.GetLength(1);
int filterHeight=filterMatrix.GetLength(0);
int filterOffsetWidth=(filterWidth-1)/2;
int filterOffsetHeight=(filterHeight-1)/2;
对于(int offsetY=filterOffsetHeight;offsetY255?255:(红色<0?0:红色);
结果缓冲[字节偏移]=(字节)蓝色;
结果缓冲区[byteOffset+1]=(字节)绿色;
结果缓冲区[字节偏移量+2]=(字节)红色;
结果缓冲区[字节偏移量+3]=255;
}
}
//ReSharper禁用一次性使用ObjectorCollectionInitializer
newImage=新位图(image.Width、image.Height、PixelFormat.Format32bppArgb);
newImage.Tag=image.Tag;
BitmapData resultData=newImage.LockBits(
新矩形(0,0,newImage.Width,newImage.Height),
ImageLockMode.WriteOnly,
PixelFormat.Format32bppArgb);
Marshal.Copy(resultBuffer,0,resultData.Scan0,resultBuffer.Length);
newImage.UnlockBits(resultData);
image.Dispose();
图像=新图像;
//保存图像以确保下游不会发生任何其他情况。
newImage.Save(“C:\\Users\\James\\Desktop\\image.jpg”,ImageFormat.Jpeg);
}
抓住
{
if(newImage!=null)
{
Dispose();
}
}
返回图像;
}
高斯计算器。
/// <summary>
/// Processes the image.
/// </summary>
/// <param name="factory">The the current instance of the
/// <see cref="T:ImageProcessor.ImageFactory" /> class containing
/// the image to process.</param>
/// <returns>
/// The processed image from the current instance of the
/// <see cref="T:ImageProcessor.ImageFactory" /> class.
/// </returns>
public Image ProcessImage(ImageFactory factory)
{
Bitmap newImage = null;
Bitmap image = (Bitmap)factory.Image;
try
{
double[,] filterMatrix = this.Calculate((int)this.DynamicParameter, 10);
// We could implement factor and bias here as parameters if
// we move this to a separate method.
const double Factor = 1;
const double Bias = 0;
BitmapData sourceData = image.LockBits(
new Rectangle(0, 0, image.Width, image.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);
image.UnlockBits(sourceData);
int filterWidth = filterMatrix.GetLength(1);
int filterHeight = filterMatrix.GetLength(0);
int filterOffsetWidth = (filterWidth - 1) / 2;
int filterOffsetHeight = (filterHeight - 1) / 2;
for (int offsetY = filterOffsetHeight; offsetY < image.Height - filterOffsetHeight; offsetY++)
{
for (int offsetX = filterOffsetWidth; offsetX < image.Width - filterOffsetWidth; offsetX++)
{
double blue = 0;
double green = 0;
double red = 0;
int byteOffset = (offsetY * sourceData.Stride) + (offsetX * 4);
for (int filterY = -filterOffsetWidth; filterY <= filterOffsetWidth; filterY++)
{
for (int filterX = -filterOffsetWidth; filterX <= filterOffsetWidth; filterX++)
{
int calcOffset = byteOffset + (filterX * 4) + (filterY * sourceData.Stride);
blue += pixelBuffer[calcOffset]
* filterMatrix[filterY + filterOffsetWidth, filterX + filterOffsetWidth];
green += pixelBuffer[calcOffset + 1]
* filterMatrix[filterY + filterOffsetWidth, filterX + filterOffsetWidth];
red += pixelBuffer[calcOffset + 2]
* filterMatrix[filterY + filterOffsetWidth, filterX + filterOffsetWidth];
}
}
blue = (Factor * blue) + Bias;
green = (Factor * green) + Bias;
red = (Factor * red) + Bias;
blue = blue > 255 ? 255 : (blue < 0 ? 0 : blue);
green = green > 255 ? 255 : (green < 0 ? 0 : green);
red = red > 255 ? 255 : (red < 0 ? 0 : red);
resultBuffer[byteOffset] = (byte)blue;
resultBuffer[byteOffset + 1] = (byte)green;
resultBuffer[byteOffset + 2] = (byte)red;
resultBuffer[byteOffset + 3] = 255;
}
}
// ReSharper disable once UseObjectOrCollectionInitializer
newImage = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppArgb);
newImage.Tag = image.Tag;
BitmapData resultData = newImage.LockBits(
new Rectangle(0, 0, newImage.Width, newImage.Height),
ImageLockMode.WriteOnly,
PixelFormat.Format32bppArgb);
Marshal.Copy(resultBuffer, 0, resultData.Scan0, resultBuffer.Length);
newImage.UnlockBits(resultData);
image.Dispose();
image = newImage;
// Save the image to ensure that nothing else is going on downstream.
newImage.Save("C:\\Users\\James\\Desktop\\image.jpg", ImageFormat.Jpeg);
}
catch
{
if (newImage != null)
{
newImage.Dispose();
}
}
return image;
}
/// <summary>
/// Calculates a Gaussian kernel with the given .
/// </summary>
/// <param name="length">
/// The length.
/// </param>
/// of the kernel to produce
/// <param name="weight">
/// The weight of the kernel.
/// </param>
/// <returns>
/// The <see><cref>double[,]</cref></see> containing the Gaussian kernel.
/// </returns>
private double[,] Calculate(int length, double weight)
{
double[,] kernel = new double[length, length];
double sumTotal = 0;
int kernelRadius = length / 2;
double calculatedEuler = 1.0 /
(2.0 * Math.PI * Math.Pow(weight, 2));
for (int filterY = -kernelRadius;
filterY <= kernelRadius; filterY++)
{
for (int filterX = -kernelRadius; filterX <= kernelRadius; filterX++)
{
double distance = ((filterX * filterX) + (filterY * filterY)) / (2 * (weight * weight));
kernel[filterY + kernelRadius,
filterX + kernelRadius] =
calculatedEuler * Math.Exp(-distance);
sumTotal += kernel[filterY + kernelRadius,
filterX + kernelRadius];
}
}
for (int y = 0; y < length; y++)
{
for (int x = 0; x < length; x++)
{
kernel[y, x] = kernel[y, x] *
(1.0 / sumTotal);
}
}
return kernel;
}
//
///计算具有给定参数的高斯核。
///
///
///长度。
///
///要生产的果仁的重量
///
///果仁的重量。
///
///
///包含高斯核的双[,]。
///
专用双精度[,]计算(整数长度,双权重)
{
double[,]内核=新的double[长度,长度];
双重总和=0;
int kernelRadius=长度/2;
双重计算规则=1.0/
(2.0*Math.PI*Math.Pow(重量,2));
对于(int filterY=-kernelRadius;
filterY您有一个黑色边框,因为您没有在那里渲染任何像素
int filterOffsetWidth = (filterWidth - 1) / 2;
int filterOffsetHeight = (filterHeight - 1) / 2;
for (int offsetY = filterOffsetHeight;
offsetY < image.Height - filterOffsetHeight;
offsetY++)
{
for (int offsetX = filterOffsetWidth;
offsetX < image.Width - filterOffsetWidth;
offsetX++)
int-filterOffsetWidth=(filterWidth-1)/2;
int filterOffsetHeight=(filterHeight-1)/2;
对于(int offsetY=filterOffsetHeight;
偏移量
如果要删除黑色边框,还必须计算offsetY
等的一些值
更新:
因此,对于15的矩阵大小,filterOffsetHeight
将为7。您的外循环从offsetY=7开始,并且您从不计算第0行到第6行的任何值。该区域中的像素的默认值为0
的红色、绿色和蓝色,在图像顶部显示为黑色边框。同样的事情是h出现在其他边界
显然,您不能只在这些边框中运行计算。因此您有两个选项:裁剪图像,或使用不同的算法计算边框。如果您想使用第二个选项,最简单的方法是假设原始图像之外的像素与图像边框上最近的像素具有相同的颜色。ing。你能在此基础上进行推断吗?原始样本似乎没有受到相同错误的影响。相比之下,我所做的只是更改一些变量名。啊……我可以翻转外部像素值并从中进行计算。我会尝试一下。