Adding footer/EXIF (binary data) to JPG image in C# - c#

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);
}

Related

Convert D3DImage to byte array

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;
}

PngBitmapEncoder creating an "empty" file

I have a DVC Chart displaying data. I have created a button to export the graph.
Here is the code:
private void btnExptGraph_Click(object sender, RoutedEventArgs e)
{
RenderTargetBitmap renderBitmap = new RenderTargetBitmap((int)mcChart.ActualWidth, (int)mcChart.ActualHeight,96d, 96d, PixelFormats.Pbgra32);
renderBitmap.Render(mcChart);
Console.WriteLine(renderBitmap.ToString());
//JpegBitmapEncoder encode = new JpegBitmapEncoder();
PngBitmapEncoder encode = new PngBitmapEncoder();
encode.Frames.Add(BitmapFrame.Create(renderBitmap));
string filename = "test.bmp";
FileStream fout = new FileStream(filename, FileMode.Create);
encode.Save(fout);
MessageBox.Show("File Saved Successfully");
fout.Close();
}
So my issue is that this executes properly with the exception that my file is basically empty. I only have a file of 1KB in size and no graph.
I've looked at the MSDN documentation and other stack overflow examples. All of them follow this form and people claim that it works. I've run with a debugger and the renderBitmap object is getting the correct height and weight values in all necessary properties. Any ideas?
My friend took a look at it and this was the solution that we came up with and does work:
Size size = new Size(mcChart.ActualWidth, mcChart.ActualHeight);
if (size.IsEmpty)
return;
size.Height *= 2;
size.Width *= 2;
RenderTargetBitmap result = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32);
DrawingVisual drawingvisual = new DrawingVisual();
using (DrawingContext context = drawingvisual.RenderOpen())
{
context.DrawRectangle(new VisualBrush(mcChart), null, new Rect(new Point(), size));
context.Close();
}
result.Render(drawingvisual);
string filename = "test.png";
FileStream fout = new FileStream(filename, FileMode.Create);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(result));
encoder.Save(fout);
fout.Close();
MessageBox.Show("Done");

How can close the image file using FileStream

I need to make image from canvas and from internet I find this code who works perfect. Is make the image and is good. But now I need to add some text in this new image and i think this is the problem. Sometimes is working, sometimes is throw me this exception. How can I close the image and change the image again. Or how can I change the image without is have exception.
int width = (int)(SystemParameters.PrimaryScreenWidth * 0.75);
int height = (int)canvas.Height;
RenderTargetBitmap renderBitmap = new RenderTargetBitmap(
width, (int)canvas.Height, 96d, 96d, PixelFormats.Pbgra32);
// needed otherwise the image output is black
canvas.Measure(new System.Windows.Size(width, height));
canvas.Arrange(new Rect(new System.Windows.Size(width, height)));
renderBitmap.Render(canvas);
//JpegBitmapEncoder encoder = new JpegBitmapEncoder();
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
using (FileStream file = File.Create(filename))
{
encoder.Save(file);
}
This is what Exception I have:
The process cannot access the file 'image.png' because it is being used by another process.
I try with file.Close(), I try without file.Close() filename is path to image file who create and then used and try delete
Here and my code This code is running every 1 second.
private void CreateSaveBitmap(Canvas canvas, string filename, string sensor)
{
int width = (int)(SystemParameters.PrimaryScreenWidth * 0.75);
int height = (int)canvas.Height;
RenderTargetBitmap renderBitmap = new RenderTargetBitmap(
width, (int)canvas.Height,
96d, 96d, PixelFormats.Pbgra32);
// needed otherwise the image output is black
canvas.Measure(new System.Windows.Size(width, height));
canvas.Arrange(new Rect(new System.Windows.Size(width, height)));
renderBitmap.Render(canvas);
//JpegBitmapEncoder encoder = new JpegBitmapEncoder();
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
using (FileStream file = File.Create(filename))
{
encoder.Save(file);
file.Close();
}
string head = SetHeadForPrintImage(sensor);
Bitmap bmp = new Bitmap(filename);
Graphics g = Graphics.FromImage(bmp);
g.DrawString(head, new System.Drawing.Font("Tahoma", 12f, System.Drawing.FontStyle.Regular), System.Drawing.Brushes.Black, new PointF(200, 0));
bmp.SetResolution(300, 300);
File.Delete(filename); // throw me exception
File.Delete(#".\Print\" + sensor + ".jpg");
bmp.Save(#".\Print\" + sensor + ".jpg");
}
private void CreateSaveBitmap(Canvas canvas, string filename, string sensor)
{
if(File.Exists(#".\Print\" + sensor + ".jpg"))
{
File.Delete(#".\Print\" + sensor + ".jpg");
}
int width = (int)(SystemParameters.PrimaryScreenWidth * 0.75);
int height = (int)canvas.Height;
RenderTargetBitmap renderBitmap = new RenderTargetBitmap(
width, (int)canvas.Height,
96d, 96d, PixelFormats.Pbgra32);
// needed otherwise the image output is black
canvas.Measure(new System.Windows.Size(width, height));
canvas.Arrange(new Rect(new System.Windows.Size(width, height)));
renderBitmap.Render(canvas);
//JpegBitmapEncoder encoder = new JpegBitmapEncoder();
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
using (FileStream file = File.Create(filename))
{
encoder.Save(file);
file.Close();
}
string head = SetHeadForPrintImage(sensor);
Bitmap bmp = new Bitmap(filename);
Graphics g = Graphics.FromImage(bmp);
g.DrawString(head, new System.Drawing.Font("Tahoma", 12f, System.Drawing.FontStyle.Regular), System.Drawing.Brushes.Black, new PointF(200, 0));
bmp.SetResolution(300, 300);
bmp.Save(#".\Print\" + sensor + ".jpg");
bmp.Dispose();
File.Delete(filename);
}
Bitmap objects often hold a reference to the file that they are read from , and the file may not be deleted while the bitmap is open (this differs depending on which file format the image is; it may be safe deleting the origninal file of a png but not jpg etc).
You should always call Dispose() on objects that are IDisposable.
The best thing would be if you could put the bmp object in a using statement

Save depth frame as a PNG 16 file - Kinect SDK

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);

Kinect Byte Stream Too Large

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.

Categories