Restoring Degraded Face Photos with AI: What Works, What Doesn't, and Why
Old scanned photos. Blurry screenshots from video calls. Low-resolution profile pictures. Compressed JPEGs that have been resaved thirty times.
Face restoration AI handles all of these — but the results vary wildly depending on the input. Here's the honest guide.
How GFPGAN Actually Works
GFPGAN (Generative Facial Prior GAN) uses a pre-trained face GAN as a prior — essentially a high-quality face generator that gets anchored to your degraded input. Instead of just upsampling pixels, it regenerates the face with realistic texture while staying faithful to the identity.
The key advantage over simple upscaling: GFPGAN can add plausible detail that doesn't exist in the original. Skin texture, eye clarity, hair strands — these get reconstructed from the model's learned prior, not copied from surrounding pixels.
The key limitation: it's a GAN. It can sometimes "correct" features that look wrong to it but are actually correct — unusual eye shapes, distinctive facial features, extreme expressions.
Results You Can Expect
| Input Type | Result |
|---|---|
| Blurry selfie (face >200px) | Excellent — sharp, natural |
| Old scanned B&W photo | Very good — works across eras |
| Video screenshot (low res) | Good — major improvement |
| Face <100px in frame | Moderate — upscale first |
| Heavily compressed JPEG | Good — removes artifacts |
| Extreme angles/occlusion | Inconsistent |
Code
import requests
def restore_face(image_url: str, api_key: str) -> str:
response = requests.post(
"https://api.pixelapi.dev/v1/edit",
headers={"Authorization": f"Bearer {api_key}"},
json={"operation": "restore", "image_url": image_url}
)
return response.json()["output_url"]
The Two-Step Pattern for Tiny Faces
For faces that are very small in the original image (headshots cropped from group photos, faces in crowd scenes):
def enhance_small_face(image_url: str, api_key: str) -> str:
# Step 1: Upscale the entire image 4x with Real-ESRGAN
upscaled = requests.post(
"https://api.pixelapi.dev/v1/edit",
headers={"Authorization": f"Bearer {api_key}"},
json={"operation": "upscale", "image_url": image_url, "scale": 4}
).json()["output_url"]
# Step 2: Now faces are large enough for GFPGAN to work well
restored = requests.post(
"https://api.pixelapi.dev/v1/edit",
headers={"Authorization": f"Bearer {api_key}"},
json={"operation": "restore", "image_url": upscaled}
).json()["output_url"]
return restored
Total cost: 75 credits (50 upscale + 25 restore). At Starter plan rates, that's a fraction of a cent.
Building a Photo Restoration App
from flask import Flask, request, jsonify
import requests
app = Flask(__name__)
PIXELAPI_KEY = "your_key"
@app.route("/restore", methods=["POST"])
def restore():
data = request.json
image_url = data["image_url"]
# Detect if face is too small (basic heuristic)
# In production, use a face detection library
needs_upscale = data.get("small_face", False)
if needs_upscale:
result = enhance_small_face(image_url, PIXELAPI_KEY)
else:
result = restore_face(image_url, PIXELAPI_KEY)
return jsonify({"restored_url": result})
Comparing Alternatives
- Remini (consumer app): excellent quality but no API, per-image fee, data stored
- VanceAI: has an API, but significantly more expensive per operation
- CodeFormer: newer model, sometimes better on extreme degradation — PixelAPI will be adding this
- Adobe Neural Filters: desktop-only, requires Creative Cloud subscription
The main advantage of an API approach: you can integrate it directly into your own product flow. Your users never leave your app, and you control the data.
Practical Applications
- Genealogy apps (restoring old family photos)
- Memorial services (preserving degraded photographs)
- Professional headshot enhancement
- Dating/social app profile photo improvement
- Legal: enhancing surveillance or incident footage (check local regulations)
Getting Started
100 free credits at pixelapi.dev. That covers 4 restore operations or 1 upscale+restore combo — enough to test on your real inputs.
GFPGAN v1.4 runs on PixelAPI's RTX GPU cluster. Typical inference: 3-6 seconds per image.
Top comments (0)