My program uses some WPF APIs to process images in a folder. I need to parse EXIF data from the images if there is any in order to get the value of the DateTimeOriginal property. I've been using a BitmapImage to load the image from a MemoryStream, but I need a BitmapFrame object to get at the BitmapMetadata object.
Here's my original code before I was given the requirement to get the DateTimeOriginal EXIF property:
BitmapImage src = new BitmapImage();
try {
using ( MemoryStream memoryStream = new MemoryStream( snapshot.ImageData ) ) {
src.BeginInit();
src.CacheOption = BitmapCacheOption.OnLoad;
src.StreamSource = memoryStream;
src.EndInit();
}
} catch ( NotSupportedException ex ) {
. . .
}
Now, to get the BitmapFrame, I need to create a BitmapDecoder and use the Frames[0] property. So I added the following right after the using statement:
BitmapDecoder decoder = BitmapDecoder.Create( memoryStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad );
This decodes the bitmap and gets the first Frame, which is cool. I still need to create the BitmapImage. I can do that by resetting the MemoryStream's position back to the beginning and keeping the code that's already there, but this will decode the same image a second time.
Is there a way I can convert the BitmapFrame into a BitmapImage? I thought I would cast the BitmapFrame into a BitmapSourceobject and then set the BitmapImage's Source propert, but the BitmapImage class doesn't seem to have a Source property.
Is this possible? If so, how is it done?
There should be no need for this conversion. The Remarks section in the BitmapImage MSDN page says:
BitmapImage primarily exists to support Extensible Application Markup
Language (XAML) syntax and introduces additional properties for bitmap
loading that are not defined by BitmapSource.
When using bitmaps, your application should usually always use the BitmapSource or ImageSource base classes. For example when you assign the Source property of the WPF Image control, you can use an instance of any type derived from ImageSource, because that is the type of the Source property.
So you could directly use a BitmapFrame because it also derives from ImageSource:
var bitmap = BitmapFrame.Create(
memoryStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
image.Source = bitmap;
Related
I use OpenFileDialog to get the path of an image and then set it to my image source property imgSelected.Source = new BitmapImage(new Uri(filenameSteg, UriKind.Absolute)); in a WPF application.
The thing is, I need to open that file again later but I can't open it since the file is being used by another process ( System.IO.IOException -> The process cannot access the file pathToFile because it is being used by another process.).
The code that needs to access it later it as follow :
bitmapSource = JpegBitmapDecoder.Create(File.Open(filenameSteg, FileMode.Open),
BitmapCreateOptions.None, BitmapCacheOption.OnLoad).Frames[0];
This bitmapSource is used to give that image to a WriteableBitmap and from there I go through the pixels.
Is there any way to dispose of a File opened with an OpenFileDialog ?
I tried to cast to to IDisposable, to use a using block and so on but this thing is persistent.
EDIT :
1 - I tried this (#ctacke answer) :
using (var stream = File.Open(filenameSteg, FileMode.Open)){
bitmapSource = JpegBitmapDecoder.Create(stream, BitmapCreateOptions.None,
BitmapCacheOption.OnLoad).Frames[0];}
But it still gives me the error about the process being already in used by another process because even though it will be disposed after, I still am trying to open the same file (filenameSteg) than I opened in the OpenFileDialog. (Or at least, that's how I see it.)
2 - I then tried this (based on #ctacke recommended link:
using (FileStream fileStream = new FileStream(filenameSteg+1, FileMode.Create)){
MemoryStream memoryStream = new MemoryStream();
BitmapImage bi = new BitmapImage();
byte[] fileBytes = File.ReadAllBytes(filenameSteg);
memoryStream.Write(fileBytes, 0, fileBytes.Length);
memoryStream.Position = 0;
bi.BeginInit();
bi.StreamSource = memoryStream;
bi.EndInit();
bitmapSource = bi;}
Note : Notice that I here ask for filenameSteg +1. That is because I wanted to test the rest of my method so I create a copy the file and simply added a 1 to its name. That being said, when using filenameSteg for real, it gave me the same error about being already in used which I again suspect is that I still am asking to open the same image that was previously opened in the OpenFileDialog.
3 - I thought of another approach which does not require me to dispose the opened image :
When I open the image for the first time in the OpenFileDialog, I store the byte array of the image in a variable so I could create the WriteableBitmap using the BitmapFactory and the byte array.
// This is in the OpenFileDialog. It is where I stock the image "pixels" in a byte array.
bytesArrayImage = File.ReadAllBytes(filenameSteg);
//And then later when I needed the WriteableBitmap, I used the byte array and the BitmapFactory
//imgSelected being the Image containing the image I opened in the OpenFileDialog, I used it's
//width and height
wb = BitmapFactory.New(Convert.ToInt32(imgSelected.Width),
Convert.ToInt32(imgSelected.Height)).FromByteArray(bytesArrayImage);
The problem with this approach is that, some pictures works fine and I can use the byte array to create the WriteableBitmap and go through it's pixels but in other cases it gives me an AccessViolationException stating : Attempted to read or write protected memory. This is often an indication that other memory is corrupt.. In other words, trying to bypass the dispose problem got me into another problem.
You should release the original image, something like this:
if(imgSelected.Source != null)
{
imgSelected.Source.Dispose;
}
imgSelected.Source = new BitmapImage(new Uri(filenameSteg, UriKind.Absolute));
Next, File.Open returns a stream, which you need to explicitly release.
using(var stream = File.Open(filenameSteg, FileMode.Open))
{
var bitmapSource = JpegBitmapDecoder.Create(stream,
BitmapCreateOptions.None, BitmapCacheOption.OnLoad).Frames[0];
}
See also: Load a BitmapSource and save using the same name in WPF -> IOException
Am using BitmapSource class for reading an image from my temp folder and then reading the metadata using BitmapMetadata class.
BitmapSource img = BitmapFrame.Create(new Uri(filepath));
BitmapMetadata meta = (BitmapMetadata)img.Metadata;
DateTime datetaken = DateTime.Parse(meta.DateTaken);
System.IO.File.Delete(filepath);
While i was trying to delete the image am getting an exception saying "The process cannot access the file 'filepath/filename' because it is being used by another process.".I thought of disposing the bitmapsource before deleting the image. While i was searching for the solution i got info like
"You do not have to Dispose() a BitmapSource. Unlike some other "image" classes in the Framework, it does not wrap any native resources.
Just let it go out of scope, and the garbage collector will free its memory." in the following link Proper way to dispose a BitmapSource .I just want to delete the file that exists in the physical folder. Is there any proper way for the deletion of physical path.
Thanks in advance.
You could do as the top suggested answer here and copy the file to a stream first and initialize the bitmap source from the stream e.g.
MemoryStream memoryStream = new MemoryStream();
byte[] fileBytes = File.ReadAllBytes(filepath);
memoryStream.Write(fileBytes, 0, fileBytes.Length);
memoryStream.Position = 0;
BitmapSource img = BitmapFrame.Create(memoryStream);
BitmapMetadata meta = (BitmapMetadata)img.Metadata;
DateTime datetaken = DateTime.Parse(meta.DateTaken);
System.IO.File.Delete(filepath);
I've tried this and it works for me
How can I convert a System.Windows.Media.ImageSource to a System.Drawing.Bitmap in C#?
its older OP, but still it can come handy for some other people, as it took some time to find cleaner solution without dll interop or clipboard hacks.
this worked for me, you can use pngencoder to cut the image size before saving to file or rtf stream
private System.Drawing.Image ImageWpfToGDI(System.Windows.Media.ImageSource image) {
MemoryStream ms = new MemoryStream();
var encoder = new System.Windows.Media.Imaging.BmpBitmapEncoder();
encoder.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(image as System.Windows.Media.Imaging.BitmapSource));
encoder.Save(ms);
ms.Flush();
return System.Drawing.Image.FromStream(ms);
}
Please see HOW TO USE IMAGESOURCE (NO HANDLER) IN WINFORMS AS SYSTEM.DRAWING.BITMAP (HBITMAP):
How to easily convert WinForms
System.Drawing.Bitmap into WPF
ImageSource you learned from this
article. Today, I'll explain how to do
it contrary. Actually, all he have to
do is to extract handler from
BitmapSource, however, such approach
is not supported, thus the only thing
we can do is just copy pixels of
BitmapSource (or BitmapFrame) into
byte array and then copy them into the
pointer of HBitmap.
I am using this C# code to access an image file in order to read metadata from it.
BitmapSource img = BitmapFrame.Create(uri);
Unfortunately the image file specified by uri becomes locked until the program ends. How do I prevent the image from being locked?
maybe this could help ?
edit
BitmapSource img = BitmapFrame.Create(uri,BitmapCreateOptions.None,BitmapCacheOption.OnLoad);
BitmapCreateOptions.None = default option
BitmapCacheOption.OnLoad = Caches the entire image into memory at load time. All requests for image data are filled from the memory store.
from here
If you want to be able to delete/change the file immediately afterwards, read the whole file into memory, and then give it the MemoryStream instead. For example:
MemoryStream data = new MemoryStream(File.ReadAllBytes(file));
BitmapSource bitmap = BitmapFrame.Create(data);
You can also use generic stream:
Stream stream = File.OpenRead(filename);
Bitmap template = new Bitmap(stream); // or (Bitmap) Bitmap.FromStream(stream)
stream.Close();
I am binding an Image.Source property to the result of the property shown below.
public BitmapSource MyImageSource
{
get
{
BitmapSource source = null;
PngBitmapDecoder decoder;
using (var stream = new FileStream(#"C:\Temp\logo.png", FileMode.Open, FileAccess.Read, FileShare.Read))
{
decoder = new PngBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
if (decoder.Frames != null && decoder.Frames.Count > 0)
source = decoder.Frames[0];
}
return source;
}
}
For some reason this is failing during the rendering of the image (Deep in the PresentationCore assembly). I am certain the image is not corrupt as I can successfully show the same image w/o the binding
<Image Name="FooImage" Source="/logo.png" />
I have to bind the image source in code because I will eventually be creating the image stream from a base64 string.
Anyone know if this is a bug w/ WPF? or am I doing something incorrectly?
The problem was the BitmapCacheOption option, changing to BitmapCacheOption.OnLoad works.
With BitmapCacheOption.None the BitmapSource isn’t decoded until the image is rendered, but the stream with the png in it is already disposed at that point. If you cache OnLoad, it’ll decode right away and cache the results, rather than trying to decode later when the stream no longer exists.
Also, have you tried just using a BitmapImage to load the image? It works fine with PNG, BMP, and JPEG.
It's also a specialized type of BitmapSource, so you could just replace your code in your property with this:
BitmapImage img = new BitmapImage(new Uri(#"C:\Temp\logo.png"));
return img;
Are you positive it's a PNG and not just a renamed Bitmap or Jpeg? If you create a new Bitmap image and then just rename it and change the file extension, this error is reproducible.
If I use a known PNG with your code, I don't get your issue, but a COM exception is thrown:
The handle is invalid. (Exception from HRESULT: 0x80070006 (E_HANDLE))
Can you try it out with a random PNG off the web and see if you get the same result?