This is a follow-up question to Crop and resize image around a custom focus point where I got helpful answers on what command parts to use for imagemagick in order to achieve what I want: Scale an image down, but instead of using a predefined gravitiy (like "Center" or "North"), I want to use a custom point of focus on the image that should be the new center of the scaled image to ensure the "important point" of the image is always visible when cropping/resizing it.
This works quite well, except for the actual size calculation. I guess something is wrong in my math, because the resulting output is missing some parts that could be visible if the image is being scaled down more than I calculate. I can't find the mistake I'm doing, maybe someone can give me a hint into the right direction.
What I am currently doing:
# Example image from https://unsplash.com/photos/p-I9wV811qk
source_image = 'input.jpg'
source_image_size = [3008, 2000]
target_image_size = [295, 195]
# Focus point position, in percentages.
focus_point = {
x: 46.7087766,
y: 24.2
}
# Calculate the focus point in px on the source image.
source_focus_point = [
source_image_size[0] / 100.0 * focus_point[:x],
source_image_size[1] / 100.0 * focus_point[:y]
]
# Calculate the distances to the focus point in percentage,
# from all four image sides, and use the smalles value.
smallest_percentage_distance = [
focus_point[:x],
(100.0 - focus_point[:x]),
focus_point[:y],
(100.0 - focus_point[:y])
].min
# Scale image to reach smallest percentage distance.
scale_factor = smallest_percentage_distance / 100.0
# Calculate the focus point on the target image for the transformation,
# which will be on the center of the new image.
target_focus_point = [
target_image_size[0].to_f / 2.0,
target_image_size[1].to_f / 2.0
]
# Define how many degrees the image should be rotated.
rotation_degrees = 0
command = [
'convert',
source_image,
"-set option:distort:viewport #{target_image_size.join('x')}",
"-distort SRT '#{source_focus_point.join(',')} #{scale_factor} #{rotation_degrees} #{target_focus_point.join(',')}'",
'output2.jpg'
].join ' '
The resulting command is:
convert input.jpg -set option:distort:viewport 295x195 -distort SRT '1405.000000128,484.0 0.242 0 147.5,97.5' output.jpg
In my understanding, imagemagick's distort SRT's second argument (here, scale_factor
) defines how much, in %, the image has to be scaled down. My idea was to calculate the distances of all sides to the focus point, take the smallest distance, convert it to a suitable argument, and use that one for defining the scaling amount.
The goal is to scale the image as much as possible to ensure that the output image does not crop off too much in order to show as much as possible of the original image. My output right now cuts off too much.
I tried to visualize it for a better understanding:
The original image can be found here: https://unsplash.com/photos/p-I9wV811qk
What am I doing wrong? How do I calculate the value for scaling down the image correctly to have as much of the picture visible as possible?
Edit 1
At the end, multiple variations of the image will be generated, having different aspect ratios. Next to the given 295x195, another one will be 1440x560; Especially in this one, a simple "crop with gravity" will most likely crop out the middle or upper part of the image, thus the focus point to ensure the vital part is always visible.
Edit 2 to the answer from fmw42
The code works so far that it does resizing and cropping without using SRT. Unfortunately, the result is further away compared to the result I'm currently having (but maybe I did something wrong when adapting the script).
What I did was
- using the image from my question
- replacing
px
andpy
with the focus point values from my question - keeping the
scale
calculation but usingwd=295
andht=195
as this is the desired target crop