Occasional OutOfMemoryException on Bitmap Processing Over The Network - c#

I'm running the following code in LinqPad 5:
var client = new MongoClient(#"mongodb://192.168.0.108:27017");
var db = client.GetDatabase("Stfc");
var fitCollection = db.GetCollection<ModelFit>("RecentFits");
var fits = fitCollection.AsQueryable();
var captureCollection = db.GetCollection<Capture>("Captures");
var captures = captureCollection.AsQueryable();
var classificationCollection = db.GetCollection<Classification>("Classifications");
var classifications = classificationCollection.AsQueryable();
var modelsDir = new DirectoryInfo(#"\\iansdesktop\Shared\Stfc\mymodels");
var imagesDir = new DirectoryInfo(#"\\iansdesktop\Shared\Stfc\Images");
var classificationDir = new DirectoryInfo(#"C:\Users\Ian\Documents\Projects\Output\StfcBot\Classification");
var capturesById = captures.ToDictionary(x => x.Id);
var systems = classifications
.Where(x => x.Label == "system");
var count = systems.Count();
var i = 0;
var pen = new Pen(Color.FromArgb(255, 255, 0, 0));
foreach (var classification in systems)
{
var capture = capturesById[classification.CaptureId];
var img = imagesDir.File(capture.ImageName);
var srcFile = imagesDir.File(capture.ImageName);
var destFile = classificationDir.File(capture.ImageName);
while (!destFile.Exists)
{
try
{
using (var bmp = Bitmap.FromFile(srcFile.FullName))
using (var dest = new Bitmap(bmp))
{
using (var g = Graphics.FromImage(dest))
{
g.DrawEllipse(pen, capture.X - 20, capture.Y - 20, 40, 40);
}
dest.Save(destFile.FullName);
dest.Dispose();
bmp.Dispose();
}
destFile.Refresh();
destFile.Name.Dump();
}
catch (IOException ex)
{
ex.Dump();
Thread.Sleep(30_000);
}
}
++i;
if (i % 10 == 0)
{
i.ToProgressSummary(count).Dump();
}
}
Am I missing anything, or could this be a bug in LinqPad?

Turns out this is because the bitmap was being loaded from a network path, and the network was occasionally disconnecting.
The documentation states:
You must keep the stream open for the lifetime of the Bitmap.
Bitmap Constructors (See Remarks)
The OOM exception obfuscates what is going on for some reason, but the underlying stream was being closed.
The solution is to copy the file locally and operate on that local copy:
var tmpFile = new DirectoryInfo(Path.GetTempPath()).File(srcFile.Name);
while (!destFile.Exists)
{
srcFile.CopyTo(tmpFile.FullName);
try
{
using (var bmp = Bitmap.FromFile(tmpFile.FullName))
using (var dest = new Bitmap(bmp))
{
.
}
destFile.Refresh();
destFile.Name.Dump();
}
catch (IOException ex)
{
...
}
finally
{
tmpFile.Delete();
}
}
Of course if the network still disconnects an exception occurs, but at least it's a sensible and understandable error instead of OOM.

Related

Recording a region with Windows Graphics Capture API

I am building a screen recording app in C# using Windows Graphics Capture API. I am using this script. I can select monitor and can record it to mp4 file. I can also select a Window and record it too. But how can we record a region with this? Ideally I need to give x,y coordinates along with width and height to record that specific region.
Here are the functions which return GraphicsCaptureItem for Window or Monitor hwnd, which can be used to record.
public static GraphicsCaptureItem CreateItemForWindow(IntPtr hwnd)
{
var factory = WindowsRuntimeMarshal.GetActivationFactory(typeof(GraphicsCaptureItem));
var interop = (IGraphicsCaptureItemInterop)factory;
var temp = typeof(GraphicsCaptureItem);
var itemPointer = interop.CreateForWindow(hwnd, GraphicsCaptureItemGuid);
var item = Marshal.GetObjectForIUnknown(itemPointer) as GraphicsCaptureItem;
Marshal.Release(itemPointer);
return item;
}
public static GraphicsCaptureItem CreateItemForMonitor(IntPtr hmon)
{
var factory = WindowsRuntimeMarshal.GetActivationFactory(typeof(GraphicsCaptureItem));
var interop = (IGraphicsCaptureItemInterop)factory;
var temp = typeof(GraphicsCaptureItem);
var itemPointer = interop.CreateForMonitor(hmon, GraphicsCaptureItemGuid);
var item = Marshal.GetObjectForIUnknown(itemPointer) as GraphicsCaptureItem;
Marshal.Release(itemPointer);
return item;
}
And this is Recording function
private async void RecordScreen(GraphicsCaptureItem item)
{
_device = Direct3D11Helpers.CreateDevice();
// Get our encoder properties
uint frameRate = 30;
uint bitrate = 3 * 1000000;
var width = (uint)item.Size.Width;
var height = (uint)item.Size.Height;
// Kick off the encoding
try
{
newFile = GetTempFile();
using (var stream = new FileStream(newFile, FileMode.CreateNew).AsRandomAccessStream())
using (_encoder = new Encoder(_device, item))
{
await _encoder.EncodeAsync(
stream,
width, height, bitrate,
frameRate);
}
}
catch (Exception ex)
{}
}
I achieved this by passing a custom region to CopySubresourceRegion in WaitForNewFrame method.
public SurfaceWithInfo WaitForNewFrame()
{
.....
using (var multithreadLock = new MultithreadLock(_multithread))
using (var sourceTexture = Direct3D11Helpers.CreateSharpDXTexture2D(_currentFrame.Surface))
{
.....
using (var copyTexture = new SharpDX.Direct3D11.Texture2D(_d3dDevice, description))
{
.....
var region = new SharpDX.Direct3D11.ResourceRegion(
_region.Left,
_region.Top,
0,
_region.Left + _region.Width,
_region.Top + _region.Height,
1
);
_d3dDevice.ImmediateContext.CopyResource(_blankTexture, copyTexture);
_d3dDevice.ImmediateContext.CopySubresourceRegion(sourceTexture, 0, region, copyTexture, 0);
result.Surface = Direct3D11Helpers.CreateDirect3DSurfaceFromSharpDXTexture(copyTexture);
}
}
....
}

Invalid cast from IDirect3DSurface to SoftwareBitmap

I try to implement processing frames from webcam to the WPF application using UWP API.
There is article how to work with MediaCapture & MediaFrameReader:
https://learn.microsoft.com/en-us/windows/uwp/audio-video-camera/process-media-frames-with-mediaframereader#handle-the-frame-arrived-event
If I set up MemoryPreference to cpu, SoftwareBitmaps are initialized to the null in the event. When I place Auto, I can see IDirect3DSurface objects are in the event, but in conversion to the SoftwareBitmap the exception "Specified cast is not valid." is raised.
How to convert IDirect3DSurface to SoftwareBitmap?
private async void MediaCaptureExample()
{
var frameSourceGroups = await MediaFrameSourceGroup.FindAllAsync();
MediaFrameSourceGroup selectedGroup = null;
MediaFrameSourceInfo colorSourceInfo = null;
foreach (var sourceGroup in frameSourceGroups)
{
foreach (var sourceInfo in sourceGroup.SourceInfos)
{
if (sourceInfo.MediaStreamType == MediaStreamType.VideoRecord && sourceInfo.SourceKind == MediaFrameSourceKind.Color)
{
colorSourceInfo = sourceInfo;
break;
}
}
if (colorSourceInfo != null)
{
selectedGroup = sourceGroup;
break;
}
}
capture = new MediaCapture();
var settings = new MediaCaptureInitializationSettings()
{
SourceGroup = selectedGroup,
SharingMode = MediaCaptureSharingMode.ExclusiveControl,
MemoryPreference = MediaCaptureMemoryPreference.Auto,
StreamingCaptureMode = StreamingCaptureMode.Video
};
await capture.InitializeAsync(settings);
var colorFrameSource = capture.FrameSources[colorSourceInfo.Id];
var preferredFormat = colorFrameSource.SupportedFormats.Where(format =>
{
return format.VideoFormat.Width >= 1080
&& String.Compare(format.Subtype, MediaEncodingSubtypes.Mjpg, true) == 0;
}).FirstOrDefault();
if (preferredFormat == null)
{
// Our desired format is not supported
return;
}
await colorFrameSource.SetFormatAsync(preferredFormat);
mediaFrameReader = await capture.CreateFrameReaderAsync(colorFrameSource);
mediaFrameReader.FrameArrived += MediaFrameReader_FrameArrived;
var result = await mediaFrameReader.StartAsync();
Console.WriteLine("Result = " + result.ToString());
}
private void MediaFrameReader_FrameArrived(MediaFrameReader sender, MediaFrameArrivedEventArgs args)
{
try
{
var mediaFrameReference = sender.TryAcquireLatestFrame();
var videoMediaFrame = mediaFrameReference?.VideoMediaFrame;
var softwareBitmap = videoMediaFrame?.SoftwareBitmap;
var direct3DSurface = videoMediaFrame?.Direct3DSurface;
if (direct3DSurface != null)
{
var softwareBitmapTask = SoftwareBitmap.CreateCopyFromSurfaceAsync(mediaFrameReference.VideoMediaFrame.Direct3DSurface).AsTask();
softwareBitmap = softwareBitmapTask.Result;
}
if (softwareBitmap != null)
{
using (var stream = new Windows.Storage.Streams.InMemoryRandomAccessStream())
{
var encoderTask = BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream).AsTask();
encoderTask.Wait();
var encoder = encoderTask.Result;
encoder.SetSoftwareBitmap(softwareBitmap);
Task t = encoder.FlushAsync().AsTask();
t.Wait();
var image = new System.Windows.Media.Imaging.BitmapImage();
image.BeginInit();
image.StreamSource = stream.AsStream();
image.CacheOption = System.Windows.Media.Imaging.BitmapCacheOption.OnLoad;
image.EndInit();
imageElement.Source = image;
}
}
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
}
The issue was in format subtype. I changed format from Mjpg to Nv12, and everything start working properly (even for MediaCaptureMemoryPreference.Auto):
var preferredFormat = colorFrameSource.SupportedFormats.Where(format =>
{
return format.VideoFormat.Width >= 1080 && String.Compare(format.Subtype, MediaEncodingSubtypes.Nv12, true) == 0;
}).FirstOrDefault();

Saving KML file, I get System out of memory exception

I'm trying to export kml file. for some reason I keep getting system out of memory exception. kindly find my code below
SharpKml.Dom.Kml root = new SharpKml.Dom.Kml();
root.Feature = doc;
SharpKml.Engine.KmlFile kmlFile = SharpKml.Engine.KmlFile.Create(root, true);
try
{
using (var stream = File.OpenWrite(kmlFileName))
kmlFile.Save(stream);
}
catch
{
throw;
}
it explodes on KmlFile.Save(stream). please help
Next code works fine for me
try
{
LineString lineString = new LineString()
{
AltitudeMode = AltitudeMode.Absolute,
Tessellate = true,
Coordinates = new CoordinateCollection()
};
Vector prevCoordinates = new Vector(45.883144378662109, 13.902674674987793, -71.5);
lineString.Coordinates.Add(prevCoordinates);
Placemark placemark = new Placemark()
{
Name = "Coordinate log",
Geometry = lineString
};
placemark.AddStyle(new Style()
{
Line = new LineStyle()
{
ColorMode = ColorMode.Normal,
Width = 3,
Color = new Color32(255, 255, 0, 0),
OuterWidth = 1,
OuterColor = new Color32(150, 255, 255, 255),
},
});
KmlFile kml = KmlFile.Create(placemark, false);
using (var stream = System.IO.File.OpenWrite(telemFileName + ".kml"))
{
kml.Save(stream);
}
}
catch (IOException)
{
//file in use
}
catch (Exception ex)
{
logger.Error("Exception: " + ex);
}

A Generic error occurred in GDI+ while saving image to file system [duplicate]

I have made one console application in which I have pre-generated all images as same as nopCommerce does for generating images.
Here is my code:
thumbFileName = !String.IsNullOrEmpty(seoFileName) ?
string.Format("{0}_{1}_{2}.{3}", pictureId.ToString("0000000"), seoFileName, _productThumbPictureSize, lastPart) :
string.Format("{0}_{1}.{2}", pictureId.ToString("0000000"), _productThumbPictureSize, lastPart);
if (_generatePictures)
{
if (storeInDb)
{
storeInDb = GetSettingByKey<bool>("media.images.storeindb");
}
// byte defaultImageQuality = GetSettingByKey<byte>("mediasettings.defaultimagequality");
if (storeInDb)
{
pictureBinary = GetPictureByProductId(Id);
}
else
{
pictureBinary = LoadPictureFromFile(pictureId, mimeType);
}
if (pictureBinary == null || pictureBinary.Length == 0)
{
url = GetDefaultPictureUrl(_productThumbPictureSize);
return url;
}
if (isNew)
{
DeletePictureThumbs(pictureId);
//we do not validate picture binary here to ensure that no exception ("Parameter is not valid") will be thrown
var picture = UpdatePicture(pictureId,
pictureBinary,
mimeType,
seoFileName,
false);
}
if (pictureBinary.Length != 0)
{
//Generating Images
string newpath = "Thumbs";
var _path = Path.Combine(_imagesPath, newpath);
var thumbFilePath = GetPictureLocalPath(thumbFileName, _path);//"C:\\Users\\Developer\\Documents\\Server2\\Solr Plugin Branch Nop-310\\Presentation\\Nop.Web\\Content\\Images\\Thumbs\\"+thumbFileName;
if (!File.Exists(thumbFilePath))
{
using (var stream = new MemoryStream(pictureBinary))
{
Bitmap b = null;
try
{
//try-catch to ensure that picture binary is really OK. Otherwise, we can get "Parameter is not valid" exception if binary is corrupted for some reasons
b = new Bitmap(stream );
}
catch (ArgumentException exc)
{
string msg = exc.ToString();
string fullmsg = string.Format("Error generating picture thumb. ID={0}", pictureId);
InsertSystemLog(msg, fullmsg);
}
if (b == null)
{
//bitmap could not be loaded for some reasons
return url;
}
var newSize = CalculateDimensions(b.Size, _productThumbPictureSize);
if (newSize.Width < 1)
newSize.Width = 1;
if (newSize.Height < 1)
newSize.Height = 1;
using (var newBitMap = new Bitmap(newSize.Width, newSize.Height))
{
using (var g = Graphics.FromImage(newBitMap))
{
g.SmoothingMode = SmoothingMode.HighQuality;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.CompositingQuality = CompositingQuality.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.DrawImage(b, 0, 0, newSize.Width, newSize.Height);
var ep = new EncoderParameters();
ep.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 80L);
ImageCodecInfo ici = GetImageCodecInfoFromExtension(lastPart);
if (ici == null)
ici = GetImageCodecInfoFromMimeType("image/jpeg");
try
{
newBitMap.Save(thumbFilePath, ici, ep);
}
catch (ArgumentException exc)
{
string msg = exc.ToString();
string fullmsg = string.Format("Unable to save Picture ID={0}", pictureId);
InsertSystemLog(msg, fullmsg);
}
}
}
b.Dispose();
}
}
}
I got GDI+ error at following line:
b = new Bitmap(stream);
Error:
The log entry message.Short message: A generic error occurred in GDI+.
The details for the log entry.Full message: at System.Drawing.Image.FromStream(Stream stream)
Notice that all images are saved on file system. And folder has write permission. And this error occurred on live site only not at localhost.

Operation is not valid due to the current state of the object

i am beginning in develop winphone and nokia imaging sdk. i have two function.
firstly, i call the function below to change image to gray color
private async void PickImageCallback(object sender, PhotoResult e)
{
if (e.TaskResult != TaskResult.OK || e.ChosenPhoto == null)
{
return;
}
using (var source = new StreamImageSource(e.ChosenPhoto))
{
using (var filters = new FilterEffect(source))
{
var sampleFilter = new GrayscaleFilter();
filters.Filters = new IFilter[] { sampleFilter };
var target = new WriteableBitmap((int)CartoonImage.ActualWidth, (int)CartoonImage.ActualHeight);
var renderer = new WriteableBitmapRenderer(filters, target);
{
await renderer.RenderAsync();
_thumbnailImageBitmap = target;
CartoonImage.Source = target;
}
}
}
SaveButton.IsEnabled = true;
}
then i call function to change image to binary color
private async void Binary(WriteableBitmap bm_image)
{
var target = new WriteableBitmap((int)CartoonImage.ActualWidth, (int)CartoonImage.ActualHeight);
MemoryStream stream= new MemoryStream();
bm_image.SaveJpeg(stream, bm_image.PixelWidth, bm_image.PixelHeight, 0, 100);
using (var source = new StreamImageSource(stream))
{
using (var filters = new FilterEffect(source))
{
var sampleFilter = new StampFilter(5, 0.7);
filters.Filters = new IFilter[] { sampleFilter };
var renderer1 =new WriteableBitmapRenderer(filters, target);
{
await renderer1.RenderAsync();
CartoonImage.Source = target;
}
}
}
}
but when it run to " await renderer1.RenderAsync();" in the second function, it doesn't work. How can i solve it. And you can explain for me about how "await" and "async" work ?
thank you very much!
I'm mostly guessing here since I do not know what error you get, but I'm pretty sure your problem lies in setting up the source. Have you made sure the memory stream position is set to the beginning (0) before creating an StreamImageSource?
Try adding:
stream.Position = 0;
before creating the StreamImageSource.
Instead of trying to create a memory stream from the writeable bitmap I suggest doing:
using Nokia.InteropServices.WindowsRuntime;
...
using (var source = new BitmapImageSource(bm_image.AsBitmap())
{
...
}

Categories