# Photo Album Splitter API – Printed Photo Extractor (`peroxo/photo-album-splitter`) Actor

Detect and crop individual printed photos from album page scans or camera shots. Returns perspective-corrected image files, metadata, and optional ZIP output.

- **URL**: https://apify.com/peroxo/photo-album-splitter.md
- **Developed by:** [Michael Beetz](https://apify.com/peroxo) (community)
- **Categories:** Automation, Developer tools, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$20.00 / 1,000 extracted photos

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use 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

## Photo Album Splitter API – Printed Photo Extractor

Extract individual printed photos from album page scans or camera shots. The Actor detects separate print rectangles, corrects perspective, optionally trims light paper borders, and returns each photo as a downloadable image with structured metadata.

### What it is useful for

Use this Actor when one scan or phone photo contains several printed photos and you want separate files automatically.

Common workflows:

- Splitting old family album pages into individual image files
- Preparing genealogy or archive scans for cataloging
- Cropping several printed photos from one flatbed scanner image
- Processing camera shots of prints laid out on a table
- Preparing images before restoration, colorization, or upscaling tools
- Building automated digitization pipelines through the Apify API

### How it works

1. The Actor downloads each URL from `imageUrls`.
2. It detects likely printed-photo rectangles using edge and surface detection.
3. It removes obvious page/background false positives.
4. It perspective-crops every detected photo.
5. If enabled, it trims neutral/off-white print borders.
6. It saves every crop as a separate image file.
7. It writes metadata rows to the dataset and optionally creates a ZIP archive.

### Input example

Try the public demo image below or replace the URL with your own album page scan or camera shot:

Demo input image:

```text
https://api.apify.com/v2/key-value-stores/2qL0GBPUDleSGv96Q/records/synthetic-vintage-album-page.png
````

```json
{
  "imageUrls": ["https://api.apify.com/v2/key-value-stores/2qL0GBPUDleSGv96Q/records/synthetic-vintage-album-page.png"],
  "sensitivity": 0.55,
  "minAreaFraction": 0.008,
  "maxImages": 20,
  "maxCropsPerImage": 30,
  "trimBorders": true,
  "createZip": true,
  "outputFormat": "jpg"
}
```

### Input fields

| Field | Default | Description |
| --- | ---: | --- |
| `imageUrls` | required | Public URLs of album page scans or camera shots containing one or more printed photos. |
| `sensitivity` | `0.55` | Higher values detect weaker borders; lower values are stricter. Try `0.45–0.7` for most images. |
| `minAreaFraction` | `0.008` | Smallest accepted photo area as a fraction of the source image. Increase this to ignore tiny false positives. |
| `maxImages` | `20` | Maximum number of source images to process in one run. |
| `maxCropsPerImage` | `30` | Maximum number of crops exported from each source image. |
| `trimBorders` | `true` | Try to remove off-white print borders after perspective cropping. |
| `createZip` | `true` | Create `extracted_photos.zip` with all exported crops. |
| `outputFormat` | `jpg` | Output crop format: `jpg` or `png`. |

### Output

Each extracted photo is saved in the default key-value store. The default dataset receives one metadata item per crop:

```json
{
  "ok": true,
  "sourceImage": "https://api.apify.com/v2/key-value-stores/2qL0GBPUDleSGv96Q/records/synthetic-vintage-album-page.png",
  "sourceImageIndex": 1,
  "cropIndex": 1,
  "detectedCount": 4,
  "exportedCountForImage": 4,
  "confidence": 0.91,
  "bbox": { "x": 120, "y": 80, "width": 640, "height": 430 },
  "width": 620,
  "height": 410,
  "storageKey": "image_001_crop_001.jpg",
  "cropUrl": "https://api.apify.com/v2/key-value-stores/.../records/image_001_crop_001.jpg"
}
```

When `createZip` is enabled, the key-value store also contains:

```text
extracted_photos.zip
```

The dataset includes a final summary row with total input count, total exported crops, and the ZIP URL.

### Pricing

This Actor uses Apify pay-per-event pricing. The chargeable event is:

```text
photo_extracted
```

You are charged once per successfully exported photo crop, according to the price shown in the Apify pricing panel. Images with no detected photos do not create `photo_extracted` events.

### Tips for best results

- Use clear, reasonably high-resolution scans or camera shots.
- Keep visible spacing between printed photos where possible.
- Avoid heavy overlap, glare, reflections, and very dark shadows.
- Place prints on a background that contrasts with the photo edges.
- If too many false positives appear, lower `sensitivity` or raise `minAreaFraction`.
- If faint photos are missed, raise `sensitivity` slightly.

### Limitations

- The Actor extracts and crops photos; it does not restore, colorize, upscale, or deblur them.
- Heavy glare, overlapping photos, or photos with no contrast against the page can reduce detection quality.
- Very large images may take longer and can be limited by the configured input limits.

### API usage

You can run the Actor from Apify Console, the Apify API, schedules, webhooks, or integrations. Outputs are available as both structured JSON metadata and downloadable image files.

# Actor input Schema

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

Public URLs of album page scans or camera shots containing one or more printed photos.

## `sensitivity` (type: `number`):

Higher values detect weaker photo borders; lower values are stricter. Try 0.45–0.7 for most album pages.

## `minAreaFraction` (type: `number`):

Smallest accepted photo area as a fraction of the source image. Increase this to ignore tiny false positives.

## `maxImages` (type: `integer`):

Stop after processing this many input images from imageUrls.

## `maxCropsPerImage` (type: `integer`):

Limit how many detected photo crops are exported from each input image.

## `trimBorders` (type: `boolean`):

Try to remove light paper borders after perspective cropping.

## `createZip` (type: `boolean`):

Create extracted\_photos.zip with all exported crops in the default key-value store.

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

Image format for extracted crops.

## Actor input object example

```json
{
  "imageUrls": [
    "https://api.apify.com/v2/key-value-stores/2qL0GBPUDleSGv96Q/records/synthetic-vintage-album-page.png"
  ],
  "sensitivity": 0.55,
  "minAreaFraction": 0.008,
  "maxImages": 20,
  "maxCropsPerImage": 30,
  "trimBorders": true,
  "createZip": true,
  "outputFormat": "jpg"
}
```

# Actor output Schema

## `extractedPhotosDataset` (type: `string`):

JSON metadata rows for every extracted crop, including crop URL, dimensions, bounding box, corners, and confidence.

## `downloadZip` (type: `string`):

ZIP file containing all extracted photo crops when createZip is enabled.

## `keyValueStore` (type: `string`):

Default key-value store containing image\_XXX\_crop\_YYY.jpg/png records and extracted\_photos.zip.

# 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://api.apify.com/v2/key-value-stores/2qL0GBPUDleSGv96Q/records/synthetic-vintage-album-page.png"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("peroxo/photo-album-splitter").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://api.apify.com/v2/key-value-stores/2qL0GBPUDleSGv96Q/records/synthetic-vintage-album-page.png"] }

# Run the Actor and wait for it to finish
run = client.actor("peroxo/photo-album-splitter").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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://api.apify.com/v2/key-value-stores/2qL0GBPUDleSGv96Q/records/synthetic-vintage-album-page.png"
  ]
}' |
apify call peroxo/photo-album-splitter --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=peroxo/photo-album-splitter",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Photo Album Splitter API – Printed Photo Extractor",
        "description": "Detect and crop individual printed photos from album page scans or camera shots. Returns perspective-corrected image files, metadata, and optional ZIP output.",
        "version": "0.1",
        "x-build-id": "wiCxF0Bdrie1KmhnW"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/peroxo~photo-album-splitter/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-peroxo-photo-album-splitter",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/peroxo~photo-album-splitter/runs": {
            "post": {
                "operationId": "runs-sync-peroxo-photo-album-splitter",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/peroxo~photo-album-splitter/run-sync": {
            "post": {
                "operationId": "run-sync-peroxo-photo-album-splitter",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "imageUrls"
                ],
                "properties": {
                    "imageUrls": {
                        "title": "Image URLs",
                        "type": "array",
                        "description": "Public URLs of album page scans or camera shots containing one or more printed photos.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "sensitivity": {
                        "title": "Detection sensitivity",
                        "minimum": 0.1,
                        "maximum": 1,
                        "type": "number",
                        "description": "Higher values detect weaker photo borders; lower values are stricter. Try 0.45–0.7 for most album pages.",
                        "default": 0.55
                    },
                    "minAreaFraction": {
                        "title": "Minimum crop area fraction",
                        "minimum": 0.001,
                        "maximum": 0.2,
                        "type": "number",
                        "description": "Smallest accepted photo area as a fraction of the source image. Increase this to ignore tiny false positives.",
                        "default": 0.008
                    },
                    "maxImages": {
                        "title": "Maximum input images",
                        "minimum": 1,
                        "maximum": 100,
                        "type": "integer",
                        "description": "Stop after processing this many input images from imageUrls.",
                        "default": 20
                    },
                    "maxCropsPerImage": {
                        "title": "Maximum crops per image",
                        "minimum": 1,
                        "maximum": 80,
                        "type": "integer",
                        "description": "Limit how many detected photo crops are exported from each input image.",
                        "default": 30
                    },
                    "trimBorders": {
                        "title": "Trim neutral photo borders",
                        "type": "boolean",
                        "description": "Try to remove light paper borders after perspective cropping.",
                        "default": true
                    },
                    "createZip": {
                        "title": "Create ZIP output",
                        "type": "boolean",
                        "description": "Create extracted_photos.zip with all exported crops in the default key-value store.",
                        "default": true
                    },
                    "outputFormat": {
                        "title": "Output format",
                        "enum": [
                            "jpg",
                            "png"
                        ],
                        "type": "string",
                        "description": "Image format for extracted crops.",
                        "default": "jpg"
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
