# Vintage Photo Restorer (`muhammadafzal/vintage-photo-restorer`) Actor

Restore faded and scratched vintage photos locally with denoising, dust repair, contrast recovery, optional sepia neutralization, and downloadable image files.

- **URL**: https://apify.com/muhammadafzal/vintage-photo-restorer.md
- **Developed by:** [Muhammad Afzal](https://apify.com/muhammadafzal) (community)
- **Categories:** AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $80.00 / 1,000 restored photos

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Vintage Photo Restorer

Restore faded, noisy, scratched, or sepia-toned vintage photos in bulk without supplying a third-party AI API key.

Vintage Photo Restorer downloads up to 20 public JPG, PNG, or WebP images, applies conservative local restoration, saves the restored files and optional before/after previews to the run key-value store, and writes one structured dataset item per successful photo. It is useful for family-history archives, museums, digitization teams, estate collections, and content production workflows.

**Apify Free plan limit:** Free-plan users can restore **1 photo per run**. Paid-plan users retain the 20-photo limit. If a Free-plan run includes multiple URLs, the Actor exits gracefully with a clear `REJECTED` status before downloading or processing any photo.

### What the Actor produces

| Field | Meaning |
|---|---|
| `restoredImageUrl` | Download URL for the restored JPG, PNG, or WebP |
| `comparisonImageUrl` | Optional side-by-side before/after JPEG |
| `originalWidth`, `originalHeight` | Decoded source dimensions |
| `outputWidth`, `outputHeight` | Restored dimensions after the optional size cap |
| `operations` | Exact restoration stages applied |
| `scratchPixelsRepaired` | Conservative inpainting-mask size |
| `processingTimeMs` | End-to-end processing time for the photo |
| `sha256` | Output checksum for archive workflows |

The `OUTPUT` key contains the run classification (`DATA`, `BLOCKED`, `REJECTED`, or `FAILED`), requested/delivered/failed counts, warnings, and result links. Diagnostics are never inserted into the homogeneous result dataset.

### Restoration pipeline

The Actor uses deterministic OpenCV processing inside its container:

1. Validates public network access and safely downloads the image with a 20 MB limit.
2. Decodes JPG, PNG, or WebP and rejects images above 36 megapixels.
3. Optionally scales down the longest side; it never enlarges a source.
4. Conservatively detects thin high-contrast dust and scratch candidates and inpaints them.
5. Optionally neutralizes a yellow/brown cast with bounded gray-world balance.
6. Applies strength-dependent non-local denoising, adaptive contrast recovery, and unsharp masking.
7. Encodes the restored image and optional side-by-side preview.

This Actor performs classical restoration, not generative reconstruction. It does not hallucinate faces, add missing objects, or AI-colorize black-and-white photos.

### Input

| Field | Default | Notes |
|---|---:|---|
| `imageUrls` | Public sample photo | 1–20 public HTTP(S) JPG, PNG, or WebP URLs (Free plan: 1 URL per run) |
| `restorationLevel` | `balanced` | `gentle`, `balanced`, or `strong` |
| `repairScratches` | `true` | Disable for text, drawings, or line art |
| `neutralizeSepia` | `false` | Reduces color cast; does not colorize |
| `outputFormat` | `jpeg` | `jpeg`, `png`, or `webp` |
| `quality` | `92` | JPEG/WebP quality from 60–100 |
| `maxDimension` | `3000` | Longest side from 512–6000 px; no upscaling |
| `createComparison` | `true` | Saves a side-by-side JPEG |
| `maxConcurrency` | `2` | 1–4 parallel photos |

Example input:

```json
{
  "imageUrls": [
    "https://upload.wikimedia.org/wikipedia/commons/d/d3/Albert_Einstein_Head.jpg"
  ],
  "restorationLevel": "balanced",
  "repairScratches": true,
  "neutralizeSepia": false,
  "outputFormat": "jpeg",
  "quality": 92,
  "maxDimension": 3000,
  "createComparison": true,
  "maxConcurrency": 2
}
```

Run it with the Apify API by POSTing this object to the Actor run endpoint. After completion, read the default dataset for metadata and the default key-value store for image files. The output schema exposes direct collection links to both.

### Output example

```json
{
  "sourceUrl": "https://example.com/archive/family-photo.jpg",
  "restoredImageUrl": "https://api.apify.com/v2/key-value-stores/abc/records/restored-001.jpg",
  "comparisonImageUrl": "https://api.apify.com/v2/key-value-stores/abc/records/comparison-001.jpg",
  "restoredImageKey": "restored-001.jpg",
  "comparisonImageKey": "comparison-001.jpg",
  "originalWidth": 1200,
  "originalHeight": 1600,
  "outputWidth": 1200,
  "outputHeight": 1600,
  "inputBytes": 284521,
  "outputBytes": 331208,
  "outputFormat": "jpeg",
  "restorationLevel": "balanced",
  "operations": ["scratchRepair", "denoise", "contrastRecovery", "sharpen"],
  "scratchPixelsRepaired": 1421,
  "processingTimeMs": 1834,
  "sha256": "3a7bd3e2360a3d29eea436fcfb7e44c735f..."
}
```

### Pay-per-event pricing

| Event | Price |
|---|---:|
| Actor start | $0.0001 per run |
| Restored photo | $0.08 per successfully delivered dataset item |

A one-photo run costs $0.0801 in configured events. A 10-photo run in which all photos succeed costs $0.8001. Failed downloads and rejected images do not create dataset items and therefore do not create restored-photo events. Apify's run spending limit is honored by the SDK; processing stops cleanly when the event limit is reached.

### Free-plan usage

Apify provides the Actor with the plan status of the user who started each run. When that status is Free, the server-side guard accepts only one photo URL per run; submitting multiple URLs is rejected before any image is downloaded. This protects the shared restoration/API budget and cannot be bypassed by changing input fields. Paid users keep the normal 1–20 photo range.

### Reliability, privacy, and limits

- Only public HTTP(S) URLs are accepted. Localhost, private, reserved, and link-local destinations are blocked to prevent server-side request forgery.
- Redirects are revalidated and limited. Downloads are capped at 20 MB and decoded images at 36 megapixels.
- Source photos and restored files remain in the run's Apify storage according to the user's storage retention settings. This deployed build performs deterministic OpenCV restoration locally and sends no photo pixels to OpenRouter or any other outside AI provider.
- Valid photos already delivered are preserved if another URL fails. Per-photo warnings and run counts appear in `OUTPUT`.
- OpenCV encoding does not preserve EXIF/IPTC metadata or alpha transparency. Download the original separately if archival metadata must be retained.
- Scratch detection is intentionally conservative, but any automated inpainting can affect real fine lines. Use `gentle` or disable `repairScratches` for documents, handwriting, drawings, and line art.
- The Actor does not repair torn-away regions, reconstruct faces, upscale resolution, or colorize monochrome photos.

Only process images you own or are authorized to transform. Review photos containing sensitive personal information before sharing run storage or exports.

### Support

For an unexpected result, include the run ID, input settings, source format and dimensions, and whether the issue affects the restored file, comparison, dataset record, or billing. Do not post private source-photo URLs in public support messages.

# Actor input Schema

## `imageUrls` (type: `array`):

Use this for publicly reachable JPG, PNG, or WebP photos. Enter 1–20 HTTPS URLs (Apify Free plan: 1 URL per run), for example https://upload.wikimedia.org/wikipedia/commons/3/3a/Cat03.jpg. Private networks, data URLs, SVG, GIF, and authenticated files are not supported.

## `restorationLevel` (type: `string`):

Use this to control denoising, local contrast, and sharpening together. Choose gentle for fragile detail, balanced for most scans, or strong for heavily degraded photos; the default is balanced.

## `repairScratches` (type: `boolean`):

Use this to detect and inpaint small high-contrast scratches and dust spots. Enabled by default; disable it for line art, text-heavy scans, or photos where fine lines must remain untouched.

## `neutralizeSepia` (type: `boolean`):

Use this to reduce yellow or brown color casts with conservative gray-world balancing. Disabled by default because it changes the historical tone; it does not AI-colorize black-and-white photos.

## `outputFormat` (type: `string`):

Use this to choose the restored file format. JPEG is compact, PNG is lossless, and WebP is compact with high quality; the default is JPEG.

## `quality` (type: `integer`):

Use this for JPEG or WebP encoding quality from 60 to 100. The default is 92; PNG ignores this value.

## `maxDimension` (type: `integer`):

Use this to cap the longest image side in pixels before restoration. Enter 512–6000; the default is 3000 and smaller photos are never enlarged.

## `createComparison` (type: `boolean`):

Use this to save a side-by-side JPEG preview beside each restored photo. Enabled by default; disable it to reduce storage and processing time.

## `maxConcurrency` (type: `integer`):

Use this to process multiple photos at once. Enter 1–4; the default is 2. Lower it for very large scans or 512 MB runs.

## Actor input object example

```json
{
  "imageUrls": [
    "https://upload.wikimedia.org/wikipedia/commons/d/d3/Albert_Einstein_Head.jpg"
  ],
  "restorationLevel": "balanced",
  "repairScratches": true,
  "neutralizeSepia": false,
  "outputFormat": "jpeg",
  "quality": 92,
  "maxDimension": 3000,
  "createComparison": true,
  "maxConcurrency": 2
}
```

# Actor output Schema

## `results` (type: `string`):

Schema-validated metadata and download URLs for every successfully restored photo.

## `restoredImages` (type: `string`):

Key-value-store records containing restored photos.

## `comparisons` (type: `string`):

Key-value-store records containing comparison previews.

## `summary` (type: `string`):

Counts, warnings, and links for this run.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "imageUrls": [
        "https://upload.wikimedia.org/wikipedia/commons/d/d3/Albert_Einstein_Head.jpg"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("muhammadafzal/vintage-photo-restorer").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "imageUrls": ["https://upload.wikimedia.org/wikipedia/commons/d/d3/Albert_Einstein_Head.jpg"] }

# Run the Actor and wait for it to finish
run = client.actor("muhammadafzal/vintage-photo-restorer").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "imageUrls": [
    "https://upload.wikimedia.org/wikipedia/commons/d/d3/Albert_Einstein_Head.jpg"
  ]
}' |
apify call muhammadafzal/vintage-photo-restorer --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,muhammadafzal/vintage-photo-restorer"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/kr7CnK9EwMNcb2bI1/builds/Mo956hbWQLQM8G4qE/openapi.json
