/nicar20-imagery-with-bash

Some helpful bash profile functions for working with earth imagery

nicar20-imagery-with-bash

Bash profile tips.

This document provides a few basic Bash scripts to get you started with earth imagery processing in the terminal.

Dependencies: FFmpeg, GDAL (👈cheat sheet), Gifsicle, ImageMagick

If wrangling imagery in the terminal is not your cup of tea 🍵 and you're lucky enough to have a Photoshop license, have no fear. The concepts outlined by Tom Patterson for Landsat 8 here can be easily applied to other sensors.

If you'd like to compose your imagery in the browser, Pierre Markuse and others share custom scripts for visualizing things like fire and urban areas using Sentinel Hub. Gowild.

Band combinations here are examples, not scientific law. I encourage you to experiment with your own combinations. If you need a starting point, Harris Geospatial and Esri have both published fun little rundowns of Landsat 8 false color band combinations. Here's a quick rundown of how Landsat 8 and Sentinel-2 bands compare.

Basically, there's many ways to approach this. Pick your favorite. If your favorite is Bash, here we go:

Composing Imagery

Create an RGB image from raw Landsat 8 bands in a folder

Takes no input. File extension in this command may need to be altered (TIF v TIFF) depending on source. 👈 This is true of all of the Landsat 8 scripts here.

function l8rgb() {
	prefix=${PWD##*/}
	gdal_merge.py -separate -co "PHOTOMETRIC=RGB" -of GTiff *\B4.TIF *\B3.TIF *\B2.TIF -o $prefix"_l8rgb.tif"
	open $prefix"_l8rgb.tif"	
}

Create a pansharpened RGB image from raw Landsat 8 bands in a folder

function l8ps(){
	prefix=${PWD##*/}
	gdal_pansharpen.py  *\B8.TIF  *\B4.TIF  *\B3.TIF  *\B2.TIF $prefix"_l8_ps_rgb.tif" -r cubic -co PHOTOMETRIC=RGB
	open $prefix"_l8_ps_rgb.tif"
}

Create an RGB image from raw Sentinel-2 bands in a folder

Takes no input. Requires proper GDAL driver install (good luck!)

function s2rgb() {
	prefix=${PWD##*/}
	gdal_merge.py -separate -co "PHOTOMETRIC=RGB" -of GTiff *\B04.jp2 *\B03.jp2 *\B02.jp2 -o $prefix"_s2rgb.tif"
	open $prefix"_s2rgb.tif"
}

Scale data values to min/max and convert to a byte image for toning

function image_min_max() {
	file_name="${1%%.*}"
	gdal_translate $1 -scale -ot byte -co COMPRESS=LZW $file_name"_stretched_to_min_max.tif"
	open $file_name"_stretched_to_min_max.tif"
}

Create a false color image using Landsat 8 bands in order to highlight active fires

function l8fire() {
	prefix=${PWD##*/}
	gdal_merge.py -separate -co "PHOTOMETRIC=RGB" -of GTiff *\B7.TIF *\B3.TIF *\B2.TIF -o $prefix"_l8_fire_false_color.tif"
	open $prefix"_l8_fire_false_color.tif"
}

Create a false color image using Sentinel-2 bands in order to highlight active fires

function s2fire() {
	prefix=${PWD##*/}
	gdal_merge.py -separate -co "PHOTOMETRIC=RGB" -of GTiff *\B12.jp2 *\B11.jp2 *\B05.jp2 -o $prefix"_s2_fire_false_color.tif"
	open $prefix"_s2_fire_false_color.tif"
}

Landsat 8 NDVI Vegetation Mapping

function l8ndvi() {
	prefix=${PWD##*/}
	gdal_calc.py -A *\B5.TIF -B *\B4.TIF --outfile=$prefix"_l8ndvi.tif" --calc="(A.astype(float)-B.astype(float))/(A.astype(float)+B.astype(float))" --NoDataValue=0 --type=Float32
	open $prefix"_l8ndvi.tif"
}

Sentinel-2 NDVI Vegetation Mapping

function s2ndvi() {
	prefix=${PWD##*/}
	echo naming based on $prefix folder
	gdal_calc.py -A *\B08.jp2.tif -B *\B04.jp2.tif --outfile=$prefix"_s2ndvi.tif" --calc="(A.astype(float)-B.astype(float))/(A.astype(float)+B.astype(float))" --NoDataValue=0 --type=Float32
	open $prefix"_s2ndvi.tif"
}

Landsat 8 NDWI Water Mapping

function l8ndwi() {
	prefix=${PWD##*/}
	gdal_calc.py -A *\B3.TIF -B *\B5.TIF --outfile=$prefix"_l8ndwi.tif" --calc="(A.astype(float)-B.astype(float))/(A.astype(float)+B.astype(float))" --NoDataValue=0 --type=Float32
	open $prefix"_l8ndwi.tif"
}

Sentinel-2 NDWI Water Mapping

function s2ndwi() {
	prefix=${PWD##*/}
	gdal_calc.py -A *\B03.jp2 -B *\B08.jp2 --outfile=$prefix"_s2ndwi.tif" --calc="(A.astype(float)-B.astype(float))/(A.astype(float)+B.astype(float))" --NoDataValue=0 --type=Float32
	open $prefix"_s2ndwi.tif"
	
}

Video/Motion

Take all images (in alphabetical order) with the same file extension and make a video at a specified frame rate.

function video_from_images(){
	frame_rate=$1
	file_extension=$2
	output=$3
	ffmpeg -r $frame_rate -f image2 -pattern_type glob -i '*.'"$file_extension" -acodec libmp3lame $output
}

Make any video (relatively) websafe. Helpful when a video seems broken (but isn't), or to maximize compatibility.

function websafe(){
	suffix=_websafe.mp4
	file_name="${1%%.*}"
	ffmpeg -an -i $1 -vcodec libx264 -pix_fmt yuv420p -profile:v baseline -level 3 -vf "pad=ceil(iw/2)*2:ceil(ih/2)*2" $file_name$suffix
	echo saved $file_name$suffix
}

Gif sequence of images with fade transition

function gif_fade(){
	file_extension=$1
	fade_length=$2
	output=$3
	convert *.$file_extension -morph $fade_length $output
}

Optimize gif

function optimize_gif(){
	input=$1
	output=$2
	gifsicle -b -O3 $input -o $output
}

Bonus Randos

Open Landsat 8 on AWS from terminal with a scene ID

function l8aws() {
	scene=$1
	pathrow=$(echo expr $1|awk -F '_' '{print $3}'|sed 's/^\(.\{3\}\)/\1\//')
	open "https://landsatonaws.com/L8/"$pathrow"/"$scene
}