Image.SelectActiveFrame memory problem - c#

I'm writing a control to show images.
My problem comes out using Image class on multipage TIFFs.
I use this (I post only relevant code) at the beginning:
Image img;
int pages;
img = Bitmap.FromFile(filename);
pages = img.GetFrameCount(FrameDimension.Page);
then, when the user wants to show a different page:
public override Image GetPage(int page)
{
if (page < 1 || page > pages) return null;
try
{
#if !TEST
img.SelectActiveFrame(FrameDimension.Page, page - 1);
return new Bitmap(img);
#else
MemoryStream ms = new MemoryStream();
img.SelectActiveFrame(FrameDimension.Page, page - 1);
img.Save(ms, ImageFormat.Jpeg);
Image ret = Image.FromStream(ms);
ms.Flush();
ms.Dispose();
return ret;
#endif
}
catch (Exception ex)
{
"Tiff GetPage error: {0}".ToDebug(ex.Message);
return null;
}
}
Using img.SelectActiveFrame(FrameDimension.Page, page - 1); (in both versions) about 7MB are allocated in memory and those are never freed (even exiting the method)!!!
If I goes to next page 7MB are allocated and not freed everytime, while going back (on an already visited pages) previously allocated memory is used.
To give you an example: think Task Manager reports my app is using x MB; going one page forward memory increases to x + y (after SelectActiveFrame()) + z (Image ret = ...). Well, I should have x + z (y part should be zero or GC collected exiting the method), but obviously that's not what happens, even calling GC.Collect manually.
Going back to a previously visited page, memory increases effectively only with z, as expected.
I find it terrible (think about a file with 80 pages...), but how can I force img object to free allocated memory? Am I doing something wrong?
I've already thought closing and reopening img, but speed is not good.
Thanks to everybody

Don't use new Bitmap(img) because it will force the system to create new memory for a new Bitmap object using 32-bit color by default.
You can just use var bitmap = (Bitmap)img; to retrieve a Bitmap object for that page.

If I'm not mistaken you're not destroying the used controls at every possible point.
I think you might need to test this - but make sure that you're disposing all the used controls.
ie. ImageControlUsed.Dispose();

I answer my question after trying different solutions and accept soluzion given by user965487 because at the end he was right (thanks to Hans Passant too).
If you have a class (call it QV) similar to this
public class QV
{
Image img;
int pages;
public QV(filename) {
img = Bitmap.FromFile(filename);
pages = img.GetFrameCount(FrameDimension.Page);
}
~QV() {
img.Dispose();
img = null;
}
public Image GetPage(int page) {
if (page < 1 || page > pages) return null;
img.SelectActiveFrame(FrameDimension.Page, page - 1);
return new Bitmap(img);
}
}
then every time you call GetPage(...) your memory will grow not only for the size of returned image, but also for img.SelectActiveFrame(...) statement. I don't know why and how, but it happens. Releasing returned image and calling frees memory for image size, not for the amount taken from SelectActiveFrame() (anyway this memory is not duplicated if you return on a previously seend page).
So you'd better open and close the image everytime, like this:
public class QV
{
Image img;
int pages;
public QV(filename) {
img = Bitmap.FromFile(filename);
pages = img.GetFrameCount(FrameDimension.Page);
img.Dispose();
}
public Image GetPage(int page) {
if (page < 1 || page > pages) return null;
img = Bitmap.FromFile(filename);
img.SelectActiveFrame(FrameDimension.Page, page - 1);
Image ret = Bitmap(img);
img.Dispose();
return ret;
}
}
Payload for opening and disposing image everytime user requests a new page is really nothing compared to dangerous memory allcation done with first solution.
I hope someone needs this.

Related

Correct way to get an image thumbnail?

