Resize images with Image Magick and Python
Introduction
For convenience I define the shell variable IM in Cygwin:
export IM="/cygdrive/c/Programs/ImageMagick-7.0.10-Q16-HDRI/magick.exe"
Example 1: image too narrow
The aspect ratio of the source is a = 3000/2000 = 1.5. The aspect ratio of the target is A = 1920/1080 = 16/9 = 1.78. Since a < A we must either pad the width or cut the height to get a = A.
Keep top
Keep center
Keep bottom
$IM caesar.jpg -resize 1920x -gravity South -crop 1920x1080+0+0 caesar_b.jpg
$IM caesar.jpg -resize 1920x -gravity Center -crop 1920x1080+0+0 caesar_m.jpg
$IM caesar.jpg -resize 1920x -gravity North -crop 1920x1080+0+0 caesar_t.jpg
$IM caesar.jpg -resize x1080 -background Black -compose Copy -gravity Center -extent 1920x1080+0+0 caesar_x.jpg
I perform these 4 transformations and select manually the picture with the most pleasing appearance. In the example any image is good except crop/keep bottom.
Example 2: image too wide
The aspect ratio of the source is a = 4000x1600 = 2.5. The aspect ratio of the target is A = 1920/1080 = 16/9 = 1.78. Since a > A we must either pad the height or cut the width to get a = A.
Keep left
Keep center
Keep right
$IM img/capitol.jpg -resize x1080 -gravity West -crop 1920x1080+0+0 capitol_l.jpg
$IM img/capitol.jpg -resize x1080 -gravity Center -crop 1920x1080+0+0 capitol_m.jpg
$IM img/capitol.jpg -resize x1080 -gravity East -crop 1920x1080+0+0 capitol_r.jpg
$IM img/capitol.jpg -resize 1920x -background Black -compose Copy -gravity Center -extent 1920x1080+0+0 capitol_x.jpg
I perform these 4 transformations and select manually the picture with the most pleasing appearance. In the example I would chose the image with padding height.
General case
Algorithm:
w x h = width x height of the source image
rot = True if the orientation of the source image is one of "BottomRight", "RightTop", "LeftBottom",
if rot: orient source image properly and swap w and h
a = w / h = aspect ratio of the source image
W x H = Width x Height of the target image
A = W / H = aspect ratio of the source image
if a < A:
$IM src.jpg -resize Wx -gravity South -crop WxH+0+0 dst_b.jpg
$IM src.jpg -resize Wx -gravity Center -crop WxH+0+0 dst_m.jpg
$IM src.jpg -resize Wx -gravity North -crop WxH+0+00 dst_t.jpg
$IM src.jpg -resize xH -background Black -compose Copy -gravity Center -extent WxH+0+0 dst_x.jpg
else if a > A
$IM src.jpg -resize xH -gravity West -crop WxH+0+0 dst_l.jpg
$IM src.jpg -resize xH -gravity Center -crop WxH+0+0 dst-m.jpg
$IM src.jpg -resize xH -gravity East -crop WxH+0+0 dst_r.jpg
$IM src.jpg -resize Wx -background Black -compose Copy -gravity Center -extent WxH+0+0 dst_x.jpg
else:
$IM src.jpg -resize WxH dst.jpg
This algorithm is applied to all images that should be resized. Then the best looking images are manually selected.
Python program
All images that should be transformed are copied into a directory. An empty target directory is provided. The code below generates for each image in the source directory four transformed images in the destination directory. The best images are then selected manually.
def execCmd(cmd):
stream = os.popen(cmd)
output = stream.read()
return output
def getProperties(imgFile):
cmd = "$IM identify -format '%w %h %[orientation]' " + imgFile
output = execCmd(cmd)
parts = output.split(' ')
w = int(parts[0])
h = int(parts[1])
orientation = parts[2].strip()
rotate = orientation == "BottomRight" or orientation == "RightTop" or orientation == "LeftBottom"
if rotate:
(w, h) = (h, w)
return (w, h, rotate)
def transformDir(srcDir, dstDir):
files = os.listdir(srcDir)
for file in files:
isImg = file.lower().endswith(('.png', '.jpg', '.jpeg', '.tiff', '.bmp', '.gif'))
if isImg:
srcFile = os.path.basename(file)
dstFile = dstDir + "/" + srcFile
transformFile(srcFile, dstFile)
else:
print("WARNING: not an image file " + file)
def transformFile(srcFile, dstFile):
(dstBaseName, dstExtension) = os.path.splitext(dstFile)
(src_w, src_h, src_rotate) = getProperties(srcFile)
src_aspect = src_w / src_h
dst_aspect = DST_W / DST_H
crop_rect = str(DST_W) + "x" + str(DST_H) + "+0+0 "
crop = "-crop " + crop_rect
cmd_resize = "$IM " + srcFile
if src_rotate:
cmd_resize += " -auto-orient "
cmd_resize += " -resize "
eps = 0.01
if src_aspect > dst_aspect + eps:
cmd = cmd_resize + "x" + str(DST_H) + " -gravity West " + crop
cmd += dstBaseName + "_l" + dstExtension
execCmd(cmd)
cmd = cmd_resize + "x" + str(DST_H) + " -gravity Center " + crop
cmd += dstBaseName + "_m" + dstExtension
execCmd(cmd)
cmd = cmd_resize + "x" + str(DST_H) + " -gravity East " + crop
cmd += dstBaseName + "_r" + dstExtension
execCmd(cmd)
cmd = cmd_resize + str(DST_W) + "x -background Black -compose Copy -gravity Center -extent " + crop_rect
cmd += dstBaseName + "_x" + dstExtension
execCmd(cmd)
elif src_aspect < dst_aspect - eps:
cmd = cmd_resize + str(DST_W) + "x -gravity South " + crop
cmd += dstBaseName + "_b" + dstExtension
execCmd(cmd)
cmd = cmd_resize + str(DST_W) + "x -gravity Center " + crop
cmd += dstBaseName + "_m" + dstExtension
execCmd(cmd)
cmd = cmd_resize + str(DST_W) + "x -gravity North " + crop
cmd += dstBaseName + "_t" + dstExtension
execCmd(cmd)
cmd = cmd_resize + "x" + str(DST_H) + " -background Black -compose Copy -gravity Center -extent " + crop_rect
cmd += dstBaseName + "_x" + dstExtension
execCmd(cmd)
else:
if SRC_W == DST_W:
print("Image has the requested size")
else:
if LOG: print("Scaling only")
cmd = cmd_resize + crop_rect
cmd += dstBaseName + "_top" + dstExtension
execCmd(cmd)
transformDir(srcDir, dstDir)
New Comment