Coverage for colorimetry/generation.py: 53%

1"""

2 Spectral Generation

3===================

4

5 Define objects for generating spectral distributions and multi-spectral

6 distributions with the specified characteristics.

7

8- :func:`colour.sd_constant`

9- :func:`colour.sd_zeros`

10- :func:`colour.sd_ones`

11- :func:`colour.msds_constant`

12- :func:`colour.msds_zeros`

13- :func:`colour.msds_ones`

14- :func:`colour.colorimetry.sd_gaussian_normal`

15- :func:`colour.colorimetry.sd_gaussian_fwhm`

16- :attr:`colour.SD_GAUSSIAN_METHODS`

17- :func:`colour.sd_gaussian`

18- :func:`colour.colorimetry.sd_single_led_Ohno2005`

19- :attr:`colour.SD_SINGLE_LED_METHODS`

20- :func:`colour.sd_single_led`

21- :func:`colour.colorimetry.sd_multi_leds_Ohno2005`

22- :attr:`colour.SD_MULTI_LEDS_METHODS`

23- :func:`colour.sd_multi_leds`

24

25 References

26----------

27- :cite:`Ohno2005` : Ohno, Yoshi. (2005). Spectral design considerations for

28 white LED color rendering. Optical Engineering, 44(11), 111302.

29 doi:10.1117/1.2130694

30- :cite:`Ohno2008a` : Ohno, Yoshiro, & Davis, W. (2008). NIST CQS simulation

31 (Version 7.4) [Computer software].

32 https://drive.google.com/file/d/1PsuU6QjUJjCX6tQyCud6ul2Tbs8rYWW9/view?\

33 usp=sharing

34"""

35

36 from __future__ import annotations

37

38 import typing

39

40 import numpy as np

41

42 from colour.algebra.interpolation import LinearInterpolator

43 from colour.colorimetry import (

44 SPECTRAL_SHAPE_DEFAULT,

45 MultiSpectralDistributions,

46 SpectralDistribution,

47 SpectralShape,

48)

49

50 if typing.TYPE_CHECKING:

51 from colour.hints import (

52 Any,

53 ArrayLike,

54 Literal,

55 NDArrayFloat,

56 Sequence,

57 )

58

59 from colour.utilities import (

60 CanonicalMapping,

61 as_float_array,

62 full,

63 ones,

64 validate_method,

65)

66

67 __author__ = "Colour Developers"

68 __copyright__ = "Copyright 2013 Colour Developers"

69 __license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"

70 __maintainer__ = "Colour Developers"

71 __email__ = "colour-developers@colour-science.org"

72 __status__ = "Production"

73

74 __all__ = [

75 "sd_constant",

76 "sd_zeros",

77 "sd_ones",

78 "msds_constant",

79 "msds_zeros",

80 "msds_ones",

81 "sd_gaussian_normal",

82 "sd_gaussian_fwhm",

83 "SD_GAUSSIAN_METHODS",

84 "sd_gaussian",

85 "sd_single_led_Ohno2005",

86 "SD_SINGLE_LED_METHODS",

87 "sd_single_led",

88 "sd_multi_leds_Ohno2005",

89 "SD_MULTI_LEDS_METHODS",

90 "sd_multi_leds",

91]

92

93

94 def sd_constant(

95 k: float, shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT, **kwargs: Any

96) -> SpectralDistribution:

97 """

98 Generate a spectral distribution of the specified spectral shape filled with

99 constant :math:`k` values.

100

101 Parameters

102 ----------

103 k

104 Constant :math:`k` to fill the spectral distribution with.

105 shape

106 Spectral shape used to create the spectral distribution.

107

108 Other Parameters

109 ----------------

110 kwargs

111 {:class:`colour.SpectralDistribution`},

112 See the documentation of the previously listed class.

113

114 Returns

115 -------

116 :class:`colour.SpectralDistribution`

117 Constant :math:`k` filled spectral distribution.

118

119 Notes

120 -----

121 - By default, the spectral distribution will use the shape specified by

122 :attr:`colour.SPECTRAL_SHAPE_DEFAULT` attribute.

123 - The interpolator is set to :class:`colour.LinearInterpolator` class.

124

125 Examples

126 --------

127 >>> sd = sd_constant(100)

128 >>> sd.shape

129 SpectralShape(360.0, 780.0, 1.0)

130 >>> sd[400]

131 100.0

132 """

