I'm messing about with Bitmaps in a WPF application. I receive the a byte array on a background thread and a converter changes it to a bitmapSource for binding.
However, if I try to directly create the bitmapSource in memory and display it, it splits the image into two. I'm haven't had a great deal of experience with bitmaps but enough so that I could always display images.
The weird thing, is that if I write the file out first, then read it back in, it works.
File.WriteAllBytes(#"C:\woot2.bmp", bytes);
var bmp = new BitmapImage(new Uri(#"C:\woot2.bmp"));
var height = bmp.Height; // 480.0
var width = bmp.Width; // 640.0
var format = bmp.Format; // Indexed8
var dpix = bmp.DpiX; // 96.0
var dpiY = bmp.DpiY; // 96.0
var pallete = bmp.Palette; // Gray256
var cacheOption = bmp.CacheOption; // Default
var createOptions = bmp.CreateOptions; // None
return bmp;
[
I've checked the height, width, pixelFormat, pixelPalette, dpi etc all match the file that I read in and it still displays it incorrectly. Even checked the header of the file.
I've tried BitmapImages, BitmapSources, WriteableBitmaps with PngBitmapEncoder and I still get the same. I feel like either i'm missing something fundamental or there's a bug. I thought the slice was wrong, but there's no skewing,
i'm displaying it in an image:
<Image Width="640" Height="480"
Source="{Binding VisionImage, Mode=OneWay,UpdateSourceTrigger=PropertyChanged,
Converter={StaticResource VisionImageConverter} }"></Image>
Here's the code i've got to convert it currently
var width = 640;
var height = 480;
var dpiX = 96;
var dpiY = 96;
var pixelFormat = PixelFormats.Indexed8;
var palleteFormat = BitmapPalettes.Gray256;
var stride = 4* (((width*pixelFormat.BitsPerPixel) + 31)/32);
return BitmapSource.Create(width, height, dpiX, dpiY,
pixelFormat, palleteFormat, bytes, stride);
Any ideas?
Apparently the byte array does not contain raw pixel data, but an encoded BMP buffer, so you can't create a BitmapSource by BitmapSource.Create.
You should instead create a BitmapImage from the encoded BMP buffer like this:
var bitmapImage = new BitmapImage();
using (var stream = new MemoryStream(bytes))
{
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();
}
Or alternatively create a BitmapFrame like this:
BitmapFrame bitmapFrame;
using (var stream = new MemoryStream(bytes))
{
bitmapFrame = BitmapFrame.Create(stream,
BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
Okay, so the answer for this ended up being quite simple. Instead of giving it just the pixel array, I was giving it the whole bitmap file array. I think when you create a bitmapImage from a file it must use a bitmapDecoder internally. Which is why writing to a file and then reading it back worked.
So I could have calculated the pixel offset from the file and then copied the bytes that I needed, but I chose to wrap the array in a memorystream and use a BmpBitmapDecoder because it was simpler
using(var ms = new MemoryStream(bytes))
{
var decoder = new BmpBitmapDecoder(ms, BitmapCreateOptions.DelayCreation, BitmapCacheOption.OnDemand);
return new WriteableBitmap(decoder.Frames.FirstOrDefault());
}
Related
I've a byte array of a tiff image. I want to form 3 images from it,that i'll do later but the problem is i'm not able to display the original image as it is.
Here is my xaml:
<Image Grid.Row="0" Grid.Column="0" Name="ReferenceImage"/>
xaml.cs code:
public MainWindow()
{
InitializeComponent();
ImagePath = #"G:\TiffImage\Test.TIF"
DisplayAllImages();
}
private void DisplayAllImages()
{
byte[] imageSize = File.ReadAllBytes(ImagePath);
ReferenceImage.Source = DisplayAllImages(imageSize, 64, 64);
}
private WriteableBitmap DisplayAllImages(byte[] imageData,int height,int width)
{
if (imageData != null)
{
PixelFormat format = PixelFormats.Gray8;
WriteableBitmap wbm = new WriteableBitmap(height, width, 96, 96, format, null);
wbm.WritePixels(new Int32Rect(0, 0, height, width), imageData, 1*width, 0);
return wbm;
}
else
{
return null;
}
}
My main aim is to display image using byte array like this way only, so that i can extract byte array to form other image as per requirements.
The image file contains encoded bitmap data. In order to access the raw pixels you would first have to decode the bitmap:
BitmapSource bitmap;
using (var fileStream = new FileStream(ImagePath, FileMode.Open, FileAccess.Read))
{
bitmap = BitmapFrame.Create(
fileStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
Now you would get the raw pixels by calling the CopyPixels method of the BitmapSource:
var width = bitmap.PixelWidth;
var height = bitmap.PixelHeight;
var stride = (width * bitmap.Format.BitsPerPixel + 7) / 8;
var imageData = new byte[height * stride];
bitmap.CopyPixels(imageData, stride, 0);
I'm not getting the expected images.Although i'm able to generate images which is matching almost 75% but not completely.
Image image = new Image ();
Bitmap bitmap = Bitmap.CreateBitmap (200, 100, Bitmap.Config.Argb8888);
Canvas canvas = new Canvas(bitmap);
var paint = new Paint();
paint.Color = Android.Graphics.Color.Red;
paint.SetStyle(Paint.Style.Fill);
Rect rect = new Rect(0, 0, 200, 100);
canvas.DrawRect(rect, paint);
Android.Widget.ImageView contains method SetImageBitmap.
What the best way to set Xamarin.Forms.Image from my bitmap?
Convert the Bitmap to byte[] via http://forums.xamarin.com/discussion/5950/how-to-convert-from-bitmap-to-byte-without-bitmap-compress
There are two solutions mentioned.
var byteArray = ByteBuffer.Allocate(bitmap.ByteCount);
bitmap.CopyPixelsToBuffer(byteArray);
byte[] bytes = byteArray.ToArray<byte>();
return bytes;
(in case first solution is still broken)
ByteBuffer buffer = ByteBuffer.Allocate(bitmap.ByteCount);
bitmap.CopyPixelsToBuffer(buffer);
buffer.Rewind();
IntPtr classHandle = JNIEnv.FindClass("java/nio/ByteBuffer");
IntPtr methodId = JNIEnv.GetMethodID(classHandle, "array", "()[B");
IntPtr resultHandle = JNIEnv.CallObjectMethod(buffer.Handle, methodId);
byte[] byteArray = JNIEnv.GetArray<byte>(resultHandle);
JNIEnv.DeleteLocalRef(resultHandle);
And then use
var image = new Image();
image.Source = ImageSource.FromStream(() => new MemoryStream(byteArray));
to create an Image.
I tried #Wosi's answer, but for some reason the rendering of the image after that didn't work and the code is specific to Android. I needed to work from a byte array to a bitmap and then back again. This is what I did:
Code for turning a bitmap into a byte array:
byte[] bitmapData;
using (var stream = new MemoryStream())
{
tempBitmap.Compress(Android.Graphics.Bitmap.CompressFormat.Png, 0, stream);
bitmapData = stream.ToArray();
}
And the code for turning a byte array into a bitmap:
Android.Graphics.Bitmap tempBitmap = Android.Graphics.BitmapFactory.DecodeByteArray(imageByteArray, 0, imageByteArray.Length, options);
Where "options" is defined as follows:
Android.Graphics.BitmapFactory.Options options = new Android.Graphics.BitmapFactory.Options
{
InJustDecodeBounds = true
};
Android.Graphics.Bitmap result = Android.Graphics.BitmapFactory.DecodeByteArray(bitmapArray, 0, byteArrayLength, options);
//int imageHeight = options.OutHeight;
//int imageWidth = options.OutWidth;
In this part the Bitmap gets decoded. This is done to get the image height and width properties. For my case I required this information to encode it as a byte array again.
There with this it is possible to encode a byte array to a string and then back again.
Setting an image source from a byte array is done as follows:
var imageSource = ImageSource.FromStream(() => new MemoryStream(ImageByteArray, 0, ImageByteArray.Length));
Going bonkers here trying to do this in WPF (with all the new image manipulation tools), but can't seem to find a working solution. So far, all the solutions are drawing them on the screen or making multiple saves, but I need to do this completely in memory.
Basically, I want to load a large jpeg into memory, resize it smaller (in memory), save as a small PNG file.
I can load the jpeg file into a BitMap object, fine. after that, I'm stumped.
I found this function that looks like it does the trick but it requires an ImageSource (unfortunately, I can't find a way to convert my in-memory BitMap object to an ImageSource that does not yield a NULL exception.)
private static BitmapFrame CreateResizedImage(ImageSource source, int width, int height, int margin)
{
dynamic rect = new Rect(margin, margin, width - margin * 2, height - margin * 2);
dynamic #group = new DrawingGroup();
RenderOptions.SetBitmapScalingMode(#group, BitmapScalingMode.HighQuality);
#group.Children.Add(new ImageDrawing(source, rect));
dynamic drawingVisual = new DrawingVisual();
using (drawingContext == drawingVisual.RenderOpen())
{
drawingContext.DrawDrawing(#group);
}
// Resized dimensions
// Default DPI values
dynamic resizedImage = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Default);
// Default pixel format
resizedImage.Render(drawingVisual);
return BitmapFrame.Create(resizedImage);
}
With WPF it's as easy as this:
private void ResizeImage(string inputPath, string outputPath, int width, int height)
{
var bitmap = new BitmapImage();
using (var stream = new FileStream(inputPath, FileMode.Open))
{
bitmap.BeginInit();
bitmap.DecodePixelWidth = width;
bitmap.DecodePixelHeight = height;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
}
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
using (var stream = new FileStream(outputPath, FileMode.Create))
{
encoder.Save(stream);
}
}
You may consider to only set one of DecodePixelWidth and DecodePixelHeight in order to preserve the original image's aspect ratio.
I have a byte array that contains a jpeg image. I was just wondering if it is possible to reduce its size?
Edit: Ok. I acknowledged my mistake. Then my question is how can I reduce the quality of the image comes from a byte array.
Please understand that there is no free lunch. Decreasing the size of a JPEG image by increasing the compression will also decrease the quality of the image. However, that said, you can reduce the size of a JPEG image using the Image class. This code assumes that inputBytes contains the original image.
var jpegQuality = 50;
Image image;
using (var inputStream = new MemoryStream(inputBytes)) {
image = Image.FromStream(inputStream);
var jpegEncoder = ImageCodecInfo.GetImageDecoders()
.First(c => c.FormatID == ImageFormat.Jpeg.Guid);
var encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, jpegQuality);
Byte[] outputBytes;
using (var outputStream = new MemoryStream()) {
image.Save(outputStream, jpegEncoder, encoderParameters);
outputBytes = outputStream.ToArray();
}
}
Now outputBytes contains a recompressed version of the image using a different JPEG quality.
By decreasing the jpegQuality (should be in the range 0-100) you can increase the compression at the cost of lower image quality. See the Encoder.Quality field for more information.
Here is an example where you can see how jpegQuality affects the image quality. It is the same photo compressed using 20, 50 and 80 as the value of jpegQuality. Sizes are 4.99, 8.28 and 12.9 KB.
Notice how the text becomes "smudged" even when the quality is high. This is why you should avoid using JPEG for images with uniformly colored areas (images/diagrams/charts created on a computer). Use PNG instead. For photos JPEG is very suitable if you do not lower the quality too much.
Please try:
// Create a thumbnail in byte array format from the image encoded in the passed byte array.
// (RESIZE an image in a byte[] variable.)
public static byte[] CreateThumbnail(byte[] PassedImage, int LargestSide)
{
byte[] ReturnedThumbnail;
using (MemoryStream StartMemoryStream = new MemoryStream(),
NewMemoryStream = new MemoryStream())
{
// write the string to the stream
StartMemoryStream.Write(PassedImage, 0, PassedImage.Length);
// create the start Bitmap from the MemoryStream that contains the image
Bitmap startBitmap = new Bitmap(StartMemoryStream);
// set thumbnail height and width proportional to the original image.
int newHeight;
int newWidth;
double HW_ratio;
if (startBitmap.Height > startBitmap.Width)
{
newHeight = LargestSide;
HW_ratio = (double)((double)LargestSide / (double)startBitmap.Height);
newWidth = (int)(HW_ratio * (double)startBitmap.Width);
}
else
{
newWidth = LargestSide;
HW_ratio = (double)((double)LargestSide / (double)startBitmap.Width);
newHeight = (int)(HW_ratio * (double)startBitmap.Height);
}
// create a new Bitmap with dimensions for the thumbnail.
Bitmap newBitmap = new Bitmap(newWidth, newHeight);
// Copy the image from the START Bitmap into the NEW Bitmap.
// This will create a thumnail size of the same image.
newBitmap = ResizeImage(startBitmap, newWidth, newHeight);
// Save this image to the specified stream in the specified format.
newBitmap.Save(NewMemoryStream, System.Drawing.Imaging.ImageFormat.Jpeg);
// Fill the byte[] for the thumbnail from the new MemoryStream.
ReturnedThumbnail = NewMemoryStream.ToArray();
}
// return the resized image as a string of bytes.
return ReturnedThumbnail;
}
// Resize a Bitmap
private static Bitmap ResizeImage(Bitmap image, int width, int height)
{
Bitmap resizedImage = new Bitmap(width, height);
using (Graphics gfx = Graphics.FromImage(resizedImage))
{
gfx.DrawImage(image, new Rectangle(0, 0, width, height),
new Rectangle(0, 0, image.Width, image.Height), GraphicsUnit.Pixel);
}
return resizedImage;
}
The byte array size can always be reduced but you will lose data about your image. You could reduce the quality of the jpeg then it would take up less data as a byte array.
Think of the JPEG bytes as being a very compressed representation of a much larger number of bytes.
So if you try to apply some function to reduce the number of bytes it would be like trying to compress an already compressed thing.
i have a filestream which im trying to convert into a image. i have the following code
FileStream imageFile = image["file"] as FileStream;
image is a map holding information on the image. please can some one direct me on what I should do next.
Image.FromStream will take a stream and return an image. Incidentally, if you use Bitmap.FromStream or Image.FromStream, or indeed any of these methods, they all return a Bitmap, so they can all be upcast to Bitmap from Image if you want them to be.
you can do the following:
int width = 128;
int height = width;
int stride = width / 8;
byte[] pixels = new byte[height * stride];
// Define the image palette
BitmapPalette myPalette = BitmapPalettes.Halftone256;
// Creates a new empty image with the pre-defined palette
BitmapSource image = BitmapSource.Create(
width,
height,
96,
96,
PixelFormats.Indexed1,
myPalette,
pixels,
stride);
FileStream stream = new FileStream("new.jpg", FileMode.Create);
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.FlipHorizontal = true;
encoder.FlipVertical = false;
encoder.QualityLevel = 30;
encoder.Rotation = Rotation.Rotate90;
encoder.Frames.Add(BitmapFrame.Create(image));
encoder.Save(stream);