Skip to main content

pixelmap-multiview-python

PyPI Python versions License: MIT

Turn a set of ordinary photos of a scene into a textured 3D model.

Python bindings for the pixelmap_multiview Rust crate. It maps every pair of three or more photos with pixelmap's dense correspondence, works out where each photo was taken from, and fuses all of the views into one mesh. You don't need calibration, markers or measurements.

These 23 phone photos were taken while walking once around a statue:

Twenty-three photos of a bronze statue of a woman carrying a child, taken from all sides while walking around it

The mesh fused from them, shown untextured in MeshLab from three sides:

The reconstructed 3D mesh of the statue, seen from three different angles

Install

pip install pixelmap-multiview-python            # the library; NumPy is the only dependency
pip install "pixelmap-multiview-python[images]"  # plus Pillow, for load_photos()

The distribution is pixelmap-multiview-python, and the import name is pixelmap_multiview. Wheels are published for Linux (x86-64, aarch64, musl), macOS (Apple silicon and Intel) and Windows (x86-64), for CPython 3.9 and newer. You only need a Rust toolchain to build from source.

Quick start

import pixelmap_multiview as pmv

photos, focal = pmv.load_photos(["a.jpg", "b.jpg", "c.jpg", "d.jpg"])
model = pmv.reconstruct(photos, quality="medium", focal_35mm=focal)

print(model)  # <pixelmap_multiview.Model 23314 vertices, 45708 triangles, 4 of 4 photos>
model.save("out/statue.obj")  # + statue.mtl + statue_texture.png, for MeshLab or Blender

load_photos turns each photo upright according to its EXIF orientation and scales it to 1200 px on the long side. It also returns the 35 mm-equivalent focal length from EXIF when every photo records the same one. Photos from anywhere else work too: pass a list of uint8 arrays (H, W, 3), (H, W, 4) or (H, W), or Pillow images, all the same size.

What you get

Attribute Contents
model.vertices, model.normals (V, 3) float64
model.triangles (T, 3) uint32, counter-clockwise seen from the normal's side
model.vertex_colours (V, 3) uint8
model.texture.atlas (H, W, 4) uint8, with each part of the surface taken from the photo that sees it best
model.texture.texcoords, .face_texcoords, .face_views the atlas mapping, OBJ convention
model.intrinsics fx, fy, cx, cy, .matrix, and whether the focal length was provided, from EXIF, estimated or refined
model.cameras a Pose per photo (rotation, translation, centre, matrix), or None for a photo left out
model.pairs what two-view geometry made of each pair: coverage, relative pose, inlier ratio, and why it was rejected

All of these are in one frame. The first camera of the best pair sits at the origin looking along +z with +y down the image, and the distance between that pair's cameras is the unit of length. Nothing is metric. model.save() writes .obj (textured), .x3d (textured) or .ply (vertex colours), turned half a revolution about x so that viewers show the model upright.

Following along and stopping

A run takes minutes of work. on_event hears about all of it, as data rather than prose:

def on_event(event):
    if event.progress is not None:  # None for log lines and dropped photos
        print(f"{event.progress:5.1%}  {event.message}")
    if isinstance(event, pmv.PairMapped):
        event.points  # (rows, cols, 2): the dense correspondence for this pair, NaN where unmapped


model = pmv.reconstruct(photos, on_event=on_event)

The events are Started, StageProgress, PairProgress, PairMapped, PairRejected, ViewDropped and Log. Each one carries stage, progress and message, along with its own fields. If on_event raises an exception, the run stops at the next checkpoint and the exception propagates. Ctrl-C stops a run the same way.

To keep your own thread free, run the reconstruction as a Job:

job = pmv.Job(photos, quality="medium")
for event in job:  # each event as it happens; ends when the run does
    print(f"{job.status.progress:.0%} {event.message}")
model = job.join()

For code with its own event loop, job.next_event(timeout=0) polls without waiting, and job.status works from any thread. After job.cancel(), join() raises CancelledError, well within a second.

When a capture fails

A run that can't produce a correct model raises an exception rather than returning a plausible-looking wrong one. Each exception names the stage that failed and says what to change:

try:
    model = pmv.reconstruct(photos)
except pmv.ReconstructionError as err:
    print(err.stage, err.reason)  # tracks too_few_tracks
    # only 338 points could be followed across three or more photos (need 500);
    # the photos share too little of the scene
    print(err)

InvalidInputError (also a ValueError) covers too few photos, photos of different sizes, and photos that are too small. ReconstructionError covers captures that can't be reconstructed: a camera that only turned, a flat scene, or photos with too little in common. Match on err.reason rather than on the message. The failure's details are attributes, such as err.found and err.required.

For a good capture:

  • Step sideways between shots rather than turning on the spot.
  • Let neighbouring photos overlap generously.
  • Keep one camera at one zoom setting throughout.

Cost

Mapping the pairs dominates: N photos take N(N−1)/2 pixelmap runs. One pair of 4:3 photos, native build:

Quality 800 px 1200 px 1800 px
low 0.60 s 0.56 s 0.50 s
medium 2.25 s 2.30 s 2.38 s
high 5.81 s

