DoublePageScanSplitter splits scans of double pages (book openings) into a left page (verso) and a right page (recto). It's a small tool for digitisation projects. Fully automatic splitting often fails on historical material, while splitting by hand in an image editor is slow. With DoublePageScanSplitter you mark the gutter of each scan with a couple of clicks and the tool does the rest.
The tool comes in two versions:
| Version | File | Requirements |
|---|---|---|
| Browser app (recommended) | code/DoublePageScanSplitter.html |
A modern web browser. No installation and no internet connection needed. |
| Jupyter notebook | code/DoublePageScanSplitter_v2.ipynb |
Python 3, OpenCV, NumPy, pandas, Jupyter |
Both versions use the same splitting logic and write the same output files.
For each double page, click on the gutter twice: once near the top and once near the bottom. The two points define the split line, so the tool also handles gutters that are slanted or skewed. The image is split along that line and the next image opens.
There are two split modes:
rotate(default): the image is rotated around the middle of the gutter until the gutter is vertical, then it is split straight down. Both pages come out deskewed. Corners that have no image content after the rotation are filled with the fill colour (white by default).cut: the image is cut along the slanted line with no rotation and no resampling, so the original pixels stay untouched. The small triangle that belongs to the other page is filled with the fill colour.
If the gutter is almost exactly vertical (less than 0.1°), both modes make a plain vertical cut.
Use it online: https://dhofu.github.io/DoublePageScanSplitter/
Even the online version processes everything in your browser and never uploads your images.
- Open the online version, or download
code/DoublePageScanSplitter.htmland open it in your browser. Double-clicking the file works, and it runs offline. - Click Open images or Open folder, or drag JPG, PNG or TIFF files onto the window.
- Optional, Chrome/Edge only: click Output folder… to write the split pages straight into a folder. In other browsers, or if you don't pick a folder, the results are offered as a ZIP download.
- Choose the Mode (
rotateorcut), the JPEG quality (used for JPEG output only), the Fill colour and, if you like, your name as Executor (it's recorded intiming.csv). - Click the gutter of each image twice. A magnifier next to the cursor helps you place the points precisely.
- When you're done, click Finish & export.
Everything runs locally in your browser and no images are uploaded anywhere.
| Input | Action |
|---|---|
| Left click ×2 | Mark the top and bottom of the gutter (order doesn't matter) |
| Right click / S | Don't split this image |
| R / Esc | Reset the clicks on the current image |
| ← / → | Previous / next image |
| Click in the file list | Go to that image (to redo a split, just click twice again) |
| Browser | Input | Output |
|---|---|---|
| Chrome, Edge (and other Chromium-based browsers) | Files, folders, drag & drop | Directly into a folder, or ZIP |
| Firefox, Safari | Files, folders, drag & drop | ZIP download |
pip install -r requirements.txt- Open
code/DoublePageScanSplitter_v2.ipynbin Jupyter. - In the configuration cell, set
sourceDir(folder with your scans) andtargetDir(output folder). You can also setMODE,FILL,MIN_ANGLEandJPEG_QUALITYthere. - Run all cells. An OpenCV window opens for each image:
- Left click twice: top and bottom of the gutter. The window moves on automatically after the second click.
- Right click: don't split this image.
- r: reset the clicks on the current image.
- Esc or closing the window: stop collecting points. The images you've already marked are still processed.
Subfolders of sourceDir are processed recursively and their structure is mirrored in targetDir.
code/DoublePageScanSplitter_v1.ipynb is the original version, which takes one click per image and splits strictly vertically. It's kept for reference.
Both versions read JPEG (.jpg, .jpeg), PNG (.png) and TIFF (.tif, .tiff). The split pages are saved in the same format and with the same extension as the source image:
| Source | Output |
|---|---|
| JPEG | JPEG with the chosen quality (default 95) |
| PNG | PNG (lossless) |
| TIFF | TIFF, Deflate compressed (lossless) |
Notes:
- Output is always 8-bit colour. 16-bit images are reduced to 8 bit and greyscale images are saved as RGB. Resolution (DPI) metadata is not carried over.
- Only the first page of a multi-page TIFF is read.
- Browsers can't read TIFF on their own, so the browser app has its own TIFF reader. It handles uncompressed, LZW, Deflate, PackBits and JPEG-compressed TIFFs (1/2/4/8/16 bit; grey, RGB, palette or CMYK; strips or tiles). It doesn't support CCITT fax compression (common in black-and-white TIFFs) or BigTIFF. Use the notebook for those files.
For every split image NAME.jpg (or .png, .tif, …) the tool writes two files:
NAME_L_verso.jpg: the left pageNAME_R_recto.jpg: the right page
It also writes two CSV files that document the run:
splitValues_v2.csv: one row per image, with a header row. New runs are appended.
| Column | Meaning |
|---|---|
Filepaths |
Path of the source image |
x1, y1 |
Upper point of the gutter (pixels) |
x2, y2 |
Lower point of the gutter (pixels) |
angle |
Gutter angle against the vertical, in degrees (positive = the bottom leans to the right) |
mode |
rotate or cut |
For images that weren't split, the coordinate, angle and mode columns are empty.
timing.csv: one row per run, with no header. Each row is appended.
sourceDir, sourceDirFileCount, targetDir, targetDirFileCount, elapsedTime (s), elapsedTime (HH:MM:SS), date/time, executor
data/images/ contains four sample double-page scans. data/split/ contains the results of splitting them with version 1 of the notebook, including the v1 log files (splitValues.csv holds one x value per image).
DoublePageScanSplitter/
├── code/
│ ├── DoublePageScanSplitter.html # browser app
│ ├── DoublePageScanSplitter_v2.ipynb # notebook, two-point split (current)
│ └── DoublePageScanSplitter_v1.ipynb # notebook, one-click vertical split (legacy)
├── data/
│ ├── images/ # sample double-page scans
│ └── split/ # sample output
├── index.html # GitHub Pages entry point (forwards to the browser app)
├── requirements.txt
├── LICENSE
└── README.md
The code is released under the MIT License.