Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEAT] Add bilinear resize to RawImage #1101

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

BritishWerewolf
Copy link
Contributor

I wanted to make a start implementing various resizing algorithms for RawImage.
Here is an example of the original canvas API implementation and this PR's bilinear implementation.

I have performed the same operation using both implementations, and cropped the same section to demonstrate the level of detail making it slightly easier to review.

Below is the original image.

And this is the code that I used to generate these images:

const resamplingMethods = ['bilinear', 'canvas'];
const sizes = [0.25, 0.5, 2];

for (let resample of resamplingMethods) {
    for (let size of sizes) {
        image
            .resize(Math.round(image.width * size), null, { resample })
            .then(image => image.save(`${resample}-${size}x.png`));
    }
}
Implementation Resize Image Crop
Canvas 0.25x
Bilinear 0.25x
Canvas 0.5x
Bilinear 0.5x
Canvas 2x
Bilinear 2x

Additionally, I realised that it is possible that the width or height could technically be a float, and so I've added an exception if the user passes in a float, and just rounded the automatic value that is assigned if the "maintain aspect ratio" route is taken.

@BritishWerewolf
Copy link
Contributor Author

BritishWerewolf commented Dec 17, 2024

The implementation of the algorithm could definitely be improved, because the Canvas API tends to have better results, however a bad implementation is better than none.

If you are not happy with this, I am more than happy to improve it, but my current plans are to go through the current supported resampling methods and implement those in some respects - then improve them going forward.

Comment on lines +421 to +429
// Get the values of the new pixel area.
// Always multiply by the width because we
// storing the data in a 1D array.
// To get the second row, we must add a full
// width, then adding the x offset.
const topLeft = this.data[(((y0 * this.width) + x0) * numChannels) + c];
const topRight = this.data[(((y0 * this.width) + x1) * numChannels) + c];
const bottomLeft = this.data[(((y1 * this.width) + x0) * numChannels) + c];
const bottomRight = this.data[(((y1 * this.width) + x1) * numChannels) + c];
Copy link
Contributor Author

@BritishWerewolf BritishWerewolf Dec 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if my comment makes sense, but basically if we had an area like this:

array
1 2 3 4 5 6 7 8 9 10 11 12

image
1  2  3  4
5  6  7  8
9 10 11 12

To get the element on the third row (3 - 1 since it is zero indexed) and second column (2 - 1), we must multiply by the width (4), then add the column.
In this case, we would do:

((row - 1) * width) + (column - 1) = index
((3 - 1) * 4) + (2 - 1) = 9

Comment on lines +431 to +437
// Perform bilinear interpolation.
// Find the horizontal position along the
// top and bottom rows.
const top = (topLeft * (1 - dx)) + (topRight * dx);
const bottom = (bottomLeft * (1 - dx)) + (bottomRight * dx);
// Find the value between these two values.
const interpolatedValue = (top * (1 - dy)) + (bottom * dy);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, we need to get the x value for the top and bottom of the new resized area, then interpolate between these two values to find what the new y value.

topLeft          topRight
+--------T--------------+
|                       |
|                       |
|          I            |
|                       |
|                       |
+------------B----------+
bottomLeft    bottomRight

Here, top is represented by T, bottom is represented by B, and interpolatedValue is represented by I.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant