使用 Marshal.Copy 时从位图中提取区域导致 AccessViolationException
Extract area from bitmap results in AccessViolationException when using Marshal.Copy
我正在尝试从位图中提取特定区域以进行进一步处理。在极少数情况下,调用 Marshal.Copy 时会发生错误。这可以通过以下示例代码重现:
Bitmap bitmap = new Bitmap(1741, 2141, PixelFormat.Format1bppIndexed);
int zoneWidth = 50;
int zoneHeight = 50;
int x = 168;
int y = bitmap.Height - zoneHeight;
Rectangle zone = new Rectangle(x, y, zoneWidth, zoneHeight);
BitmapData bitmapData = bitmap.LockBits(zone, ImageLockMode.ReadOnly, bitmap.PixelFormat);
int byteCount = Math.Abs(bitmapData.Stride) * bitmapData.Height;
byte[] pixels = new byte[byteCount];
Marshal.Copy(bitmapData.Scan0, pixels, 0, byteCount);
// some further processing
bitmap.UnlockBits(bitmapData);
在其他帖子中我读到 Stride 可以是负数。这里不是这种情况。
为什么会出现错误,我该如何预防?
编辑 1:
JonasH的第二个建议我已经实现了。但这也因 AccessViolationException 而失败。可能我没做对。
Bitmap bitmap = new Bitmap(1741, 2141, PixelFormat.Format1bppIndexed);
int zoneWidth = 50;
int zoneHeight = 50;
int zoneX = 168;
int zoneY = bitmap.Height - zoneHeight;
Rectangle zone = new Rectangle(zoneX, zoneY, zoneWidth, zoneHeight);
BitmapData bitmapData = bitmap.LockBits(zone, ImageLockMode.ReadOnly, bitmap.PixelFormat);
int rowSize = Math.Abs(bitmapData.Stride);
byte[] pixels = new byte[bitmapData.Height * rowSize];
IntPtr iptr = bitmapData.Scan0;
for (int y = 0; y < bitmapData.Height; y++)
{
Marshal.Copy(IntPtr.Add(iptr, y * rowSize),
pixels,
y * rowSize,
rowSize);
}
bitmap.UnlockBits(bitmapData);
这可能是因为您只锁定了位图的一部分。用 new Rectangle(0, 0, bitmap.Width , bitmap.Height);
替换区域,我希望你的问题消失。
另一种方法是将复制操作限制在位图的锁定部分,但这需要逐行复制而不是一次复制整个位图。
逐行复制需要小心使用偏移量,您需要跟踪源数据和目标数据的水平和垂直偏移量。我认为这样的事情应该可行:
for (int y = 0; y < zoneHeight ; y++)
{
Marshal.Copy(
IntPtr.Add(iptr,(y + zoneY ) * bitmapData.Stride + zoneX)
pixels,
y * zoneWidth,
zoneWidth );
}
我正在尝试从位图中提取特定区域以进行进一步处理。在极少数情况下,调用 Marshal.Copy 时会发生错误。这可以通过以下示例代码重现:
Bitmap bitmap = new Bitmap(1741, 2141, PixelFormat.Format1bppIndexed);
int zoneWidth = 50;
int zoneHeight = 50;
int x = 168;
int y = bitmap.Height - zoneHeight;
Rectangle zone = new Rectangle(x, y, zoneWidth, zoneHeight);
BitmapData bitmapData = bitmap.LockBits(zone, ImageLockMode.ReadOnly, bitmap.PixelFormat);
int byteCount = Math.Abs(bitmapData.Stride) * bitmapData.Height;
byte[] pixels = new byte[byteCount];
Marshal.Copy(bitmapData.Scan0, pixels, 0, byteCount);
// some further processing
bitmap.UnlockBits(bitmapData);
在其他帖子中我读到 Stride 可以是负数。这里不是这种情况。
为什么会出现错误,我该如何预防?
编辑 1:
JonasH的第二个建议我已经实现了。但这也因 AccessViolationException 而失败。可能我没做对。
Bitmap bitmap = new Bitmap(1741, 2141, PixelFormat.Format1bppIndexed);
int zoneWidth = 50;
int zoneHeight = 50;
int zoneX = 168;
int zoneY = bitmap.Height - zoneHeight;
Rectangle zone = new Rectangle(zoneX, zoneY, zoneWidth, zoneHeight);
BitmapData bitmapData = bitmap.LockBits(zone, ImageLockMode.ReadOnly, bitmap.PixelFormat);
int rowSize = Math.Abs(bitmapData.Stride);
byte[] pixels = new byte[bitmapData.Height * rowSize];
IntPtr iptr = bitmapData.Scan0;
for (int y = 0; y < bitmapData.Height; y++)
{
Marshal.Copy(IntPtr.Add(iptr, y * rowSize),
pixels,
y * rowSize,
rowSize);
}
bitmap.UnlockBits(bitmapData);
这可能是因为您只锁定了位图的一部分。用 new Rectangle(0, 0, bitmap.Width , bitmap.Height);
替换区域,我希望你的问题消失。
另一种方法是将复制操作限制在位图的锁定部分,但这需要逐行复制而不是一次复制整个位图。
逐行复制需要小心使用偏移量,您需要跟踪源数据和目标数据的水平和垂直偏移量。我认为这样的事情应该可行:
for (int y = 0; y < zoneHeight ; y++)
{
Marshal.Copy(
IntPtr.Add(iptr,(y + zoneY ) * bitmapData.Stride + zoneX)
pixels,
y * zoneWidth,
zoneWidth );
}