Manipulating Many Images at Once Using Photoshop, GIMP, ImageMagick, PIL and OpenCV: ImageMagick (Part 4 of 6)

This is part 4 of 6 of a mini-series of posts that discuss how to manipulate many images at once (instead of individually) using a variety of techniques. Please refer to part 1 of 6 for more information.

This post explores how to batch process images with Python using ImageMagick via the Python Wand library. We will resize and apply the sepia tone effect to a group of photos. For resizing, each image is scaled to three (3) different sizes, 75%, 50%, 25%, of the original. The sepia tone effect is achieved with image desaturation and blending using the algorithm discussed in the introductory post.

All code on this page was developed with & tested against Python 3.5 and Wand 0.4.2.

Batch Processing with Python Using ImageMagick

Batch processing is accomplished by creating a function to select the jpg image files in an input directory, transforming each input image and saving the results to an output directory.

For resizing, each input image is scaled 75%, 50% and 25%, and the three (3) output files are saved. When the sepia tone effect is applied, the result is saved to the specified output directory.

We will be using Wand, a ctypes-based simple ImageMagick binding for Python.

Resizing Images

We will resize images based on size percentage of the original image and not only that, but we will create three different sizes (75%, 50%, 25%) as well.

Train BridgesOriginal Image Scaled to 75%, 50% and 25%

Resizing One Image to Several Sizes

The following Python code snippet scales an input image into three (3) different sizes, 75%, 50% & 25%, and saves them into the output directory. This can be tested by saving the full script and changing the batchInputImageDir and batchOutputImageDir to valid input & output directories on your system.

resizePercentages = [75, 50, 25]    # Percentages to by which to resize input images
 
with Image(filename=batchInImageFN) as img:
 
       # Resize image by given percentages
       for resizePercentage in resizePercentages:
 
           batchOutImageFN = os.path.join(batchOutputImageDir, imageName + "_" + str(resizePercentage) + ".jpg")
           ResizeImageByPercentAndSave(img, resizePercentage, batchOutImageFN)
 
 
# *****
# ResizeImageByPercentAndSave
#
# Description: Resizes image by specified percentage and saves the result
#
# Parameters:
#    inImage : An image opened using Wand
#    inResizePercentage : Percentage by which to resize image as a non negative integer
#    inResizedImageFN : Output path and file name where the result is saved
# *****
def ResizeImageByPercentAndSave(inImage, inResizePercentage, inResizedImageFN):
 
    with inImage.clone() as imgClone:
 
        resizeHeight = int(inResizePercentage * imgClone.height / 100)
        resizeWidth = int(inResizePercentage * imgClone.width / 100)
 
        imgClone.resize(resizeWidth, resizeHeight)
 
        imgClone.save(filename=inResizedImageFN)

Resizing Multiple Images to Several Sizes

To resize multiple images at once, we include a function to find & process all jpg image files in the input directory as illustrated in the code snippet below. This can be tested by saving the full script and changing the batchInputImageDir and batchOutputImageDir to valid input & output directories on your system.

batchInputImageDir = os.path.join("..","images","in")       # Input directory where jpg files reside
batchOutputImageDir = os.path.join("..","images","out")     # Output directory where results are saves as jpg image files
resizePercentages = [75, 50, 25]    # Percentages to by which to resize input images
 
# Iterate through all jpgs in the input directory 
for jpgFile in os.listdir(batchInputImageDir):
 
    if jpgFile.endswith(".jpg"):    # Process jpg files only
 
        # Determine full path and filename
        imageName, imageExt = os.path.splitext(jpgFile)
        batchInImageFN = os.path.join(batchInputImageDir, jpgFile)
 
        # Open the input image to process
        with Image(filename=batchInImageFN) as img:
 
            # Resize image by given percentages
            for resizePercentage in resizePercentages:
 
                batchOutImageFN = os.path.join(batchOutputImageDir, imageName + "_" + str(resizePercentage) + ".jpg")
                ResizeImageByPercentAndSave(img, resizePercentage, batchOutImageFN)

Applying the Sepia Tone Effect

Applying the sepia tone effect to an image will give it an old, antique-ish photograph look. As a recap of the discussion of what the sepia tone effect algorithm entails, here is a list of steps to achieve it:

  1. Desaturate the image. The resulting image is greyscale.
  2. Apply a mild gaussian blur since most old photos are slightly blurry.
  3. Add a reddish brown (or dark orange) solid color layer above the photo layer, using the soft light blend mode.

train bridges sepia tone effectSepia Tone Effect Applied to Original Image

ImageMagick actually has a built-in function for creating sepia toned images called MagickSepiaToneImage. But for consistency, we will use the same algorithm for all approaches in this series.

For more information regarding the sepia tone effect algorithm, please refer to the introductory post.

Applying the Sepia Tone Effect to One Image

The following Python code snippet applies the sepia tone effect to an input image. Soft light blending is implemented with the composite_channel function & its operator parameter specified as ‘soft_light’.

This can be tested by saving the full script and changing the batchInputImageDir and batchOutputImageDir to valid input & output directories on your system.

# Apply the sepia tone effect
batchOutImageFN = os.path.join(batchOutputImageDir, imageName + "_sepia.jpg")
SepiaToneEffectAndSave(img, batchOutImageFN)
 
 
# SepiaToneEffectAndSave
#
# Description: Applies sepia tone effect to input image and saves the result
#
# Parameters:
#    inImage : An image opened using Wand
#    inSepiaImageFN : Output path and file name where the result is saved
# *****
def SepiaToneEffectAndSave(inImage, inSepiaImageFN):
 
    colorStr = '#e2592a'    # Sepia tone effect color
 
    # Apply the effect on a copy of the input image
    with inImage.clone() as imgClone:
 
        # Convert image to greyscale
        imgClone.type = 'grayscale'
 
        # Apply a slight blur
        imgClone.gaussian_blur(0,1)
 
        # Blend the sepia tone color with the greyscale layer using soft light
        fillColor = Color(colorStr)
        with Image(width=img.width, height=img.height, background=fillColor) as fillImg:
 
            imgClone.composite_channel('default_channels', fillImg, 'soft_light', 0, 0 )
 
        imgClone.save(filename=inSepiaImageFN)

Apply the Sepia Tone Effect to Multiple Images

To apply the sepia tone effect to multiple images at once, we include a function to find & process all jpg image files in the input directory as illustrated in the code snippet below. This can be tested by saving the full script and changing the batchInputImageDir and batchOutputImageDir to valid input & output directories on your system.

batchInputImageDir = os.path.join("..","images","in")       # Input directory where jpg files reside
batchOutputImageDir = os.path.join("..","images","out")     # Output directory where results are saves as jpg image files
 
# Iterate through all jpgs in the input directory 
for jpgFile in os.listdir(batchInputImageDir):
 
    if jpgFile.endswith(".jpg"):    # Process jpg files only
 
        # Determine full path and filename
        imageName, imageExt = os.path.splitext(jpgFile)
        batchInImageFN = os.path.join(batchInputImageDir, jpgFile)
 
        # Open the input image to process
        with Image(filename=batchInImageFN) as img:
 
            # Apply the sepia tone effect
            batchOutImageFN = os.path.join(batchOutputImageDir, imageName + "_sepia.jpg")
            SepiaToneEffectAndSave(img, batchOutImageFN)

Coming Up …

Our mini-series exploring image batch processing will continue with PIL (Python Imaging Library).

Further Reading

  1. Wand: A ctypes-based ImageMagick binding for ImageMagick
  2. ImageMagick website

 

Leave a Reply

Your email address will not be published. Required fields are marked *