Implement a Batch Image Processor
AnthropicSoftware Engineer · Technical ScreenLast updated
Design and implement an in-memory image transformation engine that applies sequential operations (grayscale, brightness scaling, nearest-neighbor resizing) over a batch of images efficiently.
Problem Statement
You are tasked with building a lightweight, CPU-based batch image processing pipeline without external libraries like OpenCV or PIL. An image is represented as a 3D array of integers of shape (height, width, 3), where each pixel contains [R, G, B] values bounded by [0, 255].
Implement the class BatchImageProcessor with methods for individual operations and a batch runner:
to_grayscale(image): Converts an RGB image into grayscale. To keep array shapes consistent along the pipeline, output an image with 3 identical channels:[Y, Y, Y], where luminance is calculated as:Y = int(0.299 * R + 0.587 * G + 0.114 * B)adjust_brightness(image, factor): Multiplies every RGB value by a floatfactorand clamps the resulting integer to[0, 255].resize(image, new_h, new_w): Resizes an image using nearest-neighbor interpolation. The pixel at target coordinate(r, c)maps to the source coordinate(int(r * orig_h / new_h), int(c * orig_w / new_w)).process_batch(images, pipeline, max_workers): Applies a list of transformations sequentially to each image inimages. For CPU-bound workloads, leverage parallel execution acrossmax_workers.
Function Signatures
class BatchImageProcessor:
@staticmethod
def to_grayscale(image: list[list[list[int]]]) -> list[list[list[int]]]:
pass
@staticmethod
def adjust_brightness(image: list[list[list[int]]], factor: float) -> list[list[list[int]]]:
pass
@staticmethod
def resize(image: list[list[list[int]]], new_h: int, new_w: int) -> list[list[list[int]]]:
pass
def process_batch(
self,
images: list[list[list[list[int]]]],
pipeline: list[tuple],
max_workers: int = 1
) -> list[list[list[list[int]]]]:
pass
Example
processor = BatchImageProcessor()
# Single 2x2 RGB image
img = [
[[100, 150, 200], [50, 50, 50]],
[[0, 255, 0], [255, 255, 255]]
]
# Brightness scaled by 1.5
scaled = processor.adjust_brightness(img, 1.5)
# Top-left pixel: [100 * 1.5, 150 * 1.5, 200 * 1.5] = [150, 225, 255] (clamped)
# Resize to 1x1
resized = processor.resize(img, 1, 1)
# Maps to source (0, 0) -> [[[100, 150, 200]]]
Constraints
- Image dimensions:
1 = 0.0 new_h, new_w >= 1- Pipeline operations are passed as tuples, e.g.,
("brightness", 1.2)or("grayscale",)
Hints
Hint 1
Ensure each pixel transformation clamps values strictly to [0, 255] before integer conversion.
Hint 2
For nearest-neighbor resizing, avoid floating point rounding errors by computing the source index using integer division on `r * orig_h // new_h`.
Hint 3
Because image processing is strictly CPU-bound in Python, using `concurrent.futures.ProcessPoolExecutor` bypasses the GIL, whereas `ThreadPoolExecutor` will serialize execution.
Follow-up questions
- Passing large raw pixel buffers between processes incurs significant IPC serialization overhead. How would you mitigate this using shared memory (e.g., `multiprocessing.shared_memory`)?
- How would you implement operator fusion (combining grayscale and brightness in a single pass) to improve CPU cache locality?
- How does nearest-neighbor interpolation compare to bilinear interpolation in terms of algorithmic complexity and visual artifacts like aliasing?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.