Skip to main content

Tapirscan for Python

This guide describes the 1.2.0 API revision. See migration. Build/install this checkout using the development guide to use these changes before publication; older registry packages use their own versioned API.

Scan Pillow images, NumPy arrays and PyTorch tensors with tapirscan.scan(image). Try the live demo · Quick start · Functions · All options · Results

Installation

Python 3.10+ is required. Platform wheels bundle all four native modes. Install from PyPI with pip install tapirscan.

Quick start

Install tapirscan plus the image libraries you use. For these examples:

pip install tapirscan tifffile pillow torch

TIFF → NumPy array, using defaults:

import tifffile
import tapirscan

pixels = tifffile.imread("label.tif", key=0)  # NumPy array: first TIFF page
result = tapirscan.scan(pixels)
print(result.values)

This example assumes an 8-bit grayscale or RGB image. Defaults are Medium effort and EAN-13. No intermediate file or explicit scanner object is needed.

JPEG, with optional settings:

from PIL import Image
import tapirscan

with Image.open("label.jpg") as image:
    result = tapirscan.scan(image, mode="high", formats="1D")

for barcode in result:
    print(barcode.text, barcode.format, barcode.polygon)

formats="1D" enables all supported linear formats; "2D" and "all" are also available. The retail formats (EAN13, UPCA, EAN8 and UPCE) are supported; formats outside this group remain experimental.

formats="retail" selects EAN13, UPCA, EAN8 and UPCE. "common1D" adds Code128, Code39 and ITF; "common" adds QRCode and DataMatrix to "common1D". See format presets and runtime behavior.

PyTorch tensor:

import torch

# Using the NumPy array from the TIFF example:
tensor = torch.from_numpy(pixels)
result = tapirscan.scan(tensor)
print(result.values)

GPU tensors and tensors with requires_grad=True work directly. Tapirscan detaches internally and transfers pixels to CPU; your tensor and its autograd graph are unchanged. Barcode decoding runs on CPU.

Functions

# One image, with automatic cleanup:
result = tapirscan.scan(image, mode="medium", formats=["EAN13"], debug=False)

# Reuse native initialization across images:
with tapirscan.Scanner(mode="high", formats="1D") as scanner:
    result = scanner.scan(image)
    best = result.best  # Barcode or None; all reads remain in result

Signatures (all settings are optional):

scan(image, *, mode="medium", formats=None, ean_add_on_policy="Ignore", debug=False, extended_budget=False,
     layout="auto", value_range="auto", color_order="RGB", library_dir=None) -> ScanResult
Scanner(mode="medium", *, formats=None, ean_add_on_policy="Ignore", library_dir=None)
scanner.scan(image, *, debug=False, formats=None, extended_budget=False,
             layout="auto", value_range="auto", color_order="RGB") -> ScanResult
scanner.close()

The one-shot helper closes its scanner on success or failure. For repeated images, use the context manager above or close() in a finally block. Repeated close is safe. Results survive closure. Calls on one scanner serialize; separate scanners can run concurrently. scanner.mode and scanner.formats are read-only. Create another scanner to change effort. Per-call formats override the constructor selection for that call only; None inherits it.

All options

Option Where Default Meaning
image Scan Required Pillow image, NumPy array, PyTorch tensor, or PixelImage. Encoded bytes and filenames must be decoded first.
mode Creation / one-shot "medium" "low", "medium", "high", "very-high": select EAN13/UPCA, Common1D and QR Code search effort.
formats Creation / scan EAN13 A single identifier, "retail", "common1D", "common", "1D", "2D", "all", or a nonempty list/tuple of exact identifiers. On a scanner, None inherits its selection.
ean_add_on_policy Creation / one-shot "Ignore" "Ignore", "Read", "Require"; optional EAN/UPC supplement policy.
extended_budget Scan / one-shot False Allow extra reader work for any format. Exact budgets may evolve.
debug Scan False Include typed search evidence and raw diagnostics. Decoded polygons are always available.
layout Scan, arrays/tensors only "auto" "HW", "HWC", "CHW"; specify when channel position is ambiguous.
value_range Scan, arrays/tensors only "auto" "0_1" or "0_255". Auto uses [0,1] for all floats and [0,255] for integers, independently of image contents. Byte-unit floats require "0_255".
color_order Scan, arrays/tensors only "RGB" Optional "BGR" for OpenCV BGR/BGRA pixels; alpha is preserved and ignored by decoding. Grayscale is unchanged.
library_dir Creation / one-shot Bundled libraries Path/string for custom native builds. Lookup: explicit path, then TAPIRSCAN_LIBRARY_DIR, then wheel libraries. The working directory is never searched implicitly.

