Magick.NET icon indicating copy to clipboard operation
Magick.NET copied to clipboard

Create a resized copy of an image

Open toptensoftware opened this issue 11 months ago • 8 comments

Is your feature request related to a problem? Please describe

I have a project that works with very large tif images. I need to keep the large image in memory for later processing, but I also need smaller versions for on-screen rendering. Currently to do this I need to duplicate the large image (doubling the memory usage) and then resize it to the smaller size.

Describe the solution you'd like

Would like the ability to directly create a resized copy of a source image (or part thereof)

Describe alternatives you've considered

As mentioned above, the current solution is to create copy of full image and then downsize it - but this requires more memory than directly resizing to a smaller image.

(Perhaps there's already a way to do this but if so, I couldn't figure out how)

Additional context

No response

toptensoftware avatar Mar 07 '24 02:03 toptensoftware

In theory this could be possible for methods of ImageMagick that return a new image. It's an interesting idea but I will need to think about an API for this. And that will probably happen after I have added a new source code generator to call the native api.

dlemstra avatar Mar 08 '24 19:03 dlemstra

Sounds good, I look forward to it.

toptensoftware avatar Mar 09 '24 08:03 toptensoftware

Do you have any ideas how I could/should integrate this in the API? I think this behavior would only be required for methods that change the dimensions of an image. Otherwise you could just use .Clone() and change the image. I am now thinking about something like this:

using var image = new MagickImage("input.png");
using var thumbnail = image.NewMethod().Resize(100, 100);
using var thumbnail = image.NewProperty.Resize(100, 100);

The NewMethod() or NewProperty would return an interface that includes a the methods that change the dimensions of the image. But the disadvantage of this would be the discoverability in the api. So I am also thinking about something like this:

using var image = new MagickImage("input.png");
using var thumbnail = image.ResizeWithSuffix(100, 100);

As you can see besides deciding which option to use the naming is also something that I have to think about. Do you have any suggestions?

dlemstra avatar Mar 23 '24 19:03 dlemstra

Not sure how feasible this is, and I realize this is adding to the original requirement, but the ideal for me would be something like this:

IMagickImage<QuantumType> CloneCropAndResize(IMagickGeometry? crop, IMagickGeometry? resize)

Where:

  • crop specifies which portion of the source image to grab. If null, the entire source image
  • resize specifies how to resize it into a new image instance. If null, no resizing.

ie: it would be the equivalent of the following but internally would combine all this into a single resize operation without the overhead of the full clone at the start.

image.Clone().Crop(crop).Resize(resize);

The nice thing about this is it would let you grab any region at any resolution. ie: you could grab tiled sections of a source image at display resolutions for rendering.

The name CloneCropAndResize is a bit wordy but sums up what it's doing. Alternative suggestion: ExtractImage, or maybe ExtractSubImage?

toptensoftware avatar Mar 25 '24 00:03 toptensoftware

Those are not the only two methods this option will become available to. Creating a new method for each possible combination is not feasible.

dlemstra avatar Mar 25 '24 22:03 dlemstra

Ah ok, I see what you mean.

Perhaps NewMethod() could be CloneThen().

var newImage = image.CloneThen().Resize(100, 100);

I guess the downside to this is it might be tempting to chain these together fluent style, but the result of the Crop call wouldn't be disposed.

var newImage = image
    .CloneThen().Crop(10, 10, 1000, 1000)
    .CloneThen().Resize(100, 100)

Thinking out loud (again no idea if this is feasible) but what if you could put together a chain of operations which you terminated with a function to actually do the work. ie: each intermediate operation just returned an interface (not an image instance) upon which you could perform additional operations. The Process() call at the end walks the chain of operations and produces a final image.

eg:

var newImage = image
    .CloneThen()
    .Crop(10, 10, 1000, 1000)
    .Resize(100, 100)
    .Whatever()
    .SomethingElse()
    .Process();

You're obviously thinking about this from a broader perspective than my narrow view... I'm sure whatever you come up with will be fine.

toptensoftware avatar Mar 26 '24 06:03 toptensoftware

Good question. But I don’t think that new methods are needed, because it's already supported quite simply:

 using MagickImage image = new MagickImage(inputStream);
 using MagickImage thumbnail = image.Clone() as MagickImage;
 thumbnail.Resize(100, 100);

alexeygritsenko avatar Mar 26 '24 07:03 alexeygritsenko

The Clone() method creates of copy of the pixels of the input image and then resizes that to the new dimensions. Here is an example what it does with the memory (the numbers are made up but should give you an idea of what happens):

flowchart TD
    A[Create image] --> B[10 mb used]
    B --> C[Clone]
    C --> D[20 mb used]
    D --> E[Resize to 50%]
    B --> F[Resize without clone]
    E --> G[15 mb used]
    F --> G

This means that with the new method the maximum amount of memory that is used is 15mb.

dlemstra avatar Mar 26 '24 21:03 dlemstra