Unless I'm using it wrong, there appears to be a memory leak in CoenM.ImageHash.HashAlgorithms.PerceptualHash. I'm attempting to generate hashes for approximately 45000 images. I get a System.OutOfMemoryException at approximately 28000 images. My code, with a bunch of database and Winforms code removed, looks like this:
`
using IH = CoenM.ImageHash.HashAlgorithms;
using IS6 = SixLabors.ImageSharp;
namespace SqlEntityFramework {
public partial class Form1: Form {
IH.PerceptualHash PHash { get; set; } = new IH.PerceptualHash();
static byte[] GetByteArray(Bitmap bitmap) {
using (var mStream = new MemoryStream()) {
bitmap.Save(mStream, ImageFormat.Bmp);
byte[] imageData = mStream.ToArray();
return imageData;
}
}
private void processImagesButton_ClickAsync(object sender, EventArgs e) {
try {
var imageFolderDI = new DirectoryInfo(@"D:\Images");
var files = imageFolderDI.GetFiles();
for (int i = 0; i < files.Length; i++) {
var imageFI = files[i];
long phash;
using (var image = new Bitmap(imageFI.FullName)) {
byte[] imageData = GetByteArray(image);
using (var image = new Bitmap(imageFI.FullName)) {
byte[] imageData = GetByteArray(image);
/* *********************
* comment out this using clause and I process all 45000 images
* uncomment and I run out of memory at 28411 images
using (var image6 = IS6.Image.Load(imageData)) {
phash = (long) PHash.Hash(image6);
image6.Dispose();
}
****************** */
// save phash to DB ...
}
// Tried adding this clause, but still get an out-of-memory exception after 17701 iterations
if ((i % 30) == 0) {
PHash = new IH.PerceptualHash();
}
}
} catch (Exception e1) { // e is a System.OutOfMemoryException exception. Call stack is empty
Console.WriteLine($"Exception processing images: {e1.Message}");
}
}
}
}
`
I'm pretty sure the leak isn't in my code, since it processes all 45000 images when the /*** using () { } ***/ clause is commented out. I'm building and running it in Visual Studio 2019 v16.8.0 with x86 configuration.
FWIW, I rebooted my Win 10 PC (16 GB memory) and tried recreating PerceptualHash() every 30 iterations. That appeared to make the memory problem occur about 10000 iterations earlier. I suspect the problem is in SixLabors.ImageSharp.Image.Mutate()? The call stack is nearly empty empty when I break in the catch clause:
SqlEntityFramework.exe!SqlEntityFramework.Form1.processImagesButton_ClickAsync(object sender, System.EventArgs e) Line 111 C#
[Resuming Async Method]
[External Code]
SqlEntityFramework.exe!SqlEntityFramework.Program.Main() Line 16 C#
The exception trace looks like:
at System.Buffers.DefaultArrayPool1.Rent(Int32 minimumLength) at SixLabors.ImageSharp.Memory.ArrayPoolMemoryAllocator.Allocate[T](Int32 length, AllocationOptions options) at SixLabors.ImageSharp.Memory.MemoryGroup
1.Allocate(MemoryAllocator allocator, Int64 totalLength, Int32 bufferAlignment, AllocationOptions options)
at SixLabors.ImageSharp.Memory.MemoryAllocatorExtensions.Allocate2D[T](MemoryAllocator memoryAllocator, Int32 width, Int32 height, AllocationOptions options)
at SixLabors.ImageSharp.ImageFrame1..ctor(Configuration configuration, Int32 width, Int32 height, TPixel backgroundColor, ImageFrameMetadata metadata) at SixLabors.ImageSharp.ImageFrameCollection
1..ctor(Image1 parent, Int32 width, Int32 height, TPixel backgroundColor) at SixLabors.ImageSharp.Image
1..ctor(Configuration configuration, Int32 width, Int32 height, ImageMetadata metadata)
at SixLabors.ImageSharp.Formats.Bmp.BmpDecoderCore.Decode[TPixel](BufferedReadStream stream, CancellationToken cancellationToken)
at SixLabors.ImageSharp.Formats.ImageDecoderUtilities.Decode[TPixel](IImageDecoderInternals decoder, Configuration configuration, Stream stream, Func3 largeImageExceptionFactory) at SixLabors.ImageSharp.Formats.ImageDecoderUtilities.Decode[TPixel](IImageDecoderInternals decoder, Configuration configuration, Stream stream) at SixLabors.ImageSharp.Formats.Bmp.BmpDecoder.Decode[TPixel](Configuration configuration, Stream stream) at SixLabors.ImageSharp.Image.Decode[TPixel](Stream stream, Configuration config) at SixLabors.ImageSharp.Image.<>c__DisplayClass133_0
1.b__0(Stream s)
at SixLabors.ImageSharp.Image.WithSeekableStream[T](Configuration configuration, Stream stream, Func`2 action)
at SixLabors.ImageSharp.Image.Load[TPixel](Configuration configuration, Stream stream, IImageFormat& format)
at SixLabors.ImageSharp.Image.Load[TPixel](Configuration configuration, Byte[] data)
at SixLabors.ImageSharp.Image.Load(Byte[] data)
at SqlEntityFramework.Helpers.ToIS6Image(Byte[] byteArray) in D:\CSharp\SqlEntityFramework\SqlEntityFramework\Helpers.cs:line 35
at SqlEntityFramework.Form1.AddImageToDB(SHA256 sha256, PixEmailEntities dbContext, FileInfo imageFI) in D:\CSharp\SqlEntityFramework\SqlEntityFramework\Form1.cs:line 64
at SqlEntityFramework.Form1.<processImagesButton_ClickAsync>d__24.MoveNext() in D:\CSharp\SqlEntityFramework\SqlEntityFramework\Form1.cs:line 91
Expected Result: You should be able to create at least 1000000 hashes