observational science with python and a webcam
DESCRIPTION
This talk is a "how I did it" talk about how I took an idea, a web cam, Python, Django, and the Python Imaging Library and created art, explored science, and illustrated concepts that our ancestors knew by watching the sky but we have lost.TRANSCRIPT
![Page 1: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/1.jpg)
Observational ScienceWith Python
And a Webcam
By: Eric Floehr
Observational Science With Python and a Webcam by Eric Floehr is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
![Page 2: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/2.jpg)
So I had a webcam...
![Page 4: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/4.jpg)
I wanted to do better.
Longer
Inside location
Automated
Once a minute
![Page 5: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/5.jpg)
So this is what I did.
Second floor window
Little-used room
Pointing west
Pull pictures down with cronjob
That runs once per minute
![Page 6: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/6.jpg)
And it looks like this.
![Page 7: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/7.jpg)
Two years later...
![Page 8: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/8.jpg)
I have...
● 896,309 image files● 11,809,972,880 bytes● 10.9989 gigabytes● From 2:14pm on August 29, 2010● To 4:11pm on July 25, 2012● Still going...
![Page 9: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/9.jpg)
Tenet #1:
Don't be afraid of big data.
![Page 10: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/10.jpg)
What can we do?
● Moar Time-lapse!● Explore phenomenon that occurs
over long periods● Unique visualizations● Have lots of fun!
![Page 11: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/11.jpg)
First...
We need to organize the data.
![Page 12: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/12.jpg)
Database!
![Page 13: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/13.jpg)
How will we access the data?Let's use Django!
![Page 14: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/14.jpg)
Why Django?
● It has a good ORM● It has a command framework● Makes extending to the web later
easy● I already am familiar with it● Django isn't just for web :-)
![Page 15: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/15.jpg)
Quick setup
● Create your virtualenv (with site packages)● PIL is a pain to compile● Install Django● django-admin.py startproject● Edit settings.py● createdb pics● django-admin.py startapp pics● django-admin.py syncdb
![Page 16: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/16.jpg)
Tenet #2:
Don't let small things keep you from your big picture.
![Page 17: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/17.jpg)
What do we need to store?
● Image file location and filename● Time the image was taken● Interesting information about the
image● Is it a valid image?
![Page 18: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/18.jpg)
class Picture(models.Model):
filepath = models.CharField(max_length=1024) filename = models.CharField(max_length=1024)
timestamp = models.DateTimeField(db_index=True) file_timestamp = models.DateTimeField(null=True) # Auto-populated, don't manually enter hour = models.SmallIntegerField(null=True, db_index=True) minute = models.SmallIntegerField(null=True, db_index=True) file_hour = models.SmallIntegerField(null=True) file_minute = models.SmallIntegerField(null=True) # End auto-populated fields
size = models.IntegerField(default=0) center_color = models.IntegerField(null=True) mean_color = models.IntegerField(null=True) median_color = models.IntegerField(null=True)
stddev_red = models.IntegerField(null=True) stddev_green = models.IntegerField(null=True) stddev_blue = models.IntegerField(null=True)
min_color = models.IntegerField(null=True) max_color = models.IntegerField(null=True) valid = models.BooleanField(default=False)
class Meta: ordering = ['timestamp']
![Page 19: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/19.jpg)
class Picture(models.Model):
filepath = models.CharField(max_length=1024) filename = models.CharField(max_length=1024)
timestamp = models.DateTimeField(db_index=True) file_timestamp = models.DateTimeField(null=True) # Auto-populated, don't manually enter hour = models.SmallIntegerField(null=True, db_index=True) minute = models.SmallIntegerField(null=True, db_index=True) file_hour = models.SmallIntegerField(null=True) file_minute = models.SmallIntegerField(null=True) # End auto-populated fields
size = models.IntegerField(default=0) center_color = models.IntegerField(null=True) mean_color = models.IntegerField(null=True) median_color = models.IntegerField(null=True)
stddev_red = models.IntegerField(null=True) stddev_green = models.IntegerField(null=True) stddev_blue = models.IntegerField(null=True)
min_color = models.IntegerField(null=True) max_color = models.IntegerField(null=True) valid = models.BooleanField(default=False)
class Meta: ordering = ['timestamp']
<-- File location and name
![Page 20: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/20.jpg)
class Picture(models.Model):
filepath = models.CharField(max_length=1024) filename = models.CharField(max_length=1024)
timestamp = models.DateTimeField(db_index=True) file_timestamp = models.DateTimeField(null=True) # Auto-populated, don't manually enter hour = models.SmallIntegerField(null=True, db_index=True) minute = models.SmallIntegerField(null=True, db_index=True) file_hour = models.SmallIntegerField(null=True) file_minute = models.SmallIntegerField(null=True) # End auto-populated fields
size = models.IntegerField(default=0) center_color = models.IntegerField(null=True) mean_color = models.IntegerField(null=True) median_color = models.IntegerField(null=True)
stddev_red = models.IntegerField(null=True) stddev_green = models.IntegerField(null=True) stddev_blue = models.IntegerField(null=True)
min_color = models.IntegerField(null=True) max_color = models.IntegerField(null=True) valid = models.BooleanField(default=False)
class Meta: ordering = ['timestamp']
<-- Image Timestamp
![Page 21: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/21.jpg)
class Picture(models.Model):
filepath = models.CharField(max_length=1024) filename = models.CharField(max_length=1024)
timestamp = models.DateTimeField(db_index=True) file_timestamp = models.DateTimeField(null=True) # Auto-populated, don't manually enter hour = models.SmallIntegerField(null=True, db_index=True) minute = models.SmallIntegerField(null=True, db_index=True) file_hour = models.SmallIntegerField(null=True) file_minute = models.SmallIntegerField(null=True) # End auto-populated fields
size = models.IntegerField(default=0) center_color = models.IntegerField(null=True) mean_color = models.IntegerField(null=True) median_color = models.IntegerField(null=True)
stddev_red = models.IntegerField(null=True) stddev_green = models.IntegerField(null=True) stddev_blue = models.IntegerField(null=True)
min_color = models.IntegerField(null=True) max_color = models.IntegerField(null=True) valid = models.BooleanField(default=False)
class Meta: ordering = ['timestamp']
<-- Interesting image data
![Page 22: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/22.jpg)
class Picture(models.Model):
filepath = models.CharField(max_length=1024) filename = models.CharField(max_length=1024)
timestamp = models.DateTimeField(db_index=True) file_timestamp = models.DateTimeField(null=True) # Auto-populated, don't manually enter hour = models.SmallIntegerField(null=True, db_index=True) minute = models.SmallIntegerField(null=True, db_index=True) file_hour = models.SmallIntegerField(null=True) file_minute = models.SmallIntegerField(null=True) # End auto-populated fields
size = models.IntegerField(default=0) center_color = models.IntegerField(null=True) mean_color = models.IntegerField(null=True) median_color = models.IntegerField(null=True)
stddev_red = models.IntegerField(null=True) stddev_green = models.IntegerField(null=True) stddev_blue = models.IntegerField(null=True)
min_color = models.IntegerField(null=True) max_color = models.IntegerField(null=True) valid = models.BooleanField(default=False)
class Meta: ordering = ['timestamp']
<-- Is it valid? How do we order?
![Page 23: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/23.jpg)
Loading the Data
![Page 24: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/24.jpg)
How do we populate the table?
● Iterate through directories of image files
● Parse the file name to get timestamp
● Get file timestamp to compare● Load image to collect information
about the image
![Page 25: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/25.jpg)
Find the image filesimport os
import fnmatch
import datetime
for dirpath, dirs, files in os.walk(directory):
for f in sorted(fnmatch.filter(files, 'image-*.jpg')):
# Pull out timestamp
timestamp = f.split('.')[0].split('-')[1]
date = datetime.datetime.fromtimestamp(int(timestamp), utc)
![Page 26: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/26.jpg)
Find the image filesimport os
import fnmatch
import datetime
for dirpath, dirs, files in os.walk(directory):
for f in sorted(fnmatch.filter(files, 'image-*.jpg')):
# Pull out timestamp
timestamp = f.split('.')[0].split('-')[1]
date = datetime.datetime.fromtimestamp(int(timestamp), utc)
![Page 27: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/27.jpg)
Find the image filesimport os
import fnmatch
import datetime
for dirpath, dirs, files in os.walk(directory):
for f in sorted(fnmatch.filter(files, 'image-*.jpg')):
# Pull out timestamp
timestamp = f.split('.')[0].split('-')[1]
date = datetime.datetime.fromtimestamp(int(timestamp), utc)
![Page 28: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/28.jpg)
Find the image filesimport os
import fnmatch
import datetime
for dirpath, dirs, files in os.walk(directory):
for f in sorted(fnmatch.filter(files, 'image-*.jpg')):
# Pull out timestamp
timestamp = f.split('.')[0].split('-')[1]
date = datetime.datetime.fromtimestamp(int(timestamp), utc)
![Page 29: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/29.jpg)
Find the image filesimport os
import fnmatch
import datetime
for dirpath, dirs, files in os.walk(directory):
for f in sorted(fnmatch.filter(files, 'image-*.jpg')):
# Pull out timestamp
timestamp = f.split('.')[0].split('-')[1]
date = datetime.datetime.fromtimestamp(int(timestamp), utc)
![Page 30: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/30.jpg)
Use PIL to get image infoimport Image, ImageStat
From util.color import rgb_to_int
try:
im = Image.open(pic.filepath)
stats = ImageStat.Stat(im)
pic.center_color = rgb_to_int(im.getpixel((320,240)))
pic.mean_color = rgb_to_int(stats.mean)
pic.median_color = rgb_to_int(stats.median)
if im.size <> (640,480):
pic.valid = False
except:
pic.valid = False
![Page 31: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/31.jpg)
Use PIL to get image infoimport Image, ImageStat
From util.color import rgb_to_int
try:
im = Image.open(pic.filepath)
stats = ImageStat.Stat(im)
pic.center_color = rgb_to_int(im.getpixel((320,240)))
pic.mean_color = rgb_to_int(stats.mean)
pic.median_color = rgb_to_int(stats.median)
if im.size <> (640,480):
pic.valid = False
except:
pic.valid = False
![Page 32: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/32.jpg)
Use PIL to get image infoimport Image, ImageStat
From util.color import rgb_to_int
try:
im = Image.open(pic.filepath)
stats = ImageStat.Stat(im)
pic.center_color = rgb_to_int(im.getpixel((320,240)))
pic.mean_color = rgb_to_int(stats.mean)
pic.median_color = rgb_to_int(stats.median)
if im.size <> (640,480):
pic.valid = False
except:
pic.valid = False
![Page 33: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/33.jpg)
Use PIL to get image infoimport Image, ImageStat
From util.color import rgb_to_int
try:
im = Image.open(pic.filepath)
stats = ImageStat.Stat(im)
pic.center_color = rgb_to_int(im.getpixel((320,240)))
pic.mean_color = rgb_to_int(stats.mean)
pic.median_color = rgb_to_int(stats.median)
if im.size <> (640,480):
pic.valid = False
except:
pic.valid = False
![Page 34: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/34.jpg)
It took a an hour or two using six threads on my desktop
![Page 35: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/35.jpg)
Tenet #3:
You have a 1990's supercomputer on your lap or
under your desk.Use it!
![Page 36: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/36.jpg)
Let's Explore the Data
![Page 37: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/37.jpg)
Size Indicates Complexity in jpegpics=# select timestamp, filepath, size from pics_picture where size=(select max(size) from pics_picture);
timestamp | filepath | size ------------------------+--------------------------------------+------- 2011-10-10 18:26:01-04 | /data/pics/1318/image-1318285561.jpg | 64016
http://bit.ly/ospw-2
![Page 38: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/38.jpg)
High Stddev Means Color Variationpics=# select timestamp, filepath, size from pics_picture where stddev_red+stddev_green+stddev_blue = (select max(stddev_red+stddev_green+stddev_blue) from pics_picture);
Timestamp | filepath | size ------------------------+--------------------------------------+------- 2011-09-29 17:20:01-04 | /data/pics/1317/image-1317331201.jpg | 22512
http://bit.ly/ospw-3
![Page 39: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/39.jpg)
About the Datasetpics=# select min(timestamp) as start, max(timestamp) as end from pics_picture;
start | end
------------------------+------------------------
2010-08-29 14:14:01-04 | 2012-07-25 16:11:01-04
(1 row)
pics=# select count(*), sum(case when valid=false then 1 else 0 end) as invalid from pics_picture;
count | invalid
--------+---------
896309 | 29555
(1 row)
pics=# select timestamp, filepath, size from pics_picture where size>0 and valid=false order by size desc limit 1;
timestamp | filepath | size
------------------------+--------------------------------------+-------
2012-04-25 08:16:01-04 | /data/pics/1335/image-1335356161.jpg | 39287
(1 row)
http://bit.ly/ospw-4
![Page 40: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/40.jpg)
Yuck! This Data Sucks!
● 29,555 invalid image files● That's 3.3% of all image files● Worse yet, there isn't a file every minute● Based on start and end times, we should
have 1,002,358 images● We are missing 10.6% of all minutes
between start and end times
![Page 41: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/41.jpg)
It's Worse...I Forgot The Bolts!
http://bit.ly/ospw-5
http://bit.ly/ospw-6
http://bit.ly/ospw-7
![Page 42: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/42.jpg)
* Interestingly, I was listening to the Serious Business remix of “All Is Not Lost” by OK Go from the Humble Music Bundle as I made the previous slide.
![Page 43: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/43.jpg)
Tenet #4:
Real world data is messy.That's ok. Just work around it.
![Page 44: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/44.jpg)
How we can work around it?
● Create table of all minutes● Accept images “near” missing
minutes● Use empty images when
acceptable images can't be found
![Page 45: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/45.jpg)
Let's Make Time-lapse Movies
![Page 46: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/46.jpg)
How do we make movies?
● Collect a set of images in frame order.
● Send that list of images to a movie maker.
● Wait for movie to be made.● Watch movie!
![Page 47: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/47.jpg)
from movies import ImageSequencefrom pics.models import NormalizedPictureimport datetimefrom pytz import timezone
ims = ImageSequence()
ettz = timezone('US/Eastern')
start = datetime.datetime(2011,4,7).replace(tzinfo=ettz)end = start + datetime.timedelta(days=1)
pics = NormalizedPicture.objects.filter(timestamp__gte=start, timestamp__lt=end)
ims = ImageSequence()
for pic in pics: if pic.picture is not None: ims.add_picture(pic.picture) else: ims.add_image('/tmp/no_image.png')
ims.make_timelapse('/tmp/{0}'.format(start.strftime("%Y%m%d")), fps=24)
The previous bullet points in code
![Page 48: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/48.jpg)
from movies import ImageSequencefrom pics.models import NormalizedPictureimport datetimefrom pytz import timezone
ims = ImageSequence()
ettz = timezone('US/Eastern')
start = datetime.datetime(2011,4,7).replace(tzinfo=ettz)end = start + datetime.timedelta(days=1)
pics = NormalizedPicture.objects.filter(timestamp__gte=start, timestamp__lt=end)
ims = ImageSequence()
for pic in pics: if pic.picture is not None: ims.add_picture(pic.picture) else: ims.add_image('/tmp/no_image.png')
ims.make_timelapse('/tmp/{0}'.format(start.strftime("%Y%m%d")), fps=24)
Collect a set of Images
![Page 49: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/49.jpg)
from movies import ImageSequencefrom pics.models import NormalizedPictureimport datetimefrom pytz import timezone
ims = ImageSequence()
ettz = timezone('US/Eastern')
start = datetime.datetime(2011,4,7).replace(tzinfo=ettz)end = start + datetime.timedelta(days=1)
pics = NormalizedPicture.objects.filter(timestamp__gte=start, timestamp__lt=end)
ims = ImageSequence()
for pic in pics: if pic.picture is not None: ims.add_picture(pic.picture) else: ims.add_image('/tmp/no_image.png')
ims.make_timelapse('/tmp/{0}'.format(start.strftime("%Y%m%d")), fps=24)
Send Images to Movie Maker
![Page 50: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/50.jpg)
from movies import ImageSequencefrom pics.models import NormalizedPictureimport datetimefrom pytz import timezone
ims = ImageSequence()
ettz = timezone('US/Eastern')
start = datetime.datetime(2011,4,7).replace(tzinfo=ettz)end = start + datetime.timedelta(days=1)
pics = NormalizedPicture.objects.filter(timestamp__gte=start, timestamp__lt=end)
ims = ImageSequence()
for pic in pics: if pic.picture is not None: ims.add_picture(pic.picture) else: ims.add_image('/tmp/no_image.png')
ims.make_timelapse('/tmp/{0}'.format(start.strftime("%Y%m%d")), fps=24)
Wait For Movie To Be Made
![Page 51: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/51.jpg)
class ImageSequence(object): def __init__(self): self.images = []
def add_picture(self, picture): self.images.append(picture.filepath)
def write_to_file(self, fileobj): for image in self.images: fileobj.write(image)
def make_timelapse(self, name, fps=25): tmpfile = tempfile.NamedTemporaryFile() self.write_to_file(tmpfile)
bitrate = int(round(60 * fps * 640 * 480 / 256.0))
execall = [] execall.append(mencoder_exe) execall.extend(mencoder_options) execall.extend(["-lavcopts", "vcodec=mpeg4:vbitrate={0}:mbd=2:keyint=132:v4mv:vqmin=3:lumi_mask=0.07:dark_mask=0.2:mpeg_quant:scplx_mask=0.1:tcplx_mask=0.1:naq".format(bitrate)]) execall.append("mf://@{0}".format(tmpfile.name)) execall.extend(["-mf", "type=jpeg:fps={0}".format(fps)]) execall.extend(["-o", "{name}.mp4".format(name=name)])
return subprocess.call(execall)
Wait For Movie To Be Made
![Page 52: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/52.jpg)
Watch Movie!
http://bit.ly/ospw-8
http://bit.ly/ospw-8-raw
![Page 53: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/53.jpg)
We aren't limited to consecutive minutes...
![Page 54: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/54.jpg)
from movies import ImageSequencefrom pics.models import NormalizedPicture
ims = ImageSequence()
Hour = 22 # UTC Timeminute = 0
last_pic = None
pics = NormalizedPicture.objects.filter(hour=hour, minute=minute)
last_pic = None
for pic in pics: if pic.picture is not None: ims.add_picture(pic.picture) last_pic = pic.picture else: # Use yesterdays' image if last_pic is not None: ims.add_picture(last_pic) else: # Give up ims.add_image('/tmp/no_image.jpg')
ims.make_timelapse('/tmp/time_{0:02d}{1:02d}'.format(hour,minute),fps=12)
Movie of a Specific Time
![Page 55: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/55.jpg)
from movies import ImageSequencefrom pics.models import NormalizedPicture
ims = ImageSequence()
Hour = 22 # UTC Timeminute = 0
last_pic = None
pics = NormalizedPicture.objects.filter(hour=hour, minute=minute)
last_pic = None
for pic in pics: if pic.picture is not None: ims.add_picture(pic.picture) last_pic = pic.picture else: # Use yesterdays' image if last_pic is not None: ims.add_picture(last_pic) else: # Give up ims.add_image('/tmp/no_image.jpg')
ims.make_timelapse('/tmp/time_{0:02d}{1:02d}'.format(hour,minute),fps=12)
Movie of a Specific Time
![Page 56: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/56.jpg)
from movies import ImageSequencefrom pics.models import NormalizedPicture
ims = ImageSequence()
hour = 22 # UTC timeminute = 0
last_pic = None
pics = NormalizedPicture.objects.filter(hour=hour, minute=minute)
last_pic = None
for pic in pics: if pic.picture is not None: ims.add_picture(pic.picture) last_pic = pic.picture else: # Use yesterdays' image if last_pic is not None: ims.add_picture(last_pic) else: # Give up ims.add_image('/tmp/no_image.jpg')
ims.make_timelapse('/tmp/time_{0:02d}{1:02d}'.format(hour,minute),fps=12)
Movie of a Specific Time
![Page 57: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/57.jpg)
Watch Movie!
http://bit.ly/ospw-9
http://bit.ly/ospw-9-raw
![Page 58: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/58.jpg)
Now what if we want the Sun the same height above the
horizon, so we can better see the seasonal progression of
the Sun?
![Page 59: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/59.jpg)
We can anchor on sunset. At a given time before sunset, the Sun will be at relatively the
same height above the horizon.
![Page 60: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/60.jpg)
Where I thank Brandon Craig Rhodes for pyephem
![Page 61: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/61.jpg)
import sky
obs = sky.webcam()
while current < end: # Get today's sunset time, but 70 minutes before pic_time = sky.sunset(obs, current) - datetime.timedelta(minutes=70) pic_time = pic_time.replace(second=0, microsecond=0)
pic = NormalizedPicture.objects.get(timestamp=pic_time)
if pic.picture is not None: ims.add_picture(pic.picture) else: piclist = NormalizedPicture.objects.get( timestamp__gte=pic_time - datetime.timedelta(minutes=20), timestamp__lte=pic_time + datetime.timedelta(minutes=20), picture__id__isnull=False)
if len(piclist) > 0: ims.add_picture(piclist[0].picture) else: # Use yesterdays' image if last_pic is not None: ims.add_picture(last_pic) else: # Give up ims.add_image('/tmp/no_image.jpg') current = current + datetime.timedelta(days=1)
Movie at Specific Time Before Sunset
![Page 62: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/62.jpg)
import sky
obs = sky.webcam()
while current < end: # Get today's sunset time, but 70 minutes before pic_time = sky.sunset(obs, current) - datetime.timedelta(minutes=70) pic_time = pic_time.replace(second=0, microsecond=0)
pic = NormalizedPicture.objects.get(timestamp=pic_time)
if pic.picture is not None: ims.add_picture(pic.picture) else: piclist = NormalizedPicture.objects.get( timestamp__gte=pic_time - datetime.timedelta(minutes=20), timestamp__lte=pic_time + datetime.timedelta(minutes=20), picture__id__isnull=False)
if len(piclist) > 0: ims.add_picture(piclist[0].picture) else: # Use yesterdays' image if last_pic is not None: ims.add_picture(last_pic) else: # Give up ims.add_image('/tmp/no_image.jpg') current = current + datetime.timedelta(days=1)
Movie at Specific Time Before Sunset
![Page 63: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/63.jpg)
import ephemfrom pytz import timezone, utc
sun = ephem.Sun()moon = ephem.Moon()est = timezone('EST')
def observer(): location = ephem.Observer() location.lon = '-83:23:24' location.lat = '40:13:48' location.elevation = 302 return location
def sunrise(observer, date): observer.date = date.astimezone(utc) return observer.next_rising(sun).datetime().replace(tzinfo=utc).astimezone(est)
def sunset(observer, date): observer.date = date.astimezone(utc) return observer.next_setting(sun).datetime().replace(tzinfo=utc).astimezone(est)
def moonset(observer, date): observer.date = date.astimezone(utc) return observer.next_setting(moon).datetime().replace(tzinfo=utc).astimezone(est)
Our sky module that uses pyephem
![Page 64: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/64.jpg)
import ephemfrom pytz import timezone, utc
sun = ephem.Sun()moon = ephem.Moon()est = timezone('EST')
def observer(): location = ephem.Observer() location.lon = '-83:23:24' location.lat = '40:13:48' location.elevation = 302 return location
def sunrise(observer, date): observer.date = date.astimezone(utc) return observer.next_rising(sun).datetime().replace(tzinfo=utc).astimezone(est)
def sunset(observer, date): observer.date = date.astimezone(utc) return observer.next_setting(sun).datetime().replace(tzinfo=utc).astimezone(est)
def moonset(observer, date): observer.date = date.astimezone(utc) return observer.next_setting(moon).datetime().replace(tzinfo=utc).astimezone(est)
Our sky module that uses pyephem
![Page 65: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/65.jpg)
import ephemfrom pytz import timezone, utc
sun = ephem.Sun()moon = ephem.Moon()est = timezone('EST')
def observer(): location = ephem.Observer() location.lon = '-83:23:24' location.lat = '40:13:48' location.elevation = 302 return location
def sunrise(observer, date): observer.date = date.astimezone(utc) return observer.next_rising(sun).datetime().replace(tzinfo=utc).astimezone(est)
def sunset(observer, date): observer.date = date.astimezone(utc) return observer.next_setting(sun).datetime().replace(tzinfo=utc).astimezone(est)
def moonset(observer, date): observer.date = date.astimezone(utc) return observer.next_setting(moon).datetime().replace(tzinfo=utc).astimezone(est)
Our sky module that uses pyephem
![Page 66: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/66.jpg)
Watch Movie!
http://bit.ly/ospw-10
http://bit.ly/ospw-10-raw
![Page 67: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/67.jpg)
August 29, 2010 6:59pm EDT
http://bit.ly/ospw-11
October 22, 20105:33pm EDT
http://bit.ly/ospw-12
![Page 68: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/68.jpg)
Ancient Astronomical Alignmentshttp://bit.ly/ospw-14
Photo credits:http://en.wikipedia.org/wiki/File:ChichenItzaEquinox.jpghttp://www.colorado.edu/Conferences/chaco/tour/images/dagger.jpg
![Page 69: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/69.jpg)
Tenet #5:
Once you have a foundation (data or code), however messy,
you can build higher.
![Page 70: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/70.jpg)
So let's build higher!
![Page 71: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/71.jpg)
We Also Take Pictures At Night
● Could I have captured a UFO in an image?
● Or a fireball?● Some other phenomenon?● What track does the moon take
through the sky?● How about Venus or Jupiter?
![Page 72: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/72.jpg)
Moon Tracks and UFOs
● We want to find interesting things● How do we easily identify “interesting things”● At night, bright things are interesting things● Brightness is a good proxy for “interesting”● It makes it easy to identify interesting things● As it is easier to spot black spots on white images,
we'll invert the images
![Page 73: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/73.jpg)
Making Night Tracks
● Process each image● Stack images into a single “all
night” image● From one hour after sunset to one
hour before sunrise the next day● Black splotches are interesting
things
![Page 74: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/74.jpg)
def make_nighttrack(rootdir, day): obs = sky.observer()
# One hour after sunset start_time = sky.sunset(obs, day) + datetime.timedelta(hours=1) # Until one hour before sunrise the next day end_time = sky.sunrise(obs, tomorrow) - datetime.timedelta(hours=1)
pics = NormalizedPicture.objects.filter( timestamp__gte=start_time, timestamp__lt=end_time, picture__id__isnull=False)
print "Drawing image with {0} images".format(len(pics)) im = Image.new("L", (640,480), background_color)
for picture in pics: source = Image.open(picture.picture.filepath) # Get the negative source_gray = ImageOps.grayscale(source) source_neg = ImageOps.invert(source_gray)
# Threshold white source_thresh = Image.eval(source_neg, lambda x: 255*(x>224)) # Merge in the new image im = ImageChops.multiply(im, source_thresh)
# Put a date on the image canvas = ImageDraw.Draw(im) canvas.text((10,460), day.strftime("%Y-%m-%d"))
# And save im.save('{root}/{date}_mt.png'.format(root=rootdir,date=day.strftime("%Y%m%d")))
![Page 75: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/75.jpg)
def make_nighttrack(rootdir, day): obs = sky.observer()
# One hour after sunset start_time = sky.sunset(obs, day) + datetime.timedelta(hours=1) # Until one hour before sunrise the next day end_time = sky.sunrise(obs, tomorrow) - datetime.timedelta(hours=1)
pics = NormalizedPicture.objects.filter( timestamp__gte=start_time, timestamp__lt=end_time, picture__id__isnull=False)
print "Drawing image with {0} images".format(len(pics)) im = Image.new("L", (640,480), background_color)
for picture in pics: source = Image.open(picture.picture.filepath) # Get the negative source_gray = ImageOps.grayscale(source) source_neg = ImageOps.invert(source_gray)
# Threshold white source_thresh = Image.eval(source_neg, lambda x: 255*(x>224)) # Merge in the new image im = ImageChops.multiply(im, source_thresh)
# Put a date on the image canvas = ImageDraw.Draw(im) canvas.text((10,460), day.strftime("%Y-%m-%d"))
# And save im.save('{root}/{date}_mt.png'.format(root=rootdir,date=day.strftime("%Y%m%d")))
![Page 76: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/76.jpg)
def make_nighttrack(rootdir, day): obs = sky.observer()
# One hour after sunset start_time = sky.sunset(obs, day) + datetime.timedelta(hours=1) # Until one hour before sunrise the next day end_time = sky.sunrise(obs, tomorrow) - datetime.timedelta(hours=1)
pics = NormalizedPicture.objects.filter( timestamp__gte=start_time, timestamp__lt=end_time, picture__id__isnull=False)
print "Drawing image with {0} images".format(len(pics)) im = Image.new("L", (640,480), background_color)
for picture in pics: source = Image.open(picture.picture.filepath) # Get the negative source_gray = ImageOps.grayscale(source) source_neg = ImageOps.invert(source_gray)
# Threshold white source_thresh = Image.eval(source_neg, lambda x: 255*(x>224)) # Merge in the new image im = ImageChops.multiply(im, source_thresh)
# Put a date on the image canvas = ImageDraw.Draw(im) canvas.text((10,460), day.strftime("%Y-%m-%d"))
# And save im.save('{root}/{date}_mt.png'.format(root=rootdir,date=day.strftime("%Y%m%d")))
![Page 77: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/77.jpg)
def make_nighttrack(rootdir, day): obs = sky.observer()
# One hour after sunset start_time = sky.sunset(obs, day) + datetime.timedelta(hours=1) # Until one hour before sunrise the next day end_time = sky.sunrise(obs, tomorrow) - datetime.timedelta(hours=1)
pics = NormalizedPicture.objects.filter( timestamp__gte=start_time, timestamp__lt=end_time, picture__id__isnull=False)
print "Drawing image with {0} images".format(len(pics)) im = Image.new("L", (640,480), background_color)
for picture in pics: source = Image.open(picture.picture.filepath) # Get the negative source_gray = ImageOps.grayscale(source) source_neg = ImageOps.invert(source_gray)
# Threshold white source_thresh = Image.eval(source_neg, lambda x: 255*(x>224)) # Merge in the new image im = ImageChops.multiply(im, source_thresh)
# Put a date on the image canvas = ImageDraw.Draw(im) canvas.text((10,460), day.strftime("%Y-%m-%d"))
# And save im.save('{root}/{date}_mt.png'.format(root=rootdir,date=day.strftime("%Y%m%d")))
![Page 81: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/81.jpg)
Lightning Bug? Aircraft? UFO!
http://bit.ly/ospw-18http://bit.ly/ospw-19http://bit.ly/ospw-20
Moon
Venus
?
![Page 82: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/82.jpg)
Shows up 3 weeks later
http://bit.ly/ospw-21http://bit.ly/ospw-22http://bit.ly/ospw-23
![Page 84: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/84.jpg)
Last Oddity: 9/2/2011
Single-Frame Oddity
http://bit.ly/ospw-30http://bit.ly/ospw-31
Crescent Moon That Night
http://bit.ly/ospw-32http://bit.ly/ospw-33
![Page 85: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/85.jpg)
Let's make some science art!
![Page 86: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/86.jpg)
Daystrips
● So far we've been using whole images...let's change that
● Instead of using a whole image, let's just use one line
● Kind of like a scanner● Start at midnight, stacking lines
![Page 88: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/88.jpg)
Daystrips
● There are 1440 minutes in a day● Images will be 1440 pixels high● We place image line at the current
minute in the day● HOUR * 60 + MINUTE
![Page 89: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/89.jpg)
def make_daystrip(rootdir, day): end = day + datetime.timedelta(days=1)
print "Getting data for {0}".format(day) pics = NormalizedPicture.objects.filter( timestamp__gte=day, timestamp__lt=end, picture__id__isnull=False)
im = Image.new('RGB', (640,1440), background_color) for picture in pics: source = Image.open(picture.picture.filepath) # Get row 1/3 of the way down, painting on proper row of canvas timestamp = picture.timestamp.astimezone(esttz)
y = timestamp.hour * 60 + timestamp.minute
im.paste(source.crop((0,160,640,161)),(0,y))
im.save('{root}/{date}_daystrip.png'.format(root=rootdir,date=day.strftime("%Y%m%d")))
This is beginning to look familiar
![Page 90: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/90.jpg)
def make_daystrip(rootdir, day): end = day + datetime.timedelta(days=1)
print "Getting data for {0}".format(day) pics = NormalizedPicture.objects.filter( timestamp__gte=day, timestamp__lt=end, picture__id__isnull=False)
im = Image.new('RGB', (640,1440), background_color) for picture in pics: source = Image.open(picture.picture.filepath) # Get row 1/3 of the way down, painting on proper row of canvas timestamp = picture.timestamp.astimezone(esttz)
y = timestamp.hour * 60 + timestamp.minute
im.paste(source.crop((0,160,640,161)),(0,y))
im.save('{root}/{date}_daystrip.png'.format(root=rootdir,date=day.strftime("%Y%m%d")))
Create Our New Image
![Page 91: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/91.jpg)
def make_daystrip(rootdir, day): end = day + datetime.timedelta(days=1)
print "Getting data for {0}".format(day) pics = NormalizedPicture.objects.filter( timestamp__gte=day, timestamp__lt=end, picture__id__isnull=False)
im = Image.new('RGB', (640,1440), background_color) for picture in pics: source = Image.open(picture.picture.filepath) # Get row 1/3 of the way down, painting on proper row of canvas timestamp = picture.timestamp.astimezone(esttz)
y = timestamp.hour * 60 + timestamp.minute
im.paste(source.crop((0,160,640,161)),(0,y))
im.save('{root}/{date}_daystrip.png'.format(root=rootdir,date=day.strftime("%Y%m%d")))
Iterate Though Our Images
![Page 92: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/92.jpg)
def make_daystrip(rootdir, day): end = day + datetime.timedelta(days=1)
print "Getting data for {0}".format(day) pics = NormalizedPicture.objects.filter( timestamp__gte=day, timestamp__lt=end, picture__id__isnull=False)
im = Image.new('RGB', (640,1440), background_color) for picture in pics: source = Image.open(picture.picture.filepath) # Get row 1/3 of the way down, painting on proper row of canvas timestamp = picture.timestamp.astimezone(esttz)
y = timestamp.hour * 60 + timestamp.minute
im.paste(source.crop((0,160,640,161)),(0,y))
im.save('{root}/{date}_daystrip.png'.format(root=rootdir,date=day.strftime("%Y%m%d")))
And Paste The Single Row
![Page 93: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/93.jpg)
def make_daystrip(rootdir, day): end = day + datetime.timedelta(days=1)
print "Getting data for {0}".format(day) pics = NormalizedPicture.objects.filter( timestamp__gte=day, timestamp__lt=end, picture__id__isnull=False)
im = Image.new('RGB', (640,1440), background_color) for picture in pics: source = Image.open(picture.picture.filepath) # Get row 1/3 of the way down, painting on proper row of canvas timestamp = picture.timestamp.astimezone(esttz)
y = timestamp.hour * 60 + timestamp.minute
im.paste(source.crop((0,160,640,161)),(0,y))
im.save('{root}/{date}_daystrip.png'.format(root=rootdir,date=day.strftime("%Y%m%d")))
Finally, save the image
![Page 94: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/94.jpg)
Daystrip Example – March 17, 2011
http://bit.ly/ospw-25
Midnight
Moon Crossing
Sunrise
More cloudy (gray)More cloudy (gray)
Less cloudy (blue)
Sun Crossing
Midnight
Sunset
![Page 95: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/95.jpg)
Shortest and Longest Day
Dec 22, 2011
http://bit.ly/ospw-26
June 20, 2012
http://bit.ly/ospw-27
![Page 96: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/96.jpg)
Tenet #6:Don't be afraid to be creative.
Science can be beautiful.
![Page 97: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/97.jpg)
Everything we've made so far spans a day or less.
![Page 98: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/98.jpg)
What we've done so far
● Viewed full images of interest● Combined full images in movies● Stacked full images to find UFOs● Took a full line from a day's worth
of images● Everything is a day or less of data
![Page 99: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/99.jpg)
Let's use ALL the days!
![Page 100: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/100.jpg)
What if...
![Page 101: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/101.jpg)
Instead of an image row being a single minute...
it was a whole day...
![Page 102: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/102.jpg)
And each pixel in the row was a minute in the day.
![Page 103: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/103.jpg)
Therefore, each of our 896,309 webcam images would
comprise a single pixel in our über-image.
![Page 104: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/104.jpg)
A Visual Representation
Minutes in a day (1440)
Day
s (e
ach
row
is o
ne d
ay)
Image
Mid
nigh
t
Mid
nigh
t
Noo
n
Start Day
End Day
Each pixel is one minute in the day
![Page 105: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/105.jpg)
pics = NormalizedPicture.objects.all()
im = Image.new('RGB', (1440,num_days), background_color)Canvas = ImageDraw.Draw(im)
for picture in pics: # Get XY timestamp = picture.timestamp.astimezone(est)
y_timedelta = timestamp - start y = y_timedelta.days x = timestamp.hour * 60 + timestamp.minute
# Paint pixel if picture.picture is not None: color = int_to_rgb(picture.picture.median_color) else: color = background_color
canvas.point((x,y), fill=color)
# All done, saveim.save(filepath)
![Page 106: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/106.jpg)
pics = NormalizedPicture.objects.all()
im = Image.new('RGB', (1440,num_days), background_color)Canvas = ImageDraw.Draw(im)
for picture in pics: # Get XY timestamp = picture.timestamp.astimezone(est)
y_timedelta = timestamp - start y = y_timedelta.days x = timestamp.hour * 60 + timestamp.minute
# Paint pixel if picture.picture is not None: color = int_to_rgb(picture.picture.median_color) else: color = background_color
canvas.point((x,y), fill=color)
# All done, saveim.save(filepath)
![Page 107: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/107.jpg)
pics = NormalizedPicture.objects.all()
im = Image.new('RGB', (1440,num_days), background_color)Canvas = ImageDraw.Draw(im)
for picture in pics: # Get XY timestamp = picture.timestamp.astimezone(est)
y_timedelta = timestamp - start y = y_timedelta.days x = timestamp.hour * 60 + timestamp.minute
# Paint pixel if picture.picture is not None: color = int_to_rgb(picture.picture.median_color) else: color = background_color
canvas.point((x,y), fill=color)
# All done, saveim.save(filepath)
![Page 108: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/108.jpg)
pics = NormalizedPicture.objects.all()
im = Image.new('RGB', (1440,num_days), background_color)Canvas = ImageDraw.Draw(im)
for picture in pics: # Get XY timestamp = picture.timestamp.astimezone(est)
y_timedelta = timestamp - start y = y_timedelta.days x = timestamp.hour * 60 + timestamp.minute
# Paint pixel if picture.picture is not None: color = int_to_rgb(picture.picture.median_color) else: color = background_color
canvas.point((x,y), fill=color)
# All done, saveim.save(filepath)
![Page 111: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/111.jpg)
Basically It Normalizes Sunrise TimeBy Making Summer Sunrise Later
http://bit.ly/ospw-29
![Page 112: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/112.jpg)
At The Expense Of Wider Sunset Time Variation, Because Of Later Summer Sunset
http://bit.ly/ospw-29
![Page 113: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/113.jpg)
Python makes it easy to answer “What If?”
So...
![Page 114: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/114.jpg)
Tenet #7:Don't be afraid to ask
“What if?”, and don't be afraid of where it takes you.
![Page 115: Observational Science With Python and a Webcam](https://reader033.vdocuments.mx/reader033/viewer/2022052900/55632afed8b42ad7398b4f34/html5/thumbnails/115.jpg)
Presentation:http://bit.ly/ospw-talk-pdf
http://bit.ly/ospw-talk (raw)
Code:http://bit.ly/ospw-code
Picture Set (9.1GB):http://bit.ly/ospw-pics
Links and more (soon):http://intellovations.com/ospw