Batch Image Resize and Rotate with Pillow: Exact Dimensions, Parallelism, Bad Files
Company: Anthropic
Role: Software Engineer
Category: Software Engineering Fundamentals
Difficulty: medium
Interview Round: Onsite
You are building a batch image processor in Python using the Python Imaging Library (Pillow, imported as `PIL`). Given a directory of input images, the processor applies geometric transformations, starting with resizing and rotating, and writes the results to an output directory. The interview grows in three stages: get the transformations exactly right on one image, make the batch fast with parallelism, and make the batch robust to problem files.
### Clarifying Questions
- When resizing to a target size, must the aspect ratio be preserved (fit inside a box), or is the image stretched to exactly the target width and height?
- May an image be enlarged, or only shrunk?
- Rotation by what angles: multiples of 90 degrees only, or arbitrary angles? Should the canvas grow to fit the rotated image, or keep its original size and crop?
- Should the orientation stored in a photo's EXIF metadata be honored before transforming?
- What output format and quality should be written, and must the output order match the input order?
- Roughly how many images, how large, and how many CPU cores are available?
### Part 1 — Resize and rotate one image, and report its exact dimensions
Write a function that opens one image, resizes it for a given target (for example, fitting a 4000 by 3000 photo into an 800 by 800 box), rotates it by a given angle, saves it, and prints the final dimensions. Before running anything, state exactly what your code will print for that photo after the resize, and again after a 90-degree rotation, and justify the numbers from the API calls you chose. Be precise about which number is the width and which is the height, and about how the same image's dimensions appear once it is converted to a NumPy array.
```hint Read the API contract, not your intuition
Check what each Pillow call guarantees about the output size: which calls preserve the aspect ratio, which ones change the canvas, and in what order `size` reports its two numbers.
```
#### What This Part Should Cover
- Correct choice between aspect-preserving and exact-size resizing, with the resulting dimensions stated before running the code
- Rotation semantics: direction, whether the canvas expands, and the effect on the reported size
- Width and height ordering in Pillow compared with array shapes
- Rounding of non-integer target dimensions, and resampling filter choice
### Part 2 — Process a large batch in parallel
The directory now holds tens of thousands of images. First make the batch use all CPU cores with multiprocessing. Then improve it by treating the work as a pipeline of stages (reading and decoding, transforming, encoding and writing) that overlap, and explain when the pipeline beats the simple parallel version.
```hint Find the bottleneck stage
Estimate how the time for one image divides between file I/O, decoding, transforming and encoding, then ask which of those stages parallel workers speed up and which they merely contend over.
```
#### What This Part Should Cover
- A correct multiprocessing version, including what is sent to workers and how results come back
- A pipeline design with bounded queues between stages, and how backpressure limits memory
- Choosing worker counts per stage from where the time actually goes
- Measuring speedup and explaining why it falls short of the core count
### Part 3 — Handle problem files
Some inputs are not well-formed images: files with an image extension that are not images, truncated or corrupted files, images far larger than expected, and images whose color mode cannot be saved in the output format. Make the batch finish anyway, and report clearly which files failed and why.
```hint Where failures surface
Consider at which call each kind of bad file actually raises an error in Pillow, and what happens to a pool of worker processes if one of them dies instead of raising.
```
#### What This Part Should Cover
- Per-file error isolation so one bad file never aborts or stalls the batch
- The specific failure kinds and where each one surfaces
- Guarding memory against oversized images, and surviving worker crashes or hangs
- A useful failure report, and whether and how to retry
### What a Strong Answer Covers
- Dimensions predicted before running, and matching what the code prints
- A clean progression from a correct single-image function to a parallel, then pipelined, batch
- Measured or clearly reasoned performance claims tied to the CPU and I/O profile
- Robustness: bad files, memory limits, worker failures, partial output and idempotent reruns
- Readable code with the transformation parameters passed in rather than hard-coded
### Follow-up Questions
- The input images live in cloud object storage instead of a local directory. How does the pipeline change?
- How would you make a rerun skip images that were already processed correctly, without reprocessing everything?
- Several transformations are chained per image. How do you avoid losing quality by encoding and decoding between steps?
- How would you process a single image too large to fit in memory?
Overview: A three-part Python image processing exercise: resize and rotate images with Pillow while predicting the exact printed dimensions, speed up a large batch with multiprocessing and a staged pipeline, then keep the batch running through corrupt, oversized and unsupported files. It tests API precision, parallelism and robustness.