pixltsnorm
Pixel-based Linear Time Series Normalizer
pixltsnorm is a small, focused Python library that:
- Bridges or harmonizes numeric time-series data (e.g., reflectance, NDVI, etc.) across multiple sensors or sources.
- Fits simple linear transformations (
y = slope*x + intercept) to map one sensor’s scale onto another. - Chains transformations to handle indirect overlaps (sensor0 → sensor1 → …).
- Filters outliers using threshold-based filtering before fitting linear models.
Although originally inspired by NDVI normalization across different Landsat sensors, pixltsnorm is domain-agnostic. You can use it for any numeric time-series that needs linear alignment.
Features
-
Outlier Filtering
- Removes large disagreements in overlapping time-series pairs, based on a simple threshold for |A - B|.
-
Local (Pixel-Level) Linear Bridging
- Regress one sensor’s measurements onto another’s (e.g., a single pixel’s time series).
- Produces an easy-to-apply transform function for new data.
-
Global Bridging
- Follows the approach of Roy et al. (2016): gather all overlapping values across the entire dataset, fit one “universal” slope/intercept.
- Useful if you need scene-wide or region-wide continuity between two or more sensors (e.g., L5 → L7 → L8).
-
Chaining
- Allows any number of sensors to be combined in sequence, producing a single transform from the first sensor to the last.
-
Lightweight
- Minimal dependencies:
numpy,scikit-learn, and optionallypandas.
- Minimal dependencies:
-
Earth Engine Submodule
- A dedicated
earth_enginesubpackage provides GEE-specific helpers (e.g., for Landsat) that you can incorporate in your Earth Engine workflows.
- A dedicated
Basic Usage
Harmonize Two Sensors (Pixel-Level Example)
import numpy as np
from pixltsnorm.harmonize import harmonize_series
# Suppose sensorA_values and sensorB_values have overlapping data
sensorA = np.array([0.0, 0.2, 0.8, 0.9])
sensorB = np.array([0.1, 0.25, 0.7, 1.0])
results = harmonize_series(sensorA, sensorB, outlier_threshold=0.2)
print("Slope:", results['coef'])
print("Intercept:", results['intercept'])
# Transform new data from sensorA scale -> sensorB scale
transform_func = results['transform']
new_data = np.array([0.3, 0.4, 0.5])
mapped = transform_func(new_data)
print("Mapped values:", mapped)
Chaining Multiple Sensors
from pixltsnorm.harmonize import chain_harmonization
import numpy as np
# Suppose we have 4 different sensors that partially overlap:
sensor0 = np.random.rand(10)
sensor1 = np.random.rand(10)
sensor2 = np.random.rand(10)
sensor3 = np.random.rand(10)
chain_result = chain_harmonization([sensor0, sensor1, sensor2, sensor3])
print("Pairwise transforms:", chain_result['pairwise'])
print("Overall slope (sensor0->sensor3):", chain_result['final_slope'])
print("Overall intercept (sensor0->sensor3):", chain_result['final_intercept'])
# Apply sensor0 -> sensor3 transform
sensor0_on_sensor3_scale = (chain_result['final_slope'] * sensor0
+ chain_result['final_intercept'])
print("sensor0 mapped onto sensor3 scale:", sensor0_on_sensor3_scale)
Global Bridging
import pandas as pd
from pixltsnorm.global_harmonize import chain_global_bridging
# Suppose we have three DataFrames: df_l5, df_l7, df_l8
# Each has row=pixels, columns=dates (plus 'lon','lat').
# The approach merges all overlapping values across the region/time:
result = chain_global_bridging(df_l5, df_l7, df_l8, outlier_thresholds=(0.2, 0.2))
# We get a single slope/intercept for L5->L7, L7->L8, plus the chain L5->L8
print("Global bridging L5->L7 =>", result["L5->L7"]["coef"], result["L5->L7"]["intercept"])
print("Global bridging L7->L8 =>", result["L7->L8"]["coef"], result["L7->L8"]["intercept"])
print("Chained L5->L8 =>", result["L5->L8"]["coef"], result["L5->L8"]["intercept"])
Earth Engine Submodule
from pixltsnorm.earth_engine import create_reduce_region_function, addNDVI, cloudMaskL457
# Use these GEE-based helpers inside your Earth Engine scripts
Please see the docs and example notebooks for more examples.
Installation
- Clone or download this repository.
- (Optional) Create and activate a virtual environment.
- Install in editable mode:
pip install -e .
Then you can do:
import pixltsnorm
and access the library’s functionality.
Acknowledgements
- Joseph Emile Honour Percival performed the initial research in 2021 during his PhD at Kyoto University, where the pixel-level time-series normalization idea was first applied to multi-sensor analysis.
- The global bridging logic is inspired by Roy et al. (2016), which outlines regression-based continuity for Landsat sensors across large areas.
Roy, David P., V. Kovalskyy, H. K. Zhang, Eric F. Vermote, L. Yan, S. S. Kumar, and A. Egorov. "Characterization of Landsat-7 to Landsat-8 reflective wavelength and normalized difference vegetation index continuity." Remote sensing of Environment 185 (2016): 57-70.
License
This project is licensed under the MIT License. See the LICENSE file for details.
Metadata
Release files for pixltsnorm 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pixltsnorm-0.1.0.tar.gz | 26.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pixltsnorm-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 48.6 kB
Release files / pixltsnorm-0.1.0.tar.gz
| Download URL | pixltsnorm-0.1.0.tar.gz |
|---|---|
| Size | 26.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9cb761a92b517a16516c1da8561c73a6bb504c441f2ea58f6ace9e51f7ae3805
|
|
BLAKE2b-256 checksum How to use checksums |
c9fcb37209d01e2505b2713d36caa17a4f27e52a70401d662717c3f843932031
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.7
|
Release files / pixltsnorm-0.1.0-py3-none-any.whl
| Download URL | pixltsnorm-0.1.0-py3-none-any.whl |
|---|---|
| Size | 22.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
78b95c10add671934f2c350ca636f14e9438c98ec88d66b0f648cf5a51e6fb70
|
|
BLAKE2b-256 checksum How to use checksums |
b98f24ee1971643c53916c1a6e710ffad06b6d4304d51987b57bc74d05d7f32e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.7
|