FastAPI service for uploading images and applying basic transformations (filter, resize, rotate, flip, watermark). Images are cached in Redis and metadata is stored in PostgreSQL via SQLAlchemy.
Background removal is supported only for people and produces visible artifacts. Quality is not comparable to top-tier tools yet, but it will improve.
- FastAPI
- Redis
- PostgreSQL + SQLAlchemy
- Pillow
- Install dependencies:
pip install -r requirements.txt
- Copy
.env.exampleto.envand update values. - Provide environment variables (see below).
- Run the API:
uvicorn app.main:app --host 0.0.0.0 --port 8000
- Added domain-level exceptions with a centralized FastAPI handler.
- Hardened upload validation (size cap and file signature checks).
- Fixed Redis cleanup for image deletion and improved not-found responses.
- Added async upload tests with mocked Redis and HTTPX client fixtures.
- Modernized SQLAlchemy base declaration and refreshed dependencies.
DATABASE_URL(required)SECRET_KEY(required)REDIS_URL(optional, defaultredis://localhost:6379)MODEL_PATH(optional, absolute path to.pthweights)MODEL_URL(optional, download URL for.pthweights)
In production, weights are typically stored outside the repo (S3/GCS/Artifacts)
and downloaded at deploy time. For small projects you can use Git LFS, but avoid
committing large .pth files directly to git.
Recommended approach:
- Keep weights in a
models/directory ignored by git. - Provide
MODEL_PATHto use a local file. - Or provide
MODEL_URLand download the file at startup or via a setup script. A GitHub Release asset URL or S3/GCS URL works well here.
The background removal model is a U-Net style decoder on top of a ResNet-101 encoder. It uses ResNet-101 blocks for feature extraction and a multi-stage decoder with upsampling, skip connections, and Conv/BN/ReLU blocks to produce a 1-channel mask.
Implementation details:
- Encoder:
torchvision.models.resnet101layers up tolayer4. - Decoder: stacked
DecoderBlockstages with bilinear upsample and skip concatenation. - Head:
1x1conv to get a single-channel logits mask. - Inference: logits are resized to input size and passed through
sigmoidto get the mask.
Weights are loaded in app/models_unet/model_arch.py from MODEL_PATH or downloaded
from MODEL_URL (or a local app/models_unet/resnet101_unet.pth if present). If
weights are missing, the remove-bg endpoint returns an error.
- Architecture: U-Net with ResNet-101 Backbone.
- Training Hardware: Kaggle P100 GPU.
- Training Duration: 10 Epochs.
- Key Strengths: High-fidelity edge detection, especially in complex areas like facial hair, headwear, and fine textures.
| Input vs. Output Segmentation |
|---|
![]() |
| *Left: Original Image |
Tip
You may notice a slight halo around complex contours (hair, cap edges).The model performs semantic segmentation (classification of pixels as "person/background") rather than Alpha Matting (calculation of edge transparency).
Photo by X-Outcast via Unsplash.
POST /images/uploadPOST /images/filterPOST /images/resizePOST /images/rotatePOST /images/flipPOST /images/watermarkDELETE /images/{image_id}
For this project, I opted for a Pragmatic Monolithic approach for the core logic instead of over-engineering with Service Layers:
- For a single-developer project, keeping the flow within the endpoint enhances readability and speed of iteration.
- While this results in a "thicker" controller, the logic is clearly decoupled internally using Python's asyncio primitives.
- The processing logic is designed to be easily extractable into a standalone ImageService or a Background Worker (like Celery) if the project grows.
