Pixel-based Linear Time Series Normalizer
Project description
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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pixltsnorm-0.1.0.tar.gz.
File metadata
- Download URL: pixltsnorm-0.1.0.tar.gz
- Upload date:
- Size: 26.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9cb761a92b517a16516c1da8561c73a6bb504c441f2ea58f6ace9e51f7ae3805
|
|
| MD5 |
d2a83ec4c7a23426214226edc5d0d714
|
|
| BLAKE2b-256 |
c9fcb37209d01e2505b2713d36caa17a4f27e52a70401d662717c3f843932031
|
File details
Details for the file pixltsnorm-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pixltsnorm-0.1.0-py3-none-any.whl
- Upload date:
- Size: 22.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
78b95c10add671934f2c350ca636f14e9438c98ec88d66b0f648cf5a51e6fb70
|
|
| MD5 |
c060fdad634d5e55e1374745844c669b
|
|
| BLAKE2b-256 |
b98f24ee1971643c53916c1a6e710ffad06b6d4304d51987b57bc74d05d7f32e
|