1

I have thousands of images in a folder and I need to crop each image(3840x2160) into 9 crops (1280x720) with the fastest way possible. The cropped images should preserve all the details of the original image as it may contain small objects of interest. I found that image_slicer does the job, but not sure about its speed.

from image_slicer import slice
slice('cake.png', 9)

I also looked at PIL (image_slicer uses it), Imagemagick and some other similar libraries, but was not able to evaluate their speed. Maybe ffmpeg has some line command to achieve my goal.

Eventually, I need a Windows executable file where a user inserts a full path to images and gets cropped images for each image in the new folder.

bit_scientist
  • 1,496
  • 2
  • 15
  • 34

3 Answers3

2

Imagemagick can do that with the tile mode for cropping. But you would have to write a bat script to loop over all the files that you want to process. For any given image

convert.exe image.suffix -crop 1280x720 +repage +adjoin newimage_%d.suffix

The +repage removes any virtual canvas that might be supported by the suffix.

The +adjoin ensure that each image is written as a new file for suffix types that support multiple pages in the same file.

Imagemagick is not the fastest processor around.

See https://imagemagick.org/Usage/crop/#crop_tile

fmw42
  • 46,825
  • 10
  • 62
  • 80
1

Given that:

  • 2GHz processors have been available for around 20 years and only around 3GHz is "mainstream" today, and
  • quad-core and up to 16-core processors are fairly common nowadays

it would seem processors are becoming "fatter" (more cores) rather than "taller" (more GHz). So you would probably do well to leverage that.

Given that Python has a GIL, it is not so good for multi-threading, so you would probably do better to use multi-processing instead of multi-threading and allow each core to work independently on an image to minimise the amount of pickling and sharing data between processes.

You didn't mention the format or dimensions of your images. If they are JPEGs, you might consider using turbo-jpeg. If they are very large, memory may be an issue with multiprocessing.

The likely candidates are probably the following:

  • OpenCV
  • vips
  • Pillow
  • ImageMagick/wand

but it will depend on many things:

  • CPU - GHz, cores, generation
  • RAM - amount, speed, channels, timings
  • disk subsystem - spinning, SSD, NVMe
  • image format, dimensions, bit-depth

So you'll need to benchmark. I did some similar benchmarking here.


If you want to replace the process_file() in John's answer with a PIL or OpenCV version, it might look like this:

import pathlib
import numpy as np
from PIL import Image
import cv2

def withPIL(filename):
    pathlib.Path(f"out/{filename}_tiles").mkdir(parents=True, exist_ok=True)
    image = Image.open(filename)
    for y in range(3):
        for x in range(3):
            top, left = y*720, x*1280
            tile = image.crop((left,top,left+1280,top+720))
            tile.save(f"out/{filename}_tiles/{x}_{y}.png")

def withOpenCV(filename):
    pathlib.Path(f"out/{filename}_tiles").mkdir(parents=True, exist_ok=True)
    image = cv2.imread(filename,cv2.IMREAD_COLOR)
    for y in range(3):
        for x in range(3):
            top, left = y*720, x*1280
            tile = image[top:top+720, left:left+1280]
            cv2.imwrite(f"out/{filename}_tiles/{x}_{y}.png",tile)
Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
1

As Mark says, you need to make a benchmark and test on your system.

I tried here with pyvips:

import sys
from multiprocessing import Pool
import pyvips
import os

def process_file(filename):
    os.mkdir(f"out/{filename}_tiles")
    image = pyvips.Image.new_from_file(filename)
    for y in range(3):
        for x in range(3):
            tile = image.crop(x * 1280, y * 720, 1280, 720)
            tile.write_to_file(f"out/{filename}_tiles/{x}_{y}.png")

with Pool(32) as p:
    p.map(process_file, sys.argv[1:])

I made 100 test PNGs:

$ vips crop ~/pics/wtc.jpg uhd.png 0 0 3840 2160
$ for i in {1..100}; do cp uhd.png test_$i.png; done

And ran the program like this:

$ mkdir out
$ /usr/bin/time -f %M:%e ../crop9.py test_*.png
206240:5.45

So about 5.5s and 200mb of ram on my PC.

PNG is a very slow format. It'll be several times quicker if you use something like JPG.

jcupitt
  • 10,213
  • 2
  • 23
  • 39