What is the correct way to get the thumbnails of images when using C#? There must be some built-in system method for that, but I seem to be unable find it anywhere.
Right now I'm using a workaround, but it seems to be much heavier on the computing side, as generating the thumbnails of 50 images, when using parallel processing takes about 1-1,5 seconds, and during that time, my CPU is 100% loaded. Not to mention that it builds up quite some garbage, which it later needs to collect.
This is what my class currently looks like:
public class ImageData
{
public const int THUMBNAIL_SIZE = 160;
public string path;
private Image _thumbnail;
public string imageName { get { return Path.GetFileNameWithoutExtension(path); } }
public string folder { get { return Path.GetDirectoryName(path); } }
public Image image { get
{
try
{
using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read))
using (BinaryReader reader = new BinaryReader(stream))
{
var memoryStream = new MemoryStream(reader.ReadBytes((int)stream.Length));
return new Bitmap(memoryStream);
}
}
catch (Exception e) { }
return null;
}
}
public Image thumbnail
{
get
{
if (_thumbnail == null)
LoadThumbnail();
return _thumbnail;
}
}
public void LoadThumbnail()
{
if (_thumbnail != null) return;
Image img = image;
if (img == null) return;
float ratio = (float)image.Width / (float)image.Height;
int h = THUMBNAIL_SIZE;
int w = THUMBNAIL_SIZE;
if (ratio > 1)
h = (int)(THUMBNAIL_SIZE / ratio);
else
w = (int)(THUMBNAIL_SIZE * ratio);
_thumbnail = new Bitmap(image, w, h);
}
I am saving up the thumbnail once generated, to save up some computing time later on. Meanwhile, I have an array of 50 elements, containing picture boxes, where I inject the thumbnails into.
Anyways... when I open a folder, containing images, my PC certainly doesn't use up 100% CPU for the thumbnails, so I am wondering what is the correct method to generate them.
Windows pregenerates the thumbnails and stores them in the thumbs.db-File (hidden) for later use.
So unless you either access the thumbs.db file and are fine with relying on it being available or cache the thumbnails yourself somewehere you always will have to render them in some way or another.
That being said, you can probably rely on whatever framework you are using for your UI to display them scaled down seeing as you load them into memory anyway.

C# and tessaract error: "Only one image can be processed at once. Please make sure you dispose of the page once your finished with it."

I am scaning pages with scanner via saraff.twain and then cheking if there is any text on page with tessaract. My code:
private bool IsBlank(Bitmap image)
{
return String.IsNullOrEmpty(tEngine.Process(image, PageSegMode.Auto).GetText());
}
public void DrawToPDF(int index)
{
try
{
// if get image, put in pdf via stream
using (var image = twain.GetImage(index))
using (var stream = new MemoryStream())
{
LOGGER.Debug("checking if blank...");
using (Bitmap bitMapImage = new Bitmap(image))
{
if (!IsBlank(bitMapImage))
{
LOGGER.Debug("page is not blank, adding...");
PdfPage page = pdf.AddPage();
XGraphics gfx = XGraphics.FromPdfPage(page);
image.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
// draw page
using (var xImage = XImage.FromStream(stream))
{
gfx.DrawImage(xImage, 0, 0, page.Width, page.Height);
}
LOGGER.Debug("page added.");
}
else
{
LOGGER.Debug("page is blank, skip.");
}
}
}
}
catch (Exception ex)
{
LOGGER.Warn(ex, $"error: {ex.Message}");
}
}
It works fine untill i call DrawToPDF second time and get "Only one image can be processed at once. Please make sure you dispose of the page once your finished with it.". To get what is wrong I used visual studio debugger and made snapshot before every DrawToPDF() call.
1
The only new object added to memory before every new DrawToPDF() call was bitmap...
2
I am confused... How am i supposed to make it right?
tEngine.Process(image, PageSegMode.Auto)
This call creates a Page object that is IDisposable. Tesseract requires destroying the Page object before calling the Process method again.
You can look at the conditions for the throw of an Exception in the Tesseract
source code
In your case, you just need to add using:
private bool IsBlank(Bitmap image)
{
using(Page page = tEngine.Process(image, PageSegMode.Auto)) {
return String.IsNullOrEmpty(page.GetText());
}
}

Sending screenshots taking too much memory

