使用 imagemagick 使用 SRT 将图像缩放到尽可能小的比例
Scale image with SRT to the smallest possible ratio with imagemagick
这是 的后续问题,在那里我得到了有用的答案,关于 imagemagick 使用哪些命令部分来实现我想要的:缩小图像,而不是使用预定义的重力(如 "Center" 或 "North"),我想在图像上使用自定义焦点,该焦点应该是缩放图像的新中心,以确保图像的 "important point" cropping/resizing 时始终可见。
除了实际大小计算之外,这工作得很好。我想我的数学有问题,因为如果图像按比例缩小比我计算的多,结果输出会丢失一些可能可见的部分。我找不到我正在做的错误,也许有人可以给我一个正确方向的提示。
我现在在做什么:
# 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 ' '
结果命令是:
convert input.jpg -set option:distort:viewport 295x195 -distort SRT '1405.000000128,484.0 0.242 0 147.5,97.5' output.jpg
根据我的理解,imagemagick 的扭曲 SRT 的第二个参数(此处为 scale_factor
)定义了图像必须按比例缩小多少(以 % 为单位)。我的想法是计算所有边到焦点的距离,取最小的距离,将其转换为合适的参数,并使用该参数来定义缩放量。
目标是尽可能地缩放图像以确保输出图像不会裁剪太多,以尽可能多地显示原始图像。我现在的输出中断太多了。
为了更好地理解,我尝试将其可视化:
原始图片可以在这里找到:https://unsplash.com/photos/p-I9wV811qk
我做错了什么?如何计算正确缩小图像以尽可能多地显示图片的值?
编辑 1
最后,将生成图像的多个变体,具有不同的纵横比。在给定的 295x195 旁边,另一个将是 1440x560;尤其是这张,简单的"crop with gravity"很可能会裁剪掉图片的中上部,从而确保要害部分始终可见的焦点。
编辑 2 到 fmw42 的答案
到目前为止,该代码可以在不使用 SRT 的情况下调整大小和裁剪。不幸的是,与我目前的结果相比,结果还差得远(但也许我在改编脚本时做错了什么)。
我做的是
- 使用我问题中的图片
- 用我的问题中的焦点值替换
px
和 py
- 保留
scale
计算,但使用 wd=295
和 ht=195
,因为这是所需的目标作物
结果输出如下所示:
这是我在 Imagemagick 中的做法(Unix 语法)
输入:
# get image dimensions and aspect ratio
WxH=`convert barn.jpg -format "%wx%h" info:`
ww=`echo "$WxH" | cut -dx -f1`
hh=`echo "$WxH" | cut -dx -f2`
aspect=`convert xc: -format "%[fx:$ww/$hh]" info:`
# define centering point
px=200
py=200
# compute distances to each side of the image
dleft=$px
dright=$((ww-px-1))
dtop=$py
dbottom=$((hh-py-1))
# find the minimum distance and to which side
dminx=`convert xc: -format "%[fx:round(min($dleft,$dright))]" info:`
dminy=`convert xc: -format "%[fx:round(min($dtop,$dbottom))]" info:`
dmin=`convert xc: -format "%[fx:round(min($dminx,$dminy))]" info:`
# compute crop width, height and scale percent
if [ $dmin -eq $dminx ]; then
wd=$((2*dminx))
ht=`convert xc: -format "%[fx:round($wd/$aspect)]" info:`
scale=`convert xc: -format "%[fx:100*$ww/$wd]" info:`
else
ht=$((2*dminy))
wd=`convert xc: -format "%[fx:round($ht*$aspect)]" info:`
scale=`convert xc: -format "%[fx:100*$hh/$ht]" info:`
fi
# compute x and y offsets to the top left corner of crop region
xoff=`convert xc: -format "%[fx:round($px-$wd/2)]" info:`
yoff=`convert xc: -format "%[fx:round($py-$ht/2)]" info:`
# crop and resize the image to original dimensions
convert barn.jpg -crop ${wd}x${ht}+${xoff}+${yoff} +repage -resize $scale% barn_result.jpg
结果:
如果您的中心点太靠近图像的一侧,则它可能不支持这些尺寸或纵横比。在这些情况下你想做什么?图像应该小于您想要的尺寸还是用黑色或透明填充?还是应该保持想要的大小,想要的中心不在中心?
这是一个 ImageMagick 解决方案,假设我现在了解您想要什么,它将裁剪所需的尺寸和方面。如果中心太靠近一侧,输出尺寸仍将符合要求,但所需的点将不在中心。我已经用红色小圆圈标记了相对于输入坐标的所需点。
再一次,Unix语法。
输入:
期望点在 200,200:
cd
cd desktop
infile="barn.jpg"
inname=`convert "$infile" -format "%t" info:`
# get image dimensions and aspect ratio
WxH=`convert "$infile" -format "%wx%h" info:`
ww=`echo "$WxH" | cut -dx -f1`
hh=`echo "$WxH" | cut -dx -f2`
# define centering point
px=50
py=50
# define crop size
wd=295
ht=195
# check if enough space on each side
left=`convert xc: -format "%[fx:round($px-$wd/2)]" info:`
left=`convert xc: -format "%[fx:$left<0?0:$left]" info:`
right=`convert xc: -format "%[fx:$left+$wd-1]" info:`
right=`convert xc: -format "%[fx:$right>$ww-1?$ww-1:$right]" info:`
top=`convert xc: -format "%[fx:round($py-$ht/2)]" info:`
top=`convert xc: -format "%[fx:$top<0?0:$top]" info:`
bottom=`convert xc: -format "%[fx:$top+$ht-1]" info:`
bottom=`convert xc: -format "%[fx:$bottom>$hh-1?$hh-1:$bottom]" info:`
# compute crop values
xoff=$left
yoff=$top
width=`convert xc: -format "%[fx:round($right-$left+1)]" info:`
height=`convert xc: -format "%[fx:round($bottom-$top+1)]" info:`
echo "$left; $right; $top; $bottom;"
echo "$xoff; $yoff; $width; $height"
# crop and resize the image to original dimensions
convert "$infile" -fill red -draw "translate $px,$py circle 0,0 0,2" -alpha off \
-crop ${width}x${height}+${xoff}+${yoff} +repage ${inname}_${px}_${py}.jpg
50,50 处的所需点:
这是
除了实际大小计算之外,这工作得很好。我想我的数学有问题,因为如果图像按比例缩小比我计算的多,结果输出会丢失一些可能可见的部分。我找不到我正在做的错误,也许有人可以给我一个正确方向的提示。
我现在在做什么:
# 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 ' '
结果命令是:
convert input.jpg -set option:distort:viewport 295x195 -distort SRT '1405.000000128,484.0 0.242 0 147.5,97.5' output.jpg
根据我的理解,imagemagick 的扭曲 SRT 的第二个参数(此处为 scale_factor
)定义了图像必须按比例缩小多少(以 % 为单位)。我的想法是计算所有边到焦点的距离,取最小的距离,将其转换为合适的参数,并使用该参数来定义缩放量。
目标是尽可能地缩放图像以确保输出图像不会裁剪太多,以尽可能多地显示原始图像。我现在的输出中断太多了。
为了更好地理解,我尝试将其可视化:
原始图片可以在这里找到:https://unsplash.com/photos/p-I9wV811qk
我做错了什么?如何计算正确缩小图像以尽可能多地显示图片的值?
编辑 1
最后,将生成图像的多个变体,具有不同的纵横比。在给定的 295x195 旁边,另一个将是 1440x560;尤其是这张,简单的"crop with gravity"很可能会裁剪掉图片的中上部,从而确保要害部分始终可见的焦点。
编辑 2 到 fmw42 的答案
到目前为止,该代码可以在不使用 SRT 的情况下调整大小和裁剪。不幸的是,与我目前的结果相比,结果还差得远(但也许我在改编脚本时做错了什么)。
我做的是
- 使用我问题中的图片
- 用我的问题中的焦点值替换
px
和py
- 保留
scale
计算,但使用wd=295
和ht=195
,因为这是所需的目标作物
结果输出如下所示:
这是我在 Imagemagick 中的做法(Unix 语法)
输入:
# get image dimensions and aspect ratio
WxH=`convert barn.jpg -format "%wx%h" info:`
ww=`echo "$WxH" | cut -dx -f1`
hh=`echo "$WxH" | cut -dx -f2`
aspect=`convert xc: -format "%[fx:$ww/$hh]" info:`
# define centering point
px=200
py=200
# compute distances to each side of the image
dleft=$px
dright=$((ww-px-1))
dtop=$py
dbottom=$((hh-py-1))
# find the minimum distance and to which side
dminx=`convert xc: -format "%[fx:round(min($dleft,$dright))]" info:`
dminy=`convert xc: -format "%[fx:round(min($dtop,$dbottom))]" info:`
dmin=`convert xc: -format "%[fx:round(min($dminx,$dminy))]" info:`
# compute crop width, height and scale percent
if [ $dmin -eq $dminx ]; then
wd=$((2*dminx))
ht=`convert xc: -format "%[fx:round($wd/$aspect)]" info:`
scale=`convert xc: -format "%[fx:100*$ww/$wd]" info:`
else
ht=$((2*dminy))
wd=`convert xc: -format "%[fx:round($ht*$aspect)]" info:`
scale=`convert xc: -format "%[fx:100*$hh/$ht]" info:`
fi
# compute x and y offsets to the top left corner of crop region
xoff=`convert xc: -format "%[fx:round($px-$wd/2)]" info:`
yoff=`convert xc: -format "%[fx:round($py-$ht/2)]" info:`
# crop and resize the image to original dimensions
convert barn.jpg -crop ${wd}x${ht}+${xoff}+${yoff} +repage -resize $scale% barn_result.jpg
结果:
如果您的中心点太靠近图像的一侧,则它可能不支持这些尺寸或纵横比。在这些情况下你想做什么?图像应该小于您想要的尺寸还是用黑色或透明填充?还是应该保持想要的大小,想要的中心不在中心?
这是一个 ImageMagick 解决方案,假设我现在了解您想要什么,它将裁剪所需的尺寸和方面。如果中心太靠近一侧,输出尺寸仍将符合要求,但所需的点将不在中心。我已经用红色小圆圈标记了相对于输入坐标的所需点。
再一次,Unix语法。
输入:
期望点在 200,200:
cd
cd desktop
infile="barn.jpg"
inname=`convert "$infile" -format "%t" info:`
# get image dimensions and aspect ratio
WxH=`convert "$infile" -format "%wx%h" info:`
ww=`echo "$WxH" | cut -dx -f1`
hh=`echo "$WxH" | cut -dx -f2`
# define centering point
px=50
py=50
# define crop size
wd=295
ht=195
# check if enough space on each side
left=`convert xc: -format "%[fx:round($px-$wd/2)]" info:`
left=`convert xc: -format "%[fx:$left<0?0:$left]" info:`
right=`convert xc: -format "%[fx:$left+$wd-1]" info:`
right=`convert xc: -format "%[fx:$right>$ww-1?$ww-1:$right]" info:`
top=`convert xc: -format "%[fx:round($py-$ht/2)]" info:`
top=`convert xc: -format "%[fx:$top<0?0:$top]" info:`
bottom=`convert xc: -format "%[fx:$top+$ht-1]" info:`
bottom=`convert xc: -format "%[fx:$bottom>$hh-1?$hh-1:$bottom]" info:`
# compute crop values
xoff=$left
yoff=$top
width=`convert xc: -format "%[fx:round($right-$left+1)]" info:`
height=`convert xc: -format "%[fx:round($bottom-$top+1)]" info:`
echo "$left; $right; $top; $bottom;"
echo "$xoff; $yoff; $width; $height"
# crop and resize the image to original dimensions
convert "$infile" -fill red -draw "translate $px,$py circle 0,0 0,2" -alpha off \
-crop ${width}x${height}+${xoff}+${yoff} +repage ${inname}_${px}_${py}.jpg
50,50 处的所需点: