-
Notifications
You must be signed in to change notification settings - Fork 299
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add FLIR visualisation for Landsat-8 (#329)
* Add EOB script for FLIR vis * Raw thermal band in celsius * Add link in Landsat page * Add readme * Add visualisation script * Add example image
- Loading branch information
1 parent
d733222
commit 999afca
Showing
6 changed files
with
199 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
--- | ||
title: FLIR visualisation of the thermal IR band | ||
parent: Landsat-8 | ||
grand_parent: Landsat | ||
layout: script | ||
permalink: /landsat-8/thermal-iron/ | ||
nav_exclude: true | ||
scripts: | ||
- - Visualization | ||
- script.js | ||
- - EO Browser | ||
- eob.js | ||
- - Raw Values | ||
- raw.js | ||
examples: | ||
- zoom: '11' | ||
lat: '35.18167' | ||
lng: '-5.4657' | ||
datasetId: AWS_LOTL2 | ||
fromTime: '2024-07-15T00:00:00.000Z' | ||
toTime: '2024-07-15T23:59:59.999Z' | ||
platform: | ||
- EOB | ||
evalscripturl: https://custom-scripts.sentinel-hub.com/custom-scripts/landsat-8/thermal-iron/eob.js | ||
--- | ||
## General description | ||
|
||
Since the inception of thermal imaging, infrared cameras have commonly employed a distinctive color palette, ranging from black to blue, magenta, orange, yellow, and culminating in bright white. This palette is frequently referred to as “Iron” or “Ironbow.” | ||
|
||
This script allows the visualisation of the thermal IR band of Landsat 8 (Band 10, centered on 10.895 µm) in a FLIR-like palette that was inspired by a StackOverflow post [1]. The script allows the adjustment of 3 parameters: | ||
|
||
- **minValue**: The minimum value of the thermal band mapped to the black color. | ||
- **maxValue**: The maximum value of the thermal band mapped to the white color. | ||
- **numSteps**: The number of increments in the color palette. | ||
|
||
The script returns the thermal band values in degrees Celsius, converted from Kelvin, for an easier interpretation. Furthermore, the script masks out clouds and cloud shadows using the QA band (BQA). | ||
|
||
## Description of representative images | ||
|
||
Thermal data over Morocco. Acquired on 15.07.2024. | ||
|
||
![Morocco thermal image](fig/fig1.png) | ||
|
||
|
||
## References | ||
[1] StackOverflow, [Thermal imaging palette](https://stackoverflow.com/questions/28495390/thermal-imaging-palette). Accessed on 19th September 2024. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
//VERSION=3 | ||
|
||
// Set Min and Max values for display and number of steps in the palette | ||
const minValue = 10 | ||
const maxValue = 45 | ||
const numSteps = 200 | ||
|
||
// Setup palette | ||
const palette_colours = palette(numSteps, minValue, maxValue) | ||
const viz = new ColorRampVisualizer(palette_colours); | ||
|
||
|
||
function setup() { | ||
return { | ||
input: ["B10", "BQA", "dataMask"], | ||
output: [ | ||
{ id: "default", bands: 4 }, | ||
{ id: "eobrowserStats", bands: 2 }, | ||
{ id: "dataMask", bands: 1 }, | ||
], | ||
}; | ||
} | ||
|
||
function evaluatePixel(samples) { | ||
let val = samples.B10 - 273; | ||
let clouds = isCloud(samples) | ||
|
||
return { | ||
default: [...viz.process(val), (samples.dataMask * (clouds ? 0 : 1))], | ||
eobrowserStats: [val, clouds ? 0 : 1], | ||
dataMask: [samples.dataMask], | ||
}; | ||
} | ||
|
||
function isCloud(sample) { | ||
const BQA = decodeL8C2Qa(sample.BQA); | ||
let cloudPresence = false | ||
if (BQA.cloud == 1 || BQA.cloudShadow == 1 || BQA.cirrus == 1 || BQA.dilatedCloud == 1) { | ||
cloudPresence = true | ||
} | ||
return cloudPresence | ||
} | ||
|
||
function componentToHex(c) { | ||
var hex = c.toString(16); | ||
return hex.length == 1 ? "0" + hex : hex; | ||
} | ||
|
||
function rgbToHex(r, g, b) { | ||
return "0x" + componentToHex(r) + componentToHex(g) + componentToHex(b); | ||
} | ||
|
||
function palette(colour_length, min_value, max_value){ | ||
let colourPairs = [] | ||
let values = Array.from({length: colour_length}, (_, i) => min_value + (max_value - min_value) * i / (colour_length - 1)); | ||
for (var idx = 0; idx < colour_length; idx++){ | ||
var x = idx * 1.0/colour_length; | ||
|
||
// Convert RGB to hex | ||
let coulours = rgbToHex(Math.round(255*Math.sqrt(x)), | ||
Math.round(255*Math.pow(x,3)), | ||
Math.round(255*(Math.sin(2 * Math.PI * x)>=0? | ||
Math.sin(2 * Math.PI * x) : | ||
0 ))) | ||
|
||
//Make pairs of colours | ||
colourPairs.push([values[idx], coulours.toString()]) | ||
|
||
} | ||
return colourPairs | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
//VERSION=3 | ||
|
||
function setup() { | ||
return { | ||
input: ["B10"], | ||
output: { | ||
bands: 1, | ||
sampleType: "FLOAT32" | ||
} | ||
}; | ||
} | ||
|
||
function evaluatePixel(samples) { | ||
// Convert to Celsius | ||
return [samples.B10 - 273]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
//VERSION=3 | ||
|
||
// Set Min and Max values for display and number of steps in the palette | ||
const minValue = 10 | ||
const maxValue = 45 | ||
const numSteps = 200 | ||
|
||
// Setup palette | ||
const palette_colours = palette(numSteps, minValue, maxValue) | ||
const viz = new ColorRampVisualizer(palette_colours); | ||
|
||
|
||
function setup() { | ||
return { | ||
input: ["B10", "BQA", "dataMask"], | ||
output: { | ||
bands: 4 | ||
} | ||
} | ||
} | ||
|
||
function evaluatePixel(samples) { | ||
let val = samples.B10 - 273; | ||
let clouds = isCloud(samples) | ||
|
||
return [...viz.process(val), (samples.dataMask * (clouds ? 0 : 1))]; | ||
} | ||
|
||
function isCloud(sample) { | ||
const BQA = decodeL8C2Qa(sample.BQA); | ||
let cloudPresence = false | ||
if (BQA.cloud == 1 || BQA.cloudShadow == 1 || BQA.cirrus == 1 || BQA.dilatedCloud == 1) { | ||
cloudPresence = true | ||
} | ||
return cloudPresence | ||
} | ||
|
||
function componentToHex(c) { | ||
var hex = c.toString(16); | ||
return hex.length == 1 ? "0" + hex : hex; | ||
} | ||
|
||
function rgbToHex(r, g, b) { | ||
return "0x" + componentToHex(r) + componentToHex(g) + componentToHex(b); | ||
} | ||
|
||
function palette(colour_length, min_value, max_value){ | ||
let colourPairs = [] | ||
let values = Array.from({length: colour_length}, (_, i) => min_value + (max_value - min_value) * i / (colour_length - 1)); | ||
for (var idx = 0; idx < colour_length; idx++){ | ||
var x = idx * 1.0/colour_length; | ||
|
||
// Convert RGB to hex | ||
let coulours = rgbToHex(Math.round(255*Math.sqrt(x)), | ||
Math.round(255*Math.pow(x,3)), | ||
Math.round(255*(Math.sin(2 * Math.PI * x)>=0? | ||
Math.sin(2 * Math.PI * x) : | ||
0 ))) | ||
|
||
//Make pairs of colours | ||
colourPairs.push([values[idx], coulours.toString()]) | ||
|
||
} | ||
return colourPairs | ||
} |