I am trying to make a small application to serve screenshot of entire screen through network. I want it to be able to serve one every 1-2 seconds through LAN. I thought it won't be a big problem, so I chose C# and nancy www self host (as the easiest option). Now, the speed is allright for my needs, but it seems to be taking way too much memory, and it grows with time. It seems to settle on taking about 3.5 GB RAM and from then no longer grows, but that's not an acceptable amount - even with big resolution (mine is 2560x1440).
Is my code bad, or is nancy not suitable for handling many big responses, or maybe C# method of capturing screen is poorly optimized and I should try pinvoke methods? Or maybe it's just a terrible way to do it and I should try something more advanced, like using VNC library?
Currently my code looks like this:
public class HomeModule : NancyModule
{
private Bitmap bitmapScreenCapture;
private Graphics graphics;
private Object lockMe = new object();
private MemoryStream memoryStream = new MemoryStream();
public HomeModule()
{
Get["image"] = parameters =>
{
lock (lockMe)
{
GC.Collect();
if (bitmapScreenCapture == null || bitmapScreenCapture.Width != Screen.PrimaryScreen.Bounds.Width || bitmapScreenCapture.Height != Screen.PrimaryScreen.Bounds.Height)
{
if (bitmapScreenCapture != null)
{
bitmapScreenCapture.Dispose();
}
bitmapScreenCapture = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
graphics = Graphics.FromImage(bitmapScreenCapture);
}
graphics.CopyFromScreen(Screen.PrimaryScreen.Bounds.X,
Screen.PrimaryScreen.Bounds.Y,
0, 0,
bitmapScreenCapture.Size,
CopyPixelOperation.SourceCopy);
bitmapScreenCapture.Save(memoryStream, ImageFormat.Png);
memoryStream.Position = 0;
return new Response()
{
Contents = stream => memoryStream.CopyTo(stream)
};
}
};
}
}
As much as possible, keep variables in the most local scope possible, and dispose of what you can.
Part of your issue may be that you're new'ing up a Graphics instance repeatedly, but never disposing of the old reference. The GC will collect it eventually, but you can place your code in a using block to let it know you're done with it.
I haven't tested this, but here I've made your instances local, and disposed of the Graphics and Bitmap instances. I didn't dispose of the MemoryStream since I'm not sure it will successfully return the value if you do, but you could play around with it.
var screen = Screen.PrimaryScreen;
using (var bitmap = new Bitmap(screen.Bounds.Width, screen.Bounds.Height))
{
using (var g = Graphics.FromImage(bitmap))
{
g.CopyFromScreen(screen.Bounds.Left, screen.Bounds.Top, 0, 0, screen.Bounds.Size);
}
var imageStream = new MemoryStream();
bitmap.Save(imageStream, ImageFormat.Png);
imageStream.Position = 0;
return new Response()
{
Contents = stream => memoryStream.CopyTo(stream)
};
}

C# MemoryStream slowing programme performance

I'm working on a project using WPF to display the Kinect ColorImageFrame and a skeleton representation. I also have to record those two videos.
I'm able to display and record (using EmguCV) those two images, but I have some performance issues. It seems that this part of my code is the reason of my loss of performance.
private void DrawSkeleton(Skeleton[] skeletons)
{
using (System.Drawing.Bitmap skelBitmap = new System.Drawing.Bitmap(640, 480))
{
foreach (Skeleton S in skeletons)
{
if (S.TrackingState == SkeletonTrackingState.Tracked)
{
DrawBonesAndJoints(S,skelBitmap);
}
else if (S.TrackingState == SkeletonTrackingState.PositionOnly)
{
}
}
_videoArraySkel.Add(ToOpenCVImage<Bgr, Byte>(skelBitmap));
BitmapSource source = ToWpfBitmap(skelBitmap);
this.skeletonStream.Source = source;
}
}
and more precisely from the ToWpfBitmap which allows me to display it in my Window:
public static BitmapSource ToWpfBitmap(System.Drawing.Bitmap bitmap)
{
using (MemoryStream stream = new MemoryStream())
{
bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);
stream.Position = 0;
BitmapImage result = new BitmapImage();
result.BeginInit();
// According to MSDN, "The default OnDemand cache option retains access to the stream until the image is needed."
// Force the bitmap to load right now so we can dispose the stream.
result.CacheOption = BitmapCacheOption.OnLoad;
result.StreamSource = stream;
result.EndInit();
result.Freeze();
return result;
}
}
The loss of performance is characterized by:
- The videos displayed on the Window are not fluent anymore
- The video recording seems to miss some frames which leads to a video going faster/lower than the normal.
Can you help me by telling me where this problem may come from?
Try to use RecyclableMemoryStream instead of MemoryStream. It was designed for solving some issue with memory.
Check out this article for details - Announcing Microsoft.IO.RecycableMemoryStream
Have you tried doing the memory write i/o in a separate thread, while maintaining the data in a buffer like a queue?

