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 dataset.
Installation
Simply install it from PyPI:
pip install pycoupole
Quick start
Four functions are exposed, in two pairs:
computeBRDFDeterministic/computeBRDFDistributiontake the measurement geometry as explicit arguments.computeBRDFDeterministicFromCSV/computeBRDFDistributionFromCSVread 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.
List of SVBRDF datasets
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 either this software of the dataset, 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},
}
License
PyCoupole is distributed under the MIT License. See LICENSE.txt for more information.
Metadata
Release files for pycoupole 0.2.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pycoupole-0.2.2.tar.gz | 17.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pycoupole-0.2.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 32.3 kB
Release files / pycoupole-0.2.2.tar.gz
| Download URL | pycoupole-0.2.2.tar.gz |
|---|---|
| Size | 17.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8c3f2933858baf4a408ca1e5448bfc4e13eff5dbc55016bcf5d68301a1d6fbf8
|
|
BLAKE2b-256 checksum How to use checksums |
7ccfdaa7b7492e1d7b7af83339ace7ffb4d8d657fdd24dc114b5681e428c2cc5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.1
|
Release files / pycoupole-0.2.2-py3-none-any.whl
| Download URL | pycoupole-0.2.2-py3-none-any.whl |
|---|---|
| Size | 15.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fbf83a14e23e932ed7d37b667142058bd03bf7c2f20e4b1669fe349a9f385ace
|
|
BLAKE2b-256 checksum How to use checksums |
5ed158c4409704ccfdff49568eb767562b185379b92887dd26429f7b0a84b6a7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.1
|