jerlov 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
jerlov/__init__.py ADDED
@@ -0,0 +1,51 @@
1
+ """Inherent optical properties of Jerlov water types.
2
+
3
+ Every coefficient carries its source, and values that a published table got
4
+ wrong are flagged rather than quietly repaired.
5
+ """
6
+
7
+ from .colour import (
8
+ CoverageWarning,
9
+ GamutWarning,
10
+ cie_1931_cmf,
11
+ d65,
12
+ integrate_response,
13
+ spectrum_to_srgb,
14
+ spectrum_to_xyz,
15
+ xyz_to_srgb,
16
+ )
17
+ from .scene import Observation, Scene, veiling_radiance_estimate
18
+ from .sources import SOURCES, Source, get_source
19
+ from .water import (
20
+ MissingQuantityError,
21
+ ProvenanceWarning,
22
+ Water,
23
+ b_from_c,
24
+ kd_spectrum,
25
+ water,
26
+ )
27
+
28
+ __all__ = [
29
+ "SOURCES",
30
+ "Scene",
31
+ "Observation",
32
+ "veiling_radiance_estimate",
33
+ "spectrum_to_xyz",
34
+ "spectrum_to_srgb",
35
+ "xyz_to_srgb",
36
+ "integrate_response",
37
+ "cie_1931_cmf",
38
+ "d65",
39
+ "GamutWarning",
40
+ "CoverageWarning",
41
+ "Source",
42
+ "get_source",
43
+ "Water",
44
+ "water",
45
+ "kd_spectrum",
46
+ "b_from_c",
47
+ "ProvenanceWarning",
48
+ "MissingQuantityError",
49
+ ]
50
+
51
+ __version__ = "0.1.1"
jerlov/_data.py ADDED
@@ -0,0 +1,113 @@
1
+ """Loading of the packaged coefficient tables.
2
+
3
+ Every table is a CSV shipped inside the package. Each row carries a ``status``
4
+ column recording what is known about that particular value; nothing is
5
+ silently cleaned up on the way in.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import csv
11
+ from functools import lru_cache
12
+ from importlib import resources
13
+
14
+ import numpy as np
15
+
16
+ #: Values whose ``status`` is one of these should not be used without the
17
+ #: caller being told. See README sections 1-6.
18
+ QUESTIONABLE = frozenset({"suspect", "missing", "extrapolated",
19
+ "model_extrapolation", "reconstructed"})
20
+
21
+
22
+ def _read(name: str) -> list[dict[str, str]]:
23
+ path = resources.files("jerlov.data").joinpath(name)
24
+ with path.open("r", encoding="utf-8", newline="") as handle:
25
+ return list(csv.DictReader(handle))
26
+
27
+
28
+ @lru_cache(maxsize=None)
29
+ def _rows(name: str) -> tuple[dict[str, str], ...]:
30
+ return tuple(_read(name))
31
+
32
+
33
+ def _to_float(text: str) -> float:
34
+ return float(text) if text.strip() else float("nan")
35
+
36
+
37
+ @lru_cache(maxsize=None)
38
+ def spectrum(
39
+ filename: str,
40
+ water_type: str,
41
+ quantity: str | None,
42
+ value_column: str,
43
+ ) -> tuple[np.ndarray, np.ndarray, tuple[str, ...]]:
44
+ """Return ``(wavelengths, values, statuses)`` for one series.
45
+
46
+ Rows whose value is empty are kept, with the value set to NaN, so that a
47
+ gap in the published table stays visible instead of being interpolated
48
+ across without comment.
49
+ """
50
+ wl: list[float] = []
51
+ values: list[float] = []
52
+ statuses: list[str] = []
53
+ for row in _rows(filename):
54
+ if row.get("water_type") != water_type:
55
+ continue
56
+ if quantity is not None and row.get("quantity") != quantity:
57
+ continue
58
+ wl.append(float(row["wavelength_nm"]))
59
+ values.append(_to_float(row[value_column]))
60
+ statuses.append(row.get("status", ""))
61
+ if not wl:
62
+ raise KeyError(
63
+ f"no rows in {filename} for water_type={water_type!r}"
64
+ + (f", quantity={quantity!r}" if quantity else "")
65
+ )
66
+ order = np.argsort(wl)
67
+ return (
68
+ np.asarray(wl, dtype=float)[order],
69
+ np.asarray(values, dtype=float)[order],
70
+ tuple(statuses[i] for i in order),
71
+ )
72
+
73
+
74
+ @lru_cache(maxsize=None)
75
+ def notes(filename: str, water_type: str, quantity: str | None) -> dict[float, str]:
76
+ """Map wavelength to the ``note`` column, for rows that carry one."""
77
+ out: dict[float, str] = {}
78
+ for row in _rows(filename):
79
+ if row.get("water_type") != water_type:
80
+ continue
81
+ if quantity is not None and row.get("quantity") != quantity:
82
+ continue
83
+ note = row.get("note", "").strip()
84
+ if note:
85
+ out[float(row["wavelength_nm"])] = note
86
+ return out
87
+
88
+
89
+ @lru_cache(maxsize=None)
90
+ def austin_model() -> tuple[np.ndarray, np.ndarray, np.ndarray]:
91
+ """Return ``(wavelengths, M, Kw)`` from Austin & Petzold (1986) Table IV."""
92
+ rows = _rows("austin1986_model.csv")
93
+ wl = np.array([float(r["wavelength_nm"]) for r in rows])
94
+ m = np.array([float(r["M_slope"]) for r in rows])
95
+ kw = np.array([float(r["Kw_pure_seawater_per_m"]) for r in rows])
96
+ order = np.argsort(wl)
97
+ return wl[order], m[order], kw[order]
98
+
99
+
100
+ @lru_cache(maxsize=None)
101
+ def b_from_c_ratio() -> tuple[np.ndarray, dict[str, np.ndarray]]:
102
+ """Return ``(wavelengths, {statistic: ratio})`` from Smart (2007) Table 1."""
103
+ rows = _rows("smart2007_b_from_c.csv")
104
+ wl = sorted({float(r["wavelength_nm"]) for r in rows})
105
+ out: dict[str, np.ndarray] = {}
106
+ for stat in ("average", "min", "max"):
107
+ by_wl = {
108
+ float(r["wavelength_nm"]): float(r["b_minus_bw_over_c_minus_cw"])
109
+ for r in rows
110
+ if r["statistic"] == stat
111
+ }
112
+ out[stat] = np.array([by_wl[w] for w in wl])
113
+ return np.array(wl), out
jerlov/colour.py ADDED
@@ -0,0 +1,255 @@
1
+ """From a spectrum to a colour, or to whatever a sensor or an eye would see.
2
+
3
+ Two things are easy to get wrong here and are checked rather than assumed.
4
+
5
+ **Coverage.** Integrating a spectrum that only spans 450-650 nm against
6
+ colour matching functions that span 360-830 nm silently drops the ends and
7
+ shifts the result. Every integration reports how much of the observer's
8
+ sensitivity the spectrum actually covered, and warns when the answer is not
9
+ essentially all of it.
10
+
11
+ **White.** A radiance spectrum has no colour until something is called white.
12
+ There is no default; the caller states the reference. Underwater the sensible
13
+ reference is usually the downwelling irradiance at that depth, which is what
14
+ makes a grey card look grey there rather than blue.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import warnings
20
+
21
+ import numpy as np
22
+
23
+ from . import _data
24
+
25
+ #: sRGB primaries and white point, IEC 61966-2-1.
26
+ SRGB_PRIMARIES = np.array([
27
+ [0.6400, 0.3300], # red
28
+ [0.3000, 0.6000], # green
29
+ [0.1500, 0.0600], # blue
30
+ ])
31
+ SRGB_WHITEPOINT_XY = np.array([0.3127, 0.3290]) # D65
32
+
33
+
34
+ def _xy_to_xyz(xy: np.ndarray) -> np.ndarray:
35
+ """Chromaticity to XYZ with Y = 1."""
36
+ x, y = xy
37
+ return np.array([x / y, 1.0, (1 - x - y) / y])
38
+
39
+
40
+ #: XYZ of the sRGB white, Y = 1. Adaptation maps a stated white onto this.
41
+ SRGB_WHITEPOINT_XYZ = _xy_to_xyz(SRGB_WHITEPOINT_XY)
42
+
43
+
44
+ class GamutWarning(UserWarning):
45
+ """A colour fell outside the range the display can show.
46
+
47
+ Underwater colours often do. Clipping changes them, so the caller is told
48
+ rather than left to wonder.
49
+ """
50
+
51
+
52
+ class CoverageWarning(UserWarning):
53
+ """The spectrum did not span the observer's sensitivity.
54
+
55
+ The integral is then over the overlap only, and the result is biased by
56
+ however much was left out.
57
+ """
58
+
59
+
60
+ def _rgb_matrix(primaries: np.ndarray, whitepoint_xy: np.ndarray) -> np.ndarray:
61
+ """Derive the XYZ to linear RGB matrix from chromaticities."""
62
+ x, y = primaries[:, 0], primaries[:, 1]
63
+ m = np.vstack([x / y, np.ones(3), (1 - x - y) / y])
64
+ scale = np.linalg.solve(m, _xy_to_xyz(whitepoint_xy))
65
+ return np.linalg.inv(m * scale)
66
+
67
+
68
+ #: Derived here rather than hard-coded, so the primaries stay the source of
69
+ #: truth. `tests/test_colour.py` checks it against the published matrix.
70
+ XYZ_TO_LINEAR_SRGB = _rgb_matrix(SRGB_PRIMARIES, SRGB_WHITEPOINT_XY)
71
+
72
+
73
+ def cie_1931_cmf() -> tuple[np.ndarray, np.ndarray]:
74
+ """Return ``(wavelengths, cmf)`` for the CIE 1931 2-degree observer.
75
+
76
+ ``cmf`` has shape ``(n, 3)``, columns x-bar, y-bar, z-bar, at 1 nm from
77
+ 360 to 830 nm.
78
+ """
79
+ rows = _data._rows("cie1931_2deg_cmf.csv")
80
+ wl = np.array([float(r["wavelength_nm"]) for r in rows])
81
+ cmf = np.array([
82
+ [float(r["x_bar"]), float(r["y_bar"]), float(r["z_bar"])] for r in rows
83
+ ])
84
+ return wl, cmf
85
+
86
+
87
+ def d65() -> tuple[np.ndarray, np.ndarray]:
88
+ """Return ``(wavelengths, relative power)`` for CIE illuminant D65.
89
+
90
+ This is the sRGB reference white, tabulated at 5 nm from 300 to 780 nm.
91
+
92
+ It is a daylight phase, so it is a reasonable stand-in for the solar
93
+ spectrum above the surface, but it is not a measurement of the light at
94
+ any particular place or time: the real spectrum depends on solar
95
+ elevation, atmosphere and the state of the surface.
96
+ """
97
+ rows = _data._rows("cie_d65.csv")
98
+ wl = np.array([float(r["wavelength_nm"]) for r in rows])
99
+ power = np.array([float(r["relative_spectral_power"]) for r in rows])
100
+ return wl, power
101
+
102
+
103
+ def _resample(values, source_wl, target_wl) -> np.ndarray:
104
+ """Interpolate onto ``target_wl``, leaving zeros outside the source."""
105
+ return np.interp(target_wl, source_wl, values, left=0.0, right=0.0)
106
+
107
+
108
+ def _coverage(spectrum_wl, weight_wl, weight) -> float:
109
+ """Fraction of the weight that the spectrum's range actually spans."""
110
+ total = np.trapezoid(weight, weight_wl)
111
+ if total == 0:
112
+ return 1.0
113
+ inside = (weight_wl >= spectrum_wl[0]) & (weight_wl <= spectrum_wl[-1])
114
+ if not inside.any():
115
+ return 0.0
116
+ return float(np.trapezoid(weight[inside], weight_wl[inside]) / total)
117
+
118
+
119
+ def integrate_response(spectrum, wavelengths, response, response_wavelengths,
120
+ *, name: str = "response") -> np.ndarray:
121
+ """Integrate a spectrum against one or more spectral sensitivities.
122
+
123
+ Parameters
124
+ ----------
125
+ spectrum:
126
+ Spectral radiance or irradiance, on ``wavelengths``.
127
+ response:
128
+ Shape ``(m,)`` for a single channel or ``(m, k)`` for ``k`` channels,
129
+ on ``response_wavelengths``. Camera sensitivities and photoreceptor
130
+ absorbances both fit here.
131
+
132
+ Returns
133
+ -------
134
+ Array of shape ``(k,)``: the integral of spectrum times each channel over
135
+ wavelength, in the units of the spectrum times nm.
136
+ """
137
+ spectrum = np.asarray(spectrum, dtype=float)
138
+ wavelengths = np.asarray(wavelengths, dtype=float)
139
+ response = np.atleast_2d(np.asarray(response, dtype=float))
140
+ if response.shape[0] != np.size(response_wavelengths):
141
+ response = response.T
142
+ response_wavelengths = np.asarray(response_wavelengths, dtype=float)
143
+ if spectrum.shape != wavelengths.shape:
144
+ raise ValueError("spectrum must have the same shape as wavelengths")
145
+ if response.shape[0] != response_wavelengths.shape[0]:
146
+ raise ValueError("response must be aligned with response_wavelengths")
147
+ if wavelengths.size < 2:
148
+ raise ValueError("at least two wavelengths are needed to integrate")
149
+
150
+ worst = min(
151
+ _coverage(wavelengths, response_wavelengths, response[:, k])
152
+ for k in range(response.shape[1])
153
+ )
154
+ if worst < 0.999:
155
+ warnings.warn(
156
+ f"the spectrum spans {wavelengths[0]:g}-{wavelengths[-1]:g} nm and "
157
+ f"covers only {worst:.1%} of the {name}; the integral is over the "
158
+ "overlap and is biased by what was left out",
159
+ CoverageWarning,
160
+ stacklevel=2,
161
+ )
162
+
163
+ resampled = np.stack(
164
+ [_resample(response[:, k], response_wavelengths, wavelengths)
165
+ for k in range(response.shape[1])],
166
+ axis=1,
167
+ )
168
+ return np.trapezoid(spectrum[:, None] * resampled, wavelengths, axis=0)
169
+
170
+
171
+ def spectrum_to_xyz(spectrum, wavelengths) -> np.ndarray:
172
+ """Integrate a spectrum against the CIE 1931 2-degree observer.
173
+
174
+ The result is unnormalised: it carries the units of the spectrum. Divide
175
+ by the XYZ of whatever counts as white before converting to a display
176
+ colour, or use :func:`spectrum_to_srgb`, which requires that reference.
177
+ """
178
+ cmf_wl, cmf = cie_1931_cmf()
179
+ return integrate_response(
180
+ spectrum, wavelengths, cmf, cmf_wl, name="CIE 1931 2-degree observer"
181
+ )
182
+
183
+
184
+ def xyz_to_srgb(xyz, *, clip: bool = True) -> np.ndarray:
185
+ """Convert XYZ, normalised so that white is Y = 1, to sRGB.
186
+
187
+ Applies the sRGB transfer function of IEC 61966-2-1. Warns if any channel
188
+ falls outside the display gamut, which underwater colours often do.
189
+ """
190
+ xyz = np.asarray(xyz, dtype=float)
191
+ linear = np.tensordot(xyz, XYZ_TO_LINEAR_SRGB.T, axes=([-1], [0]))
192
+
193
+ outside = np.any(linear < -1e-9) or np.any(linear > 1 + 1e-9)
194
+ if outside:
195
+ warnings.warn(
196
+ "the colour lies outside the sRGB gamut"
197
+ + (" and has been clipped" if clip else ""),
198
+ GamutWarning,
199
+ stacklevel=2,
200
+ )
201
+ if clip:
202
+ linear = np.clip(linear, 0.0, 1.0)
203
+
204
+ a = 0.055
205
+ with np.errstate(invalid="ignore"):
206
+ return np.where(
207
+ linear <= 0.0031308,
208
+ 12.92 * linear,
209
+ (1 + a) * np.power(np.maximum(linear, 0.0), 1 / 2.4) - a,
210
+ )
211
+
212
+
213
+ def spectrum_to_srgb(spectrum, wavelengths, *, white, clip: bool = True):
214
+ """Convert a spectrum to sRGB, relative to a stated white.
215
+
216
+ Parameters
217
+ ----------
218
+ white:
219
+ The spectrum that should come out white, on the same wavelengths.
220
+ There is no default: a radiance spectrum has no colour until
221
+ something is called white, and underwater the answer is usually the
222
+ downwelling irradiance at that depth rather than a surface daylight.
223
+
224
+ Notes
225
+ -----
226
+ The spectrum's XYZ is scaled component by component so that the stated
227
+ white lands on the sRGB white point, which makes it come out exactly
228
+ neutral. This is a von Kries-type adaptation carried out in XYZ rather
229
+ than in cone space: transparent, and less accurate than CAT02 or Bradford
230
+ for strongly coloured illumination.
231
+ Underwater illumination is strongly coloured, so treat the result as
232
+ "what a white-balanced camera would record" rather than as a prediction
233
+ of appearance.
234
+
235
+ Absolute brightness is lost: a perfect diffuser under the stated white
236
+ maps to white whatever the light level.
237
+ """
238
+ if white is None:
239
+ raise ValueError(
240
+ "white has no default: a spectrum has no colour until something "
241
+ "is called white. Underwater this is usually the downwelling "
242
+ "irradiance at that depth."
243
+ )
244
+ white = np.asarray(white, dtype=float)
245
+ if white.shape != np.shape(spectrum):
246
+ raise ValueError("white must have the same shape as the spectrum")
247
+
248
+ xyz = spectrum_to_xyz(spectrum, wavelengths)
249
+ white_xyz = spectrum_to_xyz(white, wavelengths)
250
+ if np.any(white_xyz <= 0):
251
+ raise ValueError(
252
+ "the white reference has no power in one of X, Y or Z, so it "
253
+ "cannot serve as a white"
254
+ )
255
+ return xyz_to_srgb(xyz / white_xyz * SRGB_WHITEPOINT_XYZ, clip=clip)
@@ -0,0 +1 @@
1
+ """Packaged coefficient tables. See the project README for provenance."""
@@ -0,0 +1,91 @@
1
+ water_type,wavelength_nm,Kd_downwelling_per_m,status,note
2
+ I,350,0.0510,pure_seawater,the type I row is pure sea water Kw; Jerlov's own values fall below it
3
+ I,375,0.0302,pure_seawater,the type I row is pure sea water Kw; Jerlov's own values fall below it
4
+ I,400,0.0217,pure_seawater,the type I row is pure sea water Kw; Jerlov's own values fall below it
5
+ I,425,0.0185,pure_seawater,the type I row is pure sea water Kw; Jerlov's own values fall below it
6
+ I,450,0.0176,pure_seawater,the type I row is pure sea water Kw; Jerlov's own values fall below it
7
+ I,475,0.0184,pure_seawater,the type I row is pure sea water Kw; Jerlov's own values fall below it
8
+ I,500,0.0280,pure_seawater,the type I row is pure sea water Kw; Jerlov's own values fall below it
9
+ I,525,0.0504,pure_seawater,the type I row is pure sea water Kw; Jerlov's own values fall below it
10
+ I,550,0.0640,pure_seawater,the type I row is pure sea water Kw; Jerlov's own values fall below it
11
+ I,575,0.0931,pure_seawater,the type I row is pure sea water Kw; Jerlov's own values fall below it
12
+ I,600,0.2408,pure_seawater,the type I row is pure sea water Kw; Jerlov's own values fall below it
13
+ I,625,0.3174,pure_seawater,the type I row is pure sea water Kw; Jerlov's own values fall below it
14
+ I,650,0.3559,pure_seawater,the type I row is pure sea water Kw; Jerlov's own values fall below it
15
+ I,675,0.4372,pure_seawater,the type I row is pure sea water Kw; Jerlov's own values fall below it
16
+ I,700,0.6513,pure_seawater,the type I row is pure sea water Kw; Jerlov's own values fall below it
17
+ IA,350,0.0632,model,computed from K(475) by Austin & Petzold Eq. (6)
18
+ IA,375,0.0412,model,computed from K(475) by Austin & Petzold Eq. (6)
19
+ IA,400,0.0316,model,computed from K(475) by Austin & Petzold Eq. (6)
20
+ IA,425,0.0280,model,computed from K(475) by Austin & Petzold Eq. (6)
21
+ IA,450,0.0257,model,computed from K(475) by Austin & Petzold Eq. (6)
22
+ IA,475,0.0250,jerlov_original,475 nm is identical to Jerlov (1976) Table XXVII
23
+ IA,500,0.0332,model,computed from K(475) by Austin & Petzold Eq. (6)
24
+ IA,525,0.0545,model,computed from K(475) by Austin & Petzold Eq. (6)
25
+ IA,550,0.0674,model,computed from K(475) by Austin & Petzold Eq. (6)
26
+ IA,575,0.0960,model,computed from K(475) by Austin & Petzold Eq. (6)
27
+ IA,600,0.2437,model,computed from K(475) by Austin & Petzold Eq. (6)
28
+ IA,625,0.3206,model,computed from K(475) by Austin & Petzold Eq. (6)
29
+ IA,650,0.3601,model,computed from K(475) by Austin & Petzold Eq. (6)
30
+ IA,675,0.4410,model,computed from K(475) by Austin & Petzold Eq. (6)
31
+ IA,700,0.6530,model,computed from K(475) by Austin & Petzold Eq. (6)
32
+ IB,350,0.0782,model,computed from K(475) by Austin & Petzold Eq. (6)
33
+ IB,375,0.0546,model,computed from K(475) by Austin & Petzold Eq. (6)
34
+ IB,400,0.0438,model,computed from K(475) by Austin & Petzold Eq. (6)
35
+ IB,425,0.0395,model,computed from K(475) by Austin & Petzold Eq. (6)
36
+ IB,450,0.0355,model,computed from K(475) by Austin & Petzold Eq. (6)
37
+ IB,475,0.0330,jerlov_original,475 nm is identical to Jerlov (1976) Table XXVII
38
+ IB,500,0.0396,model,computed from K(475) by Austin & Petzold Eq. (6)
39
+ IB,525,0.0596,model,computed from K(475) by Austin & Petzold Eq. (6)
40
+ IB,550,0.0715,model,computed from K(475) by Austin & Petzold Eq. (6)
41
+ IB,575,0.0995,model,computed from K(475) by Austin & Petzold Eq. (6)
42
+ IB,600,0.2471,model,computed from K(475) by Austin & Petzold Eq. (6)
43
+ IB,625,0.3245,model,computed from K(475) by Austin & Petzold Eq. (6)
44
+ IB,650,0.3652,model,computed from K(475) by Austin & Petzold Eq. (6)
45
+ IB,675,0.4457,model,computed from K(475) by Austin & Petzold Eq. (6)
46
+ IB,700,0.6550,model,computed from K(475) by Austin & Petzold Eq. (6)
47
+ II,350,0.1325,model,computed from K(475) by Austin & Petzold Eq. (6)
48
+ II,375,0.1031,model,computed from K(475) by Austin & Petzold Eq. (6)
49
+ II,400,0.0878,model,computed from K(475) by Austin & Petzold Eq. (6)
50
+ II,425,0.0814,model,computed from K(475) by Austin & Petzold Eq. (6)
51
+ II,450,0.0714,model,computed from K(475) by Austin & Petzold Eq. (6)
52
+ II,475,0.0620,jerlov_original,475 nm is identical to Jerlov (1976) Table XXVII
53
+ II,500,0.0627,model,computed from K(475) by Austin & Petzold Eq. (6)
54
+ II,525,0.0779,model,computed from K(475) by Austin & Petzold Eq. (6)
55
+ II,550,0.0863,model,computed from K(475) by Austin & Petzold Eq. (6)
56
+ II,575,0.1122,model,computed from K(475) by Austin & Petzold Eq. (6)
57
+ II,600,0.2595,model,computed from K(475) by Austin & Petzold Eq. (6)
58
+ II,625,0.3389,model,computed from K(475) by Austin & Petzold Eq. (6)
59
+ II,650,0.3837,model,computed from K(475) by Austin & Petzold Eq. (6)
60
+ II,675,0.4626,model,computed from K(475) by Austin & Petzold Eq. (6)
61
+ II,700,0.6623,model,computed from K(475) by Austin & Petzold Eq. (6)
62
+ III,350,0.2335,model,computed from K(475) by Austin & Petzold Eq. (6)
63
+ III,375,0.1935,model,computed from K(475) by Austin & Petzold Eq. (6)
64
+ III,400,0.1697,model,computed from K(475) by Austin & Petzold Eq. (6)
65
+ III,425,0.1594,model,computed from K(475) by Austin & Petzold Eq. (6)
66
+ III,450,0.1381,model,computed from K(475) by Austin & Petzold Eq. (6)
67
+ III,475,0.1160,jerlov_original,475 nm is identical to Jerlov (1976) Table XXVII
68
+ III,500,0.1056,model,computed from K(475) by Austin & Petzold Eq. (6)
69
+ III,525,0.1120,model,computed from K(475) by Austin & Petzold Eq. (6)
70
+ III,550,0.1139,model,computed from K(475) by Austin & Petzold Eq. (6)
71
+ III,575,0.1359,model,computed from K(475) by Austin & Petzold Eq. (6)
72
+ III,600,0.2826,model,computed from K(475) by Austin & Petzold Eq. (6)
73
+ III,625,0.3655,model,computed from K(475) by Austin & Petzold Eq. (6)
74
+ III,650,0.4181,model,computed from K(475) by Austin & Petzold Eq. (6)
75
+ III,675,0.4942,model,computed from K(475) by Austin & Petzold Eq. (6)
76
+ III,700,0.6760,model,computed from K(475) by Austin & Petzold Eq. (6)
77
+ 1C,350,0.3345,model,computed from K(475) by Austin & Petzold Eq. (6)
78
+ 1C,375,0.2839,model,computed from K(475) by Austin & Petzold Eq. (6)
79
+ 1C,400,0.2516,model,computed from K(475) by Austin & Petzold Eq. (6)
80
+ 1C,425,0.2374,model,computed from K(475) by Austin & Petzold Eq. (6)
81
+ 1C,450,0.2048,model,computed from K(475) by Austin & Petzold Eq. (6)
82
+ 1C,475,0.1700,jerlov_original,475 nm is identical to Jerlov (1976) Table XXVII
83
+ 1C,500,0.1486,model,computed from K(475) by Austin & Petzold Eq. (6)
84
+ 1C,525,0.1461,model,computed from K(475) by Austin & Petzold Eq. (6)
85
+ 1C,550,0.1415,model,computed from K(475) by Austin & Petzold Eq. (6)
86
+ 1C,575,0.1596,model,computed from K(475) by Austin & Petzold Eq. (6)
87
+ 1C,600,0.3057,model,computed from K(475) by Austin & Petzold Eq. (6)
88
+ 1C,625,0.3922,model,computed from K(475) by Austin & Petzold Eq. (6)
89
+ 1C,650,0.4525,model,computed from K(475) by Austin & Petzold Eq. (6)
90
+ 1C,675,0.5257,model,computed from K(475) by Austin & Petzold Eq. (6)
91
+ 1C,700,0.6896,model,computed from K(475) by Austin & Petzold Eq. (6)
@@ -0,0 +1,72 @@
1
+ wavelength_nm,M_slope,Kw_pure_seawater_per_m,status,note
2
+ 350,2.1442,0.0510,extrapolated,the paper states this M was extrapolated and should be used with caution
3
+ 355,2.0968,0.0453,extrapolated,the paper states this M was extrapolated and should be used with caution
4
+ 360,2.0504,0.0405,extrapolated,the paper states this M was extrapolated and should be used with caution
5
+ 365,2.0051,0.0365,ok,
6
+ 370,1.9610,0.0331,ok,
7
+ 375,1.9183,0.0302,ok,
8
+ 380,1.8772,0.0278,ok,
9
+ 385,1.8379,0.0258,ok,
10
+ 390,1.8009,0.0242,ok,
11
+ 395,1.7671,0.0228,ok,
12
+ 400,1.7383,0.0217,ok,
13
+ 405,1.7463,0.0208,ok,
14
+ 410,1.7591,0.0200,ok,
15
+ 415,1.7312,0.0194,ok,
16
+ 420,1.6974,0.0189,ok,
17
+ 425,1.6550,0.0185,ok,
18
+ 430,1.6108,0.0182,ok,
19
+ 435,1.5648,0.0180,ok,
20
+ 440,1.5169,0.0178,ok,
21
+ 445,1.4673,0.0176,ok,
22
+ 450,1.4158,0.0176,ok,
23
+ 455,1.3627,0.0175,ok,
24
+ 460,1.3077,0.0176,ok,
25
+ 465,1.2521,0.0177,ok,
26
+ 470,1.1982,0.0179,ok,
27
+ 475,1.1460,0.0184,ok,
28
+ 480,1.0955,0.0193,ok,
29
+ 485,1.0469,0.0206,ok,
30
+ 490,1.0000,0.0224,ok,
31
+ 495,0.9550,0.0248,ok,
32
+ 500,0.9118,0.0280,ok,
33
+ 505,0.8704,0.0320,ok,
34
+ 510,0.8310,0.0369,ok,
35
+ 515,0.7934,0.0428,ok,
36
+ 520,0.7578,0.0498,ok,
37
+ 525,0.7241,0.0504,ok,
38
+ 530,0.6924,0.0526,ok,
39
+ 535,0.6627,0.0550,ok,
40
+ 540,0.6350,0.0577,ok,
41
+ 545,0.6094,0.0607,ok,
42
+ 550,0.5860,0.0640,ok,
43
+ 555,0.5647,0.0678,ok,
44
+ 560,0.5457,0.0723,ok,
45
+ 565,0.5289,0.0776,ok,
46
+ 570,0.5146,0.0842,ok,
47
+ 575,0.5027,0.0931,ok,
48
+ 580,0.4935,0.1065,ok,
49
+ 585,0.4871,0.1341,ok,
50
+ 590,0.4840,0.1578,ok,
51
+ 595,0.4853,0.2043,ok,
52
+ 600,0.4903,0.2409,ok,
53
+ 605,0.4983,0.2688,ok,
54
+ 610,0.5090,0.2892,ok,
55
+ 615,0.5223,0.3040,ok,
56
+ 620,0.5380,0.3124,ok,
57
+ 625,0.5659,0.3174,ok,
58
+ 630,0.6231,0.3196,ok,
59
+ 635,0.6683,0.3227,ok,
60
+ 640,0.7001,0.3290,ok,
61
+ 645,0.7201,0.3397,ok,
62
+ 650,0.7300,0.3559,ok,
63
+ 655,0.7323,0.3789,ok,
64
+ 660,0.7301,0.4105,ok,
65
+ 665,0.7205,0.4208,ok,
66
+ 670,0.7008,0.4278,ok,
67
+ 675,0.6693,0.4372,ok,
68
+ 680,0.6245,0.4521,ok,
69
+ 685,0.5651,0.4755,ok,
70
+ 690,0.4901,0.5116,ok,
71
+ 695,0.3984,0.5671,ok,
72
+ 700,0.2891,0.6514,ok,