Skip to main content
The Stitcher library provides two powerful blending algorithms to merge overlapping images seamlessly: multiband blending and feather blending. Each method has distinct characteristics and use cases.

Blending Types

The library supports two blending modes defined by the BlenderType enum:
blending.h
typedef enum {
    MULTIBAND,
    FEATHER
} BlenderType;

Multiband Blending

Multiband blending uses Laplacian and Gaussian pyramids to blend images at multiple frequency bands, producing the highest quality results with minimal visible seams.

How It Works

1

Build Image Pyramids

The algorithm creates a pyramid of progressively downsampled versions of each input image. The number of pyramid levels is determined by:
blending.c
double max_len = (double)(out_size.width > out_size.height ? 
                          out_size.width : out_size.height);
blender->num_bands = min(blender->num_bands, 
                        (int)ceil(log(max_len) / log(2.0)));
This ensures the pyramid has enough levels to cover the entire image while respecting the MAX_BANDS limit of 7.
2

Compute Laplacian Pyramids

For each pyramid level, the algorithm computes the Laplacian by upsampling the next level and subtracting it from the current level:
blending.c
void compute_laplacian(ImageS *original, ImageS *upsampled) {
    for (int i = 0; i < total_size; ++i) {
        upsampled->data[i] = original->data[i] - upsampled->data[i];
    }
}
This captures the high-frequency details at each scale.
3

Create Gaussian Mask Pyramids

Masks are downsampled to create a Gaussian pyramid that matches the image pyramid structure. This allows smooth transitions between images at each frequency band.
4

Blend Each Level

At each pyramid level, images are blended using their corresponding mask weights:
blending.c
float maskVal = mask_gaussian[level].data[maskIndex] * (1.0 / 255.0);
float imgVal = img_laplacians[level].data[imgIndex] * maskVal;
out[level].data[outLevelIndex] += imgVal;
5

Reconstruct Final Image

The blended Laplacian pyramid is collapsed by progressively upsampling and adding each level:
blending.c
for (int level = num_bands; level > 0; --level) {
    blended_image = upsample_image_s(&blended_image, 4.f);
    blended_image.data[i] = blended_image.data[i] + out_level.data[i];
}

When to Use Multiband Blending

Best For

  • High-quality panoramas
  • Images with varying exposure
  • Complex texture boundaries
  • Professional photography workflows

Trade-offs

  • Higher computational cost
  • More memory usage
  • Requires more processing time
  • Best with 5-7 bands

Creating a Multiband Blender

Example Usage
// Create output canvas
StitchRect out_size = {0, 0, 2000, 1000};
int num_bands = 5;  // Typically 5-7 bands

// Create multiband blender
Blender *blender = create_blender(MULTIBAND, out_size, num_bands);

// Feed images with masks
StitchPoint pt1 = {0, 0};
feed(blender, &img1, &mask1, pt1);

StitchPoint pt2 = {500, 0};
feed(blender, &img2, &mask2, pt2);

// Perform blending
blend(blender);

// Access result
if (blender->result.data != NULL) {
    save_image(&blender->result, "output.jpg");
}

destroy_blender(blender);

Feather Blending

Feather blending is a simpler, faster technique that performs weighted averaging based on mask values. It’s ideal for real-time applications or when computational resources are limited.

How It Works

Feather blending directly weights pixel values by their mask values and accumulates them:
blending.c
float weight = mask_img->data[mask_pos] / 255.0f;
out_mask->data[dst_mask] += weight;

for (int channel = 0; channel < RGB_CHANNELS; channel++) {
    out->data[dst_idx + channel] += img->data[image_pos + channel] * weight;
}
The final pixel values are normalized by the accumulated weights:
blending.c
final_out[level].data[imgIndex] = 
    (short)(out[level].data[imgIndex] / (w + WEIGHT_EPS));
The WEIGHT_EPS constant (1e-5f) prevents division by zero in areas with no image coverage.

When to Use Feather Blending

Best For

  • Real-time stitching
  • Mobile applications
  • Video stitching
  • Memory-constrained environments

Trade-offs

  • Visible seams possible
  • Less effective with exposure differences
  • Requires careful mask design
  • May show ghosting artifacts

Creating a Feather Blender

Example Usage
StitchRect out_size = {0, 0, 2000, 1000};

// Create feather blender (num_bands ignored)
Blender *blender = create_blender(FEATHER, out_size, -1);

// Optionally enable distance transform for better masks
blender->do_distance_transform = 1;

// Feed images
StitchPoint pt1 = {0, 0};
feed(blender, &img1, &mask1, pt1);

StitchPoint pt2 = {500, 0};
feed(blender, &img2, &mask2, pt2);

// Blend
blend(blender);

save_image(&blender->result, "output.jpg");
destroy_blender(blender);

Blender Structure

The Blender struct manages the blending state:
blending.h
typedef struct {
    int num_bands;              // Number of pyramid levels (multiband only)
    StitchRect output_size;     // Padded output dimensions
    StitchRect real_out_size;   // Actual output dimensions
    int *out_width_levels;      // Width at each pyramid level
    int *out_height_levels;     // Height at each pyramid level
    ImageF *out;                // Accumulated float images
    ImageF *out_mask;           // Accumulated mask weights
    ImageS *final_out;          // Final normalized output
    Image result;               // Final 8-bit result
    ImageS *img_laplacians;     // Laplacian pyramid (multiband)
    ImageS *mask_gaussian;      // Gaussian mask pyramid (multiband)
    BlenderType blender_type;   // MULTIBAND or FEATHER
    float sharpness;            // Sharpness parameter (feather)
    int do_distance_transform;  // Enable distance transform
} Blender;

API Reference

Core Functions

create_blender
function
Blender *create_blender(BlenderType blender_type, StitchRect out_size, int nb);
Creates a new blender instance.Parameters:
  • blender_type: MULTIBAND or FEATHER
  • out_size: Output canvas dimensions
  • nb: Number of bands (multiband only, ignored for feather)
Returns: Pointer to blender or NULL on failure
feed
function
int feed(Blender *b, Image *img, Image *mask_img, StitchPoint tl);
Feeds an image and its mask into the blender at a specified position.Parameters:
  • b: Blender instance
  • img: Input image (RGB)
  • mask_img: Grayscale mask (same dimensions as img)
  • tl: Top-left position in output canvas
Returns: 1 on success, 0 on failure
Image and mask must have identical dimensions (asserted).
blend
function
void blend(Blender *b);
Performs the blending operation. After calling, the result is available in b->result.
destroy_blender
function
void destroy_blender(Blender *blender);
Frees all memory associated with the blender.

Performance Considerations

Multiband: Requires memory for multiple pyramid levels. For an image of size W × H:
Memory ≈ W × H × channels × (1 + 1/2 + 1/4 + ... + 1/2^n) × sizeof(type)
Feather: Only requires memory for the output canvas, approximately:
Memory ≈ W × H × channels × (sizeof(float) + sizeof(short) + sizeof(unsigned char))
  • Multiband: O(n × w × h × bands) where n is number of images
  • Feather: O(n × w × h)
  • Both use multi-threading with pthread for parallel processing
Use multiband blending when quality is paramount and processing time is acceptable. Use feather blending for interactive applications or when processing many images in sequence.

Next Steps

Image Types

Learn about Image, ImageF, and ImageS types used in blending

Masks

Understand how to create and use masks for optimal blending

Build docs developers (and LLMs) love