Crop Flip Normalize Reformat

Overview

The Crop Flip Normalize Reformat sample demonstrates how to combine four common image preprocessing steps — spatial cropping, horizontal or vertical flipping, per-channel normalization, and layout reformatting (HWC → NCHW) — into a single GPU kernel call using CV-CUDA’s crop_flip_normalize_reformat operator. This fused pipeline is typical in deep-learning inference pipelines where images must be cropped to a region of interest, augmented with a flip, and then normalized and reformatted before being fed to a model.

Usage

Basic Usage

Run the pipeline on the default tabby-cat image:

python3 crop_flip_normalize_reformat.py -i input.jpg

Custom Input / Output

Specify your own input image and save the result to a custom path:

python3 crop_flip_normalize_reformat.py -i image.jpg -o cat_crop_flip_normalize_reformat.jpg

Command-Line Arguments

Argument

Short Form

Default

Description

--input

-i

tabby_tiger_cat.jpg

Input image file path

--output

-o

cvcuda/.cache/cat_crop_flip_normalize_reformat.jpg

Output image file path

Implementation

Crop, Flip, Normalize, and Reformat

# crop_flip_normalize_reformat operates on an ImageBatchVarShape, so
# wrap the single HWC tensor into a one-image batch.
height, width, channels = input_image.shape
cvcuda_image = cvcuda.as_image(input_image.cuda(), cvcuda.Format.RGB8)
batch = cvcuda.ImageBatchVarShape(1)
batch.pushback([cvcuda_image])

# Define a crop rectangle [crop_x, crop_y, crop_width, crop_height] per image.
# We crop the central 80% of the image so the result still looks good.
crop_x = int(width * 0.1)
crop_y = int(height * 0.1)
crop_w = int(width * 0.8)
crop_h = int(height * 0.8)
crop_data = np.array([[[[crop_x, crop_y, crop_w, crop_h]]]], dtype=np.int32)
crop_rect_host = np.ascontiguousarray(crop_data)

# Allocate crop rect tensor on GPU (shape: [N, 1, 1, 4], layout NHWC)
crop_tensor = cvcuda.Tensor((1, 1, 1, 4), np.int32, "NHWC")
upload_tensor(crop_rect_host, crop_tensor)

# flip_code per image: 1 = flip around y-axis (horizontal flip)
flip_data = np.array([[1]], dtype=np.int32)
flip_host = np.ascontiguousarray(flip_data)
flip_tensor = cvcuda.Tensor((1, 1), np.int32, "NC")
upload_tensor(flip_host, flip_tensor)

# Normalization parameters: base (mean) and scale (std-dev) per channel.
# Using ImageNet-style mean and std for demonstration.
base_data = np.array([[[[0.485, 0.456, 0.406]]]], dtype=np.float32)
scale_data = np.array([[[[0.229, 0.224, 0.225]]]], dtype=np.float32)
base_host = np.ascontiguousarray(base_data)
scale_host = np.ascontiguousarray(scale_data)

base_tensor = cvcuda.Tensor((1, 1, 1, 3), np.float32, "NHWC")
scale_tensor = cvcuda.Tensor((1, 1, 1, 3), np.float32, "NHWC")
upload_tensor(base_host, base_tensor)
upload_tensor(scale_host, scale_tensor)

# The output will be in NCHW layout (planar), float32, at the cropped size.
out_shape = (1, channels, crop_h, crop_w)

Key points:

  1. Fused kernel: All four operations (crop, flip, normalize, reformat) run in a single GPU kernel, avoiding intermediate allocations and memory-bandwidth overhead.

  2. ImageBatchVarShape input: The operator requires images wrapped in a cvcuda.ImageBatchVarShape, which supports batches of varying-size images.

  3. Crop rectangle tensor: The rect tensor has shape [N, 1, 1, 4] with [crop_x, crop_y, crop_width, crop_height] stored per image in the last dimension.

  4. SCALE_IS_STDDEV flag: When cvcuda.NormalizeFlags.SCALE_IS_STDDEV is set the scale argument is interpreted as per-channel standard deviation, matching the common (pixel/255 - mean) / std convention used by PyTorch models.

  5. NCHW output layout: Passing out_layout="NCHW" reformats the data from the interleaved HWC format that the camera/decoder produces into the planar CHW format expected by most deep-learning frameworks — no separate reformat call is required.

Expected Output

The output shows the central 80% of the image, flipped horizontally, with pixel values de-normalized back to uint8 for display:

../../_images/tabby_tiger_cat.jpg

Original Input Image

../../_images/cat_crop_flip_normalize_reformat.jpg

Output: Cropped, Flipped, Normalized (visualized as uint8)

CV-CUDA Operators Used

Operator

Purpose

cvcuda.crop_flip_normalize_reformat()

Crop a region of interest, optionally flip, normalize per channel, and reformat the layout in a single fused GPU kernel

Common Utilities Used

  • read_image() - Load image as CV-CUDA tensor

  • write_image() - Save the result image

  • cuda_memcpy_h2d - Upload crop-rect, flip-code, and normalization parameter arrays to GPU

  • cuda_memcpy_d2h - Download the float32 result to CPU for inverse-normalization

See Also