133

134 settings = {"name": f"{k} Constant", "interpolator": LinearInterpolator}

135 settings.update(kwargs)

136

137 values = full(len(shape.wavelengths), k)

138

139 return SpectralDistribution(values, shape.wavelengths, **settings)

140

141

142 def sd_zeros(

143 shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT, **kwargs: Any

144) -> SpectralDistribution:

145 """

146 Generate a spectral distribution of the specified spectral shape filled with

147 zeros.

148

149 Parameters

150 ----------

151 shape

152 Spectral shape used to create the spectral distribution.

153

154 Other Parameters

155 ----------------

156 kwargs

157 {:func:`colour.sd_constant`},

158 See the documentation of the previously listed definition.

159

160 Returns

161 -------

162 :class:`colour.SpectralDistribution`

163 Zeros-filled spectral distribution.

164

165 Notes

166 -----

167 - By default, the spectral distribution will use the shape specified by

168 :attr:`colour.SPECTRAL_SHAPE_DEFAULT` attribute.

169 - The interpolator is set to :class:`colour.LinearInterpolator` class.

170

171 Examples

172 --------

173 >>> sd = sd_zeros()

174 >>> sd.shape

175 SpectralShape(360.0, 780.0, 1.0)

176 >>> sd[400]

177 0.0

178 """

179

180 return sd_constant(0, shape, **kwargs)

181

182

183 def sd_ones(

184 shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT, **kwargs: Any

185) -> SpectralDistribution:

186 """

187 Generate a spectral distribution of the specified spectral shape filled

188 with ones.

189

190 Parameters

191 ----------

192 shape

193 Spectral shape used to create the spectral distribution.

194

195 Other Parameters

196 ----------------

197 kwargs

198 {:func:`colour.sd_constant`},

199 See the documentation of the previously listed definition.

200

201 Returns

202 -------

203 :class:`colour.SpectralDistribution`

204 Ones-filled spectral distribution.

205

206 Notes

207 -----

208 - By default, the spectral distribution will use the shape specified by

209 :attr:`colour.SPECTRAL_SHAPE_DEFAULT` attribute.

210 - The interpolator is set to :class:`colour.LinearInterpolator` class.

211

212 Examples

213 --------

214 >>> sd = sd_ones()

215 >>> sd.shape

216 SpectralShape(360.0, 780.0, 1.0)

217 >>> sd[400]

218 1.0

219 """

220

221 return sd_constant(1, shape, **kwargs)

222

223

224 def msds_constant(

225 k: float,

226 labels: Sequence,

227 shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT,

228 **kwargs: Any,

229) -> MultiSpectralDistributions:

230 """

231 Generate multi-spectral distributions with the specified labels and spectral

232 shape filled with constant :math:`k` values.

233

234 Parameters

235 ----------

236 k

237 Constant :math:`k` to fill the multi-spectral distributions with.

238 labels

239 Names to use for the :class:`colour.SpectralDistribution` class

240 instances.

241 shape

242 Spectral shape used to create the multi-spectral distributions.

243

244 Other Parameters

245 ----------------

246 kwargs

247 {:class:`colour.MultiSpectralDistributions`},

248 See the documentation of the previously listed class.

249

250 Returns

251 -------

252 :class:`colour.MultiSpectralDistributions`

253 Constant :math:`k` filled multi-spectral distributions.

254

255 Notes

256 -----

257 - By default, the multi-spectral distributions will use the shape

258 specified by :attr:`colour.SPECTRAL_SHAPE_DEFAULT` attribute.

259 - The interpolator is set to :class:`colour.LinearInterpolator` class.

260

261 Examples

262 --------

263 >>> msds = msds_constant(100, labels=["a", "b", "c"])

264 >>> msds.shape

265 SpectralShape(360.0, 780.0, 1.0)

266 >>> msds[400]

267 array([ 100., 100., 100.])

268 >>> msds.labels # doctest: +SKIP

269 ['a', 'b', 'c']

270 """

