我需要在照片中执行一些数学运算,为此我需要图像的浮点灰度版本(可能来自具有各种颜色深度的JPG,PNG或BMP文件)。
我曾经使用PIL
和scipy.ndimage
在Python中执行此操作,使用PIL
转换为灰度,然后转换为带{的浮点数数组非常简单{1}},但现在我需要在C#中做类似的事情,我很困惑如何这样做。
我已阅读this very nice tutorial,这似乎是一个反复出现的参考,但仅涵盖“转换为灰度”部分,我不知道如何从Bitmap获取双精度数组,然后(在某些时刻)将其转换回System.Drawing.Bitmap以供查看。
答案 0 :(得分:1)
我确信有很多最佳方法可以做到这一点。
正如@Groo在评论部分中指出的那样,人们可以使用LockBits方法来编写和读取Bitmap
实例的像素颜色。
更进一步,可以使用计算机的图形卡进行实际计算。
此外,将颜色变为其中的方法Color ToGrayscaleColor(Color color)
灰度版本不是光学正确的。实际上需要将一组比率应用于颜色分量强度。我只使用了1,1,1比率。这对我来说是可以接受的,对于艺术家或科学家来说可能是可怕的。
在评论部分,@ plinth非常高兴指出您应该注意的这个问题,如果您想进行解剖学上正确的转换:Converting RGB to grayscale/intensity
只想分享这个非常容易理解和实施的解决方案:
首先帮助将Color变成灰度版本:
public static Color ToGrayscaleColor(Color color) {
var level = (byte)((color.R + color.G + color.B) / 3);
var result = Color.FromArgb(level, level, level);
return result;
}
然后将颜色位图转换为灰度位图:
public static Bitmap ToGrayscale(Bitmap bitmap) {
var result = new Bitmap(bitmap.Width, bitmap.Height);
for (int x = 0; x < bitmap.Width; x++)
for (int y = 0; y < bitmap.Height; y++) {
var grayColor = ToGrayscaleColor(bitmap.GetPixel(x, y));
result.SetPixel(x, y, grayColor);
}
return result;
}
双打部分非常简单。 Bitmap
对象是实际图像的内存表示,您可以在各种操作中使用它。 colordepth和image格式详细信息只是将Bitmap
的实例加载和保存到流或文件上的问题。我们现在不必关心那些:
public static double[,] FromGrayscaleToDoubles(Bitmap bitmap) {
var result = new double[bitmap.Width, bitmap.Height];
for (int x = 0; x < bitmap.Width; x++)
for (int y = 0; y < bitmap.Height; y++)
result[x, y] = (double)bitmap.GetPixel(x, y).R / 255;
return result;
}
将双阵列变回灰度图像:
public static Bitmap FromDoublesToGrayscal(double[,] doubles) {
var result = new Bitmap(doubles.GetLength(0), doubles.GetLength(1));
for (int x = 0; x < result.Width; x++)
for (int y = 0; y < result.Height; y++) {
int level = (int)Math.Round(doubles[x, y] * 255);
if (level > 255) level = 255; // just to be sure
if (level < 0) level = 0; // just to be sure
result.SetPixel(x, y, Color.FromArgb(level, level, level));
}
return result;
}
以下几行:
if (level > 255) level = 255; // just to be sure
level < 0) level = 0; // just to be sure
真的存在,如果你操作双打,你想留出一些小错误的空间。
答案 1 :(得分:1)
最终代码,主要基于评论中的提示,特别是LockBits
部分(blog post here)以及R,G和B值之间的感知平衡(这里不是最重要的,但是对于知道):
private double[,] TransformaImagemEmArray(System.Drawing.Bitmap imagem) {
// Transforma a imagem de entrada em um array de doubles
// com os valores grayscale da imagem
BitmapData bitmap_data = imagem.LockBits(new System.Drawing.Rectangle(0,0,_foto_franjas_original.Width,_foto_franjas_original.Height),
ImageLockMode.ReadOnly, _foto_franjas_original.PixelFormat);
int pixelsize = System.Drawing.Image.GetPixelFormatSize(bitmap_data.PixelFormat)/8;
IntPtr pointer = bitmap_data.Scan0;
int nbytes = bitmap_data.Height * bitmap_data.Stride;
byte[] imagebytes = new byte[nbytes];
System.Runtime.InteropServices.Marshal.Copy(pointer, imagebytes, 0, nbytes);
double red;
double green;
double blue;
double gray;
var _grayscale_array = new Double[bitmap_data.Height, bitmap_data.Width];
if (pixelsize >= 3 ) {
for (int I = 0; I < bitmap_data.Height; I++) {
for (int J = 0; J < bitmap_data.Width; J++ ) {
int position = (I * bitmap_data.Stride) + (J * pixelsize);
blue = imagebytes[position];
green = imagebytes[position + 1];
red = imagebytes[position + 2];
gray = 0.299 * red + 0.587 * green + 0.114 * blue;
_grayscale_array[I,J] = gray;
}
}
}
_foto_franjas_original.UnlockBits(bitmap_data);
return _grayscale_array;
}