I am trying to update an image by setting the source property every second, this works however causes a flicker when updated.
CurrentAlbumArt = new BitmapImage();
CurrentAlbumArt.BeginInit();
CurrentAlbumArt.UriSource = new Uri((currentDevice as AUDIO).AlbumArt);
CurrentAlbumArt.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
CurrentAlbumArt.EndInit();
If I don't set IgnoreImageCache, the image does not update thus no flickering either.
Is there a way around this caveat?
Cheers.
The following code snippet downloads the whole image buffer before setting the Image's Source property to a new BitmapImage. This should eliminate any flicker.
var webClient = new WebClient();
var url = ((currentDevice as AUDIO).AlbumArt;
var bitmap = new BitmapImage();
using (var stream = new MemoryStream(webClient.DownloadData(url)))
{
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
}
image.Source = bitmap;
If the download takes some time, it would make sense to run it in a separate thread. You would then have to take care for proper cross-thread access by also calling Freeze on the BitmapImage and assigning Source in the Dispatcher.
var bitmap = new BitmapImage();
using (var stream = new MemoryStream(webClient.DownloadData(url)))
{
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
}
bitmap.Freeze();
image.Dispatcher.Invoke((Action)(() => image.Source = bitmap));
Related
I'm developing a kind of real-time video chatting and also trying to make a binary image using OpenCV. I'm working on C# WPF and I have to draw images on Canvas. This means I need 'BitmapImage' to draw the screen. But I have to use 'Mat' to make a binary image.
So I tried many solutions found from StackOverflow but never worked for me.
Below is my code.
Mat tempMat = new Mat();
Bitmap tempImage = BitmapImage2Bitmap(tempOriginal);
tempMat = BitmapConverter.ToMat(tempImage);
Converting BitmapImage to Bitmap first, and then convert Bitmap to Mat.
After that, make image binary. Below is the code.
Mat hsv = new Mat();
Mat binary = new Mat();
Cv2.CvtColor(tempMat, hsv, ColorConversionCodes.BGR2HSV);
Cv2.InRange(hsv, new Scalar(minH, minS, minV), new Scalar(maxH, maxS, maxV), binary);
tempMat = binary.Clone();
binary.Release();
hsv.Release();
After this operation, convert the mat back to Bitmap and BitmapImage again.
Below is the code.
tempImage = BitmapConverter.ToBitmap(tempMat);
BitmapImage resultBitmap = Bitmap2BitmapImage(tempImage);
ipWindow.ipView.ImageSource = resultBitmap;
I searched a lot and tried several solutions to change Bitmap to BitmapImage and vice versa.
But never worked. Below codes are solutions I tried.
I'm new to C# and also WPF, so I have no idea how to solve this problem.
Thanks for reading this long question.
Solution1. BitmapImage -> Bitmap
private Bitmap BitmapImage2Bitmap(BitmapImage bitmapImage){
using (MemoryStream outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapImage));
enc.Save(outStream);
Bitmap bitmap = new Bitmap(outStream);
outStream.Close();
return new Bitmap(bitmap);
}
}
Solution1. Bitmap -> BitmapImage
private BitmapImage Bitmap2BitmapImage(Bitmap bitmap){
BitmapSource i = Imaging.CreateBitmapSourceFromHBitmap(
bitmap.GetHbitmap(),
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
return (BitmapImage)i;
}
Solution2. Bitmap -> BitmapImage
private BitmapImage Bitmap2BitmapImage(Bitmap inputBitmap){
Bitmap bitmap = new Bitmap(inputBitmap.Width, inputBitmap.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
MemoryStream ms = new MemoryStream();
bitmap.Save(ms, ImageFormat.Bmp);
var bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = ms;
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.EndInit();
return bi;
}
Solution3. Bitmap -> BitmapImage
private BitmapImage Bitmap2BitmapImage(Bitmap inputBitmap){
using (var memory = new MemoryStream())
{
inputBitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
Solution4. Bitmap -> BitmapImage
private BitmapImage Bitmap2BitmapImage(Bitmap inputBitmap){
MemoryStream ms = new MemoryStream();
inputBitmap.Save(ms, ImageFormat.Bmp);
BitmapImage image = new BitmapImage();
image.BeginInit();
ms.Seek(0, SeekOrigin.Begin);
image.StreamSource = ms;
image.EndInit();
return image;
}
I solved. The problems were two.
One was OpenCvSharp and another was Bitmap to BitmapImage.
Actually OpenCvSharp problem was severe so I couldn't find proper solutions between above. But after I uninstall and reinstall the openCvSharp, I found out what solution works for me. The code is below.
private BitmapImage Bitmap2BitmapImage(Bitmap inputBitmap)
{
using (var memory = new MemoryStream())
{
inputBitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
}
We are trying to get bitmap from SVG but its not giving the exact co-ordinate to change the pixel color.Can anyone help me how to change the color for bitmap pixel/svg pixel in wpf.
WpfDrawingSettings wpfDrawingSettings = new WpfDrawingSettings { IncludeRuntime = false, TextAsGeometry = false, OptimizePath = true };
StreamSvgConverter converter = new StreamSvgConverter(wpfDrawingSettings);
MemoryStream ms = new MemoryStream();
converter.Convert(str, ms);
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = ms;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.EndInit();
I have a problem with Convert WriteableBitmap to BitmapImage using BmpBitmapEncoder.
this is my method:
public BitmapImage ConvertWriteableBitmapToBitmapImage(WriteableBitmap wbm)
{
bmp = new BitmapImage();
using (MemoryStream stream = new MemoryStream())
{
/*PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(wbm));
encoder.Save(stream);*/
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(wbm));
encoder.Save(stream);
bmp.BeginInit();
bmp.UriSource = new Uri(MyImage.Source.ToString());
bmp.CacheOption = BitmapCacheOption.OnLoad;
bmp.CreateOptions = BitmapCreateOptions.IgnoreImageCache | BitmapCreateOptions.PreservePixelFormat;
bmp.StreamSource = stream;
bmp.EndInit();
bmp.Freeze();
}
return bmp;
}
I'm using BmpBitmapEncoder because this is only way to Save without change size of Image(*.bmp). I want to Save Image with changed table of pixels and the specified format pixel (Bgr24). Using BmpBitmapEncoder forces to set bmp.UriSource and this is a problem. WriteableBitmap doesn't have this Property. Moreover, when I comment line //bmp.UriSource shows me an exception: "System.ArgumentNullException" in bmp.EndInit().
When I change my Method to this:
public BitmapImage ConvertWriteableBitmapToBitmapImage(WriteableBitmap wbm)
{
bmp = new BitmapImage();
using (MemoryStream stream = new MemoryStream())
{
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(wbm));
encoder.Save(stream);
/*BmpBitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(wbm));
encoder.Save(stream);*/
bmp.BeginInit();
//bmp.UriSource = new Uri(MyImage.Source.ToString());
bmp.CacheOption = BitmapCacheOption.OnLoad;
//bmp.CreateOptions = BitmapCreateOptions.IgnoreImageCache | BitmapCreateOptions.PreservePixelFormat;
bmp.StreamSource = stream;
bmp.EndInit();
bmp.Freeze();
}
return bmp;
}
everything works fine but the result is the Image increase size and change pixel format to Bgr32 and this is not the result, what I expect. My method which Save Image is fine because I tested it on unchanged pixels and the result is good - Image don't change format and size. Plz help me with this.
For changing pixel formats WPF has the FormatConvertedBitmap class, which is a BitmapSource:
WriteableBitmap wbm;
...
var bm = new FormatConvertedBitmap(wbm, PixelFormats.Bgr24, null, 0);
It is not a BitmapImage, but you do not really need that anywhere. All WPF methods and properties (e.g. Image.Source) use either ImageSource or the derived BitmapSource as their type. There is no API that explicitly needs a BitmapImage, so conversion to BitmapImage is never necessary.
I resolve this problem. This is a code which Convert WriteableBitmap to BitmapImage using BmpBitmapEncoder. Using this method ensure us, that our Image while saving stay unchanged. Unchanged I mean - size don't increase and Format Pixel stay on Bgr24.
public BitmapImage ConvertWriteableBitmapToBitmapImage(WriteableBitmap wbm)
{
bmp = new BitmapImage();
using (MemoryStream stream = new MemoryStream())
{
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(wbm));
encoder.Save(stream);
bmp.BeginInit();
bmp.CacheOption = BitmapCacheOption.OnLoad;
bmp.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
bmp.StreamSource = new MemoryStream(stream.ToArray()); //stream;
bmp.EndInit();
bmp.Freeze();
}
return bmp;
}
I mostly would like advice..
My program displays multiple Large images on the screen..
Which are between 1-4 mbs each..
150 DPI, and 6000x4000 resolution, 24 bit depth..
I have many other settings which will give me smaller images.. but I need high qualitity images..
Ok, so It's slow.. but not that slow...
The only really bad part is when I switch from 1 image to the other..
So, I have basically 2 Images for every image.. 1 Thumb, and 1 HQ..
When the user zooms into an image I load the HQ, and when the user zooms out, it switches to the thumb image..
Right at that switching point, the user has to wait about 4-8 seconds for the HQ image to load into the memory and draw to the screen..
Below is the code I use when I load the HQ image
using (DrawingContext dc = ActiveImage.imageDV.RenderOpen())
{
FileStream fs = new FileStream(ActiveImage.imagePath, FileMode.Open, FileAccess.Read, FileShare.Read);
MemoryStream ms = new MemoryStream();
fs.CopyTo(ms);
ms.Seek(0, SeekOrigin.Begin);
fs.Close();
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.CreateOptions = BitmapCreateOptions.DelayCreation;
bitmap.StreamSource = ms;
bitmap.EndInit();
//bitmap.Freeze();
dc.DrawImage(bitmap, new Rect(ActiveImage.position, ActiveImage.size));
}
Below is the code I use when I load the Thumb image
using (DrawingContext dc = ActiveImage.imageDV.RenderOpen())
{
FileStream fs = new FileStream(ActiveImage.thumbPath, FileMode.Open, FileAccess.Read, FileShare.Read);
MemoryStream ms = new MemoryStream();
fs.CopyTo(ms);
ms.Seek(0, SeekOrigin.Begin);
fs.Close();
BitmapImage bitmap = new BitmapImage();
RenderOptions.SetBitmapScalingMode(bitmap, BitmapScalingMode.NearestNeighbor);
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.CreateOptions = BitmapCreateOptions.DelayCreation;
bitmap.StreamSource = ms;
bitmap.EndInit();
//bitmap.Freeze();
dc.DrawImage(bitmap, new Rect(ActiveImage.position, ActiveImage.size));
}
Any way to optimize this? Is there some better way?
Remember I need a HQ crystal clear image only when the user is looking at a single image..
I suppose the MemoryStream could come out, but I added that as a kind of test..
Just a guess, but wouldn't it perhaps be the "best" solution to leave all potential optimization to WPF, and just load the image by its URI:
using (DrawingContext dc = ActiveImage.imageDV.RenderOpen())
{
var bitmap = new BitmapImage(new Uri(ActiveImage.imagePath));
// alternatively, if it is a relative path
// var bitmap = new BitmapImage(new Uri(ActiveImage.imagePath, UriKind.Relative));
dc.DrawImage(bitmap, new Rect(ActiveImage.position, ActiveImage.size));
}
I use this code to populate WPF Image Control.
string pathToImage = System.IO.Path.Combine(Settings.ContentFolderPath, file);
Image image = new Image();
BitmapImage src = new BitmapImage();
src.BeginInit();
src.UriSource = new Uri(pathToImage, UriKind.Absolute);
src.EndInit();
double ratio = src.Width / src.Height;
image.Source = src;
image.Stretch = Stretch.Uniform;
image.Height = marquee1.Height;
image.Width = marquee1.Height * ratio;
lstItems.Items.Add(image);
Also I have some parallel Task to update this image file.
But when I try to delete it I am getting the error: File is busy by other process you cannot delete this file.
How to resolve this issue?
Thank you!
UPDATES
So thank you all of you!
The final solution needs to implement
src.CacheOption = BitmapCacheOption.OnLoad;
src.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
the working code looks like
Image image = new Image();
BitmapImage src = new BitmapImage();
src.BeginInit();
src.CacheOption = BitmapCacheOption.OnLoad;
src.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
src.UriSource = new Uri(pathToImage, UriKind.Absolute);
src.EndInit();
double ratio = src.Width / src.Height;
image.Source = src;
image.Stretch = Stretch.Uniform;
image.Height = marquee1.Height;
image.Width = marquee1.Height * ratio;
lstItems.Items.Add(image);
result = image.Width;
MSND BitmapImage.CacheOption says:
Set the CacheOption to BitmapCacheOption.OnLoad if you wish to close a
stream used to create the BitmapImage.
Set the BitmapImage.CacheOption to OnLoad:
BitmapImage src = new BitmapImage();
src.BeginInit();
src.UriSource = new Uri(pathToImage, UriKind.Absolute);
src.CacheOption = BitmapCacheOption.OnLoad;
src.EndInit();
Set the CacheOption property of the BitmapImage to BitmapCacheOption.OnLoad. This will cause it to load the image into memory and close the original file, which should allow you to delete it.
Also I have some parallel Task to update this image file ... when I try to delete it I am getting the error
In general, you can't delete a Windows file while a process is using it. The consumer Windows FAQs include this page to describe this restriction. And as MSDN describes, File.Delete will refuse to delete a file that's in use.