271

272 settings = {"name": f"{k} Constant", "interpolator": LinearInterpolator}

273 settings.update(kwargs)

274

275 wavelengths = shape.wavelengths

276 values = full((len(wavelengths), len(labels)), k)

277

278 return MultiSpectralDistributions(values, wavelengths, labels=labels, **settings)

279

280

281 def msds_zeros(

282 labels: Sequence,

283 shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT,

284 **kwargs: Any,

285) -> MultiSpectralDistributions:

286 """

287 Generate multi-spectral distributions with the specified labels and spectral

288 shape filled with zeros.

289

290 Parameters

291 ----------

292 labels

293 Names to use for the :class:`colour.SpectralDistribution` class

294 instances.

295 shape

296 Spectral shape used to create the multi-spectral distributions.

297

298 Other Parameters

299 ----------------

300 kwargs

301 {:func:`colour.msds_constant`},

302 See the documentation of the previously listed definition.

303

304 Returns

305 -------

306 :class:`colour.MultiSpectralDistributions`

307 Zero-filled multi-spectral distributions.

308

309 Notes

310 -----

311 - By default, the multi-spectral distributions will use the shape

312 specified by :attr:`colour.SPECTRAL_SHAPE_DEFAULT` attribute.

313 - The interpolator is set to :class:`colour.LinearInterpolator` class.

314

315 Examples

316 --------

317 >>> msds = msds_zeros(labels=["a", "b", "c"])

318 >>> msds.shape

319 SpectralShape(360.0, 780.0, 1.0)

320 >>> msds[400]

321 array([ 0., 0., 0.])

322 >>> msds.labels # doctest: +SKIP

323 ['a', 'b', 'c']

324 """

325

326 return msds_constant(0, labels, shape, **kwargs)

327

328

329 def msds_ones(

330 labels: Sequence,

331 shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT,

332 **kwargs: Any,

333) -> MultiSpectralDistributions:

334 """

335 Generate multi-spectral distributions with the specified labels and

336 spectral shape filled with ones.

337

338 Parameters

339 ----------

340 labels

341 Names to use for the :class:`colour.SpectralDistribution` class

342 instances.

343 shape

344 Spectral shape used to create the multi-spectral distributions.

345

346 Other Parameters

347 ----------------

348 kwargs

349 {:func:`colour.msds_constant`},

350 See the documentation of the previously listed definition.

351

352 Returns

353 -------

354 :class:`colour.MultiSpectralDistributions`

355 Ones-filled multi-spectral distributions.

356

357 Notes

358 -----

359 - By default, the multi-spectral distributions will use the shape

360 specified by :attr:`colour.SPECTRAL_SHAPE_DEFAULT` attribute.

361 - The interpolator is set to :class:`colour.LinearInterpolator`

362 class.

363

364 Examples

365 --------

366 >>> msds = msds_ones(labels=["a", "b", "c"])

367 >>> msds.shape

368 SpectralShape(360.0, 780.0, 1.0)

369 >>> msds[400]

370 array([ 1., 1., 1.])

371 >>> msds.labels # doctest: +SKIP

372 ['a', 'b', 'c']

373 """

374

375 return msds_constant(1, labels, shape, **kwargs)

376

377

378 def sd_gaussian_normal(

379 mu: float,

380 sigma: float,

381 shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT,

382 **kwargs: Any,

383) -> SpectralDistribution:

384 r"""

385 Generate a Gaussian spectral distribution of the specified spectral shape at

386 specified mean wavelength :math:`\mu` and standard deviation

387 :math:`\sigma`.

388

389 Parameters

390 ----------

391 mu

392 Mean wavelength :math:`\mu` at which the Gaussian spectral

393 distribution will peak.

394 sigma

395 Standard deviation :math:`\sigma` of the Gaussian spectral

396 distribution.

397 shape

398 Spectral shape used to create the spectral distribution.

399

400 Other Parameters

401 ----------------

402 kwargs

403 {:class:`colour.SpectralDistribution`},

404 See the documentation of the previously listed class.

405

406 Returns

407 -------

408 :class:`colour.SpectralDistribution`

409 Gaussian spectral distribution.

410

411 Notes

412 -----

413 - By default, the spectral distribution will use the shape specified by

414 :attr:`colour.SPECTRAL_SHAPE_DEFAULT` attribute.

415

416 Examples

417 --------

418 >>> sd = sd_gaussian_normal(555, 25)

419 >>> sd.shape

420 SpectralShape(360.0, 780.0, 1.0)

421 >>> sd[555] # doctest: +SKIP

422 1.0

423 >>> sd[530] # doctest: +ELLIPSIS

424 0.6065306...

425 """

426

427 settings = {"name": f"{mu}nm - {sigma} Sigma - Gaussian"}

428 settings.update(kwargs)

429

430 values = np.exp(-((shape.wavelengths - mu) ** 2) / (2 * sigma**2))

431

432 return SpectralDistribution(values, shape.wavelengths, **settings)

433

434

435 def sd_gaussian_fwhm(

436 peak_wavelength: float,

437 fwhm: float,

438 shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT,

439 **kwargs: Any,

440) -> SpectralDistribution:

441 """

442 Generate a Gaussian spectral distribution of the specified spectral shape at

443 specified peak wavelength and full width at half maximum (FWHM).

444

445 Parameters

446 ----------

447 peak_wavelength

448 Wavelength at which the Gaussian spectral distribution peaks.

449 fwhm

450 Full width at half maximum, i.e., width of the Gaussian spectral

451 distribution measured between those points on the *y* axis which are

452 half the maximum amplitude.

453 shape

454 Spectral shape used to create the spectral distribution.

455

456 Other Parameters

457 ----------------

458 kwargs

459 {:class:`colour.SpectralDistribution`},

460 See the documentation of the previously listed class.

461

462 Returns

463 -------

464 :class:`colour.SpectralDistribution`

465 Gaussian spectral distribution.

466

467 Notes

468 -----

469 - By default, the spectral distribution will use the shape specified by

470 :attr:`colour.SPECTRAL_SHAPE_DEFAULT` attribute.

471

472 Examples

473 --------

474 >>> sd = sd_gaussian_fwhm(555, 25)

475 >>> sd.shape

476 SpectralShape(360.0, 780.0, 1.0)

477 >>> sd[555] # doctest: +SKIP

478 1.0

479 >>> sd[530] # doctest: +ELLIPSIS

480 0.062...

481 """

482

483 settings = {"name": f"{peak_wavelength}nm - {fwhm} FWHM - Gaussian"}

484 settings.update(kwargs)

485

486 mu, sigma = peak_wavelength, fwhm / (2 * np.sqrt(2 * np.log(2)))

487 values = np.exp(-((shape.wavelengths - mu) ** 2) / (2 * sigma**2))

488

489 return SpectralDistribution(values, shape.wavelengths, **settings)

490

491

492 SD_GAUSSIAN_METHODS: CanonicalMapping = CanonicalMapping(

493 {"Normal": sd_gaussian_normal, "FWHM": sd_gaussian_fwhm}

494)

495 SD_GAUSSIAN_METHODS.__doc__ = """

496 Supported gaussian spectral distribution generation methods.

497"""

498

499

500 def sd_gaussian(

501 mu_peak_wavelength: float,

502 sigma_fwhm: float,

503 shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT,

504 method: Literal["Normal", "FWHM"] | str = "Normal",

505 **kwargs: Any,

506) -> SpectralDistribution:

