Imagesharp: Create image strip

Created on 14 Feb 2018  路  14Comments  路  Source: SixLabors/ImageSharp

Hi,
I'd like to merge some images (same size side by side) and I tried the DrawImage method:

```c#
image1.Mutate(x => x
.DrawImage(
image2,
1,
new Size(image1.Width * 2, image1.Height),
new Point(image1.Width, 0)
)
);


but the output image width is not like expected (image1.Width * 2) but it keeps the image1 width.

To solve this I make first a Resize of first image adding padding on the right side:

```c#
.Resize(new ResizeOptions
   {
      Mode = ResizeMode.Pad,
      Position = AnchorPosition.TopLeft,
      Size = new Size(image1.Width * 2, image1.Height)
   }
)

Is it the right solution or the DrawImage must work resizing the final output too?

Thanks
Marco

question

All 14 comments

@valse why don't you create a new image with the desired width of im1.Width+im2.Width, and draw the two input images here? Resize() is for scaling the input image.

Hi,
I saw a strange memory behavior about DrawImage: in my project I take about 20 Full HD video thumbnails (1920x1080) that I merge together like this result image (that I created empty :-P referring my previous post):

test

Each thumb will be resized to 110x62 but if I let this job to the DrawImage method, the dotnet.exe process grow a lot for each request.

If I resize the thumb before using it in the DrawImage method, the memory keeps low.

This is my snippet code:
```c#
using (var thumb = Image.Load(buffer))
{
// without this memory leaks
thumb.Mutate(x =>
x.Resize(thumbWidth, thumbHeight)
);

strip.Mutate(x =>
    x.DrawImage(
        thumb,
        1,
        new Size(thumbWidth, thumbHeight),
        new Point(
            nIndex % thumbsPerColumn * thumbWidth,
            (int) Math.Floor(nIndex / (decimal) thumbsPerColumn) * thumbHeight)
    )
);                            

nIndex++;

}
```
Thanks
Marco

@valse thanks that's very useful feedback! 馃憤 Normally, the memory usage should not grow with your use case, so this might be a bug. Gonna try to investigate it during my work on #225.

It's a huge job however, so I really need all the help the community can provide! I you could make a small, self containing console app reproducing this issue, there is higher chance I'll be able to fix the issue and maybe provide a workaround for you that works with the current official beta.

Hi @antonfirsov sure I attached my demo web app; it takes images from a folder inside the wwwroot one and merge together.
The output is on the http context response stream.

@valse thanks! Gonna memory profile it within a few days.

@JimBobSquarePants I think SixLabors.ImageSharp.Web.Memory.BufferDataPool shouldn't be public! It's has all the drawbacks of an uncofigurable/unreplaceable singleton!
(I don't think it's the main issue in this example however.)

I used BufferDataPool because I made my own resolvers implementation (Amazon S3, Google Cloud Storage and Highwinds) for the ImageSharp.Web library... and they return a buffer array like the PhysicalFileResolver do.

I made a dotnet core web app using the ImageSharp.Web middleware and reading from an Amazon S3 bucket the original images... But with a massive stress test the memory still growing up to 3GB and more 馃槬

@valse can you also try with the newest nightlies? I'm curious if it's any better.

Finishing #225 should provide a solution for this. There will be a PR this week, and I hope it will be merged before the end of the next week.

@JimBobSquarePants I think SixLabors.ImageSharp.Web.Memory.BufferDataPool shouldn't be public! It's has all the drawbacks of an uncofigurable/unreplaceable singleton!
(I don't think it's the main issue in this example however.)

Ah... How do you suggest we make it possible for devs implementing IImageResolver to use pooling then? We use the pool to allow reading of input streams for the PhysicalFileSystemResolver without allocation.

@antonfirsov @valse

In DrawImage we're making a deep clone of the input image if the dimensions don't match. It looks like we're cleaning up after ourselves but that's gonna create a whole heap of extra memory pressure if those images are large.

I'd like to refactor this to remove the clone and make it a bit more readable.

How do you suggest we make it possible for devs implementing IImageResolver to use pooling then?

Hard question. Maybe they can use an ArrayPool instance configured by their own. Maybe you can try passing around a configurable array pool abstraction (IBufferDataPool) with dependency injection. But I have only partial understanding of the web middleware, so these are just random thoughts + demonstraiting my usual reaction to singletons again /* 馃槺 馃拃 */ 馃槅

Re DrawImage:
Yeah that cloning seems unnecessary. Somehow we managed to eliminate it in other processors.

Just created a PR to remove the clone. Making the pool configurable sounds like a plan, I'll do that next.

@antonfirsov I tried the latest nightlies but the issue still exists.

@valse #471 will get rid of the issue.

The cloning is necessary in DrawImage otherwise we don't have the source pixels being the correct size for the drawing operation. Only way to remove the clone from that operation is to remove the ability to specify the destination size and draw the image at source dimension size.

Was this page helpful?
0 / 5 - 0 ratings