Custom native libraries must implement ABI 4. Scanner creation reports ABI mismatches with version details and rebuild instructions.

All scans return all decoded instances, including spatially separate copies of the same value. Use result.best for one highest-support read; this does not reduce scanning work. Support is a ranking heuristic, not a confidence probability.

Formats and group exports: Format, FormatSelection, retail_formats, common_formats, common_linear_formats, linear_formats, matrix_formats. See identifiers and reader limitations. Retail formats (EAN13, UPCA, EAN8 and UPCE) are supported. Other readers remain experimental. Modes tune EAN13/UPCA, Common1D and QR Code; other matrix readers use fixed effort. ROI, resizing, rotation and camera acquisition belong to the caller. Exact work budgets, timeouts and confidence thresholds are not exposed as scan options.

EAN/UPC supplements

Set ean_add_on_policy="Read" in Python when creating a scanner or calling one-shot scan(). The policy is fixed for that scanner; its default is "Ignore".

Policy Behavior
"Ignore" Decode the main barcode without reading its supplement.
"Read" Try reading the two- or five-digit supplement; keep the main barcode if none is readable.
"Require" Return an EAN/UPC barcode only when its supplement is readable. Other formats remain unaffected.

barcode.polygon and barcode.rect describe the main barcode, excluding the supplement. Supplement geometry is not exposed separately.

The supplement appears separately in barcode.ean_add_on; barcode.text remains the main payload. Reading supplements enables additional decoding work independently of the effort mode. Retail reads rejected by "Require" remain available in result.undecoded.

Evidence and work limits

Most applications need barcode.text, .format, .polygon and .rect. barcode.support exposes the evidence used by .best. It is an uncalibrated, reader-specific ranking heuristic, not a certainty percentage; values are not comparable confidence across formats or effort modes. Consequently, .best means the largest support value, not the most reliable barcode in a mixed-format image. Select by the format or payload your application needs when that distinction matters. Checksums and consistency checks reduce wrong reads but cannot guarantee that every returned decode is correct.

Use mode="low" through "very-high" at creation to select EAN13/UPCA, Common1D and QR Code search effort. Other matrix readers use fixed effort. result.unfinished is available without debug and combines reported decoding and localization limits. Returned reads are still usable. Candidate, retry and parsing caps are reported, including bounded searches that also returned reads. False does not promise exhaustive scanning. Exact budgets and interruptible timeouts are not public options.

debug=True adds attempted search windows, localization proposals, candidate outcomes and engine traces. It is unnecessary for drawing decoded barcode locations.

Pillow I, F and I;16* images use byte-unit intensities in [0,255], matching Pillow's usual conversion convention. Nonfinite and out-of-range pixels are rejected rather than silently clipped. Scale higher-bit-depth images explicitly; array/tensor value_range overrides do not apply to Pillow images.

Raw pixels

image = tapirscan.PixelImage(pixels, width=640, height=480, channels=3)
result = tapirscan.scan(image)

pixels contains decoded bytes, not a JPEG/PNG file. Dimensions and storage settings live on the image, so scan options always describe scanning.

PixelImage field Default Meaning
data Required, positional bytes, bytearray, or a contiguous byte memoryview.
width, height Required, keyword-only Integer dimensions, at least 3 pixels each.
channels 1 1 grayscale, 3 RGB, 4 RGBA. Alpha is ignored.
stride width * channels Bytes between row starts. Larger values allow padding.

Storage must cover (height - 1) * stride + width * channels bytes and fit the 128 MiB input limit. Images are limited to 32 megapixels (33,554,432 pixels), checked before copying raw buffers, converting Pillow/NumPy inputs, or transferring tensors to CPU. Scanning snapshots the addressed bytes before the native call. layout, value_range and color_order overrides apply only to arrays/tensors, not PixelImage or Pillow.

Array and tensor inputs

Accepts one HW/HWC/CHW image, optionally with a leading batch axis of size one, with 1/3/4 channels. NumPy views and noncontiguous tensors are accepted. Colors default to RGB/RGBA. OpenCV users can opt into BGR/BGRA with color_order="BGR". Booleans become black/white. NaN, infinity and out-of-range values are rejected; explicitly scale higher-bit-depth intensities and undo mean/std normalization. The input buffer limit is 128 MiB.

GPU tensors (including CUDA and MPS) and requires_grad=True tensors can be passed directly. The adapter detaches internally, copies to CPU as needed, and makes pixels contiguous without changing the input or its autograd graph. The scan is not differentiable and decoding does not run on GPU. Device transfer adds latency. Sparse, quantized, complex and meta tensors are rejected.

Optional OpenCV input

import cv2
import tapirscan

image = cv2.imread("label.jpg")
if image is None:
    raise ValueError("Could not load label.jpg")
result = tapirscan.scan(image, color_order="BGR")

OpenCV is not a dependency. Existing RGB, Pillow and grayscale calls need no new argument. BGR conversion leaves your array or tensor unchanged, supports CHW/HWC and preserves alpha. Floating-point images default to unit intensities; explicitly use value_range="0_255" for floats stored in byte units.

Results and public types

ScanResult is an immutable sequence: iterate, index, slice, use len(result) or check its truth value. Empty results are false.

Field/method Meaning
result.barcodes Tuple of immutable Barcode objects.
result.values Fresh list of decoded strings.
result.best Highest-support barcode, or None. Support is not a confidence probability.
result.image ImageSize(width, height) of supplied pixels.
result.mode Applied effort mode.
result.elapsed_ms Native scanner time; excludes image conversion and result construction.
result.unfinished Incomplete scanning work; returned reads can still be useful.
result.undecoded Localized proposals without accepted decodes; always available.
result.debug Diagnostics, or None when not requested.
result.as_dict() Independent JSON-compatible application result, including decoded and undecoded geometry; excludes diagnostics.
result.to_raw_dict() Independent native schema-2 JSON; may contain evidence even without public debug.
barcode.payload_bytes Immutable decoded payload bytes before character-set interpretation, or None when unavailable.
barcode.text, .format Decoded text and format identifier.
barcode.polygon Tuple of Point(x, y) source-image coordinates.
barcode.rect Enclosing integer Rect(left, top, width, height).
barcode.support Reader-specific ranking evidence; not confidence or a probability.
barcode.gs1 GS1 indicator, or None if not supplied by the reader.
barcode.reader_initialization Whether the payload is reader initialization data, or None if unspecified; never executed.
barcode.structured_append Immutable StructuredAppend(index, count, id, parity), or None; index is one-based.
barcode.ean_add_on Optional EAN supplement text; populated when ean_add_on_policy is "Read" or "Require".

Coordinates start at the top left. The API returns geometry, not a cropped bitmap. If you resize before scanning, map coordinates back when drawing on the original. Other exported option types are Mode, Layout, ValueRange, ColorOrder, ImageInput and PixelImage. The package includes py.typed for static type checkers.

Saving or returning results

import json

payload = result.as_dict()  # also works after scanner.close()
print(json.dumps(payload))

as_dict() uses public Python field names, polygon coordinate pairs and rectangle objects. Payload bytes become lists of integers (or null). Each call returns an independent nested dictionary; no private bytes or native-schema fields leak into it. barcode.as_dict() exports a single read. Export debug evidence separately with result.debug.to_raw_dict() when requested.

payload_bytes is currently supplied by QR Code, Data Matrix, Aztec, PDF417 and MaxiCode readers. These are decoded data bytes, not error-correction codewords; Aztec Rune represents its numeric value as decimal ASCII. Other readers return None. UTF-8 encoding .text is not a substitute for original payload bytes. Unsupported character encodings can still prevent a decode; this change preserves the readers' existing decoding behavior.

Diagnostics

result = tapirscan.scan(image, debug=True)
if result.debug is not None and result.debug.regions is not None:
    print(result.debug.regions.proposals)
    print(result.debug.regions.search_windows)
    print(result.debug.regions.undecoded)
    print(result.debug.regions.candidates)

regions.undecoded contains immutable UndecodedRegion objects with polygon and a format hint ("Unknown" when unavailable). These are geometry evidence, not decoded Barcode objects: they have no text or payload. Use result.barcodes for successful reads. proposals and search_windows are None when that reader does not expose them; an empty tuple means evidence was available but contained no entries.

Diagnostics also exposes barcodes (support, axis, candidate indices in result order), localization_limited and to_raw_dict(). Region types are available in tapirscan.results. GS1, reader initialization and structured append are available directly on each barcode without debug=True; raw metadata is also retained in JSON. Evidence varies by reader. Candidate indices inside a recovery crop are local to that crop, not identifiers for tracking across frames.

Errors

Invalid input raises ValueError/TypeError; native errors raise ScannerError with a descriptive message and a numeric .code attribute. Native codes are: 1 invalid arguments, 2 invalid/closed handle, 3 result buffer too small, 4 internal failure, and 5 resource capacity exceeded. For capacity errors, close unused scanners. Unknown codes retain their number. Scanning after close raises RuntimeError.

result.to_raw_dict() and result.debug.to_raw_dict() return independent native schema-2 dictionaries. Raw results can include evidence even when debug=False, because public undecoded geometry uses that evidence. These are engine exports, not serialization of the public Python object. Support is available directly as barcode.support and in result.debug.barcodes.