507 """

508 Generate a Gaussian spectral distribution with the specified spectral

509 shape using the specified method.

510

511 Parameters

512 ----------

513 mu_peak_wavelength

514 Mean wavelength :math:`\\mu` at which the Gaussian spectral

515 distribution will peak.

516 sigma_fwhm

517 Standard deviation :math:`\\sigma` of the Gaussian spectral

518 distribution or full width at half maximum (FWHM), i.e., the width

519 of the Gaussian spectral distribution measured between those points

520 on the *y* axis which are half the maximum amplitude.

521 shape

522 Spectral shape used to create the spectral distribution.

523 method

524 Computation method.

525

526 Other Parameters

527 ----------------

528 kwargs

529 {:func:`colour.colorimetry.sd_gaussian_normal`,

530 :func:`colour.colorimetry.sd_gaussian_fwhm`},

531 See the documentation of the previously listed definitions.

532

533 Returns

534 -------

535 :class:`colour.SpectralDistribution`

536 Gaussian spectral distribution.

537

538 Notes

539 -----

540 - By default, the spectral distribution will use the shape specified

541 by :attr:`colour.SPECTRAL_SHAPE_DEFAULT` attribute.

542

543 Examples

544 --------

545 >>> sd = sd_gaussian(555, 25)

546 >>> sd.shape

547 SpectralShape(360.0, 780.0, 1.0)

548 >>> sd[555] # doctest: +SKIP

549 1.0

550 >>> sd[530] # doctest: +ELLIPSIS

551 0.6065306...

552 >>> sd = sd_gaussian(555, 25, method="FWHM")

553 >>> sd.shape

554 SpectralShape(360.0, 780.0, 1.0)

555 >>> sd[555] # doctest: +SKIP

556 1.0

557 >>> sd[530] # doctest: +ELLIPSIS

558 0.062...

559 """

560

561 method = validate_method(method, tuple(SD_GAUSSIAN_METHODS))

562

563 return SD_GAUSSIAN_METHODS[method](mu_peak_wavelength, sigma_fwhm, shape, **kwargs)

564

565

566 def sd_single_led_Ohno2005(

567 peak_wavelength: float,

568 half_spectral_width: float,

569 shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT,

570 **kwargs: Any,

571) -> SpectralDistribution:

572 """

573 Generate a single *LED* spectral distribution with the specified spectral

574 shape at specified peak wavelength and half spectral width

575 :math:`\\Delta\\lambda_{0.5}` using *Ohno (2005)* method.

576

577 Parameters

578 ----------

579 peak_wavelength

580 Wavelength at which the single *LED* spectral distribution peaks.

581 half_spectral_width

582 Half spectral width :math:`\\Delta\\lambda_{0.5}`.

583 shape

584 Spectral shape used to create the spectral distribution.

585

586 Other Parameters

587 ----------------

588 kwargs

589 {:class:`colour.SpectralDistribution`},

590 See the documentation of the previously listed class.

591

592 Returns

593 -------

594 :class:`colour.SpectralDistribution`

595 Single *LED* spectral distribution.

596

597 Notes

598 -----

599 - By default, the spectral distribution will use the shape specified

600 by :attr:`colour.SPECTRAL_SHAPE_DEFAULT` attribute.

601

602 References

603 ----------

604 :cite:`Ohno2005`, :cite:`Ohno2008a`

605

606 Examples

607 --------

608 >>> sd = sd_single_led_Ohno2005(555, 25)

609 >>> sd.shape

610 SpectralShape(360.0, 780.0, 1.0)

611 >>> sd[555] # doctest: +ELLIPSIS

612 1...

613 """

614

615 settings = {

616 "name": f"{peak_wavelength}nm - {half_spectral_width} "

617 f"Half Spectral Width LED - Ohno (2005)"

618 }

619 settings.update(kwargs)

620

621 values = np.exp(

622 -(((shape.wavelengths - peak_wavelength) / half_spectral_width) ** 2)

623 )

624 values = (values + 2 * values**5) / 3

625

626 return SpectralDistribution(values, shape.wavelengths, **settings)

