Skip to content

Instantly share code, notes, and snippets.

@eoghain
Last active February 23, 2016 00:44
Show Gist options
  • Save eoghain/e1a900017bd6fba531cb to your computer and use it in GitHub Desktop.
Save eoghain/e1a900017bd6fba531cb to your computer and use it in GitHub Desktop.
Download and stitch together images from the Himawari8 satellite and create a movie from them. https://en.wikipedia.org/wiki/Himawari_8
#!/usr/bin/env python
# Easy Combine output movies
# ls -tr himawari8_*.mp4 | perl -ne 'print "file $_"' | ffmpeg -f concat -i - -c copy FullMovie.mp4
# Requirements
# brew install libtiff libjpeg webp little-cms2 ffmpeg
# pip install Pillow
import datetime
import json
import urllib2
import io
import cStringIO
import sys
import math
import argparse
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw
from subprocess import call
# Parameters
latestInfoUri = "http://himawari8-dl.nict.go.jp/himawari8/img/D531106/latest.json"
width = 550
numblocks = 4 #can be 4, 8, 16, 20
def urlForTime(urlTime):
timeString = urlTime.strftime('%Y/%m/%d/%H%M%S')
level = "%sd" % numblocks
url = "http://himawari8-dl.nict.go.jp/himawari8/img/D531106/%s/%s/%s" % (level, width, timeString)
return url
def getLatestURL():
response = json.load(urllib2.urlopen(latestInfoUri))
latest = datetime.datetime.strptime(response['date'], '%Y-%m-%d %H:%M:%S');
return urlForTime(latest)
def downloadImagesFromURL(url):
images = [[0 for x in range(numblocks)] for x in range(numblocks)]
for y in range(0, numblocks):
for x in range(0, numblocks):
imageName = "%s_%s.png" % (x, y)
tileURL = "%s_%s" % (url, imageName)
print "\t%s" % tileURL
data = urllib2.urlopen(tileURL).read()
file = cStringIO.StringIO(data)
image = Image.open(file)
images[x][y] = image
return images
def stitchImages(images, path, name, dateString):
fullImage = Image.new("RGB", (numblocks * width, numblocks * width));
for y in range(0, numblocks):
for x in range(0, numblocks):
img = images[x][y]
w, h = img.size
fullImage.paste(img, (x * w, y * h))
draw = ImageDraw.Draw(fullImage)
font = ImageFont.truetype("/Library/Fonts/AvenirNextLTW04-Thin.ttf", 48)
# font = ImageFont.load_default().font
draw.text((5, (width * numblocks - 60)),dateString,(255,255,255),font=font)
fullImage.save("%s/%s.png" % (path, name))
def timeStamps(startTime):
times = []
# timestamp = startTime - datetime.timedelta(minutes=startTime.minute % 10, seconds=startTime.second, microseconds=startTime.microsecond)
timestamp = startTime.replace(hour=14, minute=50, second=0, microsecond=0)
# timestamp = timestamp - datetime.timedelta(days=1)
times.append(timestamp)
for i in range(0, 143):
timestamp = timestamp - datetime.timedelta(minutes=10)
times.append(timestamp)
return times
def yesterdaysTimestamps():
times = []
timestamp = datetime.datetime.now().replace(hour=14, minute=50, second=0, microsecond=0)
timestamp = timestamp - datetime.timedelta(days=1)
times.append(timestamp)
for i in range(0, 143):
timestamp = timestamp - datetime.timedelta(minutes=10)
times.append(timestamp)
return times
def generateMovie(dateString, baseImageName, outputDir):
movieName = "himawari8_%s.mp4" % dateString
print "Generating movie: %s" % movieName
ffmpegImageFilter = "%s_%%d.png" % baseImageName
ffmpegOutputFile = "%s%s" % (outputDir, movieName)
print ffmpegImageFilter
print ffmpegOutputFile
call(['ffmpeg', '-f', 'image2', '-framerate', '15', '-i', ffmpegImageFilter, '-vcodec', 'mpeg4', '-y', ffmpegOutputFile])
# http://stackoverflow.com/questions/1927660/compare-two-images-the-python-linux-way
def allTilesMatch(images):
rms = 0.0
h1 = images[0][0].histogram()
for x in range(0, len(images)):
for y in range(0, len(images[x])):
h2 = images[x][y].histogram()
if (len(h1) == len(h2)):
diff_squares = [(h1[i] - h2[i]) ** 2 for i in xrange(len(h1))]; rms = math.sqrt(sum(diff_squares) / len(h1));
rms += math.sqrt(sum(diff_squares) / len(h1))
else:
rms += 1
return rms < 1
def outputHeader():
print "Generating movie of himawari8 images"
print "Block Count: %s" % numblocks
print "Tile Size : %sx%s" % (width, width)
print "Full Size : %sx%s" % (width * numblocks, width * numblocks)
print "----------------------------------------"
def main(startTime, currentIndex, outputDir):
outputHeader()
startTimestamp = datetime.datetime.strptime(startTime, '%Y-%m-%d')
timestamps = timeStamps(startTimestamp)
# timestamps = yesterdaysTimestamps()
startTimeString = startTimestamp.strftime('%Y%m%d%H%M%S')
baseName = 'Image'
movieDateString = timestamps[-1].strftime('%Y-%m-%d')
for (index, timestamp) in enumerate(reversed(timestamps)):
if (index >= currentIndex):
start = datetime.datetime.now()
imageName = '%s_%d' % (baseName, currentIndex)
print 'Processing %s' % (imageName)
sys.stdout.flush()
url = urlForTime(timestamp)
images = downloadImagesFromURL(url)
if allTilesMatch(images):
print "------------------------------------------"
print "----- No Image Found! -----"
print "----- Moving to next time frame and -----"
print "----- fetching image into same index -----"
print "------------------------------------------"
continue
dateString = timestamp.strftime('%Y-%m-%d %H:%M:%S')
stitchImages(images, outputDir, imageName, dateString)
done = datetime.datetime.now()
print "\t----- %s -----" % (done - start)
currentIndex += 1
generateMovie(movieDateString, baseName, outputDir)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("-l", "--length", nargs=1, type=int, help="number of images to use (defaults to 144 i.e. 24 hours)", default=[144])
parser.add_argument("-d", "--day", help="day to get images for (defaults to most recent)", action="store", default=datetime.datetime.now().replace(hour=14, minute=50, second=0, microsecond=0).strftime('%Y-%m-%d'))
parser.add_argument("-i", "--index", nargs=1, type=int, help="starting index, for resuming failed run. (in-progress)", default=[0])
# parser.add_argument("-n", "--numblocks", nargs=1, type=int, choices=[4,8,16,20], help="number of blocks larger takes longer (default: 4)", default=[4])
# parser.add_argument("-w", "--width", nargs=1, type=int, help="width of individual tiles (numblocks * width = final movie width) (default: 550)", default=[550])
parser.add_argument("outputDir", help="directory to place images and movie")
args = parser.parse_args()
# width = args.width[0]
# numblocks = args.numblocks[0]
outputDir = args.outputDir
if outputDir.endswith('/') == False:
outputDir += '/'
if (args.day == datetime.datetime.now().replace(hour=14, minute=50, second=0, microsecond=0).strftime('%Y-%m-%d')):
timestamp = datetime.datetime.strptime(args.day, '%Y-%m-%d')
timestamp = timestamp - datetime.timedelta(days=1)
args.day = timestamp.strftime('%Y-%m-%d')
else:
timestamp = datetime.datetime.strptime(args.day, '%Y-%m-%d')
timestamp = timestamp + datetime.timedelta(days=1)
args.day = timestamp.strftime('%Y-%m-%d')
main(args.day, args.index[0], outputDir)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment