Skip to main content

PyCoupole

GPU-accelerated Monte Carlo uncertainty quantification for SVBRDF measurements on the La Coupole setup. Companion code to the associated Optics Express paper, Supplemental Document and measured dataset.

Installation

[!important] This code is built on NVIDIA's Pythonic, GPU-friendly library Warp, version 1.14.0 or newer. Python version 3.10 or newer is required. Warp can run on x86-64 and ARMv8 CPUs on Windows and Linux. macOS requires Apple Silicon (ARM64). GPU support requires a CUDA-capable NVIDIA GPU and driver (minimum GeForce GTX 9xx).

Simply install it from PyPI:

pip install pycoupole

Quick start

Four functions are exposed, in two pairs:

  • computeBRDFDeterministic / computeBRDFDistribution take the measurement geometry as explicit arguments.
  • computeBRDFDeterministicFromCSV / computeBRDFDistributionFromCSV read that geometry directly from a measurement CSV (the typical entry point).

From a measurement CSV

Most of the time the inputs come from a measurement CSV, where each row is one pixel-channel sample. Rows are addressed by zero-based index (the header is not counted).

import pycoupole

# Deterministic estimate on selected rows -> list of (rowIndex, brdf in sr^-1)
# Pass a single int for one row, an iterable for several, or None for all rows
results = pycoupole.computeBRDFDeterministicFromCSV("measurements.csv", rows=[0, 1, 2])
for index, brdf in results:
    print(f"row {index}: {brdf:.6e}")

# Monte Carlo distribution on a single row.
result     = pycoupole.computeBRDFDistributionFromCSV("measurements.csv", row=0)
pdf        = result["pdf"]        # density, in sr
brdfValues = result["brdfValues"] # bin centres, in sr^-1

The distribution helper takes exactly one row: a single distribution already saturates the GPU, so for several rows you loop in Python over the row indices.

From explicit arguments

When the geometry does not come from a CSV, call the underlying functions directly.

import pycoupole

sample = dict(
    pixelID        = (1521, 915),
    channelID      = 1,
    pixelValueLSB  = 10,
    exposureTimeS  = 0.122374,
    lightCenter    = (-1.14251,   0.507025,    0.391015),
    lightNormal    = ( 0.965928,  0.00172073, -0.258806),
    triangleCenter = ( 0.490908, -0.0674868,   0.0267361),
    triangleNormal = ( 0.0135288,-0.00660755,  0.999887),
)

value  = pycoupole.computeBRDFDeterministic(**sample) # point estimate, sr^-1
result = pycoupole.computeBRDFDistribution(**sample)  # MC distribution

Both *FromCSV functions accept a device argument and forward extra keyword arguments (nbBins, batchSize, seed, ...) to the underlying estimators.

Interpreting the output

computeBRDFDistribution returns a probability density, not a histogram of counts. The density carries the inverse unit of the BRDF (sr), and integrates to 1 over its support — so its values are not bounded by 1 and will be large for a sharply peaked distribution. Recover the per-bin probability mass, then the moments and credible intervals, as:

binWidth = brdfValues[1] - brdfValues[0]
mass     = pdf * binWidth                    # sums to 1

mean   = float(np.sum(mass * brdfValues))
std    = float(np.sqrt(np.sum(mass * (brdfValues - mean) ** 2)))

cdf      = np.cumsum(mass)
median   = float(np.interp(0.50,  cdf, brdfValues))
ci95_low = float(np.interp(0.025, cdf, brdfValues))
ci95_up  = float(np.interp(0.975, cdf, brdfValues))

For a well-conditioned acquisition the deterministic value sits near the centre of the distribution and the out-of-range fraction (1 - nbAccepted / nbTotal) is negligible.

Citation

If you use this software, please cite the associated paper as below (see also CITATION.cff).

La Coupole: an SVBRDF measurement device for large and non-planar objects. Antoine Lucat, Pierre Mézières, François Margall, Louis De Oliveira, Marjorie Paillet, Arnaud Tizon, Pierre Bénard, Romain Pacanowski. Optics Express, Vol. 34, Issue 7, pp. 11695-11709 (March 2026). DOI: 10.1364/OE.587877.

Associated BibTeX entry
@article{Lucat:26,
  author    = {Antoine Lucat and Pierre M\'{e}zi\`{e}res and Fran\c{c}ois Margall and Louis De Oliveira and Marjorie Paillet and Arnaud Tizon and Pierre B\'{e}nard and Romain Pacanowski},
  journal   = {Optics Express},
  keywords  = {Camera calibration; Imaging systems; Light sources; Physiology; Printed circuit boards; Spatial resolution},
  number    = {7},
  pages     = {11695--11709},
  publisher = {Optica Publishing Group},
  title     = {La Coupole: an SVBRDF measurement device for large and non-planar objects},
  volume    = {34},
  month     = {Apr},
  year      = {2026},
  url       = {https://opg.optica.org/oe/abstract.cfm?URI=oe-34-7-11695},
  doi       = {10.1364/OE.587877},
}

[!note] For a detailed derivation of the uncertainty estimation method, including assumptions, propagation steps, and validation, see Section 3 (pp. 14–20) of the associated Supplemental Material (DOI: 10.6084/m9.figshare.31418393.v1).

License

PyCoupole is distributed under the MIT License. See LICENSE.txt for more information.

Metadata

Release files for pycoupole 0.2.1

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

Source distribution (sdist)

Source distribution for pycoupole 0.2.1
File Size Uploaded
pycoupole-0.2.1.tar.gz 15.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pycoupole 0.2.1
File Interpreter ABI Platform
pycoupole-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 30.7 kB

Release files / pycoupole-0.2.1.tar.gz

Download URL pycoupole-0.2.1.tar.gz
Size 15.9 kB
Tags Source
SHA-256 checksum
How to use checksums
9d13b69396c7ffd4855194feb47bb39ce630f95de045d39408e5ce22a6e98002
BLAKE2b-256 checksum
How to use checksums
b0fe459529edf57f041fba13b26872709c9232331d216357463932aaf82ec845
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.10.20

Release files / pycoupole-0.2.1-py3-none-any.whl

Download URL pycoupole-0.2.1-py3-none-any.whl
Size 14.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d7b2799106e062b2527be8ffc4590f1adccdfadd766388a22e8048cadd20be58
BLAKE2b-256 checksum
How to use checksums
52843605d219e6350426f614353cf2e7c9e0a97f387d2e47f0ab519ba8140067
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.10.20

Release history Release notifications | RSS feed

0.2.2

2 release files

This release

0.2.1 This release

2 release files

0.2.0

2 release files

0.1.1

2 release files

0.1.0

2 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