Blog
Copyright © 2021 Jiri Kriz, www.nosco.ch

Resize images with Image Magick and Python

Comments (0)
Cropping and resizing of many images with Image Magick and Python.

Introduction

We often need to change the size of our photographs such that they suit the requirements. If only a few few images are considered then a manual resize is appropriate e.g. by using GIMP or Picasa or one of many available on line tools. If many images are affected a batch processing is adequate, e.g. using Image Magick and Python.
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

caesar Source 3000x2000
black Target 1920x1080

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.

caesar_x Padding width
 
caesar_t Crop height
Keep top
caesar_m Crop height
Keep center
caesar_b Crop height
Keep bottom
The above results are achieved by the following Image Magic commands:
$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

capitol_4000x1600 Source 4000x1600
black Target 1920x1080

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.

capitol_x Padding height
 
capitol_l Crop width
Keep left
capitol_m Crop width
Keep center
capitol_r Crop width
Keep right
The above results are achieved by the following Image Magic commands:
$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)

Comments

New Comment