Skip to content
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion codecs/scale_offset/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,12 @@ The following snippet of array metadata demonstrates the metadata for the `scale

### Uint16 range reduction

In this example, a `uint16` array with values in the range `[1000, 1255]` is shifted down by `1000` so that values fall in the range `[0, 255]`, then cast to `uint8` via the `cast_value` codec.
In this example, a `uint16` array with values in the range `[1000, 1255]` is shifted down by `1000` so that values fall in the range `[0, 255]`, then cast to `uint8` via the `cast_value` codec. Note that with these codecs,`fill_value` is constrained to the range `[1000, 1255]`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can handle fill values outside the decoded range of [1000, 1255] by prepending a cast_value codec with a scalar map that sends fill_value to -> 1256 on the encode path, and sends 1256 -> fill_value on the decode path. Would that example be helpful here?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this would make the transformation lossy because we would be squeezing 257 input values (256 consecutive numbers + fill value) into 256 outputs, so maybe we need to reduce the number of input values by 1

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would that example be helpful here?

Yes, I agree that would be useful too. I think it'd be useful to include two examples

  1. this one - with a "simple" cast_value codec that emphasizes the constraints on fill_value
  2. a more involved cast_value like what you suggest to make fill_value less constrained.'

If you agree, I can add (2).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so maybe we need to reduce the number of input values by 1

agreed. maybe we show that for what I'm calling example (2) above.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that would be great!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, latest commit adds such an example, though as you'll see I decided to just map 0 -> 1000 without shrinkng the range of values as I feel it gets the point across, but feel free to edit if you prefer.


```json
{
"data_type": "uint16",
"fill_value": 1000,
"codecs": [
{
"name": "scale_offset",
Expand All @@ -113,6 +114,41 @@ In this example, a `uint16` array with values in the range `[1000, 1255]` is shi
}
```

Using a adding a `cast_value` codec with a `scalar_map` makes it possible for any `fill_value` to be used by mapping it into the valid range without changing the data type. Here, first mapping the `fill_value` `0 → 1000` has equivalent behavior to the example above.
Comment thread
bogovicj marked this conversation as resolved.
Outdated

```json
{
"data_type": "uint16",
"fill_value": 0,
"codecs": [
{
"name": "cast_value",
"configuration": {
"data_type": "uint16"
"scalar_map": {
"encode": [[0, 1000]],
"decode": [[1000, 0]]
}
}
},
{
"name": "scale_offset",
"configuration": {
"offset": 1000
}
},
{
"name": "cast_value",
"configuration": {
"data_type": "uint8"
}
},
"bytes"
]
}
```


### Float64 to uint8 with NaN preservation

In this example, a `float64` array with values in the range `[0.0, 2540.0]` and a fill value of `NaN` is stored as `uint8`. The `scale_offset` codec maps values from `[0.0, 2540.0]` to `[1.0, 255.0]` by applying `(x - offset) * scale` with `offset = -10` and `scale = 0.1`, reserving `0` for `NaN`. The `cast_value` codec then casts to `uint8`, using `scalar_map` to explicitly map `NaN` to `0` on encode and `0` to `NaN` on decode. This ensures a lossless round-trip for the fill value. There is no such assurance for
Expand Down