I'm need to save depth frames as Gray PNG 16 images.
I only know how to save them as 32bgr images.
Here's my code:
private byte[] depthFrame32;
using (DepthImageFrame imageFrame = e.OpenDepthImageFrame())
{
if (imageFrame != null)
{
int stride = imageFrame.Width * 4;
BitmapSource bmp = BitmapSource.Create(imageFrame.Width, imageFrame.Height,
96, 96, PixelFormats.Bgr32, null, this.depthFrame32, stride);
using (var fileStream = new FileStream(full_path, FileMode.Create))
{
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmp));
encoder.Save(fileStream);
}
}
}
Thanks in advance
Not sure where your depthFrame32 array is filled with image data.
Anyway, you said you know how to save a depth frame as BGR32 bitmap. You could then always create a FormatConvertedBitmap from the original bitmap and save that:
...
var gray16Bitmap = new FormatConvertedBitmap(bmp, PixelFormats.Gray16, null, 0d);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(gray16Bitmap));
using (var fileStream = new FileStream(full_path, FileMode.Create))
{
encoder.Save(fileStream);
}
Assuming that you already have 16-bit grayscale data (e.g. from the converted outcome of DepthImageFrame.CopyPixelDataTo), you may of course also directly create a BitmapSource like this:
var stride = imageFrame.Width * 2;
var bmp = BitmapSource.Create(imageFrame.Width, imageFrame.Height,
96, 96, PixelFormats.Gray16, null, grayScaleData, stride);
Related
Is there any way to do that ?
I can cast the object to ImageSource and assign to Image but I have to be able to store it in byte[]. All the methods I found use casting to BitMap and that won't work here.
Here is a solution I found. The key was the use of DrawingVisual to create temporary image internally.
public static byte[] ImageToBytes(ImageSource imageSource)
{
var bitmapSource = imageSource as BitmapSource;
if (bitmapSource == null)
{
var width = (int)imageSource.Width;
var height = (int)imageSource.Height;
var dv = new DrawingVisual();
using (var dc = dv.RenderOpen())
{
dc.DrawImage(imageSource, new Rect(0, 0, width, height));
}
var rtb = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
rtb.Render(dv);
bitmapSource = BitmapFrame.Create(rtb);
}
byte[] data;
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
using (var ms = new MemoryStream())
{
encoder.Save(ms);
data = ms.ToArray();
}
return data;
}
Hello all I have an image and I want to add binary data as a footer to that image.
RGBImage rgbImage = (RGBImage) RGBImage.LoadImage(#"test.tiff");
byte[] bytes = File.ReadAllBytes(#"C:\TEMP\gili.bin");
int padding =(int) Math.Ceiling((double)bytes.Length/(rgbImage.Width*3));
byte[] newMakerNoteImage = new byte[rgbImage[0].Data.Length + (rgbImage.Width * 3 * padding)];
Buffer.BlockCopy(rgbImage[0].Data, 0, newMakerNoteImage, 0, rgbImage[0].Data.Length);
Buffer.BlockCopy(bytes, 0, newMakerNoteImage, rgbImage[0].Data.Length, bytes.Length);
BitmapPalette myPalette = BitmapPalettes.WebPalette;
// Creates a new empty image with the pre-defined palette
BitmapSource image = BitmapSource.Create(
rgbImage.Width,
rgbImage.Height,
96,
96,
PixelFormats.Bgr24,
myPalette,
newMakerNoteImage,
rgbImage.Width * 3);
FileStream stream = new FileStream(#"C:\TEMP\new.jpg", FileMode.Create);
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.FlipHorizontal = false;
encoder.FlipVertical = false;
encoder.QualityLevel = 30;
encoder.Frames.Add(BitmapFrame.Create(image));
encoder.Save(stream);
the image outputs fine however the binary data is not added to the end of the image.
Can you tell me if I'm doing it correctly?
I think i might be looking at it all wrong and I need to use EXIF in order to add this makernote data into the image. the data shoudln't be visible to the user of the image.
here is my tested solution for EXIF/MakerNote creation
As far as I have read 37500 is the makernote hexdecimal tag inside EXIF.
http://nicholasarmstrong.com/2010/02/exif-quick-reference/
public void CreateMakerNoteJpgImage(byte[] makerNoteArray, string path)
{
BitmapPalette myPalette = BitmapPalettes.WebPalette;
// Creates a new empty image with the pre-defined palette
BitmapSource image = BitmapSource.Create(
Width,
Height,
96,
96,
PixelFormats.Bgr24,
myPalette,
_channels[0].Data,
Width * 3);
FileStream stream = new FileStream(path, FileMode.Create);
BitmapMetadata metadata = new BitmapMetadata("jpg");
//adding makernote data into EXIF of the jpeg image
metadata.SetQuery("/app1/ifd/exif:{uint=37500}", makerNoteArray);
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.FlipHorizontal = false;
encoder.FlipVertical = false;
encoder.QualityLevel = 30;
BitmapFrame frame = BitmapFrame.Create(image, null, metadata, null);
encoder.Frames.Add(frame);
encoder.Save(stream);
}
I have a WPF control in my PowerPoint add-in that hosts an image that I want to be able to drag & drop onto the active slide. I can get the image to appear on the slide, but the transparent areas are rendered in black.
My code to initialize the drag from my attached behavior:
var targetBitmap = new RenderTargetBitmap(
(int) MyWpfControl.ActualWidth,
(int) MyWpfControl.ActualHeight,
96d, 96d, PixelFormats.Default);
targetBitmap.Render(MyWpfControl);
var dataObject = new DataObject(
DataFormats.Bitmap,
targetBitmap);
DragDrop.DoDragDrop(MyWpfControl, dataObject, DragDropEffects.Copy)
Thinking that maybe I needed to pass a System.Drawing.Image, I attempted this modification, which only resulted in the transparent areas being rendered in gray:
var targetBitmap = new RenderTargetBitmap(
(int) MyWpfControl.ActualWidth,
(int) MyWpfControl.ActualHeight,
96d, 96d, PixelFormats.Default);
targetBitmap.Render(MyWpfControl);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(targetBitmap));
var ms = new MemoryStream();
encoder.Save(ms);
var dataObject = new DataObject(DataFormats.Bitmap, Image.FromStream(ms, true))
DragDrop.DoDragDrop(MyWpfControl, dataObject, DragDropEffects.Copy)
I did a test where I replaced the memory stream with a file stream, and the image that was written did indeed have the correct transparency.
So what am I missing here? How can I maintain transparency?
I was able to resolve this by following the instructions in this blog post. The solution was to use the EnhancedMetafile DataFormat in my DataObject.
Edit:
Here’s the code that initiates the drag operation.
private void Image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Bitmap bitmap = ImageToBitmap(e.Source as System.Windows.Controls.Image);
DataObject data = new DataObject(DataFormats.EnhancedMetafile, MakeMetafileStream(bitmap));
DragDrop.DoDragDrop((DependencyObject)e.Source, data, DragDropEffects.Copy);
}
This makes use of a utility function to convert the Image to a Bitmap:
private Bitmap ImageToBitmap(System.Windows.Controls.Image image)
{
RenderTargetBitmap rtBmp = new RenderTargetBitmap((int)image.ActualWidth, (int)image.ActualHeight,
96.0, 96.0, PixelFormats.Pbgra32);
image.Measure(new System.Windows.Size((int)image.ActualWidth, (int)image.ActualHeight));
image.Arrange(new Rect(new System.Windows.Size((int)image.ActualWidth, (int)image.ActualHeight)));
rtBmp.Render(image);
PngBitmapEncoder encoder = new PngBitmapEncoder();
MemoryStream stream = new MemoryStream();
encoder.Frames.Add(BitmapFrame.Create(rtBmp));
// Save to memory stream and create Bitamp from stream
encoder.Save(stream);
return new System.Drawing.Bitmap(stream);
}
This also requires a utility function that converts a Bitmap to a stream containing a Metafile, taken from Stack Overflow.
// From Convert an image into WMF with .NET?
private MemoryStream MakeMetafileStream(Bitmap image)
{
Graphics graphics = null;
Metafile metafile = null;
var stream = new MemoryStream();
try
{
using (graphics = Graphics.FromImage(image))
{
var hdc = graphics.GetHdc();
metafile = new Metafile(stream, hdc);
graphics.ReleaseHdc(hdc);
}
using (graphics = Graphics.FromImage(metafile))
{ graphics.DrawImage(image, 0, 0); }
}
finally
{
if (graphics != null)
{ graphics.Dispose(); }
if (metafile != null)
{ metafile.Dispose(); }
}
return stream;
}
I'm trying to stream Kinect video data (just the image, not depth/infared) but I find the default buffer size on the image is very large (1228800) and incapable of sending over a network. I was wondering if there was any way of getting access to a smaller array without having to go down the route of codec compression. Here's is how I declare the Kinect which I took from a Microsoft sample;
// Turn on the color stream to receive color frames
this.sensor.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30);
// Allocate space to put the pixels we'll receive
this.colorPixels = new byte[this.sensor.ColorStream.FramePixelDataLength];
// This is the bitmap we'll display on-screen
this.colorBitmap = new WriteableBitmap(this.sensor.ColorStream.FrameWidth,
this.sensor.ColorStream.FrameHeight, 96.0, 96.0, PixelFormats.Bgr32, null);
// Set the image we display to point to the bitmap where we'll put the image data
this.kinectVideo.Source = this.colorBitmap;
// Add an event handler to be called whenever there is new color frame data
this.sensor.ColorFrameReady += this.SensorColorFrameReady;
// Start the sensor!
this.sensor.Start();
And here is the New Frame event which I then try to send each frame;
private void SensorColorFrameReady(object sender,
ColorImageFrameReadyEventArgs e)
{
using (ColorImageFrame colorFrame = e.OpenColorImageFrame())
{
if (colorFrame != null)
{
// Copy the pixel data from the image to a temporary array
colorFrame.CopyPixelDataTo(this.colorPixels);
// Write the pixel data into our bitmap
this.colorBitmap.WritePixels(
new Int32Rect(0, 0, this.colorBitmap.PixelWidth,
this.colorBitmap.PixelHeight),
this.colorPixels,
this.colorBitmap.PixelWidth * sizeof(int),
0);
if (NetworkStreamEnabled)
{
networkStream.Write(this.colorPixels, 0,
this.colorPixels.GetLength(0));
}
}
}
}
UPDATE
I'm using the following two methods to convert the ImageFrame to a Bitmap and then the Bitmap to a Byte[]. This has brought the buffer size down to ~730600. Still not enough but progress. (Source: Convert Kinect ColorImageFrame to Bitmap)
public static byte[] ImageToByte(Image img)
{
ImageConverter converter = new ImageConverter();
return (byte[])converter.ConvertTo(img, typeof(byte[]));
}
Bitmap ImageToBitmap(ColorImageFrame Image)
{
byte[] pixeldata = new byte[Image.PixelDataLength];
Image.CopyPixelDataTo(pixeldata);
Bitmap bmap = new Bitmap(Image.Width, Image.Height, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
BitmapData bmapdata = bmap.LockBits(
new Rectangle(0, 0, Image.Width, Image.Height),
ImageLockMode.WriteOnly,
bmap.PixelFormat);
IntPtr ptr = bmapdata.Scan0;
Marshal.Copy(pixeldata, 0, ptr, Image.PixelDataLength);
bmap.UnlockBits(bmapdata);
return bmap;
}
My recommendation would be to store the colorframe in a bitmap, then send those files over the network and reassemble them in a video program. A project I've been doing with the Kinect does this:
//Save to file
if (skeletonFrame != null)
{
RenderTargetBitmap bmp = new RenderTargetBitmap(800, 600, 96, 96, PixelFormats.Pbgra32);
bmp.Render(window.image);
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
// create frame from the writable bitmap and add to encoder
if (skeletonFrame.Timestamp - lastTime > 90)
{
encoder.Frames.Add(BitmapFrame.Create(bmp));
string myPhotos = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
string path = "C:your\\directory\\here" + skeletonFrame.Timestamp + ".jpg";
using (FileStream fs = new FileStream(path, FileMode.Create))
{
encoder.Save(fs);
}
lastTime = skeletonFrame.Timestamp;
}
}
Of course, if you need this to be in real time, you're not going to like this solution, and I think my "comment" button is gone after the bounty.
Can someone please suggest how to save bgr101010 format .tiff file in 32bpp? My code is saving in 48bpp? Basically I want to save tiff a file with 10 bit color depth.
private void Bgr()
{
BitmapImage myBitmapImage = new BitmapImage();
BitmapSource bs = new BitmapImage(new Uri(#"img\android1.png", UriKind.Relative));
int stride = bs.PixelWidth * (bs.Format.BitsPerPixel / 8);
byte[] data = new byte[stride * bs.PixelHeight];
bs.CopyPixels(data, stride, 0);
WriteableBitmap w2Bmp = new WriteableBitmap(bs.PixelWidth, bs.PixelWidth, 96.0, 96.0,PixelFormats.Bgr101010, null);
w2Bmp.WritePixels(
new Int32Rect(0, 0, bs.PixelWidth, bs.PixelHeight),
data, stride, 0);
image1.Source = w2Bmp;
var encoder = new TiffBitmapEncoder();
BitmapFrame frame = BitmapFrame.Create(w2Bmp);
encoder.Frames.Add(frame);
using (var stream = File.Create("XXX3.tiff"))
{
encoder.Save(stream);
}
}
A cursory glance at the decompiled sources of TiffBitmapEncoder reveals that it calls a native method to actually write to the TIFF. If, even on explicitly being passed the PixelFormat to write to it chooses to write something else, it might be a limitation of the underlying TIFF encoder.
Have you tried using ImageMagick or something similar with TIFF support?