I have a list of frames for my video. It contains byte arrays. I am using this to prevent high memory usage, and it works just fine. But when i try to save these frames, memory usage is going way to up and in some cases it cause out of memory exception.
VideoFileWriter _writer = new VideoFileWriter();
_writer.Open(newRecordedDataFile, videoSource.VideoResolution.FrameSize.Width, videoSource.VideoResolution.FrameSize.Height, 20, VideoCodec.Default);
for (int i = 0; i < bitmaps.Count; i++)
{
var array = bitmaps[i];
using (MemoryStream mStream = new MemoryStream())
{
mStream.Write(array, 0, array.Length);
mStream.Seek(0, SeekOrigin.Begin);
using (Bitmap bm = new Bitmap(mStream))
{
_writer.WriteVideoFrame(bm);
}
}
}
bitmaps.Clear();
How can i prevent this high memory usage?
Related
This is my code to load 100 images from MySQL database to a Panel,
and each of the image is about 28 KB.
However, the resource monitor shows that it takes more than 2 GB of memory,
and sometimes it leads to out of memory exception.
for (int x = 0; x < Topic4Number; x++)
{
ReaderKeyword.Read();
pbTopic4[x] = new PictureBox();
if (x == 0)
{
pbTopic4[x].Location = new Point(45, 75);
}
else
{
if (pbTopic4[x - 1].Right < 1300)
{
pbTopic4[x].Location = new Point(pbTopic4[x - 1].Right + 35, pbTopic4[x - 1].Top);
}
else
{
pbTopic4[x].Location = new Point(45, pbTopic4[x - 1].Top + 445);
}
}
pbTopic4[x].Size = new Size(Topic4pb_width, Topic4pb_height);
pbTopic4[x].SizeMode = PictureBoxSizeMode.Zoom;
if (ReaderKeyword.HasRows)
{
long len = ReaderKeyword.GetBytes(0, 0, null, 0, 0);
byte[] buffer = new byte[len];
len = ReaderKeyword.GetBytes(0, 0, buffer, 0, (int)len);
MemoryStream ms = new MemoryStream(buffer);
Bitmap img = new Bitmap(ms);
pbTopic4[x].Image = img;
}
}
PanelAll.Controls.AddRange(pbTopic4);
I did use .Dispose() to free the memory once I don't need those PictureBox.
if (pbTopic4 != null)
{
for (int x = 0; x < Topic4Number; x++)
{
pbTopic4[x].Dispose();
pbTopic4[x] = null;
GC.Collect();
}
}
Here are my questions:
Is there any way to reduce the memory used?
Is there any way to improve my code so that it can work faster?
Why it takes that much memory to show the PictureBox?
I made some modification about creating thumbnail of the images, the program works faster and takes much lesser memory.
public bool ThumbnailCallback()
{
return false;
}
if (ReaderTopic4.HasRows)
{
long len = ReaderTopic4.GetBytes(0, 0, null, 0, 0);
byte[] buffer = null;
buffer = new byte[len];
len = ReaderTopic4.GetBytes(0, 0, buffer, 0, (int)len);
MemoryStream ms = new MemoryStream(buffer);
Image.GetThumbnailImageAbort myCallback = new Image.GetThumbnailImageAbort(ThumbnailCallback);
Bitmap img = new Bitmap(ms);
Image myThumbnail = img.GetThumbnailImage(353, 250, myCallback, IntPtr.Zero);
pbTopic4[x].Image = myThumbnail;
}
Reference:
https://learn.microsoft.com/zh-tw/dotnet/api/system.drawing.image.getthumbnailimage?view=dotnet-plat-ext-6.0
I have a collection of Bitmaps that are stored in a List<byte[]> object. I want to combine all those Bitmaps into one image (kinda like a paneramic) without converting them back to bitmaps. I've tried using a MemoryStream (see below) but it doesn't seem to work. I always run out of memory or the image comes out corrupted. I would appreciate any thoughts/advice on the matter.
List<byte[]> FrameList = new List<byte[]>();
using (MemoryStream ms = new MemoryStream())
{
for (int i = 0; i < FrameList.Count; i++)
{
//Does not work
ms.Write(FrameList[i], i * FrameList[i].Length, FrameList[i].Length);
//Does not work
ms.Write(FrameList[i], 0, FrameList[i].Length);
}
Bitmap img = (Bitmap)Bitmap.FromStream(ms);
img.Save("D:\\testing.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
}
The steps:
calculate the final image size as sum of size of the individual
images
create a Bitmap with the final size
get the Graphics object
draw all images using the Graphics object
save the final Bitmap
var final = new Bitmap(width, height); // calculated final size
using (var graphics = Graphics.FromImage(final))
{
graphics.DrawImage(...); // draw all partial images onto the final bitmap
}
I am trying to save a WritableBitmap, and make sure the resulting file is below a certain size. My code is as follows
//Fetch Image from file
BitmapImage bitmap = new BitmapImage(new Uri("YourImage.jpg", UriKind.Relative));
WriteableBitmap writableBitmapImage = new WriteableBitmap(bitmap);
//initialize variables and values
MemoryStream stream;
JpegBitmapEncoder encoder;
byte[] result;
int quality = 100;
//loop through save process, checking file size and changing quality
while (selected.maxFileSize < result.Length && quality > 1)
{
encoder = new JpegBitmapEncoder();
stream = new MemoryStream();
encoder.QualityLevel = quality;
encoder.Frames.Add(BitmapFrame.Create(
ReplaceTransparency(writableBitmapImage, Colors.White))
);
encoder.Save(stream);
result = stream.GetBuffer();
if (quality > 5) quality += -5;
else quality--;
}
When I run this code, I don't see any file size changes from the starting point of QualityLevel = 100; to QualityLevel = 10;.
At QualityLevel = 5; I see a large change in file size.
Is there any way to control file size more granularly?
Read the documentation for MemoryStream.GetBuffer():
Note that the buffer contains allocated bytes which might be unused. For example, if the string "test" is written into the MemoryStream object, the length of the buffer returned from GetBuffer is 256, not 4, with 252 bytes unused. To obtain only the data in the buffer, use the ToArray method; however, ToArray creates a copy of the data in memory.
Then use .ToArray() instead.
I'm working on a mandelbrot fractal renderer and managed to get a basic version working. As a next step I tried to make the writing to the bitmap in chunks so the user gets faster feedback. Doing this, I ran into an error on calling the WritePixels method on my WriteableBitmap ("Buffer size is not sufficient").
var bytesPerPixel = _bitmap.Format.BitsPerPixel/8;
var stride = _bitmap.PixelWidth*bytesPerPixel;
var imageBytes = new byte[stride*_bitmap.PixelHeight];
var chunks = _bitmap.PixelHeight;
var chunkSize = _bitmap.PixelWidth;
for (var chunk = 0; chunk < chunks; chunk++)
{
var chunkPixelIndex = chunk*chunkSize;
for (var pixel = chunkPixelIndex; pixel < chunkPixelIndex+chunkSize; pixel++)
{
ColorPixel(pixel, bytesPerPixel, imageBytes);
}
var imageRect = new Int32Rect(0, 0, _bitmap.PixelWidth, _bitmap.PixelHeight);
_bitmap.WritePixels(imageRect, imageBytes, stride, chunkPixelIndex*bytesPerPixel);
}
Here's the last know working version of the code for comparison:
var bytesPerPixel = _bitmap.Format.BitsPerPixel/8;
var stride = _bitmap.PixelWidth*bytesPerPixel;
var imageBytes = new byte[stride*_bitmap.PixelHeight];
var numberOfPixels = _bitmap.PixelWidth*_bitmap.PixelHeight;
for (var pixel = 0; pixel < numberOfPixels; pixel++)
{
ColorPixel(pixel, bytesPerPixel, imageBytes);
}
var imageRect = new Int32Rect(0, 0, _bitmap.PixelWidth, _bitmap.PixelHeight);
_bitmap.WritePixels(imageRect, imageBytes, stride, 0);
EDIT: Using the debugger I've established that the first time the code throws is when it's calling WritePixels with an offset of 4320 (1080*4). imageBytes has a size of 3110400 so I really don't see how it's size isn't sufficient. Am I just blatantly misunderstanding this api?
I'm implementing a small imageviewer, unfortunately I'am facing a memory leak.
Following is my loading routine.
public BitmapSource getImage(string fileName, double width, double height)
{
FileStream s = File.Open(fileName, FileMode.Open);
Image i = Image.FromStream(s, false, false);
double iWidth = i.Width;
double iHeight = i.Height;
i.Dispose();
s.Close();
BitmapImage tmpImage = new BitmapImage();
tmpImage.BeginInit();
tmpImage.CacheOption = BitmapCacheOption.OnLoad;
tmpImage.UriSource = new Uri(fileName);
if (iWidth > iHeight)
{
tmpImage.DecodePixelWidth = (int)width;
}
else
{
tmpImage.DecodePixelHeight = (int)height;
}
tmpImage.EndInit();
return tmpImage;
}
This is how I call the loader
private void whenArrowKeyPressed(int index)
{
CurrentImage = fh.getImage(fileList[index], 1920, 1080);
}
CurrentImage is a property, which is bound to a WPF ViewBox.
Any Ideas?
I also tried to read from StreamSource, with the same effect.
Only issue I could see is you are not disposing you FileStream. BitmapImage is not Disposable and it will release its memory if there are no references to it.
How did you find there is a memory leak? It’s recommended to use a profiling tool. Garbage collector doesn't release memory immediately something goes out of scope, it waits until memory usages exceeds certain thresholds (usually when Gen 0 going to exceed the threshold). So, you will see some memory increase and it will release memory only after garbage collector executes.
Apparently you are not using a profiling tool. In this case if you want to check if there is any memory leak, you can manually execute GC.Collect and wait for finalization before you get memory reading. Keep in mind that you don’t have to execute GC.Collect manually as it will occur automatically in an optimized way when required.
public BitmapSource getImage(string fileName, double width, double height)
{
using(FileStream s = File.Open(fileName, FileMode.Open))
using(Image i = Image.FromStream(s, false, false))
{
double iWidth = i.Width;
double iHeight = i.Height;
}
BitmapImage tmpImage = new BitmapImage();
tmpImage.BeginInit();
tmpImage.CacheOption = BitmapCacheOption.OnLoad;
tmpImage.UriSource = new Uri(fileName);
if (iWidth > iHeight)
{
tmpImage.DecodePixelWidth = (int)width;
}
else
{
tmpImage.DecodePixelHeight = (int)height;
}
tmpImage.EndInit();
return tmpImage;
}
private void whenArrowKeyPressed(int index)
{
CurrentImage = fh.getImage(fileList[index], 1920, 1080);
// Remove this once you finish testing.
GC.Collect();
GC.WaitForPendingFinalizers();
}