How to keep EXIF of an edited image? - c#

So my program is fixing some image artefacts, it goes like this:
void FixFile(string path)
{
var bmp = new WriteableBitmap(new BitmapImage(new Uri(path)));
bmp.Lock();
// magick
bmp.Unlock();
using (var stream = new FileStream(path.Replace("DSC", "fix_DSC"), FileMode.Create))
{
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmp));
encoder.Save(stream);
}
}
The problem is that fixed image contains no EXIF data. How do I transfer EXIF data from the original image?

Load the source bitmap as BitmapFrame, not BitmapImage. Then pass the Metadata property of the source to the new BitmapFrame that is added to the Frames collection of the encoder.
public void FixFile(string path)
{
var source = BitmapFrame.Create(new Uri(path));
var metadata = (BitmapMetadata)source.Metadata;
var bmp = new WriteableBitmap(source);
bmp.Lock();
// magick
bmp.Unlock();
var target = BitmapFrame.Create(bmp, null, metadata, null); // here
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(target);
using (var stream = File.OpenWrite(path.Replace("DSC", "fix_DSC")))
{
encoder.Save(stream);
}
}

Related

How can i clear the memory after converting BitmapImage to Byte

I have two functions: one to convert from image to byte and other to convert from byte to bitmapImage.
So, when I open the window with that images, I convert from byte to bitmapImage and it works great, but when I close and open it again it just keeps on memory and if I continue to do that time and time again it just throws an exception Out Of Memory exception
Image to byte->
private byte[] ConvertImageToBinary(Image img)
{
using (MemoryStream ss = new MemoryStream())
{
img.Save(ss, System.Drawing.Imaging.ImageFormat.Jpeg);
var s = ss.ToArray();
var jpegQuality = 50;
Image image;
using (var inputStream = new MemoryStream(s))
{
image = Image.FromStream(inputStream);
var jpegEncoder = System.Drawing.Imaging.ImageCodecInfo.GetImageDecoders()
.First(c => c.FormatID == System.Drawing.Imaging.ImageFormat.Jpeg.Guid);
var encoderParameters = new System.Drawing.Imaging.EncoderParameters(1);
encoderParameters.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, jpegQuality);
Byte[] outputBytes;
using (var outputStream = new MemoryStream())
{
image.Save(outputStream, jpegEncoder, encoderParameters);
return outputBytes = outputStream.ToArray();
}
}
}
}
Byte to bitmap ->
public BitmapImage ConvertBinaryToImage(byte[] array)
{
var image = new BitmapImage();
using (var ms = new MemoryStream(array))
{
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad; // here
image.StreamSource = ms;
image.EndInit();
image.Freeze();
}
return image;
}
When I open the WindowDragAndDrop it loads all the images
But when I close it it still uses the same amount of memory
Image is indeed disposable (https://learn.microsoft.com/en-us/dotnet/api/system.drawing.image?view=netframework-4.8), so you also need:
using (var image = Image.FromStream(inputStream)){
}
Around everywhere you use Image objects.

Convert a WPF into a PNG file with C#

I would like to convert the WPF display into a PNG file.
This is being used to create an invoice for a holiday. The code I currently have creates the PNG file but it creates a blank document.
Here is what I am trying to do:
var screenCap = new RenderTargetBitmap((int)this.Width, (int)this.Height, 96d, 96d, PixelFormats.Default);
var encoder = new PngBitmapEncoder();
var outputScreen = BitmapFrame.Create(screenCap);
encoder.Frames.Add(outputScreen);
using (var file = File.OpenWrite(#"Invoice.png"))
{
encoder.Save(file);
}
You need to call screenCap.Render(this) after you've constructed the object, but before you call BitmapFrame.Create(screenCap)
The following code will capture a specified element from the display and write it to a specified path:
private void SaveImage(FrameworkElement targetElement, string savePath)
{
var bmp = new RenderTargetBitmap((int)targetElement.ActualWidth, (int)targetElement.ActualHeight, 96, 96, PixelFormats.Default);
bmp.Render(targetElement);
using (var stream = new MemoryStream())
{
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmp));
encoder.QualityLevel = 100;
encoder.Save(stream);
File.WriteAllBytes(savePath, stream.ToArray());
}
}

convert gdcm_image to .NET bitmap fail

Hi am trying to get bitmap to display from DICOM file , but when try to create bitmap from the buffer I get Parameter is not valid for the Bitmap constructor call. I use the following code
gdcm.ImageReader reader = new gdcm.ImageReader();
reader.SetFileName(_dicomFilePath);
if (reader.Read())
{
var image = reader.GetImage();
if (image != null)
{
byte[] imageByteArray = new byte[image.GetBufferLength()];
if (image.GetBuffer(imageByteArray))
{
MemoryStream stream = new MemoryStream();
stream.Write(imageByteArray, 0, imageByteArray.Length);
stream.Seek(0, SeekOrigin.Begin);
Bitmap bmp = new Bitmap(stream);
CurrentFrameDataGDCM = Imaging.CreateBitmapSourceFromHBitmap(
bmp.GetHbitmap(),
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromWidthAndHeight((int)image.GetRows(), (int)image.GetColumns()));
}
}
}

Cannot save Bitmapsource to Bitmapimage

I am trying to save an image from a users Clipboard. I am able to get all the correct data from the clipboard image into the Bitmapsource. I am trying to save to a Bitmapimage so I can upload a file to a website. When converting the Bitmapsource to a Bitmapimage, all of the Bitmapimages data stays null and will throw an exception.
private void Window_KeyDown(object sender, KeyEventArgs e)
{
if ((Keyboard.Modifiers == ModifierKeys.Control) && (e.Key == Key.V))
{
if (Clipboard.ContainsImage())
{
BitmapSource bitmapSource = Clipboard.GetImage();
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
MemoryStream memoryStream = new MemoryStream();
BitmapImage bImg = new BitmapImage();
encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
encoder.Save(memoryStream);
bImg.BeginInit();
bImg.StreamSource = new MemoryStream(memoryStream.ToArray());
bImg.EndInit();
var client = new WebClient();
var uri = bImg.UriSource;
var path = uri.AbsolutePath;
//client.UploadFile(link, path);
}
}
}
Both
var uri = bImg.UriSource;
var path = uri.AbsolutePath;
Will throw an unhandled exception of type 'System.NullReferenceException' occurred in WpfApplication1.exe
Additional information: Object reference not set to an instance of an object
Creating a BitmapImage from its StreamSource property will not magically set its UriSource property. You do not need that BitmapImage at all.
Write the original BitmapSource to a FileStream instead of a MemoryStream, and upload that file:
string path = ...
using (var fileStream = new FileStream(path, FileMode.Create))
{
encoder.Save(fileStream);
}
client.UploadFile(link, path);
It may even be unnecessary to create an intermediate file. Just upload the buffer of the MemoryStream:
using (var memoryStream = new MemoryStream())
{
encoder.Save(memoryStream);
client.UploadData(link, memoryStream.ToArray());
}

Convert resource to byte[]

I am having trouble converting an image resource into byte[].
For example, I have the following resource:
pack://application:,,,/AppName;component/Assets/Images/sampleimage.jpg
in my program. How do I convert this into a byte[].
I've tried using a BitMapImage, but it's ImageSource ends up being null after initialised.
This seems to work:
var info = Application.GetResourceStream(uri);
var memoryStream = new MemoryStream();
info.Stream.CopyTo(memoryStream);
return memoryStream.ToArray();
A general solution to convert a BitmapSource into a byte[] would look like this:
public byte[] GetImageBuffer(BitmapSource bitmap, BitmapEncoder encoder)
{
encoder.Frames.Add(BitmapFrame.Create(bitmap));
using (var stream = new MemoryStream())
{
encoder.Save(stream);
return stream.ToArray();
}
}
You would use it like shown below, with any of the BitmapEncoders that are available in WPF.
var uri = new Uri("pack://application:,,,/AppName;component/Assets/Images/sampleimage.jpg");
var bitmap = new BitmapImage(uri);
var buffer = GetImageBuffer(bitmap, new JpegBitmapEncoder());

Categories