使用AForge将位图转换为灰度非常简单:
public static Bitmap ConvertToGrayScale(this Bitmap me)
{
if (me == null)
return null;
// first convert to a grey scale image
var filterGreyScale = new Grayscale(0.2125, 0.7154, 0.0721);
me = filterGreyScale.Apply(me);
return me;
}
但我需要更棘手的事情:
想象一下,您希望将所有内容转换为灰度(除外),以便在位图中间使用圆圈。换句话说:给定位图中间的圆圈应保持其原始颜色。
我们假设圆的半径是20px,我应该如何处理?
答案 0 :(得分:3)
这可以使用MaskedFilter使用掩码来完成,该掩码定义了您描述的圆圈区域。正如文档所述
可以将掩码指定为.NET的托管位图,如UnmanagedImage或 作为字节数组。如果将mask指定为image,则它必须为8 bpp灰度图像。在所有情况下,掩模大小必须与大小相同 要处理的图像。
因此必须根据源图像的宽度和高度生成蒙版图像。
我没有编译下面的代码,但它应该让你顺利。如果圆圈始终位于同一点,则可以在方法外生成图像蒙版,以便每次应用滤镜时都不必重新生成。实际上,如果除了源图像没有任何变化,你可以在应用它的方法之外生成整个 MaskedFilter
。
public static Bitmap ConvertToGrayScale(this Bitmap me)
{
if (me == null)
return null;
var radius = 20, x = me.Width / 2, y = me.Height / 2;
using (Bitmap maskImage = new Bitmap(me.Width, me.Height, PixelFormat.Format8bppIndexed))
{
using (Graphics g = Graphics.FromImage(maskImage))
using (Brush b = new SolidBrush(ColorTranslator.FromHtml("#00000000")))
g.FillEllipse(b, x, y, radius, radius);
var maskedFilter = new MaskedFilter(new Grayscale(0.2125, 0.7154, 0.0721), maskImage);
return maskedFilter.Apply(me);
}
}
修改强>
对此的解决方案比我预期的要复杂得多。主要问题是MaskedFilter
不允许使用更改图像格式的过滤器,Grayscale
过滤器会这样做(它将源更改为8bpp或16bpp图像)。
以下是我测试过的结果代码,并在ConvertToGrayScale
方法的每个部分添加了注释,解释了它背后的逻辑。由于Merge
滤镜不支持合并具有不同格式的两张图像,因此必须将图像的灰度部分转换回RGB。
static class MaskedImage
{
public static void DrawCircle(byte[,] img, int x, int y, int radius, byte val)
{
int west = Math.Max(0, x - radius),
east = Math.Min(x + radius, img.GetLength(1)),
north = Math.Max(0, y - radius),
south = Math.Min(y + radius, img.GetLength(0));
for (int i = north; i < south; i++)
for (int j = west; j < east; j++)
{
int dx = i - y;
int dy = j - x;
if (Math.Sqrt(dx * dx + dy * dy) < radius)
img[i, j] = val;
}
}
public static void Initialize(byte[,] arr, byte val)
{
for (int i = 0; i < arr.GetLength(0); i++)
for (int j = 0; j < arr.GetLength(1); j++)
arr[i, j] = val;
}
public static void Invert(byte[,] arr)
{
for (int i = 0; i < arr.GetLength(0); i++)
for (int j = 0; j < arr.GetLength(1); j++)
arr[i, j] = (byte)~arr[i, j];
}
public static Bitmap ConvertToGrayScale(this Bitmap me)
{
if (me == null)
return null;
int radius = 20, x = me.Width / 2, y = me.Height / 2;
// Generate a two-dimensional `byte` array that has the same size as the source image, which will be used as the mask.
byte[,] mask = new byte[me.Height, me.Width];
// Initialize all its elements to the value 0xFF (255 in decimal).
Initialize(mask, 0xFF);
// "Draw" a circle in the `byte` array setting the positions inside the circle with the value 0.
DrawCircle(mask, x, y, radius, 0);
var grayFilter = new Grayscale(0.2125, 0.7154, 0.0721);
var rgbFilter = new GrayscaleToRGB();
var maskFilter = new ApplyMask(mask);
// Apply the `Grayscale` filter to everything outside the circle, convert the resulting image back to RGB
Bitmap img = rgbFilter.Apply(grayFilter.Apply(maskFilter.Apply(me)));
// Invert the mask
Invert(mask);
// Get only the cirle in color from the original image
Bitmap circleImg = new ApplyMask(mask).Apply(me);
// Merge both the grayscaled part of the image and the circle in color in a single one.
return new Merge(img).Apply(circleImg);
}
}