I need to convert Texture2D to BitmapImage and i found one way to do this, but this solution on windows phone 7 has known issue with memory leak in Texture2D.SaveAsJpeg method call. What i have tried: save texture2d in isolatedstorage by calling Texture2D.SaveAsJpeg, then load from isolatedstorage stream with this texture and create BitmapImage from that stream
public static void SaveTextureToISF(string fileName, Texture2D texture)
{
using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication())
{
using (
IsolatedStorageFileStream fileStream = new IsolatedStorageFileStream(fileName, FileMode.Create, file)
)
{
texture.SaveAsPng(fileStream, texture.Width, texture.Height); //Memory leak Here
fileStream.Close();
}
}
}
public static BitmapImage LoadTextureFrom(string fileName)
{
using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication())
{
using (
IsolatedStorageFileStream fileStream = new IsolatedStorageFileStream(fileName,
FileMode.Open,
FileAccess.Read, file))
{
BitmapImage bmp = new BitmapImage();
bmp.SetSource(fileStream);
return bmp;
}
}
}
Any other solutions, without leaking?
ImageTools do the trick.
public BitmapImage ConvertTexture2DToBitmapImage(Texture2D texture2D)
{
byte[] textureData = new byte[4 * texture2D.Width * texture2D.Height];
texture2D.GetData(textureData);
ExtendedImage extendedImage = new ExtendedImage();
extendedImage.SetPixels(texture2D.Width, texture2D.Height, textureData);
JpegEncoder encoder = new JpegEncoder();
using (Stream picStream = new MemoryStream())
{
encoder.Encode(extendedImage, picStream);
picStream.Position = 0;
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.SetSource(picStream);
return bitmapImage;
}
}
Related
I try to compress ImageSource with MagickImage in memory. But it consumes too much memory. With VS performance tool, every call of this method with consume a lot of memory. It should take 0Mb once it exists, right?
internal static System.Windows.Media.ImageSource compressImage(System.Windows.Media.ImageSource ims)
{
using (MemoryStream stream = new MemoryStream())
{
using (MemoryStream outS = new MemoryStream())
{
BitmapSource bs = ims as BitmapSource;
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
BitmapFrame bf = BitmapFrame.Create(bs);
//encoder.Frames.Add(BitmapFrame.Create(image1.Source));
encoder.Frames.Add(bf);
encoder.Save(stream);
stream.Flush();
try
{
// Read first frame of gif image
using (MagickImage image = new MagickImage(stream))
{
image.Quality = 75;
image.Resize(new Percentage(0.65));
image.Density = new Density(200, DensityUnit.PixelsPerInch);
image.Write(outS);
}
stream.Close();
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
outS.Position = 0;
bitmap.StreamSource = outS;
//
bitmap.EndInit();
//bitmap.Freeze();
outS.Flush();
outS.Close();
ims = null;
return bitmap;
}
catch (Exception e)
{
return null;
}
}
}
}
}
Since Image is stored in memory pixel by pixel. It will consume much memory depends on its size.
Specifying its size will reduce much memory.
bitmap.DecodePizelWidth = 800;
I have jpeg.
I convert it to a BitmapImage.
I then reduce the quality (compression).
I then covert to byte array.
I have found the memory escalates very quickly in task manager.
I am certain I am disposing all that I can.
This is my code:
//I am calling this from within a timer set 500ms
byte[] datatest = JpegXr.SaveJpegXrToBytes((Bitmap)frame.Clone(), 40f);
frame.Dispose();
public static class JpegXr
{
public static Bitmap BitmapImage2Bitmap(BitmapImage bitmapImage)
{
// BitmapImage bitmapImage = new BitmapImage(new Uri("../Images/test.png", UriKind.Relative));
using (MemoryStream outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapImage));
enc.Save(outStream);
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(outStream);
// return bitmap; <-- leads to problems, stream is closed/closing ...
return new Bitmap(bitmap);
}
}
public static byte[] SaveJpegXrToBytes(Bitmap bitmap, float quality)
{
byte[] data = null;
var stream = new MemoryStream();
SaveJpegXr(bitmap, quality, stream);
stream.Seek(0, SeekOrigin.Begin);
data= stream.ToArray();
stream.Close();
bitmap.Dispose();
return data;
}
private static BitmapImage Bitmap2BitmapImage(Bitmap bitmap)
{
BitmapSource bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(
bitmap.GetHbitmap(),
IntPtr.Zero, System.Windows.Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
//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();
memoryStream.Close();
bitmap.Dispose();
return bImg;
//return (BitmapImage)i;
//return i;
}
private static void SaveJpegXr(Bitmap bitmap, float quality, Stream output)
{
BitmapImage bitmapSource = Bitmap2BitmapImage((Bitmap)bitmap.Clone());
//var bitmapSource = bitmap.ToWpfBitmap();
var bitmapFrame = BitmapFrame.Create(bitmapSource);
var jpegXrEncoder = new WmpBitmapEncoder();
jpegXrEncoder.Frames.Add(bitmapFrame);
jpegXrEncoder.ImageQualityLevel = quality / 100f;
jpegXrEncoder.Save(output);
bitmap.Dispose();
}
}
Is it just the case that doing these conversions is memory consuming by its very nature and as such calling it frequently from within a timer does not give the garbage collector time to dispose objects correctly?
Appreciate peoples wisdom on this...
I am copying all images from my device to directory. While copying the images I am getting this error Operation not permitted on IsolatedStorageFileStream.
Here is my code to copy the files.
MediaLibrary m = new MediaLibrary();
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
{
if (store.DirectoryExists("ImagesZipFolder"))
{
deleteFileFolder("ImagesZipFolder");
}
if (!store.DirectoryExists("ImagesZipFolder"))
{
store.CreateDirectory("ImagesZipFolder");
foreach (var picture in m.Pictures)
{
using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(#"ImagesZipFolder/" + picture.Name, FileMode.CreateNew, store))
{
BitmapImage image = new BitmapImage();
image.SetSource(picture.GetImage());
byte[] bytes = ConvertToBytes(image);
stream.Write(bytes, 0, bytes.Length);
}
}
}
}
Here is my ConvertToBytes method.
public byte[] ConvertToBytes(BitmapImage bitmapImage)
{
byte[] data = null;
WriteableBitmap wBitmap = null;
using (MemoryStream stream = new MemoryStream())
{
wBitmap = new WriteableBitmap(bitmapImage);
wBitmap.SaveJpeg(stream, wBitmap.PixelWidth, wBitmap.PixelHeight, 0, 100);
stream.Seek(0, SeekOrigin.Begin);
//data = stream.GetBuffer();
data = stream.ToArray();
DisposeImage(bitmapImage);
return data;
}
}
Basically what I am trying is to create a zip file of all images. I have total 222 images in my device. So how can I solve this issue ? How can I create a zip of this images?
Most probably this is due to the concurrent access to the file
you can refer to the link:
Operation not permitted on IsolatedStorageFileStream. error
I checked your code and it seems to be working (providing there's no error in DisposeImage() method) There is no OperationNotPermittedException occuring. However, if there is error in your code, then it can only be because of deleteFileFolder("ImagesZipFolder") line. Can you give me the snippet so that I can study it further. I m posting the working code... I have replaced that method with simple predefined one--
MediaLibrary m = new MediaLibrary();
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
{
if (store.DirectoryExists("ImagesZipFolder"))
{
store.DeleteDirectory("ImagesZipFolder");
}
if (!store.DirectoryExists("ImagesZipFolder"))
{
store.CreateDirectory("ImagesZipFolder");
foreach (var picture in m.Pictures)
{
using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(#"ImagesZipFolder/" + picture.Name, FileMode.CreateNew, store))
{
BitmapImage image = new BitmapImage();
image.SetSource(picture.GetImage());
byte[] bytes = ConvertToBytes(image);
stream.Write(bytes, 0, bytes.Length);
}
}
}
}
According to the image encoding example here I should be able to use JpegBitmapEncoder to encode an image for saving as a jpeg file but get this compile error:
error CS1503: Argument 1: cannot convert from 'System.Windows.Controls.Image' to 'System.Uri'
I don't see a way (property or method in Image) to get System.Uri from Image.
What am I missing?
The Image xaml code is
<Image Name="ColorImage"/>
The SaveImage C# is
...
SaveImage(ColorImage, path);
...
private void SaveImage(Image image, string path)
{
var jpegEncoder = new JpegBitmapEncoder();
jpegEncoder.Frames.Add(BitmapFrame.Create(image));
using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write))
{
jpegEncoder.Save(fs);
}
}
The code below (taken mostly from the kinect-sdk) streams 640 x 480 RBG to a WriteableBitmap at 30 Fps (the kinect ColorImageFormat is RgbResolution640x480Fps30).
using (var colorImageFrame = allFramesReadyEventArgs.OpenColorImageFrame())
{
if (colorImageFrame == null) return;
var haveNewFormat = currentColorImageFormat != colorImageFrame.Format;
if (haveNewFormat)
{
currentColorImageFormat = colorImageFrame.Format;
colorImageData = new byte[colorImageFrame.PixelDataLength];
colorImageWritableBitmap = new WriteableBitmap(
colorImageFrame.Width,
colorImageFrame.Height, 96, 96, PixelFormats.Bgr32, null);
ColorImage.Source = colorImageWritableBitmap;
}
// Make a copy of the color frame for displaying.
colorImageFrame.CopyPixelDataTo(colorImageData);
colorImageWritableBitmap.WritePixels(
new Int32Rect(0, 0, colorImageFrame.Width, colorImageFrame.Height),
colorImageData,
colorImageFrame.Width*Bgr32BytesPerPixel,
0);
}
private void SaveImage(string path)
{
var jpegEncoder = new JpegBitmapEncoder();
jpegEncoder.Frames.Add(BitmapFrame.Create(colorImageWritableBitmap));
using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write))
{
jpegEncoder.Save(fs);
}
}
The problem occurs, because you pass an Image to BitmapFrame.Create. Imageis more common in Windows Forms. A simple approach would be to create a MemoryStream first and the pass this:
private void SaveImage(Image image, string path)
{
MemoryStream ms = new MemoryStream();
image.Save(ms,System.Drawing.Imaging.ImageFormat.Jpeg);
var jpegEncoder = new JpegBitmapEncoder();
jpegEncoder.Frames.Add(BitmapFrame.Create(ms));
using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write))
{
jpegEncoder.Save(fs);
}
}
Addition (see conversation in comments):
you could try to use the CopyPixels method on the writeableBitmap object and copy the pixels to a byte array which you load to a MemoryStream and the write it to a Jpeg File withe the JpegBitmapEncoder. But it's just a guess.
This could work, too:
private void SaveImage (WriteableBitmap img, string path)
{
FileStream stream = new FileStream(path, FileMode.Create);
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(img));
encoder.Save(stream);
stream.Close();
}
You will just have to extract the WriteableBitmap from your Image control
Try: BitmapFrame.Create(image.Source)
I am looking to create a function that takes a BitmapImage and saves it as a JPEG on the local Windows Phone 7 device in isolated storage:
static public void saveImageLocally(string barcode, BitmapImage anImage)
{
// save anImage as a JPEG on the device here
}
How do I accomplish this? I'm assuming I used IsolatedStorageFile somehow?
Thanks.
EDIT:
Here is what I have found so far... can anyone confirm if this is the correct way to do this?
static public void saveImageLocally(string barcode, BitmapImage anImage)
{
WriteableBitmap wb = new WriteableBitmap(anImage);
using (var isf = IsolatedStorageFile.GetUserStoreForApplication())
{
using (var fs = isf.CreateFile(barcode + ".jpg"))
{
wb.SaveJpeg(fs, wb.PixelWidth, wb.PixelHeight, 0, 100);
}
}
}
static public void deleteImageLocally(string barcode)
{
using (IsolatedStorageFile MyStore = IsolatedStorageFile.GetUserStoreForApplication())
{
MyStore.DeleteFile(barcode + ".jpg");
}
}
static public BitmapImage getImageWithBarcode(string barcode)
{
BitmapImage bi = new BitmapImage();
using (var isf = IsolatedStorageFile.GetUserStoreForApplication())
{
using (var fs = isf.OpenFile(barcode + ".jpg", FileMode.Open))
{
bi.SetSource(fs);
}
}
return bi;
}
To save it:
var bmp = new WriteableBitmap(bitmapImage);
using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream stream = storage.CreateFile(#"MyFolder\file.jpg"))
{
bmp.SaveJpeg(stream, 200, 100, 0, 95);
stream.Close();
}
}
Yes, the stuff you added in your edit is exactly what I have done before :) it works.
This is my code but you can take the neccesary points from there:
var fileName = String.Format("{0:}.jpg", DateTime.Now.Ticks);
WriteableBitmap bmpCurrentScreenImage = new WriteableBitmap(480, 552);
bmpCurrentScreenImage.Render(yourCanvas, new MatrixTransform());
bmpCurrentScreenImage.Invalidate();
SaveToMediaLibrary(bmpCurrentScreenImage, fileName, 100);
public void SaveToMediaLibrary(WriteableBitmap bitmap, string name, int quality)
{
using (var stream = new MemoryStream())
{
// Save the picture to the Windows Phone media library.
bitmap.SaveJpeg(stream, bitmap.PixelWidth, bitmap.PixelHeight, 0, quality);
stream.Seek(0, SeekOrigin.Begin);
new MediaLibrary().SavePicture(name, stream);
}
}