manga-page-splitter
Split scanned manga double-page spreads into single pages, in right-to-left reading order.
Scanned manga is usually distributed as one image per printed spread: two pages side by side, separated by the fold of the book. That is fine for reading, but it is awkward for anything that has to work page by page — e-ink readers, translation tooling, OCR, re-binding a volume into a single PDF with a sane page count. This package does the boring part: it finds the fold, cuts the spread in two, and hands you the pages in the order they are meant to be read.
Installation
pip install manga-page-splitter
The only runtime dependency is Pillow.
Quick start
from manga_page_splitter import split_file
# Writes page_001.png and page_002.png into ./pages
written = split_file("spread.png", "pages")
print(written)
From the command line:
manga-page-splitter spread.png -o pages
Reading order
Japanese manga is read from the right page to the left one, so by default the
right half of the spread is emitted first. For comics that read
left-to-right, pass right_to_left=False or --ltr:
pages = split_spread(spread, gutter=400, right_to_left=False)
manga-page-splitter spread.png --ltr
Finding the gutter
gutter is the x coordinate the spread is cut at. You can supply it
yourself, but you usually do not have to: detect_gutter scans a band around
the horizontal centre of the image and returns the lightest column in it. On
a real scan the fold is printed with no artwork across it, so it is almost
always the cleanest vertical strip near the middle.
from manga_page_splitter import detect_gutter
x = detect_gutter("spread.png")
if x is None:
print("no clean fold found — pass an explicit gutter")
else:
print(f"cutting at x={x}")
The search is configurable:
| Parameter | Default | Meaning |
|---|---|---|
search_ratio |
0.12 |
Half-width of the scanned band, as a fraction of the image width |
threshold |
200 |
Grayscale value below which a pixel counts as ink |
min_ink_ratio |
0.02 |
A column is only accepted if its ink count is at most this fraction of the height |
Raise min_ink_ratio for noisy scans, lower it to demand a genuinely clean
fold. When nothing in the band qualifies, detect_gutter returns None and
split_spread raises GutterNotFoundError rather than guessing — a wrong
cut silently corrupts a whole chapter, so failing loudly is the better
default. Pass gutter=<x> to force a position.
To check a folder of scans before committing to a batch:
manga-page-splitter scans/*.png --detect-only
Trimming the margins
Scans usually carry a white or grey margin, and a few millimetres of the
facing page. trim=True (or --trim) crops each page to the content box,
using the colour of the four corners as the background:
pages = split_spread("spread.png", trim=True, padding=4)
padding keeps a few pixels of breathing room around the artwork, which is
usually what you want if the pages are headed for a reader that adds its own
margins.
API
| Function | Purpose |
|---|---|
detect_gutter(image, ...) |
Return the x coordinate of the fold, or None |
split_spread(image, gutter=None, right_to_left=True, trim=False, padding=0) |
Return the two page images in reading order |
split_result(image, ...) |
Same, plus the gutter and image size that were used |
split_file(path, out_dir, ...) |
Split one file and write the pages to disk |
split_many(paths, out_dir, ...) |
Split a batch, one sub-directory per source file |
trim_borders(image, tolerance=8, padding=0) |
Crop the uniform border from a single page |
Every function accepts either a PIL.Image.Image or a path. split_result
returns a frozen SplitResult with pages, gutter, size, detected and
a count property.
Command line reference
manga-page-splitter IMAGE [IMAGE ...] [-o DIR] [-g X] [--ltr] [--trim]
[--padding N] [--prefix STEM] [--format FMT]
[--quality N] [--batch] [--detect-only] [-q]
Exit codes: 0 on success, 1 for I/O or argument errors, 2 when no
gutter could be found.
Batch use
# Each scan gets its own folder: pages/chapter01/page_001.png, ...
manga-page-splitter scans/*.png -o pages --batch --trim
In Python, split_many does the same and returns one list of paths per
source file.
Notes and limits
- Only two-page spreads are handled. A single page passed in will either be cut in the wrong place or rejected, depending on how much ink sits in the centre band.
- The gutter search assumes the fold is roughly centred. Heavily cropped
scans where one page takes two thirds of the width need an explicit
gutter. - Colour profiles are preserved; nothing is re-encoded unless you ask for a
lossy
--format. - Pages are written in reading order, numbered from
001, so the file names themselves sort correctly.
Related
Cutting a spread in two is the first step of a scanlation workflow; the
harder half is the text. Mee Manga Translator
takes the separated pages and translates the dialogue while keeping the
original layout, which pairs well with the --trim output above.
License
MIT. See LICENSE.
Metadata
Release files for manga-page-splitter 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| manga_page_splitter-1.0.0.tar.gz | 16.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| manga_page_splitter-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 27.8 kB
Release files / manga_page_splitter-1.0.0.tar.gz
| Download URL | manga_page_splitter-1.0.0.tar.gz |
|---|---|
| Size | 16.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d83b88eb45247afc040a707ac545cac02acde2b34c6c40e801dd5661aded2429
|
|
BLAKE2b-256 checksum How to use checksums |
04b514084f3946e215ac15202b4c3f54c0e3f9dc3b55b1ef09fbb3d0d6dbcafe
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|
Release files / manga_page_splitter-1.0.0-py3-none-any.whl
| Download URL | manga_page_splitter-1.0.0-py3-none-any.whl |
|---|---|
| Size | 11.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
75bf3e0bfa90fcb6ab3b58647831d18e9dff091ca96f267d8b8567f86047a06d
|
|
BLAKE2b-256 checksum How to use checksums |
c611f61cf331702a87a86301fd72b63e52a970264059897ba0b24b00f59a5cc3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|