Reputation: 13
I need to save a intraoral x-ray image, I'm try to use the 8bppIndexed pixel format, and using this code to save:
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);
}
}
And with this line Marshal.Copy(data, 0, pixelStartAddress, data.Length);
I receive the AcessViolationException.
I'm not used to c# so I really don't undertand why I got this error. What I read is that maybe the ptr has been in use, but if I use other pixelFormat, like 16bppRgb565, I can capture the image and transform to grayScale, but the image return deformed. Like this
I'm using an array of short because It's the data I received from the sdk I received from the company we bought the sensor;
In my researches, I found the 16bppGrayScale is a good option, but the GDI+ don't have it encoded yet. But we can make a DICOM for this, but we need to run the application on nootbooks with a hardware not that good, so for that I'm trying to use the 8bppIndexed pixel format.
Upvotes: 0
Views: 491
Reputation: 13
I dicided to answer my own question to make the post more easy to read.
I managed to find a solution through this post: Generate 16-bit grayscale BitmapData and save to file
So please vote on the answer to make more people find the solution more easily.
And the code I'm using now is like this now:
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;
}
So now we change the 16bppGrayScale to Gray16.
To use that, you will need to add the reference to the "PresentationCore" on your project.
That you can use then
System.Windows.Media.Format
And anothers objects like BitmapSource
.
Upvotes: 0
Reputation: 9804
One issue is that you are dealing with unmanaged code. The other is that the exception handling is extremely questionable.
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);
}
There are a number of issues in that code:
Use of Disposeable resoruces without using
The code uses the class Bitmap which needs disposing, but forgoes the using statement. while there is a call to Dispose(), it will not be executed in a exception case. Using is there for that precise
Catching Exception
Catching Exception for anything but logging and letting it go on to kill the process, is a deadly sin of Exception handling. Swallowing anything but a Vexing or Exogenous exception sets you up for more and less undertanable followup exceptions.
There are two articles on proper exception handling I would advise you to read. They explain the things much better then I could:
Ignoring any exception can cause more and less understandable follwup errors. Especially if you ignore errors regarding Arrays, you can easily cause a Access Violation Exception.
Note that a Access Violation Exception is good example for a Fatal Exception itself. If you have one, it is safer to let the process die!
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");
}
Asuming the Access Violation was not caused by ignoring another Exception:
These 3 lines are the unamanaged code:
IntPtr pixelStartAddress = picData.Scan0;
Marshal.Copy(data, 0, pixelStartAddress, data.Length);
pic.UnlockBits(picData);
I try to avoid unmanaged code so I can not truly help you debugging it. But I noticed that you use data
twice - but it seems not defined anywhere in this code.
Upvotes: 0
Reputation: 34152
short[] data
the type of data is short
, while it should be byte[]
but I think what actually causing the problem is: data.Length
should be equal to picData.Stride * pic.Height
and in your case, it looks like it is not.
Upvotes: 1