Asset Management Automation
Efficient Digital Asset Management is a critical cost factor in e-commerce and digital media production. This project implements a high-performance pipeline for automated image processing using the BiRefNet model. The system is designed to standardize and remove backgrounds from large volumes of visual assets without manual intervention.
By leveraging GPU acceleration and state-of-the-art segmentation technology, this solution replaces time-consuming manual editing processes, drastically reduces time-to-market for new products, and lowers operational costs in content creation.

Business Impact & Use Cases
This automation solution addresses key bottlenecks in visual workflows:
- E-Commerce Automation: Instant, standardized preparation of product catalogs through batch processing.
- Cost Reduction: Eliminating repetitive manual tasks (background removal) frees up focus for high-value creative work.
- Data Preparation for AI: Automated cleaning and preparation of image datasets for computer vision training pipelines.
- High Scalability: Robust processing of thousands of assets per hour with consistent quality, regardless of subject complexity.
Technical Approach
The script is based on the BiRefNet model (ZhengPeng7/BiRefNet), a state-of-the-art neural network for image segmentation specifically trained for precise object delineation. The implementation uses PyTorch and Hugging Face Transformers for model inference.
1. GPU Optimization for Modern Hardware
Special attention was paid to supporting the latest NVIDIA GPUs such as the RTX 50 series. To avoid compatibility issues with CUDA kernels, I set special environment variables and PyTorch configurations:
os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'expandable_segments:True'
os.environ['TORCH_USE_CUDA_DSA'] = '1'
torch.backends.cuda.matmul.allow_tf32 = True
torch.backends.cudnn.allow_tf32 = True
These settings allow the model to fully utilize the tensor cores of modern GPUs while avoiding memory fragmentation. The script automatically detects available hardware and displays details such as GPU name, CUDA version, and compute capability.
2. Image Preprocessing and Normalization
Before an image is processed by the model, it goes through a standardized preprocessing pipeline:
transform = transforms.Compose([
transforms.Resize((1024, 1024)),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
Normalization with ImageNet standard values ensures the model delivers optimal results, since it was trained on similarly normalized data. The resize operation to 1024×1024 pixels ensures consistent input sizes for the network.
3. Precise Mask Generation and Post-Processing
The heart of the processing is mask generation. The BiRefNet model produces a pixel-accurate segmentation mask indicating which pixels belong to the foreground:
with torch.no_grad():
preds = model(input_tensor)[-1].sigmoid().cpu()
pred_mask = preds[0].squeeze()
pred_mask_np = pred_mask.numpy()
The mask is then upscaled to the original image size (using LANCZOS interpolation for best quality) and added as an alpha channel to the image. The result is a transparent PNG with a perfectly extracted object.
4. Robust Error Handling and Fallback Mechanisms
A special feature is the automatic CPU fallback on CUDA errors. If GPU processing of an image fails (e.g., due to memory shortage), the script automatically switches to CPU processing for that single image:
except Exception as e:
if "CUDA" in str(e) and device.type == 'cuda':
print(f"\nRetrying {filename} on CPU...")
torch.cuda.empty_cache()
model.to('cpu')
result_image = remove_background(input_path, model, torch.device('cpu'))
model.to(device)
This mechanism ensures that batch processing does not completely abort due to problematic images, but continues robustly.
Batch Processing with Progress Display
The script uses tqdm for detailed progress display during processing. It iterates over all images in an input folder and automatically saves the extracted versions as PNG files in the output folder:
for filename in tqdm(image_files, desc="Removing backgrounds"):
try:
input_path = os.path.join(input_folder, filename)
result_image = remove_background(input_path, model, device)
output_filename = Path(filename).stem + ".png"
output_path = os.path.join(output_folder, output_filename)
result_image.save(output_path, "PNG")
successful += 1
except Exception as e:
print(f"\nError processing {filename}: {str(e)}")
failed += 1
At the end of processing, the script outputs a clear summary showing successful and failed operations.
Technologies Used
- Language: Python 3
- Deep Learning Framework: PyTorch with CUDA support
- Model: BiRefNet (
ZhengPeng7/BiRefNet) from Hugging Face - Image Processing: PIL (Pillow), torchvision transforms
- API Access: Hugging Face Transformers with token authentication
- Progress Display: tqdm
- Configuration: python-dotenv for environment variables
Security Aspects and Best Practices
The script follows modern best practices for machine learning applications:
- Token Security: The Hugging Face API token is loaded via a
.envfile, never hardcoded. - Memory Management: Automatic GPU cache clearing (
torch.cuda.empty_cache()) on errors. - Evaluation Mode: The model is set to evaluation mode with
model.eval()to disable dropout and batch normalization. - Transparency Preservation: All outputs are saved as PNG to preserve the alpha channel.
The Complete Script
import os
from pathlib import Path
from PIL import Image
import torch
from transformers import AutoModelForImageSegmentation
from torchvision import transforms
import numpy as np
from tqdm import tqdm
from dotenv import load_dotenv
# Force PyTorch to recompile CUDA kernels for RTX 50-series
os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'expandable_segments:True'
os.environ['TORCH_COMPILE_DEBUG'] = '0'
os.environ['CUDA_LAUNCH_BLOCKING'] = '1'
os.environ['TORCH_USE_CUDA_DSA'] = '1'
# Load environment variables
load_dotenv()
# Configuration
INPUT_FOLDER = r"C:\Users\grisc\Desktop\py\shoes\training\boots"
OUTPUT_FOLDER = r"C:\Users\grisc\Desktop\py\shoes\training\boots_no_bg"
MODEL_NAME = "ZhengPeng7/BiRefNet"
HF_TOKEN = os.getenv("HF_TOKEN")
# Image transformation
image_size = (1024, 1024)
def load_model():
"""Load the BiRefNet model."""
print(f"Loading model: {MODEL_NAME}")
if not HF_TOKEN:
raise ValueError("HF_TOKEN not found in .env file. Please add your Hugging Face token.")
# Configure CUDA for RTX 50-series
if torch.cuda.is_available():
# Use memory efficient settings
torch.backends.cudnn.benchmark = False
torch.backends.cuda.matmul.allow_tf32 = True
torch.backends.cudnn.allow_tf32 = True
model = AutoModelForImageSegmentation.from_pretrained(
MODEL_NAME,
trust_remote_code=True,
token=HF_TOKEN
)
# Use GPU if available
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
if torch.cuda.is_available():
print(f"✓ Using GPU: {torch.cuda.get_device_name(0)} (CUDA {torch.version.cuda})")
print(f" Compute capability: sm_{torch.cuda.get_device_capability(0)[0]}{torch.cuda.get_device_capability(0)[1]}")
else:
print("⚠ GPU not available, using CPU")
model.to(device)
model.eval()
print(f"Model loaded on: {device}")
return model, device
def preprocess_image(image):
"""Preprocess image for the model."""
# Resize and normalize
transform = transforms.Compose([
transforms.Resize(image_size),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
return transform(image).unsqueeze(0)
def remove_background(image_path, model, device):
"""Remove background from a single image."""
# Load image
original_image = Image.open(image_path).convert("RGB")
original_size = original_image.size
# Preprocess
input_tensor = preprocess_image(original_image).to(device)
# Generate mask
with torch.no_grad():
if device.type == 'cuda':
# Try running without autocast (float32) to avoid kernel issues
preds = model(input_tensor)[-1].sigmoid().cpu()
else:
preds = model(input_tensor)[-1].sigmoid().cpu()
# Post-process mask
pred_mask = preds[0].squeeze()
pred_mask_np = pred_mask.numpy()
# Resize mask to original image size
mask_pil = Image.fromarray((pred_mask_np * 255).astype(np.uint8))
mask_pil = mask_pil.resize(original_size, Image.LANCZOS)
# Apply mask to original image
original_image.putalpha(mask_pil)
return original_image
def process_folder(input_folder, output_folder, model, device):
"""Process all images in a folder."""
# Create output folder
os.makedirs(output_folder, exist_ok=True)
# Get all image files
image_extensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp']
image_files = []
for file in os.listdir(input_folder):
if any(file.lower().endswith(ext) for ext in image_extensions):
image_files.append(file)
print(f"\nFound {len(image_files)} images to process")
print(f"Output folder: {output_folder}\n")
# Process images with progress bar
successful = 0
failed = 0
for filename in tqdm(image_files, desc="Removing backgrounds"):
try:
input_path = os.path.join(input_folder, filename)
# Remove background
result_image = remove_background(input_path, model, device)
# Save as PNG (to preserve transparency)
output_filename = Path(filename).stem + ".png"
output_path = os.path.join(output_folder, output_filename)
result_image.save(output_path, "PNG")
successful += 1
except Exception as e:
if "CUDA" in str(e) and device.type == 'cuda':
print(f"\nCUDA error for {filename}: {str(e)}")
print(f"Retrying {filename} on CPU...")
try:
torch.cuda.empty_cache()
model.to('cpu')
result_image = remove_background(input_path, model, torch.device('cpu'))
# Save as PNG
output_filename = Path(filename).stem + ".png"
output_path = os.path.join(output_folder, output_filename)
result_image.save(output_path, "PNG")
successful += 1
# Move model back to GPU
model.to(device)
continue
except Exception as cpu_e:
print(f"Failed on CPU retry: {str(cpu_e)}")
# Ensure model is back on GPU
model.to(device)
print(f"\nError processing {filename}: {str(e)}")
failed += 1
return successful, failed
def main():
"""Main function."""
print("="*60)
print("BACKGROUND REMOVAL - BiRefNet")
print("="*60)
print(f"Input folder: {INPUT_FOLDER}")
print(f"Output folder: {OUTPUT_FOLDER}")
print("="*60)
# Check if CUDA is available
if torch.cuda.is_available():
print(f"GPU: {torch.cuda.get_device_name(0)}")
else:
print("GPU: Not available, using CPU")
# Load model
model, device = load_model()
# Process images
successful, failed = process_folder(INPUT_FOLDER, OUTPUT_FOLDER, model, device)
# Summary
print("\n" + "="*60)
print("PROCESSING COMPLETE")
print("="*60)
print(f"✓ Successful: {successful}")
print(f"✗ Failed: {failed}")
print(f"Total: {successful + failed}")
print("="*60)
if __name__ == "__main__":
main()
Possible Extensions
- Web Interface: Integration into a Flask or FastAPI web application for browser-based uploads.
- Batch API: RESTful API endpoint for integration into automated workflows.
- Quality Control: Automatic evaluation of mask quality and flagging of problematic images.
- Multi-Model Support: Comparison of different segmentation models and selection of the best result.
- Cloud Deployment: Scaling processing on AWS Lambda or Google Cloud Functions for serverless batch jobs.