将 System.Drawing 位图转换为 Dlib Array2D
Posted
技术标签:
【中文标题】将 System.Drawing 位图转换为 Dlib Array2D【英文标题】:Converting System.Drawing Bitmap to Dlib Array2D 【发布时间】:2018-09-29 07:21:54 【问题描述】:在这种情况下,ShapePredictor 的灰度 Array2D。
这是我正在尝试的,但没有太大成功。
using DlibDotNet;
using Rectangle = System.Drawing.Rectangle;
using System.Runtime.InteropServices;
public static class Extension
public static Array2D<byte> ToArray2D(this Bitmap bitmap)
var bits = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppPArgb);
var length = bits.Stride * bits.Height;
var data = new byte[length];
Marshal.Copy(bits.Scan0, data, 0, length);
bitmap.UnlockBits(bits);
var array = new Array2D<byte>(bitmap.Width, bitmap.Height);
for (var x = 0; x < bitmap.Width; x++)
for (var y = 0; y < bitmap.Height; y++)
var offset = x * 4 + y * bitmap.Width * 4;
array[x][y] = data[offset];
return array;
我已经搜索过,但还没有找到明确的答案。
【问题讨论】:
您正在以 32bpp 的速度访问数据,但试图将其放入Byte
的 Array2D 中。您可能需要Aray2D<Int32>
,并且每 4 个字节读取一次。
请注意,出于同样的原因,我正在使用偏移量offset = x * 4 + y * bitmap.Width
。
是的,但是如果您的二维数组的每个条目中只有一个字节,您的返回类型是否错误?对于 32bpp 像素,您需要完整的 Int32
(可能是 UInt32
)。我不知道ShapePredictor,所以不知道它期望什么样的数据,但是你不能把四个字节的数据放在一个字节中。
返回类型没问题。 ShapePredictor 适用于灰度(8 位)。从文件加载的Array2D<byte> array = Dlib.LoadPng<byte>("file.png")
按预期工作,但我真正需要的是能够将位图转换为 Array2D,以便我可以处理已加载到内存中的数据。
您需要的不止这些。您还需要代码来实际将您的图像转换为灰度。您的代码中没有这样的东西;您正在以 32bpp alpha 的 1600 万色数据的形式访问它。这种转换最好在之前作为字节访问它。
【参考方案1】:
如前所述,您首先需要将图像转换为灰度。 *** 上有很多答案可以帮助您解决这个问题。我建议在这个答案中使用ColorMatrix
方法:
A: Convert an image to grayscale
我将在下面的代码中使用该答案中显示的MakeGrayscale3(Bitmap original)
方法。
通常,图像会逐行循环进行处理,因此为清楚起见,您应该将 Y 循环作为外循环。它还使数据偏移量的计算更加高效。
对于实际数据,如果图像是灰度的,那么R、G和B字节应该都是一样的。 32 位像素数据中的“ARGB”顺序是指一个UInt32
值,但它们是小端的,这意味着字节的实际顺序是 [B, G, R, A]。这意味着在每次循环迭代中,我们可以只取四个字节中的第一个,它将是蓝色分量。
public static Array2D<Byte> ToArray2D(this Bitmap bitmap)
Int32 stride;
Byte[] data;
// Removes unnecessary getter calls.
Int32 width = bitmap.Width;
Int32 height = bitmap.Height;
// 'using' block to properly dispose temp image.
using (Bitmap grayImage = MakeGrayscale(bitmap))
BitmapData bits = grayImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppPArgb);
stride = bits.Stride;
Int32 length = stride*height;
data = new Byte[length];
Marshal.Copy(bits.Scan0, data, 0, length);
grayImage.UnlockBits(bits);
// Constructor is (rows, columns), so (height, width)
Array2D<Byte> array = new Array2D<Byte>(height, width);
Int32 offset = 0;
for (Int32 y = 0; y < height; y++)
// Offset variable for processing one line
Int32 curOffset = offset;
// Get row in advance
Array2D<Byte>.Row<Byte> curRow = array[y];
for (Int32 x = 0; x < width; x++)
curRow[x] = data[curOffset]; // Should be the Blue component.
curOffset += 4;
// Stride is the actual data length of one line. No need to calculate that;
// not only is it already given by the BitmapData object, but in some situations
// it may differ from the actual data length. This also saves processing time
// by avoiding multiplications inside each loop.
offset += stride;
return array;
【讨论】:
你的代码更精简,虽然我注意到一些错别字,很容易修复。转换仍然不起作用。主要问题是 Array2D 数据的初始化似乎没有记录,这就是我在数组循环中执行此操作的原因。我看到很多人问如何做到这一点,但目前还没有明确的答案。 @O.Coder 我想我找到了。 DlibDotNet 源代码按顺序使用rows
和columns
。这意味着 height 和 width。你到处都换了。正如我已经指出的那样,多维数组如果按行查看则更加合乎逻辑,因此访问权限也是array[y][x]
。
哈哈,天哪。谢谢奈尔古兹。这解决了它。
似乎每次检索行都会执行一次函数调用,因此在 X 循环之外检索它会更有效。我编辑了代码以显示这一点。以上是关于将 System.Drawing 位图转换为 Dlib Array2D的主要内容,如果未能解决你的问题,请参考以下文章
使用medata数据wpf c#将位图保存为png [重复]
将 System.Drawing.Icon 转换为 System.Media.ImageSource