=== versions === python 3.12.0 | rasterio 1.5.2 | odc-stac 0.5.3 | s2cloudless 1.7.3 | omnicloudmask 1.8.0 | torch 2.14.1+cpu === Step 1: Sentinel-2 L2A scenes for tile 30UVF === 2026-05-26 S2B_MSIL2A_20260526T113309_R080_T30UVF_20260526T141142 tile cloud 10.0% sun zenith 34.1 azimuth 165.0 2026-08-08 S2A_MSIL2A_20260808T112131_R037_T30UVF_20260808T192910 tile cloud 67.6% sun zenith 39.9 azimuth 159.5 2026-09-23 S2B_MSIL2A_20260923T113319_R080_T30UVF_20260923T151947 tile cloud 21.8% sun zenith 55.1 azimuth 170.5 === Step 2: load L2A bands on a 20 m grid === 2026-05-26: 1000 x 1000 px, median B02 0.070, B08 0.390 (4 s) 2026-08-08: 1000 x 1000 px, median B02 0.052, B08 0.384 (5 s) 2026-09-23: 1000 x 1000 px, median B02 0.047, B08 0.336 (5 s) === Step 3: Scene Classification Layer (SCL) === 2026-05-26 cloud shadow 18.2% | vegetation 44.2% | not vegetated 2.5% | water 2.7% | cloud medium prob. 14.7% | cloud high prob. 17.1% 2026-08-08 cloud shadow 2.8% | vegetation 48.8% | not vegetated 2.9% | water 2.0% | unclassified 0.7% | cloud medium prob. 9.5% | cloud high prob. 7.2% | thin cirrus 25.9% 2026-09-23 dark area 1.1% | cloud shadow 22.1% | vegetation 44.3% | not vegetated 2.1% | water 2.5% | unclassified 1.4% | cloud medium prob. 14.0% | cloud high prob. 12.4% === Step 4: s2cloudless (L1C from the Google Cloud public bucket) === 2026-05-26: S2B_MSIL1C_20260526T113309_N0512_R080_T30UVF_20260526T135808.SAFE cloud 39.2% projected shadow 7.2% (6 s) 2026-08-08: S2A_MSIL1C_20260808T112131_N0512_R037_T30UVF_20260808T181539.SAFE cloud 40.3% projected shadow 1.3% (5 s) 2026-09-23: S2B_MSIL1C_20260923T113319_N0513_R080_T30UVF_20260923T150444.SAFE cloud 36.9% projected shadow 9.4% (6 s) === Step 5: OmniCloudMask (red, green and NIR) === 2026-05-26: thick 33.2% thin 0.0% shadow 16.0% (1 s on CPU) 2026-08-08: thick 24.0% thin 11.5% shadow 5.9% (1 s on CPU) 2026-09-23: thick 38.6% thin 0.0% shadow 18.1% (1 s on CPU) === Step 6: threshold baseline === 2026-05-26: cloud 25.3% shadow 6.4% 2026-08-08: cloud 14.8% shadow 2.2% 2026-09-23: cloud 20.4% shadow 8.4% === Step 7: agreement between methods === masked (% of the area): date 2026-05-26 2026-08-08 2026-09-23 method SCL 50.1 45.4 48.5 s2cloudless 46.4 41.5 46.4 OmniCloudMask 49.2 41.3 56.7 Threshold 31.7 17.0 28.9 cloud (% of the area): date 2026-05-26 2026-08-08 2026-09-23 method SCL 31.8 42.6 26.5 s2cloudless 39.2 40.3 36.9 OmniCloudMask 33.2 35.4 38.6 Threshold 25.3 14.8 20.4 shadow (% of the area): date 2026-05-26 2026-08-08 2026-09-23 method SCL 18.2 2.8 22.1 s2cloudless 7.2 1.3 9.4 OmniCloudMask 16.0 5.9 18.1 Threshold 6.4 2.2 8.4 pixel agreement, masked vs not masked, all three dates (%): SCL s2cloudless OmniCloudMask Threshold SCL 100.0 81.6 81.7 73.6 s2cloudless 81.6 100.0 87.6 78.9 OmniCloudMask 81.7 87.6 100.0 74.8 Threshold 73.6 78.9 74.8 100.0 Cohen's kappa: SCL s2cloudless OmniCloudMask Threshold SCL 1.00 0.63 0.63 0.46 s2cloudless 0.63 1.00 0.75 0.56 OmniCloudMask 0.63 0.75 1.00 0.49 Threshold 0.46 0.56 0.49 1.00 all four methods agree on 63.4% of pixels === Step 8: check against visually interpreted reference points === reference labels: {'clear': 100, 'cloud': 61, 'uncertain': 54, 'shadow': 25} 186 usable points (clear 100, cloud 61, shadow 25); masked = cloud or shadow overall_acc_% kappa omission_% commission_% cloud_found_% shadow_found_% clear_kept_% method SCL 84.95 0.70 13.95 17.78 85.25 88.0 84.0 s2cloudless 90.32 0.80 12.79 8.54 91.80 76.0 93.0 OmniCloudMask 95.16 0.90 2.33 7.69 98.36 96.0 93.0 Threshold 82.80 0.65 33.72 5.00 77.05 40.0 97.0 confusion matrices (rows: reference, columns: method) -- SCL SCL clear cloud shadow label clear 84 8 8 cloud 9 49 3 shadow 3 3 19 -- s2cloudless s2cloudless clear cloud shadow label clear 93 6 1 cloud 5 56 0 shadow 6 7 12 -- OmniCloudMask OmniCloudMask clear cloud shadow label clear 93 5 2 cloud 1 59 1 shadow 1 3 21 -- Threshold Threshold clear cloud shadow label clear 97 0 3 cloud 14 47 0 shadow 15 1 9 === figures: masks === === Step 9: cloud-free median composite, May-September 2026 === 56 scenes with tile cloud cover < 90% loaded 45 dates x 1000 x 1000 px in 79 s OmniCloudMask on 45 dates: 31 s date SCL_% OCM_% 2026-05-06 73.9 74.9 2026-05-10 65.8 67.2 2026-05-11 63.1 72.4 2026-05-18 98.6 99.2 2026-05-26 50.1 49.2 2026-05-30 12.1 24.3 2026-05-31 99.0 99.5 2026-06-02 60.6 58.2 2026-06-09 98.2 98.8 2026-06-10 92.7 96.7 2026-06-12 98.1 99.5 2026-06-15 100.0 98.6 2026-06-17 99.7 100.0 2026-06-20 80.6 70.2 2026-06-25 0.0 0.0 2026-06-27 90.3 92.6 2026-07-02 84.4 89.0 2026-07-09 8.5 12.6 2026-07-10 14.8 0.5 2026-07-12 0.0 1.5 2026-07-15 8.5 10.0 2026-07-17 91.1 88.0 2026-07-19 0.7 0.9 2026-07-20 5.3 7.0 2026-07-22 85.0 89.2 2026-07-30 83.7 85.3 2026-08-01 88.6 93.3 2026-08-06 100.0 100.0 2026-08-08 45.4 41.3 2026-08-11 19.3 5.5 2026-08-14 90.8 97.5 2026-08-19 93.8 98.2 2026-08-21 61.0 59.0 2026-08-24 8.6 5.5 2026-08-28 100.0 100.0 2026-08-31 99.4 99.9 2026-09-05 96.6 98.0 2026-09-07 95.9 99.1 2026-09-08 99.9 100.0 2026-09-15 91.0 91.8 2026-09-17 99.6 100.0 2026-09-20 94.6 97.2 2026-09-23 48.5 56.7 2026-09-28 79.7 71.5 2026-09-30 65.6 64.7 dates at least 90% clear: SCL 7, OmniCloudMask 8 composites compared with the clear date 2026-07-12 (SCL masks 0.0% of it, OmniCloudMask 1.5%): composite median_obs min_obs no_data_% brighter_blue_% darker_nir_% median_ndvi No mask 45.0 45 0.0 99.153 1.952 0.359 SCL 15.0 5 0.0 0.116 3.226 0.764 OmniCloudMask 14.0 5 0.0 0.069 2.854 0.766 SCL + OmniCloudMask 13.0 5 0.0 0.058 2.125 0.770 wrote output\lake_district_median_2026.tif