Extended work budget

Use scanner.scan(image, extended_budget=True) to allow additional reader work. The default is false. This option is valid for every format; the exact budgets and stages are implementation details that may evolve. Effort mode remains a separate setting.

Today this relaxes shared EAN-13/UPC-A retry and association limits. Other readers currently retain their existing budgets. Per-candidate limits and intentional deferrals remain; unfinished can still be true. This is not unlimited search, an exhaustiveness guarantee or a wall-clock deadline. Custom primary-reader engines must support the extended-work capability or report an error.

Undecoded regions

result.undecoded is always available, independently of debug. Each entry has a source-image polygon and a format hint. It is a localized proposal without an accepted decode, not proof of a real or permanently unreadable barcode. Entries can overlap or describe false candidates. An empty collection does not prove that every barcode was found. Raw candidate attempts remain in debug diagnostics.

Release files for tapirscan 1.2.0

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

Built distributions (wheels)

Table of built distributions (wheels) for tapirscan 1.2.0
File
tapirscan-1.2.0-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
tapirscan-1.2.0-py3-none-manylinux_2_28_x86_64.whl Python 3 none Linux glibc 2.28+ x86-64 Details
tapirscan-1.2.0-py3-none-manylinux_2_28_aarch64.whl Python 3 none Linux glibc 2.28+ ARM64 Details
tapirscan-1.2.0-py3-none-macosx_11_0_x86_64.whl Python 3 none macOS 11.0+ x86-64 Details
tapirscan-1.2.0-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details

Total release size: 26.0 MB

Release files / tapirscan-1.2.0-py3-none-win_amd64.whl

Download URL tapirscan-1.2.0-py3-none-win_amd64.whl
Size 4.8 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
6054c8dff9db0f649f510f8c04a538ec12b81de4f87aa2c46cd2e0b9e2348f88
BLAKE2b-256 checksum
How to use checksums
bf626e36a791db93f68fae7fb032ae626cde69d12e049e39b54b6a75ae8d671b
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 17, 2026.

Transparency log

Release files / tapirscan-1.2.0-py3-none-manylinux_2_28_x86_64.whl

Download URL tapirscan-1.2.0-py3-none-manylinux_2_28_x86_64.whl
Size 5.6 MB
Tags Linux glibc 2.28+ x86-64 Python 3
SHA-256 checksum
How to use checksums
4c5b84b55a18fd3825e992ab15c882f06060a8bbdef0a643aff0e6caa8ac2d01
BLAKE2b-256 checksum
How to use checksums
9e05494c5b2420c198375ded7e1b4a1dd212d417ff3da6c6c11e89e87d13beae
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 17, 2026.

Transparency log

Release files / tapirscan-1.2.0-py3-none-manylinux_2_28_aarch64.whl

Download URL tapirscan-1.2.0-py3-none-manylinux_2_28_aarch64.whl
Size 5.2 MB
Tags Linux glibc 2.28+ ARM64 Python 3
SHA-256 checksum
How to use checksums
ed3d0e4468d9ca52e5f3163c35a559ec63409c924e4483dfe0ae23510ed53f1a
BLAKE2b-256 checksum
How to use checksums
fbd8a6e70f1b0edf93a0f344c629c6eebfde2e503c1c3d22e62463e3c27b9ccb
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 17, 2026.

Transparency log

Release files / tapirscan-1.2.0-py3-none-macosx_11_0_x86_64.whl

Download URL tapirscan-1.2.0-py3-none-macosx_11_0_x86_64.whl
Size 5.4 MB
Tags Python 3 macOS 11.0+ x86-64
SHA-256 checksum
How to use checksums
44662ff46e61e4b71f4244731fef11e07b771e1633694342bc502f0791b445eb
BLAKE2b-256 checksum
How to use checksums
8d35b685a3df6925fd4551d14f34d675e4d16c9a565989a8090de99604f7b6a1
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 17, 2026.

Transparency log

Release files / tapirscan-1.2.0-py3-none-macosx_11_0_arm64.whl

Download URL tapirscan-1.2.0-py3-none-macosx_11_0_arm64.whl
Size 4.9 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
26a945bc984e402f878125aa71ce5b9f546878bfc3a0ef862140bf18ea907322
BLAKE2b-256 checksum
How to use checksums
abc438bf97fde2e04f6515b297d4aa9982ea48ff39944dc14933612dac2af6d0
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 17, 2026.

Transparency log

Release history Release notifications | RSS feed

1.2.1

5 release files

This release

1.2.0 This release

5 release files

1.1.0

5 release files

1.0.0

5 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