Input size barely changes the matching time, because pixelmap scales every photo to a fixed working width first. What a larger input buys is precision in the geometry that follows. Runs are deterministic: the same photos, quality and seed give the same model.

Development

python -m venv .venv && . .venv/bin/activate
pip install maturin pytest pillow ruff numpy
maturin develop --release   # release matters: the tests run whole reconstructions
pytest

License

MIT

Release files for pixelmap-multiview-python 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pixelmap-multiview-python 0.1.0
File Size Uploaded
pixelmap_multiview_python-0.1.0.tar.gz 38.2 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for pixelmap-multiview-python 0.1.0
File
pixelmap_multiview_python-0.1.0-cp39-abi3-win_amd64.whl CPython 3.9 abi3 Windows x86-64 Details
pixelmap_multiview_python-0.1.0-cp39-abi3-musllinux_1_2_x86_64.whl CPython 3.9 abi3 Linux musl 1.2+ x86-64 Details
pixelmap_multiview_python-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.9 abi3 Linux glibc 2.17+ x86-64 Details
pixelmap_multiview_python-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.9 abi3 Linux glibc 2.17+ ARM64 Details
pixelmap_multiview_python-0.1.0-cp39-abi3-macosx_11_0_arm64.whl CPython 3.9 abi3 macOS 11.0+ ARM64 Details
pixelmap_multiview_python-0.1.0-cp39-abi3-macosx_10_12_x86_64.whl CPython 3.9 abi3 macOS 10.12+ x86-64 Details

Total release size: 4.3 MB

Release files / pixelmap_multiview_python-0.1.0.tar.gz

Download URL pixelmap_multiview_python-0.1.0.tar.gz
Size 38.2 kB
Tags Source
SHA-256 checksum
How to use checksums
1c3a68a34db7ec6b52d7b39b059de958b67e91877319097035cdedd9db659aa3
BLAKE2b-256 checksum
How to use checksums
69407d6266f6204c9635774ae4cc09b04b21a99b80f0cf61236a00f86a49aacd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / pixelmap_multiview_python-0.1.0-cp39-abi3-win_amd64.whl

Download URL pixelmap_multiview_python-0.1.0-cp39-abi3-win_amd64.whl
Size 602.8 kB
Tags CPython 3.9 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
b5cad1990133461d5b4576bedb5dd6898555f6e5766cb3558794a4a0cdf597ea
BLAKE2b-256 checksum
How to use checksums
e6f8ea46bba0298411a5562f1188bd19d51a2f31a4a420cbf25a04eec675b3e3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / pixelmap_multiview_python-0.1.0-cp39-abi3-musllinux_1_2_x86_64.whl

Download URL pixelmap_multiview_python-0.1.0-cp39-abi3-musllinux_1_2_x86_64.whl
Size 936.3 kB
Tags CPython 3.9 Linux musl 1.2+ x86-64 abi3
SHA-256 checksum
How to use checksums
2de7fdc582df9a8d7859a7dd2ac981c683a97e23eafd86e1217fbaaec35c4f64
BLAKE2b-256 checksum
How to use checksums
9c8aec6b2fe5b11ecd07855ce37c70302f9c742f814866b47531d65dba42b27f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / pixelmap_multiview_python-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL pixelmap_multiview_python-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 726.9 kB
Tags CPython 3.9 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
190a48efca70c7365d36266ab355ef503d5d9f26234a1b4bbff7632df0ffae28
BLAKE2b-256 checksum
How to use checksums
91a451700114aced5a9433d57d6256248d76d8d02b832cadd487cd7797f2ce3a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / pixelmap_multiview_python-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL pixelmap_multiview_python-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 692.3 kB
Tags CPython 3.9 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
789645681f06c5ab2b17831a22d118035ad5d25f29069f1ae6a09e95168ea9e9
BLAKE2b-256 checksum
How to use checksums
472736f65acb209f7532a08fe22ea2946c901db1941392ebbdd670ceabb413dd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / pixelmap_multiview_python-0.1.0-cp39-abi3-macosx_11_0_arm64.whl

Download URL pixelmap_multiview_python-0.1.0-cp39-abi3-macosx_11_0_arm64.whl
Size 646.2 kB
Tags CPython 3.9 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
7efd2aa28267edaec9a8e5cd675d2e3d5857186bd53678763b98dbb9991a41f7
BLAKE2b-256 checksum
How to use checksums
d2c9e25c94e944f2277a2de651375f156ab7ed033f010c35a1c8388677d4c8d0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / pixelmap_multiview_python-0.1.0-cp39-abi3-macosx_10_12_x86_64.whl

Download URL pixelmap_multiview_python-0.1.0-cp39-abi3-macosx_10_12_x86_64.whl
Size 677.0 kB
Tags CPython 3.9 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
56ddf50cc4d2634419cbf2973be74e67d64855dbc71c7f019bb1852c27787d5c
BLAKE2b-256 checksum
How to use checksums
28d6c41ba8a39c7317c0191a66ecc089d0a90465661127b00a8693021a56c88e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

7 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page