DTACSNet: onboard cloud detection and atmospheric correction with end-to-end deep learning emulators
This repo contains an open implementation to run inference with DTACSNet models for atmospheric correction and also for cloud detection. These two models are independent (you could run one or the other or both). The trained models provided here are customized to the band configuration that will be available in Phi-Sat-II (excluding PAN band). Trained models are released under a Creative Commons non-commercial licence
.
> C. Aybar et al., Onboard Cloud Detection and Atmospheric Correction With Efficient Deep Learning Models, IEEE Journal of Selected Topics in Applied Earth Observations and Remote Sensing, vol. 17, pp. 19518–19529, Oct. 2024, doi: 10.1109/JSTARS.2024.3480520.
Phi-Sat-II simulated image run
In this notebook we run the DTACSNet model over a simulated Phi-Sat-II image. To run this tutorial we downloaded this image from here and save it in the tutorials/examples
folder.
# pip install dtacs
Load data
Load the simulated Phi-Sat-II image. We multiply the values by 10,000 to follow the same convention as in Sentinel-2.
import rasterio
import rasterio.windows
import os
phisat2_bands = ["B2","B3","B4","B5","B6","B7","B8"]
PHISAT2_PAN_BANDS = ["B2", "B3", "B4", "PAN", "B8", "B5", "B6", "B7"]
folder_examples = "examples"
with rasterio.open("https://cloud.sinergise.com/s/RSgHGwWWCxYp5yq/download/phisat2-l1c.tiff")) as rst:
indexes_read = [PHISAT2_PAN_BANDS.index(b) + 1 for b in phisat2_bands]
data = rst.read(indexes_read).astype("float32") * 10_000 # Convert to the same units as S2 TOA images
transform_phisat2 = rst.transform
data.shape
# Data are TOA reflectance values. We multiply them by 10_000 to use the same convention as in Sentinel-2
data
Atmospheric correction model
Load model
from dtacs.model_wrapper import ACModel
import torch
model_atmospheric_correction = ACModel(model_name="CNN_corrector_phisat2")
model_atmospheric_correction.load_weights()
Run inference
%%time
ac_output = model_atmospheric_correction.predict(data)
ac_output
Plot
import matplotlib.pyplot as plt
from dtacs import plot
import rasterio.plot as rstplt
fig, ax = plt.subplots(2,2,figsize=(12,12),tight_layout=True)
rstplt.show(data[2::-1,...]/4_000,ax=ax[0,0])
ax[0,0].set_title("RGB PhiSat-II L1C")
rstplt.show(ac_output[2::-1,...]/4_000,ax=ax[0,1])
ax[0,1].set_title("RGB PhiSat-II L2A (DTACSNet corrected)")
nirredgreen = [-1, 2, 1]
rstplt.show(data[nirredgreen,...]/10_000,ax=ax[1,0])
ax[1,0].set_title("NIRRG PhiSat-II L1C")
rstplt.show(ac_output[nirredgreen,...]/10_000,ax=ax[1,1])
ax[1,1].set_title("NIRRG PhiSat-II L2A (DTACSNet corrected)")
Compare L2A Phi-Sat-II image with L2A Sentinel-2 image
The simulated Phi-Sat-II image that we have shown below has been created from the Sentinel-2 tile: S2A_MSIL1C_20191111T132241_N0208_R038_T23LLK_20191111T145626
. We have copied a 500x500 subset of this tile in the tutorials/examples
folder. We have also downloaded the corresponding L2A Sentinel-2 file to compare the atmospheric correction output of the two products.
# Load Sentinel-2 L2A
with rasterio.open("https://github.com/spaceml-org/DTACSNet/raw/refs/heads/main/tutorials/examples/S2L2A.tif")) as rst:
indexes_read = [rst.descriptions.index(b) + 1 for b in phisat2_bands]
data_l2a = rst.read(indexes_read)
# Load Sentinel-2 L1C
with rasterio.open(os.path.join(folder_examples,"S2L1C.tif")) as rst:
indexes_read = [rst.descriptions.index(b) + 1 for b in phisat2_bands]
data_l1c = rst.read(indexes_read)
bounds = rst.bounds
window_in = rasterio.windows.from_bounds(*bounds, transform=transform_phisat2)
slice_read = (slice(None),) + window_in.toslices()
fig, ax = plt.subplots(2,4,figsize=(24,12),tight_layout=True)
rstplt.show(data[2::-1,...][slice_read]/4_000,ax=ax[0,0])
ax[0,0].set_title("RGB PhiSat-II L1C")
rstplt.show(ac_output[2::-1,...][slice_read]/4_000,ax=ax[0,1])
ax[0,1].set_title("RGB PhiSat-II L2A (DTACSNet corrected)")
rstplt.show(data_l1c[2::-1,...]/4_000,ax=ax[0,2])
ax[0,2].set_title("RGB Sentinel-2 L1C")
rstplt.show(data_l2a[2::-1,...]/4_000,ax=ax[0,3])
ax[0,3].set_title("RGB Sentinel-2 L2A")
nirredgreen = [-1, 2, 1]
rstplt.show(data[nirredgreen,...][slice_read]/10_000,ax=ax[1,0])
ax[1,0].set_title("NIRRG PhiSat-II L1C")
rstplt.show(ac_output[nirredgreen,...][slice_read]/10_000,ax=ax[1,1])
ax[1,1].set_title("NIRRG PhiSat-II L2A (DTACSNet corrected)")
rstplt.show(data_l1c[nirredgreen,...]/10_000,ax=ax[1,2])
ax[1,2].set_title("NIRRG PhiSat-II L1C")
rstplt.show(data_l2a[nirredgreen,...]/10_000,ax=ax[1,3])
ax[1,3].set_title("NIRRG Sentinel-2 L2A")
Licence
The DTACS package is published under a GNU Lesser GPL v3 licence
The DTACS notebooks and docs are released under a Creative Commons non-commercial licence.
If you find this work useful please cite:
@article{aybar_onboard_2024,
title = {Onboard {Cloud} {Detection} and {Atmospheric} {Correction} {With} {Efficient} {Deep} {Learning} {Models}},
volume = {17},
issn = {2151-1535},
url = {https://ieeexplore.ieee.org/abstract/document/10716772},
doi = {10.1109/JSTARS.2024.3480520},
journal = {IEEE Journal of Selected Topics in Applied Earth Observations and Remote Sensing},
author = {Aybar, Cesar and Mateo-GarcĂa, Gonzalo and Acciarini, Giacomo and RĹŻĹľiÄŤka, VĂt and Meoni, Gabriele and LongĂ©pĂ©, Nicolas and GĂłmez-Chova, Luis},
month = oct,
year = {2024}
}