I have to convert a BitmapSource into a BitmapImage because of the Interface I am using. If I display the BitmapSource to an Image view, it works. but when I try and convert the BitmapSource into a BitmapImage, I get this error: "The parameter value cannot be less than '3686400'.\r\nParameter name: buffer" string
My PixelFormat is Bgr24
Here is my code :
void GetFrame(BitmapSource image)
{
try
{
BitmapSourceImageChanged?.Invoke(image);
BitmapImage tempImage = new BitmapImage();
//tempImage = image as BitmapImage;
// Frame = tempImage; var temp = image.Format;
int imagePixelHeight = image.PixelHeight;
int imagePixelWidth = image.PixelWidth;
byte[] pixels = new byte[imagePixelHeight * imagePixelWidth];
int stride = (imagePixelWidth * image.Format.BitsPerPixel +7) / 8;
image.CopyPixels(pixels,stride,0);
Frame = BitmapImageFromArray(pixels, image.PixelWidth, image.PixelHeight);
if (Frame == null)
{
int stop = 0;
}
}
catch (Exception err)
{
Error = err.Message;
}
}
BitmapImage BitmapImageFromArray(byte[] transfer, int imageWidth, int imageHeight)
{
BitmapImage image = new BitmapImage();
try
{
using (
// Convert byte array to BitmapImage()
MemoryStream stream = new MemoryStream(transfer))
{
image.BeginInit();
image.DecodePixelWidth = imageWidth;
image.DecodePixelHeight = imageHeight;
image.StreamSource = stream;
image.CacheOption = BitmapCacheOption.OnLoad;
image.EndInit();
}
}
catch (Exception err)
{
}
return image;
}
My code throws the exception at the CopyPixels method.
Is my stride equation not correct?
Your stride calculation is ok, but not the calculation of the buffer size.
The buffer size is the product of the number of scan lines and the bytes per scan line, i.e. the stride:
int stride = (imagePixelWidth * image.Format.BitsPerPixel + 7) / 8;
byte[] pixels = new byte[imagePixelHeight * stride]; // here
However, your code will still not work, since you can not decode a BitmapImage from a raw pixel buffer. Decoding means to read an encoded bitmap frame, for example a JPG or PNG.
In order to decode a BitmapImage, you would have to create an encoded frame first:
public static BitmapImage BitmapSourceToBitmapImage(BitmapSource bitmapSource)
{
var bitmapImage = bitmapSource as BitmapImage;
if (bitmapImage == null)
{
bitmapImage = new BitmapImage();
var encoder = new BmpBitmapEncoder(); // or some other encoder
encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
using (var stream = new MemoryStream())
{
encoder.Save(stream);
stream.Position = 0;
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();
}
}
return bitmapImage;
}
That said, there should actually never be a need to have a method like the above at all. All properties that are relevant for a bitmap in WPF are declared in the BitmapSource class. The additional properties in BitmapImage are only used during creation, but have no relevance afterwards.
Related
I am trying to convert fingerprint byte array to imagesource in the WPF application but my tricks not working.
Here is the code :
byte[] fp_image; //fp_image contains the fingerprint byte array result
using (MemoryStream ms = new MemoryStream(fp_image))
{
Bitmap bmp = new Bitmap(ms);
FpImage.Source = ConvertToImageSource(bmp);
}
public static ImageSource ConvertToImageSource(Bitmap bitmap)
{
var imageSourceConverter = new ImageSourceConverter();
using (var memoryStream = new MemoryStream())
{
bitmap.Save(memoryStream, ImageFormat.Png);
var snapshotBytes = memoryStream.ToArray();
return (ImageSource)imageSourceConverter.ConvertFrom(snapshotBytes); ;
}
}
Note : If I convert any other image to byte array and convert it back to image with this code it's working.
Assuming byte[] fp_image contains raw data then you could directly create a BitmapSource (which is a subclass of ImageSource) with the method shown below. However, you need to know the width, height and pixel format of the bitmap in your buffer.
public BitmapSource GetBitmapFromRawBytes(
byte[] rawBytes, int width, int height, PixelFormat format)
{
var stride = (width * format.BitsPerPixel + 7) / 8;
return BitmapSource.Create(width, height, 96, 96, format, null, rawBytes, stride);
}
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 have a simple Byte array representing an image in gray scale (value come from 0 to 255)
There is an example :
Byte[] sample = new Byte[16] {
55 ,0 ,0 ,255,
12 ,255,75 ,255,
255,150,19 ,255,
42 ,255,78 ,255 };
I want to save this in jpeg format but I don't know how I can do this.
I've try 2 ways but it doesn't work. I think my byte array is not right formatted but I don't know how to do.
I have tried this :
TypeConverter tc = TypeDescriptor.GetConverter(typeof(Bitmap));
Bitmap bmp = (Bitmap)tc.ConvertFrom(sample);
bmp.Save(myPath, System.Drawing.Imaging.ImageFormat.Jpeg);
And this :
MemoryStream str = new MemoryStream(sample);
str.Position = 0;
using (Image image = Image.FromStream(str))
{
image.Save(myPath, System.Drawing.Imaging.ImageFormat.Jpeg);
}
With all your indications, I finally find a way :
public static void SaveByteArryToJpeg(string imagePath, Byte[] data, int width, int height)
{
if (width * height != data.Length)
throw new FormatException("Size does not match");
Bitmap bmp = new Bitmap(width, height);
for (int r = 0; r < height; r++)
{
for (int c = 0; c < width; c++)
{
Byte value = data[r * width + c];
bmp.SetPixel(c, r, Color.FromArgb(value, value, value));
}
}
bmp.Save(imagePath, System.Drawing.Imaging.ImageFormat.Jpeg);
}
BitmapImage photo = byteToImage(byte[] sample)
this functionto convert byte to BitmapImage
public BitmapImage byteToImage(byte[] buffer)
{
using(var ms = new MemoryStream(buffer))
{
var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = ms;
image.EndInit();
}
return image;
}
and then byteToImage will return an image, so you can save.
Hopefully this can help you
I use the following code for resizing a gif file.
public void Resize(int newWidth, int newHeight)
{
var dimension = new FrameDimension(_image.FrameDimensionsList[0]);
var frameCount = _image.GetFrameCount(dimension);
// load frames
var encoder = new GifBitmapEncoder();
for (var i = 0; i < frameCount; ++i)
{
_image.SelectActiveFrame(dimension, i);
encoder.Frames.Add(BitmapFrame.Create(
new Bitmap(_image, newWidth, newHeight).CreateBitmapSource()));
}
using (var ms = new MemoryStream())
{
encoder.Save(ms);
ms.Position = 0;
_image = new Bitmap(ms);
}
}
public static BitmapSource CreateBitmapSource(this Bitmap bitmap)
{
if (bitmap == null)
throw new ArgumentNullException("bitmap");
lock (bitmap)
{
IntPtr hBitmap = bitmap.GetHbitmap();
return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
hBitmap,
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
}
If I output it to a filestream, the gif is animated (altough the frames seem a little slower), but if i load it into an image, it doesn't matter wether from the memorystream or loading it from the "working" gif that came from the filestream, it only contains 1 frame.
What am I doing wrong?
I'm trying to rotate the image.. I have a pictureBox 369x276. But when I rotate, this size decrease.
The pictureBox sizeMode is PictureBoxSizeMode.StretchImage
here is my code:
Bitmap oldBitmap = (Bitmap)pictureBox1.Image;
float angle = 90;
var newBitmap = new Bitmap(oldBitmap.Width, oldBitmap.Height);
var graphics = Graphics.FromImage(newBitmap);
graphics.TranslateTransform((float)oldBitmap.Width / 2, (float)oldBitmap.Height / 2);
graphics.RotateTransform(angle);
graphics.TranslateTransform(-(float)oldBitmap.Width / 2, -(float)oldBitmap.Height / 2);
graphics.DrawImage(oldBitmap, new Point(0, 0));
pictureBox1.Image = newBitmap;
Just use RotateFlip:
Bitmap oldBitmap = (Bitmap)pictureBox1.Image;
oldBitmap.RotateFlip(RotateFlipType.Rotate90FlipNone);
pictureBox1.Image = oldBitmap;
As #Dan-o has pointed out, this allows a rotation of any of the degree's in the System.Drawing.RotateFlipType enum.
To rotate a Bitmap any angle without losing the size, you could do the following, but it's a bit convoluted!
One - Add the WriteableBitmapEx library to your project
Two - Add the XAML, WindowsBase and PresentationCore libraries to your project
Three - Use the following to rotate your Bitmap any amount of degrees:
class Program
{
static void Main(string[] args)
{
Bitmap oldBitmap = (Bitmap)pictureBox1.Image;;
var bitmapAsWriteableBitmap = new WriteableBitmap(BitmapToBitmapImage(oldBitmap));
bitmapAsWriteableBitmap.RotateFree(23);
var rotatedImageAsMemoryStream = WriteableBitmapToMemoryStream(bitmapAsWriteableBitmap);
oldBitmap = new Bitmap(rotatedImageAsMemoryStream);
}
public static BitmapImage BitmapToBitmapImage(Bitmap bitmap)
{
var memStream = BitmapToMemoryStream(bitmap);
return MemoryStreamToBitmapImage(memStream);
}
public static MemoryStream BitmapToMemoryStream(Bitmap image)
{
var memoryStream = new MemoryStream();
image.Save(memoryStream, ImageFormat.Bmp);
return memoryStream;
}
public static BitmapImage MemoryStreamToBitmapImage(MemoryStream ms)
{
ms.Position = 0;
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = ms;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.EndInit();
bitmap.Freeze();
return bitmap;
}
private static MemoryStream WriteableBitmapToMemoryStream(WriteableBitmap writeableBitmap)
{
var ms = new MemoryStream();
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(writeableBitmap));
encoder.Save(ms);
return ms;
}
}
Pain in the ass, but works!
The smaller image size is to be expected. I've never figured out why, but the Graphics.DrawImage really only works if you provide it not only a start location, but also a size. One of the overloads allows you to include size.