627

628

629 SD_SINGLE_LED_METHODS: CanonicalMapping = CanonicalMapping(

630 {

631 "Ohno 2005": sd_single_led_Ohno2005,

632 }

633)

634 SD_SINGLE_LED_METHODS.__doc__ = """

635 Supported single *LED* spectral distribution computation methods.

636"""

637

638

639 def sd_single_led(

640 peak_wavelength: float,

641 shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT,

642 method: Literal["Ohno 2005"] | str = "Ohno 2005",

643 **kwargs: Any,

644) -> SpectralDistribution:

645 """

646 Generate a single *LED* spectral distribution with the specified spectral

647 shape at the specified peak wavelength using the specified method.

648

649 Parameters

650 ----------

651 peak_wavelength

652 Wavelength the single *LED* spectral distribution will peak at.

653 shape

654 Spectral shape used to create the spectral distribution.

655 method

656 Computation method.

657

658 Other Parameters

659 ----------------

660 kwargs

661 {:func:`colour.colorimetry.sd_single_led_Ohno2005`},

662 See the documentation of the previously listed definition.

663

664 Returns

665 -------

666 :class:`colour.SpectralDistribution`

667 Single *LED* spectral distribution.

668

669 Notes

670 -----

671 - By default, the spectral distribution will use the shape specified by

672 :attr:`colour.SPECTRAL_SHAPE_DEFAULT` attribute.

673

674 References

675 ----------

676 :cite:`Ohno2005`, :cite:`Ohno2008a`

677

678 Examples

679 --------

680 >>> sd = sd_single_led(555, half_spectral_width=25)

681 >>> sd.shape

682 SpectralShape(360.0, 780.0, 1.0)

683 >>> sd[555] # doctest: +ELLIPSIS

684 1...

685 """

686

687 method = validate_method(method, tuple(SD_SINGLE_LED_METHODS))

688 kwargs["shape"] = shape

689

690 return SD_SINGLE_LED_METHODS[method](peak_wavelength, **kwargs)

691

692

693 def sd_multi_leds_Ohno2005(

694 peak_wavelengths: ArrayLike,

695 half_spectral_widths: ArrayLike,

696 peak_power_ratios: ArrayLike | None = None,

697 shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT,

698 **kwargs: Any,

699) -> SpectralDistribution:

700 """

701 Generate a multi-*LED* spectral distribution with the specified spectral

702 shape at specified peak wavelengths, half spectral widths

703 :math:`\\Delta\\lambda_{0.5}`, and peak power ratios according to the

704 *Ohno (2005)* method.

705

706 The multi-*LED* spectral distribution is computed by summing multiple

707 single *LED* spectral distributions generated with the

708 :func:`colour.sd_single_led_Ohno2005` function.

709

710 Parameters

711 ----------

712 peak_wavelengths

713 Wavelengths at which the multi-*LED* spectral distribution will

714 peak, i.e., the peak wavelengths for each constituent single *LED*

715 spectral distribution.

716 half_spectral_widths

717 Half spectral widths :math:`\\Delta\\lambda_{0.5}` for each

718 constituent single *LED* spectral distribution.

719 peak_power_ratios

720 Peak power ratios for each constituent single *LED* spectral

721 distribution. If not specified, defaults to unity for all *LEDs*.

722 shape

723 Spectral shape used to create the spectral distribution.

724

725 Other Parameters

726 ----------------

727 kwargs

728 {:func:`colour.colorimetry.sd_single_led_Ohno2005`},

729 See the documentation of the previously listed definition.

730

731 Returns

732 -------

733 :class:`colour.SpectralDistribution`

734 Multi-*LED* spectral distribution.

735

736 Notes

737 -----

738 - By default, the spectral distribution will use the shape specified

739 by :attr:`colour.SPECTRAL_SHAPE_DEFAULT` attribute.

740

741 References

742 ----------

743 :cite:`Ohno2005`, :cite:`Ohno2008a`

744

745 Examples

746 --------

747 >>> sd = sd_multi_leds_Ohno2005(

748 ... np.array([457, 530, 615]),

749 ... np.array([20, 30, 20]),

750 ... np.array([0.731, 1.000, 1.660]),

751 ... )

752 >>> sd.shape

753 SpectralShape(360.0, 780.0, 1.0)

754 >>> sd[500] # doctest: +ELLIPSIS

755 0.1295132...

756 """

757

758 peak_wavelengths = as_float_array(peak_wavelengths)

759 half_spectral_widths = np.resize(half_spectral_widths, peak_wavelengths.shape)

760 if peak_power_ratios is None:

761 peak_power_ratios = ones(peak_wavelengths.shape)

762 else:

763 peak_power_ratios = np.resize(peak_power_ratios, peak_wavelengths.shape)

764

765 sd = sd_zeros(shape)

766

767 for peak_wavelength, half_spectral_width, peak_power_ratio in zip(

768 peak_wavelengths, half_spectral_widths, peak_power_ratios, strict=True

769 ):

770 sd += (

771 sd_single_led_Ohno2005(peak_wavelength, half_spectral_width, **kwargs)

772 * peak_power_ratio

773 )

774

775 def _format_array(a: NDArrayFloat) -> str:

776 """Format specified array :math:`a`."""

777

778 return ", ".join([str(e) for e in a])

779

780 sd.name = (

781 f"{_format_array(peak_wavelengths)}nm - "

782 f"{_format_array(half_spectral_widths)} FWHM - "

783 f"{_format_array(peak_power_ratios)} Peak Power Ratios - "

784 f"LED - Ohno (2005)"

785 )

786

787 return sd

788

789

790 SD_MULTI_LEDS_METHODS: CanonicalMapping = CanonicalMapping(

791 {

792 "Ohno 2005": sd_multi_leds_Ohno2005,

793 }

794)

795 SD_MULTI_LEDS_METHODS.__doc__ = """

796 Supported multi-*LED* spectral distribution computation methods.

797"""

798

799

800 def sd_multi_leds(

801 peak_wavelengths: ArrayLike,

802 shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT,

803 method: Literal["Ohno 2005"] | str = "Ohno 2005",

804 **kwargs: Any,

805) -> SpectralDistribution:

806 """

807 Generate a multi-*LED* spectral distribution with the specified spectral

808 shape at specified peak wavelengths.

809

810 Parameters

811 ----------

812 peak_wavelengths

813 Wavelengths at which the multi-*LED* spectral distribution will

814 peak, i.e., the peak wavelengths for each generated single *LED*

815 spectral distribution.

816 shape

817 Spectral shape used to create the spectral distribution.

818 method

819 Computation method.

820

821 Other Parameters

822 ----------------

823 kwargs

824 {:func:`colour.colorimetry.sd_multi_leds_Ohno2005`},

825 See the documentation of the previously listed definition.

826

827 Returns

828 -------

829 :class:`colour.SpectralDistribution`

830 Multi-*LED* spectral distribution.

831

832 Notes

833 -----

834 - By default, the spectral distribution will use the shape specified

835 by :attr:`colour.SPECTRAL_SHAPE_DEFAULT` attribute.

836

837 References

838 ----------

839 :cite:`Ohno2005`, :cite:`Ohno2008a`

840

841 Examples

842 --------

843 >>> sd = sd_multi_leds(

844 ... np.array([457, 530, 615]),

845 ... half_spectral_widths=np.array([20, 30, 20]),

846 ... peak_power_ratios=np.array([0.731, 1.000, 1.660]),

847 ... )

848 >>> sd.shape

849 SpectralShape(360.0, 780.0, 1.0)

850 >>> sd[500] # doctest: +ELLIPSIS

851 0.1295132...

852 """

853

854 method = validate_method(method, tuple(SD_MULTI_LEDS_METHODS))

855 kwargs["shape"] = shape

856

857 return SD_MULTI_LEDS_METHODS[method](peak_wavelengths, **kwargs)