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.
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.
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:
https://blogs.msdn.microsoft.com/ericlippert/2008/09/10/vexing-exceptions/
https://www.codeproject.com/Articles/9538/Exception-Handling-Best-Practices-in-NET
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.
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.
Related
I am trying to create an image surface in c# CairoSharp using these two constructors:
public ImageSurface(byte[] data, Format format, int width, int height, int stride); public ImageSurface(IntPtr data, Format format, int width, int height, int stride);
I am trying to get the array of the linux framebuffer from a memorymappedfile:
var file = MemoryMappedFile.CreateFromFile("/dev/fb0", FileMode.Open, null, (3840 * 2160 * (32 / 8)));
I know I have to use an unsafe context to get it but am unsure the proper syntax to get the sequential pointer from the memeoryMapped object.
The constructors for the ImageSurface will not work with the MemoryMappedFile directly. You will have to Read bytes from the MemoryMappedFile and use those bytes to create the ImageSurface.
I never used C# on Linux before so I don't really know if all those objects are available but maybe like this?
private static void test()
{
Bitmap bmp = (Bitmap)Image.FromFile("some image");
BitmapData imgData = null;
try
{
imgData = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.ReadWrite,
bmp.PixelFormat
);
int finalLength = imgData.Stride * imgData.Height;
byte[] buf = new byte[finalLength];
IntPtr ptr = imgData.Scan0;
System.Runtime.InteropServices.Marshal.Copy(ptr, buf, 0, finalLength);
bmp.UnlockBits(imgData);
// Pointer to first byte lives inside of fixed context.
unsafe
{
fixed (byte* p = buf)
{
// your cairo code...
}
}
// Alternative...
var ptPinned = System.Runtime.InteropServices.GCHandle.Alloc(
buf, System.Runtime.InteropServices.GCHandleType.Pinned);
IntPtr ptCairo = ptPinned.AddrOfPinnedObject();
ptPinned.Free();
}
finally
{
if (imgData != null) {
bmp.UnlockBits(imgData);
}
}
}
In any case I am certain that you have to pass the pointer of an already allocated buffer. In the test above I just loaded the image pixels of a bitmap into a byte array. In order to get the pointer you Marshal it or use fixed. That is all on Windows though.
I am trying to convert some video playback code from Unity to C# WPF, and I have hit a wall with the conversion.
I have my video frame as a
private byte[] _textureBuffer;
and I am using this code:
MemoryStream ms = new MemoryStream(_textureBuffer, 0, _textureBuffer.Length);
ms.Seek(0, SeekOrigin.Begin);
System.Drawing.Image x = System.Drawing.Image.FromStream(ms);
Which results in a 'Parameter is invalid' crash on the last line.
The data is loaded into the buffer correctly and the buffer is filled:
_textureBuffer.Length = 4147200
The image is 3840 x 2160 resolution.
The original fully functional Unity code is:
private void UpdateTexture(int frameAt)
{
var block = _lz4Blocks[frameAt];
_filestream.Seek((long)block.address, SeekOrigin.Begin);
_filestream.Read(_lz4Buffer, 0, (int)block.size);
Marshal.Copy(_lz4Buffer, 0, _lz4BufferNative, (int)block.size);
Lz4Native.lz4_decompress_safe_native(_lz4BufferNative, _textureBufferNative, (int)block.size, _frameBytes);
Marshal.Copy(_textureBufferNative, _textureBuffer, 0, _frameBytes);
_texture.LoadRawTextureData(_textureBuffer);
_texture.Apply();
}
And my (crashing) WPF version is:
public System.Drawing.Image UpdateTexture(int frameAt)
{
var block = _lz4Blocks[frameAt];
_filestream.Seek((long)block.address, SeekOrigin.Begin);
_filestream.Read(_lz4Buffer, 0, (int)block.size);
Marshal.Copy(_lz4Buffer, 0, _lz4BufferNative, (int)block.size);
Lz4Native.lz4_decompress_safe_native(_lz4BufferNative, _textureBufferNative, (int)block.size, _frameBytes);
Marshal.Copy(_textureBufferNative, _textureBuffer, 0, _frameBytes);
MemoryStream ms = new MemoryStream(_textureBuffer, 0, _textureBuffer.Length);
ms.Seek(0, SeekOrigin.Begin);
System.Drawing.Image x = System.Drawing.Image.FromStream(ms);
return x;
// _texture.LoadRawTextureData(_textureBuffer);
// _texture.Apply();
}
This question says that the image data is not in the correct format, but has no answer for how to fix it.
Where am i going wrong?
EDIT: Using lockbits as suggested, yields me an image:
Bitmap x = new Bitmap(_width, _height, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
BitmapData bmpData = x.LockBits(new System.Drawing.Rectangle(0, 0, x.Width, x.Height), ImageLockMode.WriteOnly, x.PixelFormat);
Marshal.Copy(_textureBuffer, 0, bmpData.Scan0, _textureBuffer.Length);
x.UnlockBits(bmpData);
However it is scrambled.
When i expect:
and using Format8bppIndexed as suggested:
I am receiving the following exception:
Exception thrown:
'System.AccessViolationException' in System.Drawing.dll
When calling the Save function of a Bitmap. The procedure works fine the first time around, but subsequent calls throw this exception.
My application takes a single long image and vertically tiles it out into several separate images. I do this by first breaking out the whole image into bytes, then in a Parallel.For loop I generate the bitmap from a subset byte array.
// Generate Bitmap from width, height and bytes
private Bitmap GenerateBitmap(int width, int height, byte[] bytes)
{
Bitmap bmp = new Bitmap(width, height, Stride(width),
PixelFormat.Format8bppIndexed,
Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0));
bmp.SetPalette();
return bmp;
}
That is the bitmap generation routine.
Here is the loop body that calls it.
Parallel.For(0, tileCount, i =>
{
byte[] bytes = new byte[imageWidth * tileHeight];
for (int j = 0; j < bytes.Length; j++)
{
bytes[j] = imageBytes[j + (imageWidth * (tileHeight * i))];
}
arr[i] = GenerateBitmap(imageWidth, tileHeight, bytes);
});
And here is the code elsewhere that the exception is thrown.
foreach(Bitmap tile in pattern.Tiles)
{
Console.WriteLine("Creating Tile " + count);
using (Bitmap bmp = new Bitmap(tile))
{
bmp.Save(Globals.patternOutputPath + "tile_" + count + ".png");
}
count += 1;
}
Where Tiles is a property of the pattern that calls the for loop function (which returns a list of Bitmaps).
I am assuming I'm missing some clean up somewhere in here.
Additional info: all images (input and output) are 256 (index) color format.
Edit: The comments below address the problem at hand, and I THINK I've solved the problem. I changed the GenerateBitmap routine to the following and am no longer getting this exception, but I have some more testing to do.
private Bitmap GenerateBitmap(int width, int height, byte[] bytes)
{
Bitmap bmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
Marshal.Copy(bytes, 0, bmpData.Scan0, bytes.Length);
bmp.UnlockBits(bmpData);
return bmp;
/*Bitmap bmp = new Bitmap(width, height, Stride(width),
PixelFormat.Format8bppIndexed,
Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0));
bmp.SetPalette();
return bmp;*/
}
I'm using a C# library for reading of QRCodes. A lot of the samples I've found are based off the old version of zxing where the RGBLuminanceSource constructor still takes in bitmap. In the latest version RGBLuminanceSource only takes byte[]. I've tried to convert bitmap to byte[], but the decode result is always null.
Here's the code I used for conversion:
private byte[] GetRGBValues(Bitmap bmp)
{
// Lock the bitmap's bits.
System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = bmpData.Stride * bmp.Height;
byte[] rgbValues = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
bmp.UnlockBits(bmpData);
return rgbValues;
}
and for decode:
Bitmap bitmap = Bitmap.FromFile(#"C:\QRimages.jpg") as Bitmap;
LuminanceSource source = new RGBLuminanceSource(GetRGBValues(bitmap), bitmap.Width, bitmap.Height);
var binarizer = new HybridBinarizer(source);
var binBitmap = new BinaryBitmap(binarizer);
QRCodeReader reader = new QRCodeReader();
var result = reader.decode(binBitmap);
Somehow the result is always null.
Also, our requirement is that we have to use image taken by camera. I've tried this:
Bitmap bitmap = Bitmap.FromFile(#"C:\QRimages.jpg") as Bitmap;
BarcodeReader reader = new BarcodeReader { AutoRotate = true, TryHarder = true };
Result result = reader.Decode(bitmap);
It only works for the QR image I download online, but if I print out that image and take a picture of that with my phone, then try to process that image, the result is back to null.
Any suggestions would be appreciated.
Here's the image I'm using:
I've attempted two different methods to achieve this, the first being an Android-style method and the second being an OpenGL style method. From my activity, I create a view which contains the OpenGL (1.1) code.
The first method (android):
Bitmap b = gameView.GetDrawingCache (true); // this is always null
And the second method (opengl):
public Bitmap GrabScreenshot()
{
int size = Width * Height * 4;
byte[] bytes = new byte[size];
GL.ReadPixels<byte>(0, 0, Width, Height, All.Rgba, All.UnsignedByte, bytes);
Bitmap bmp = BitmapFactory.DecodeByteArray (bytes, 0, size);
return bmp;
}
I have not tested this code. I was thinking you might be able to use it as a guide.
How about trying something like this (derived from: OpenTK Forums):
public Bitmap GrabScreenshot()
{
Bitmap bmp = new Bitmap(Width, Height);
System.Drawing.Imaging.BitmapData data =
bmp.LockBits(otkViewport.ClientRectangle, System.Drawing.Imaging.ImageLockMode.WriteOnly,
System.Drawing.Imaging.PixelFormat.Format24bppRgb);
GL.Finish();
GL.ReadPixels(0, 0, this.otkViewport.Width, this.otkViewport.Height, PixelFormat.Bgr, PixelType.UnsignedByte, data.Scan0);
bmp.UnlockBits(data);
bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
return bmp;
}
I believe a problem might occur due to the formatting of the bytes. In the example, they explicitly state the beginning to the array of data with
data.Scan0
However, you just sends in a byte array.
Here a version that works on Xamarin.Android:
private static Bitmap GraphicsContextToBitmap(int width, int height)
{
GL.Flush();
GL.PixelStore (PixelStoreParameter.PackAlignment, 1);
var bitmap = Bitmap.CreateBitmap(width, height, Bitmap.Config.Argb8888);
var data = bitmap.LockPixels();
GL.ReadPixels(0, 0, width, height, PixelFormat.Rgba, PixelType.UnsignedByte, data);
GL.Finish();
bitmap.UnlockPixels();
return bitmap;
}