OutOfMemoryException after CameraCaptureDialog - Compact Framework [duplicate]

I'm developing an application that uses a mobile device to take a photo and send it using a webservice. But after I've taken 4 photos I am getting an OutOfMemoryException in the code below. I tried calling GC.Collect() but it didn't help either. Maybe someone here could be give me an advice how to handle this problem.
public static Bitmap TakePicture()
{
var dialog = new CameraCaptureDialog
{
Resolution = new Size(1600, 1200),
StillQuality = CameraCaptureStillQuality.Default
};
dialog.ShowDialog();
// If the filename is empty the user took no picture
if (string.IsNullOrEmpty(dialog.FileName))
return null;
// (!) The OutOfMemoryException is thrown here (!)
var bitmap = new Bitmap(dialog.FileName);
File.Delete(dialog.FileName);
return bitmap;
}
The function is called by an event handler:
private void _pictureBox_Click(object sender, EventArgs e)
{
_takePictureLinkLabel.Visible = false;
var image = Camera.TakePicture();
if (image == null)
return;
image = Camera.CutBitmap(image, 2.5);
_pictureBox.Image = image;
_image = Camera.ImageToByteArray(image);
}
I suspect you are holding onto references. As a minor cause, note that dialogs don't dispose themselves when using ShowDialog, so you should be using the dialog (although I would expect GC to still collect an undisposed but non-referenced dialog).
Likewise, you should probably be using the image, but again: not sure I'd expect this to make-or-break; worth a try, though...
public static Bitmap TakePicture()
{
string filename;
using(var dialog = new CameraCaptureDialog
{
Resolution = new Size(1600, 1200),
StillQuality = CameraCaptureStillQuality.Default
}) {
dialog.ShowDialog();
filename = dialog.FileName;
}
// If the filename is empty the user took no picture
if (string.IsNullOrEmpty(filename))
return null;
// (!) The OutOfMemoryException is thrown here (!)
var bitmap = new Bitmap(filename);
File.Delete(filename);
return bitmap;
}
private void _pictureBox_Click(object sender, EventArgs e)
{
_takePictureLinkLabel.Visible = false;
using(var image = Camera.TakePicture()) {
if (image == null)
return;
image = Camera.CutBitmap(image, 2.5);
_pictureBox.Image = image;
_image = Camera.ImageToByteArray(image);
}
}
I'd also be a little cautious of the CutBitmap etc, to ensure that things are released ASAP.
Your mobile device usually does not have any memory swapping to disk option, so since you choose to store your images as bitmaps in memory rather than files on disk, you quickly consume your phone's memory. Your "new Bitmap()" line allocates a large chunk of memory, so it is very likely to throw the exception there. Another contender is your Camera.ImageToByteArray that will allocate a large amount of memory. This probably isn't large to what you're used to with your computer, but for your mobile this is gigantic
Try keeping the pictures on disk until you use them, i.e. until sending them to the webservice. For displaying them, use your built-in controls, they are probably the most memory efficient and you can usually point them to the image files.
Cheers
Nik

Categories

Resources