question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Extending sharp::CalculateCrop to work for gravity specified using random x,y coordinates

See original GitHub issue

This can solve a use case where you want to specify a custom x,y centre for the focus region of the image. Can be exposed as .crop({x : xCenter, y : yCenter})

Let me know your thoughts on this

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Reactions:1
  • Comments:6 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
rn4391commented, Apr 26, 2018

Works. But the idea was to abstract out this logic inside of sharp. Let me know if it sounds like a good idea to develop it separately. Closing the issue for now.

If anyone else lands on this issue, this is how you can solve it with resize and extract. Metadata is of the original image for which the focal point is known. height, width is the final size required

  1. Resize with intermediate parameters obtained below in obj
    var hRatio = metadata.height / height;
    var wRatio = metadata.width / width;

    var factor;
    //height resize required is smaller than width
    if(hRatio < wRatio) {
        factor = metadata.height / height;
        obj.height = Math.round(height);
        obj.width = Math.round(metadata.width / factor);
    } else {
        //width resize required is smaller than height
        factor = metadata.width / width;
        obj.width = Math.round(width);
        obj.height = Math.round(metadata.height / factor);
    }
  1. The new focus point in the intermediate resize will be at xCenter / factor and yCenter / factor

  2. If intermediate height is less than width, set left to newXCenter - (width / 2) else set top to newYCenter - (height / 2) for extract

  3. Remeber to check the left or top for < 0 and more than the maximum possible for the required height or width

0reactions
michaelbromleycommented, Feb 7, 2020

Here’s a complete implementation (in TypeScript) of the method described above by @rn4391:

import { Region } from 'sharp';

type Dimensions = { w: number; h: number };
type Point = { x: number; y: number };

/**
 * Resize an image but keep it centered on the focal point.
 * Based on the method outlined in https://github.com/lovell/sharp/issues/1198#issuecomment-384591756
 */
export function resizeToFocalPoint(
    original: Dimensions,
    target: Dimensions,
    focalPoint: Point,
): { width: number; height: number; region: Region } {
    const { width, height, factor } = getIntermediateDimensions(original, target);
    const region = getExtractionRegion(factor, focalPoint, target, { w: width, h: height });
    return { width, height, region };
}

/**
 * Calculates the dimensions of the intermediate (resized) image.
 */
function getIntermediateDimensions(
    original: Dimensions,
    target: Dimensions,
): { width: number; height: number; factor: number } {
    const hRatio = original.h / target.h;
    const wRatio = original.w / target.w;

    let factor: number;
    let width: number;
    let height: number;

    if (hRatio < wRatio) {
        factor = hRatio;
        height = Math.round(target.h);
        width = Math.round(original.w / factor);
    } else {
        factor = wRatio;
        width = Math.round(target.w);
        height = Math.round(original.h / factor);
    }
    return { width, height, factor };
}

/**
 * Calculates the Region to extract from the intermediate image.
 */
function getExtractionRegion(
    factor: number,
    focalPoint: Point,
    target: Dimensions,
    intermediate: Dimensions,
): Region {
    const newXCenter = focalPoint.x / factor;
    const newYCenter = focalPoint.y / factor;
    const region: Region = {
        left: 0,
        top: 0,
        width: target.w,
        height: target.h,
    };

    if (intermediate.h < intermediate.w) {
        region.left = clamp(0, intermediate.w - target.w, Math.round(newXCenter - target.w / 2));
    } else {
        region.top = clamp(0, intermediate.h - target.h, Math.round(newYCenter - target.h / 2));
    }
    return region;
}

/**
 * Limit the input value to the specified min and max values.
 */
function clamp(min: number, max: number, input: number) {
    return Math.min(Math.max(min, input), max);
}

It can be used as follows:

const image = sharp(sourceImage);
const metadata = await image.metadata();

const { width, height, region } = resizeToFocalPoint(
    { w: metadata.width, h: metadata.height },
    { w: targetWidth, h: targetHeight },
    { x: xCenter, y: yCenter },
);
return image.resize(width, height).extract(region);
Read more comments on GitHub >

github_iconTop Results From Across the Web

python - Calculate area of polygon given (x,y) coordinates
and simple to use: from shapely.geometry import Polygon pgon = Polygon(zip(x, y)) # Assuming the OP's x,y coordinates print(pgon.area) ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found