保存位图时出现访问冲突异常
Acess Violation Exception When saving a Bitmap
我需要保存口内 X 光图像,我尝试使用 8bppIndexed 像素格式,并使用此代码保存:
private void SaveImage(short[] data, int widht, int height)
{
try
{
Bitmap pic = new Bitmap(widht, height, PixelFormat.Format8bppIndexed);
Rectangle dimension = new Rectangle(0, 0, pic.Width, pic.Height);
BitmapData picData = pic.LockBits(dimension, ImageLockMode.ReadWrite, pic.PixelFormat);
IntPtr pixelStartAddress = picData.Scan0;
Marshal.Copy(data, 0, pixelStartAddress, data.Length);
pic.UnlockBits(picData);
pic = ConvertToGrayscale(pic);
pic.Save("C:\Users\WIM\Desktop\teste\teste\teste.jpeg");
pic.Dispose();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
通过这一行 Marshal.Copy(data, 0, pixelStartAddress, data.Length);
我收到了 AcessViolationException。
我不习惯 c# 所以我真的不明白为什么我会收到这个错误。我读到的是,也许 ptr 已被使用,但如果我使用其他 pixelFormat,如 16bppRgb565,我可以捕获图像并转换为 grayScale,但图像 return 变形了。 Like this
我使用的是短数组,因为它是我从我们购买传感器的公司收到的 sdk 收到的数据;
在我的研究中,我发现 16bppGrayScale 是一个不错的选择,但 GDI+ 还没有对它进行编码。但是我们可以为此制作一个 DICOM,但我们需要 运行 笔记本电脑上的应用程序硬件不是很好,因此我正在尝试使用 8bppIndexed 像素格式。
short[] data
数据类型是short
,而应该是byte[]
但我认为真正导致问题的是:data.Length
应该等于 picData.Stride * pic.Height
而在你的情况下,它看起来不是。
一个问题是您正在处理非托管代码。另一个是异常处理非常有问题。
try
{
Bitmap pic = new Bitmap(widht, height, PixelFormat.Format8bppIndexed);
Rectangle dimension = new Rectangle(0, 0, pic.Width, pic.Height);
BitmapData picData = pic.LockBits(dimension, ImageLockMode.ReadWrite, pic.PixelFormat);
IntPtr pixelStartAddress = picData.Scan0;
Marshal.Copy(data, 0, pixelStartAddress, data.Length);
pic.UnlockBits(picData);
pic = ConvertToGrayscale(pic);
pic.Save("C:\Users\WIM\Desktop\teste\teste\teste.jpeg");
pic.Dispose();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
该代码中存在许多问题:
使用一次性资源而不使用
代码使用了需要处理的class位图,而放弃了using statement。当有对 Dispose() 的调用时,它将 不会 在异常情况下执行。使用那里是为了那个精确
捕获异常
捕获除日志记录之外的任何异常并让它继续终止进程,是异常处理的致命罪过。除了恼人的或外来的异常之外,吞下任何东西都会让你为越来越多和不太容易理解的后续异常做好准备。
我建议您阅读两篇关于正确异常处理的文章。他们比我能更好地解释事情:
- https://blogs.msdn.microsoft.com/ericlippert/2008/09/10/vexing-exceptions/
- https://www.codeproject.com/Articles/9538/Exception-Handling-Best-Practices-in-NET
忽略任何异常都会导致更多或更难理解的后续错误。特别是如果您忽略有关数组的错误,您很容易导致访问冲突异常。
请注意,访问冲突异常是致命异常本身的一个很好的例子。如果有的话,还是让进程死掉比较安全!
using(Bitmap pic = new Bitmap(widht, height, PixelFormat.Format8bppIndexed)){
Rectangle dimension = new Rectangle(0, 0, pic.Width, pic.Height);
BitmapData picData = pic.LockBits(dimension, ImageLockMode.ReadWrite, pic.PixelFormat);
IntPtr pixelStartAddress = picData.Scan0;
Marshal.Copy(data, 0, pixelStartAddress, data.Length);
pic.UnlockBits(picData);
pic = ConvertToGrayscale(pic);
pic.Save("C:\Users\WIM\Desktop\teste\teste\teste.jpeg");
}
假设访问冲突不是由忽略另一个异常引起的:
这 3 行是非托管代码:
IntPtr pixelStartAddress = picData.Scan0;
Marshal.Copy(data, 0, pixelStartAddress, data.Length);
pic.UnlockBits(picData);
我尽量避免非托管代码,所以我不能真正帮助你调试它。但我注意到您使用了 data
两次 - 但它似乎没有在此代码中的任何地方定义。
我决定回答我自己的问题以使 post 更易于阅读。
我设法通过这个 post 找到了解决方案:Generate 16-bit grayscale BitmapData and save to file
所以请对答案进行投票,让更多的人更容易找到解决方案。
而我现在使用的代码是这样的:
private void SaveImage(short[] data, int widht, int height)
{
try
{
Bitmap pic = new Bitmap(widht, height, System.Drawing.Imaging.PixelFormat.Format16bppGrayScale);
Rectangle dimension = new Rectangle(0, 0, pic.Width, pic.Height);
BitmapData picData = pic.LockBits(dimension, ImageLockMode.ReadWrite, pic.PixelFormat);
IntPtr pixelStartAddress = picData.Scan0;
//Marshal.Copy(data, 0, pixelStartAddress, data.Length);
Marshal.Copy(data, 0, pixelStartAddress, data.Length);
pic.UnlockBits(picData);
//SaveBmp(pic, Path.Combine(Directory.GetCurrentDirectory(), "imagem"));
SaveBmp(pic, "C:\Users\WIM\Desktop\teste\teste\teste.bmp");
pic.Dispose();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
private static void SaveBmp(Bitmap bmp, string path)
{
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bitmapData = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat);
var pixelFormats = ConvertBmpPixelFormat(bmp.PixelFormat);
BitmapSource source = BitmapSource.Create(bmp.Width,
bmp.Height,
bmp.HorizontalResolution,
bmp.VerticalResolution,
pixelFormats,
null,
bitmapData.Scan0,
bitmapData.Stride * bmp.Height,
bitmapData.Stride);
bmp.UnlockBits(bitmapData);
FileStream stream = new FileStream(path, FileMode.Create);
TiffBitmapEncoder encoder = new TiffBitmapEncoder();
encoder.Compression = TiffCompressOption.Zip;
encoder.Frames.Add(BitmapFrame.Create(source));
encoder.Save(stream);
stream.Close();
}
private static System.Windows.Media.PixelFormat ConvertBmpPixelFormat(System.Drawing.Imaging.PixelFormat pixelformat)
{
System.Windows.Media.PixelFormat pixelFormats = System.Windows.Media.PixelFormats.Default;
switch (pixelformat)
{
case System.Drawing.Imaging.PixelFormat.Format32bppArgb:
pixelFormats = PixelFormats.Bgr32;
break;
case System.Drawing.Imaging.PixelFormat.Format8bppIndexed:
pixelFormats = PixelFormats.Gray8;
break;
case System.Drawing.Imaging.PixelFormat.Format16bppGrayScale:
pixelFormats = PixelFormats.Gray16;
break;
}
return pixelFormats;
}
所以现在我们将 16bppGrayScale 更改为 Gray16。
要使用它,您需要在项目中添加对“PresentationCore”的引用。
那你就可以用了
System.Windows.Media.Format
还有人反对 BitmapSource
。
我需要保存口内 X 光图像,我尝试使用 8bppIndexed 像素格式,并使用此代码保存:
private void SaveImage(short[] data, int widht, int height)
{
try
{
Bitmap pic = new Bitmap(widht, height, PixelFormat.Format8bppIndexed);
Rectangle dimension = new Rectangle(0, 0, pic.Width, pic.Height);
BitmapData picData = pic.LockBits(dimension, ImageLockMode.ReadWrite, pic.PixelFormat);
IntPtr pixelStartAddress = picData.Scan0;
Marshal.Copy(data, 0, pixelStartAddress, data.Length);
pic.UnlockBits(picData);
pic = ConvertToGrayscale(pic);
pic.Save("C:\Users\WIM\Desktop\teste\teste\teste.jpeg");
pic.Dispose();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
通过这一行 Marshal.Copy(data, 0, pixelStartAddress, data.Length);
我收到了 AcessViolationException。
我不习惯 c# 所以我真的不明白为什么我会收到这个错误。我读到的是,也许 ptr 已被使用,但如果我使用其他 pixelFormat,如 16bppRgb565,我可以捕获图像并转换为 grayScale,但图像 return 变形了。 Like this
我使用的是短数组,因为它是我从我们购买传感器的公司收到的 sdk 收到的数据;
在我的研究中,我发现 16bppGrayScale 是一个不错的选择,但 GDI+ 还没有对它进行编码。但是我们可以为此制作一个 DICOM,但我们需要 运行 笔记本电脑上的应用程序硬件不是很好,因此我正在尝试使用 8bppIndexed 像素格式。
short[] data
数据类型是short
,而应该是byte[]
但我认为真正导致问题的是:
data.Length
应该等于picData.Stride * pic.Height
而在你的情况下,它看起来不是。
一个问题是您正在处理非托管代码。另一个是异常处理非常有问题。
try
{
Bitmap pic = new Bitmap(widht, height, PixelFormat.Format8bppIndexed);
Rectangle dimension = new Rectangle(0, 0, pic.Width, pic.Height);
BitmapData picData = pic.LockBits(dimension, ImageLockMode.ReadWrite, pic.PixelFormat);
IntPtr pixelStartAddress = picData.Scan0;
Marshal.Copy(data, 0, pixelStartAddress, data.Length);
pic.UnlockBits(picData);
pic = ConvertToGrayscale(pic);
pic.Save("C:\Users\WIM\Desktop\teste\teste\teste.jpeg");
pic.Dispose();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
该代码中存在许多问题:
使用一次性资源而不使用
代码使用了需要处理的class位图,而放弃了using statement。当有对 Dispose() 的调用时,它将 不会 在异常情况下执行。使用那里是为了那个精确
捕获异常
捕获除日志记录之外的任何异常并让它继续终止进程,是异常处理的致命罪过。除了恼人的或外来的异常之外,吞下任何东西都会让你为越来越多和不太容易理解的后续异常做好准备。
我建议您阅读两篇关于正确异常处理的文章。他们比我能更好地解释事情:
- https://blogs.msdn.microsoft.com/ericlippert/2008/09/10/vexing-exceptions/
- https://www.codeproject.com/Articles/9538/Exception-Handling-Best-Practices-in-NET
忽略任何异常都会导致更多或更难理解的后续错误。特别是如果您忽略有关数组的错误,您很容易导致访问冲突异常。
请注意,访问冲突异常是致命异常本身的一个很好的例子。如果有的话,还是让进程死掉比较安全!
using(Bitmap pic = new Bitmap(widht, height, PixelFormat.Format8bppIndexed)){
Rectangle dimension = new Rectangle(0, 0, pic.Width, pic.Height);
BitmapData picData = pic.LockBits(dimension, ImageLockMode.ReadWrite, pic.PixelFormat);
IntPtr pixelStartAddress = picData.Scan0;
Marshal.Copy(data, 0, pixelStartAddress, data.Length);
pic.UnlockBits(picData);
pic = ConvertToGrayscale(pic);
pic.Save("C:\Users\WIM\Desktop\teste\teste\teste.jpeg");
}
假设访问冲突不是由忽略另一个异常引起的:
这 3 行是非托管代码:
IntPtr pixelStartAddress = picData.Scan0;
Marshal.Copy(data, 0, pixelStartAddress, data.Length);
pic.UnlockBits(picData);
我尽量避免非托管代码,所以我不能真正帮助你调试它。但我注意到您使用了 data
两次 - 但它似乎没有在此代码中的任何地方定义。
我决定回答我自己的问题以使 post 更易于阅读。
我设法通过这个 post 找到了解决方案:Generate 16-bit grayscale BitmapData and save to file
所以请对答案进行投票,让更多的人更容易找到解决方案。
而我现在使用的代码是这样的:
private void SaveImage(short[] data, int widht, int height)
{
try
{
Bitmap pic = new Bitmap(widht, height, System.Drawing.Imaging.PixelFormat.Format16bppGrayScale);
Rectangle dimension = new Rectangle(0, 0, pic.Width, pic.Height);
BitmapData picData = pic.LockBits(dimension, ImageLockMode.ReadWrite, pic.PixelFormat);
IntPtr pixelStartAddress = picData.Scan0;
//Marshal.Copy(data, 0, pixelStartAddress, data.Length);
Marshal.Copy(data, 0, pixelStartAddress, data.Length);
pic.UnlockBits(picData);
//SaveBmp(pic, Path.Combine(Directory.GetCurrentDirectory(), "imagem"));
SaveBmp(pic, "C:\Users\WIM\Desktop\teste\teste\teste.bmp");
pic.Dispose();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
private static void SaveBmp(Bitmap bmp, string path)
{
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bitmapData = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat);
var pixelFormats = ConvertBmpPixelFormat(bmp.PixelFormat);
BitmapSource source = BitmapSource.Create(bmp.Width,
bmp.Height,
bmp.HorizontalResolution,
bmp.VerticalResolution,
pixelFormats,
null,
bitmapData.Scan0,
bitmapData.Stride * bmp.Height,
bitmapData.Stride);
bmp.UnlockBits(bitmapData);
FileStream stream = new FileStream(path, FileMode.Create);
TiffBitmapEncoder encoder = new TiffBitmapEncoder();
encoder.Compression = TiffCompressOption.Zip;
encoder.Frames.Add(BitmapFrame.Create(source));
encoder.Save(stream);
stream.Close();
}
private static System.Windows.Media.PixelFormat ConvertBmpPixelFormat(System.Drawing.Imaging.PixelFormat pixelformat)
{
System.Windows.Media.PixelFormat pixelFormats = System.Windows.Media.PixelFormats.Default;
switch (pixelformat)
{
case System.Drawing.Imaging.PixelFormat.Format32bppArgb:
pixelFormats = PixelFormats.Bgr32;
break;
case System.Drawing.Imaging.PixelFormat.Format8bppIndexed:
pixelFormats = PixelFormats.Gray8;
break;
case System.Drawing.Imaging.PixelFormat.Format16bppGrayScale:
pixelFormats = PixelFormats.Gray16;
break;
}
return pixelFormats;
}
所以现在我们将 16bppGrayScale 更改为 Gray16。
要使用它,您需要在项目中添加对“PresentationCore”的引用。
那你就可以用了
System.Windows.Media.Format
还有人反对 BitmapSource
。