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:
Related
I am trying to save jpeg image from buffer array of RGBA.
I tried this code
byte[] buffer = new byte[m_FrameProps.ImgSize];
Marshal.Copy(m_BM.BackBuffer, buffer, 0, m_FrameProps.ImgSize); //m_BM is WriteableBitmap
using (MemoryStream imgStream = new MemoryStream(buffer))
{
using (System.Drawing.Image image = System.Drawing.Image.FromStream(imgStream))
{
image.Save(m_WorkingDir + "1", ImageFormat.Jpeg);
}
}
But I am getting run-time error: "An unhandled exception of type 'System.ArgumentException' occurred in System.Drawing.dll
Additional information: Parameter is not valid."
I tried also to create bitmap and then to use JpegBitmapEncoder
Bitmap bitmap;
using (var ms = new MemoryStream(buffer))
{
bitmap = new Bitmap(ms);
}
But I am getting the same error.
I guess it is because the alpha.
How should I do it? Do I need to loop the values and copy without alpha?
It is not possible to construct an image from an array of pixel data alone. At minimum pixel format information and image dimensions would also be required. This means any attempt to create a Bitmap directly from an ARGB array using streams will fail, the Image.FromStream() and Bitmap() methods both require that the stream contain some kind of header information to construct an image.
That said, given that you appear to know the dimensions and pixel format of the image you wish to save you can use the following method:
public void SaveAsJpeg(int width, int height, byte[] argbData, int sourceStride, string path)
{
using (Bitmap img = new Bitmap(width, height, PixelFormat.Format32bppPArgb))
{
BitmapData data = img.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, img.PixelFormat);
for (int y = 0; y < height; y++)
{
Marshal.Copy(argbData, sourceStride * y, data.Scan0 + data.Stride * y, width * 4);
}
img.UnlockBits(data);
img.Save(path, ImageFormat.Jpeg);
}
}
I have a System.Windows.Media.Drawing object that I am wanting to convert into a Bitmap object, and then from there extract the bytes that represent the image. I've looked about the internet and I can't seem to find how to do what I need, so any help would be appreciated.
So I finally found a way to convert a System.Windows.Media.Drawing object to a System.Drawing.Bitmap object, and from that get a byte[] object representing the image data. The following is not pretty but does actually work.
public static byte[] DrawingToBytes(Drawing drawing)
{
DrawingVisual visual = new DrawingVisual();
using (DrawingContext context = visual.RenderOpen())
{
// If using the BitmapEncoder uncomment the following line to get a white background.
// context.DrawRectangle(Brushes.White, null, drawing.bounds);
context.DrawDrawing(drawing);
}
int width = (int)(drawing.Bounds.Width)
int height = (int)(drawing.Bounds.Height)
Bitmap bmp = new Bitmap(width, height);
Bitmap bmpOut;
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(System.Drawing.Color.White);
RenderTargetBitmap rtBmp = new RenderTargetBitmap(width, height,
bmp.HorizontalResolution,
bmp.VerticalResolution,
PixelFormats.Pbgra32);
rtBmp.Render(visual);
// Alternative using BmpBitmapEncoder, use in place of what comes after if you wish.
// MemoryStream stream = new MemoryStream();
// BitmapEncoder encoder = new BmpBitmapEncoder();
// encoder.Frames.Add(BitmapFrame.Create(rtBmp));
// encoder.save(stream);
int stride = width * ((rtBmp.Format.BitsPerPixel + 7) / 8);
byte[] bits = new byte[height * stride];
bitmapSource.CopyPixels(bits, stride, 0);
unsafe
{
fixed (byte* pBits = bits)
{
IntPtr ptr = new IntPtr(pBits);
bmpOut = new Bitmap(width, height, stride,
System.Drawing.Imaging.PixelFormat.Format32bppPArgb, ptr);
}
}
g.DrawImage(bmpOut, 0, 0, bmp.Width, bmp.Height);
}
byte[] bytes;
using (MemoryStream ms = new MemoryStream())
{
bmp.Save(ms, ImageFormat.bmp);
data = ms.ToArray();
}
return bytes;
}
So yeah, it's horrible but it actually works.
You can try that:
byte[] ImageToByte(Image image)
{
ImageConverter converter = new ImageConverter();
return (byte[])converter.ConvertTo(img, typeof(byte[]));
}
This work also for Bitmap.
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 want to start learning about how to tear images apart to find patterns in them but in order to do that I need to first see what makes it up. I want to take a png and convert it into a byte array so I can print it out and see if I can recognize simple patterns in the array values.
So far I have this
public MainWindow()
{
InitializeComponent();
System.Drawing.Image image;
image = System.Drawing.Image.FromFile("one.png");
byte[] imArray = imageToByteArray(image);
String bytes = "";
foreach (Char bite in imArray)
{
bytes += "-"+bite;
}
MessageBox.Show(bytes);
}
public byte[] imageToByteArray(System.Drawing.Image imageIn)
{
MemoryStream ms = new MemoryStream();
imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
return ms.ToArray();
}
But it doesn't seem to be working. It gives me a null error when the conversion method is called. I have NO clue why this isn't working because my understanding of the compenents is nill.
If you can suggest an easier way to make this conversion feel free to post it. Im not stuck on this code I just want a working example so I have a starting point.
Thanks!
I'd recommend starting with Bitmap to look at binary data - most other formats store data compressed, so you have no chance to understand what is inside an image by looking at the bytes.
The method you want is Bitmap.LockBits. The article also includes complete sample how to read from file and look t bits, excerpt below:
Bitmap bmp = new Bitmap("c:\\fakePhoto.jpg");
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData =
bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] rgbValues = new byte[bytes];
// Copy the RGB values into the array.
Marshal.Copy(bmpData.Scan0, rgbValues, 0, bytes);
you could try converting the image to a dataURI then converting it to a blob, heres an example of how you can convert dataURIs to blobs
Blob from DataURL?
function dataURItoBlob(dataURI) {
var byteString = atob(dataURI.split(',')[1]);
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
var bb = new BlobBuilder();
bb.append(ab); return bb.getBlob(mimeString);
}
Or you can just open the file in a binary editor.
I´m using Kinect (Microsoft SDK) with XNA. I want to use GRATF for marker-recognition
How to convert the data of a Kinect ColorImageFrame to a System.Drawing.Bitmap or AForge.Imaging.UnmanagedImage that I can process them with GRATF?
void kinectSensor_ColorFrameReady(object sender, ColorImageFrameReadyEventArgs e)
{
Bitmap bitmap = null;
ColorImageFrame frame = e.OpenColorImageFrame();
byte[] buffer = new byte[frame.PixelDataLength];
frame.CopyPixelData(buffer);
// how to convert the data in buffer to a bitmap?
var glyphs = recognizer.FindGlyphs(bitmap);
...
}
You can find the answer in this article.
To summarize it, this method should do the trick:
Bitmap ImageToBitmap(ColorImageFrame img)
{
byte[] pixeldata = new byte[img.PixelDataLength];
img.CopyPixelDataTo(pixeldata);
Bitmap bmap = new Bitmap(img.Width, img.Height, PixelFormat.Format32bppRgb);
BitmapData bmapdata = bmap.LockBits(
new Rectangle(0, 0, img.Width, img.Height),
ImageLockMode.WriteOnly,
bmap.PixelFormat);
IntPtr ptr = bmapdata.Scan0;
Marshal.Copy(pixeldata, 0, ptr, img.PixelDataLength);
bmap.UnlockBits(bmapdata);
return bmap;
}