In [19]:
from ahh import vis, ext, era, pre, sci
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw
import pandas as pd
import numpy as np
import datetime
import exifread
import os
In [20]:
EST_OFFSET = 1 # camera metadata still uses CST
GM1_OFFSET = -4 # the camera is 4 minutes faster
D7100_OFFSET = +4 # the camera is 4 minutes slower
START_DT_STR = '2017-07-08 08:00'
END_DT_STR = '2017-07-08 20:30'
FONT = ImageFont.truetype("data/GOTHIC.TTF", 100)
COLOR = (255, 255, 255, 0)
SHADOW_COLOR = (50, 50, 50, 0)
PLOT_TXT_FMT = 'Total Floors: {5:.0f}\nTotal Steps: {3:d}\nTotal Calories: {6:.0f}\nTotal Kilometers: {4:0.2f}\n\n' \
'Steps per Minute Avg: {0:.2f}\nBeats per Minute Avg: {1:.2f}\nKilometers per Hour Avg: {2:.2f}'
IMG_TXT_FMT = '{hour}:{dt.minute:02d} {ampm} EST {month} {dt.day}, {dt.year}\n' \
'{km:04.2f} km | {mi:04.2f} mi\n{steps:.0f} steps\n{bpm:.0f} bpm'
XLABEL = 'Time [EST]'
OVERVIEW_TITLE_FMT = 'Overview of July 8, 2017 from {hr1}:{min1} {ampm1} - {hr2}:{min2} {ampm2}'
In [44]:
def get_dt_from_img(fn):
with open(fn, 'rb') as f:
tags = exifread.process_file(f)
time_mdata = tags['EXIF DateTimeOriginal']
dt = (datetime.datetime.strptime(time_mdata.values,
'%Y:%m:%d %H:%M:%S')
+ datetime.timedelta(hours=EST_OFFSET))
model = tags['Image Model']
if 'GM1' in str(model):
dt += datetime.timedelta(minutes=GM1_OFFSET)
elif 'D7100' in str(model):
dt += datetime.timedelta(minutes=D7100_OFFSET)
return dt
def round_to_nearest_minute(dt):
secs = dt.second
if secs > 30:
dt += datetime.timedelta(minutes=1)
return dt.replace(second=0)
def plot_summary(df, base_dir, dts=None):
if dts is not None:
df = df.loc[df.index <= dts[-1] + datetime.timedelta(minutes=15)]
mark = True
elif dts is None:
dts = pd.date_range(START_DT_STR, END_DT_STR, freq='1Min')
mark = False
steps_scale = [2 ** (n / 12.5) + 15 for n in df.steps]
steps_scale = np.clip(np.array(steps_scale), 0, 2000)
steps_avg = df.loc[df.steps > 0, 'steps'].mean()
bpm_avg = df.loc[df.bpm > 0, 'bpm'].mean()
dist_hourly_avg = df.resample('1H').sum()['distance'].mean()
total_steps = df.steps.sum()
total_calories = df.calories.sum()
total_floors = df.floors.sum()
total_beats = df.bpm.sum()
total_distance = df.distance.sum()
title_fmtd = OVERVIEW_TITLE_FMT.format(hr1=dts[0].strftime('%I'),
min1=dts[0].strftime('%M'),
ampm1=dts[0].strftime('%p'),
hr2=dts[-1].strftime('%I'),
min2=dts[-1].strftime('%M'),
ampm2=dts[-1].strftime('%p')
)
ax = vis.plot_scatter(df.index, df['distance_cumsum'], c=df.bpm, s=steps_scale, figsize=(15, 7),
title=title_fmtd, label='Size Scaled to Steps per Minute', length_scale=False,
xlabel=XLABEL, ylabel='Total Distance [km]', cbar_label='Heart Rate [BPM]',
alpha=0.75, vmin=60, vmax=130, interval=10, pad=0.175, title_pad=1)
plot_txt_fmtd = PLOT_TXT_FMT.format(steps_avg, bpm_avg, dist_hourly_avg,
total_steps, total_distance,
total_floors, total_calories)
vis.set_axtext(ax, plot_txt_fmtd, loc='bottom right')
base_dir = pre.mkdir(base_dir)
if mark:
for i, dt in enumerate(dts):
vis.annotate_point(ax, dt,
df['distance_cumsum'][round_to_nearest_minute(dt)],
'<-- Pic {0}'.format(i + 1), size=12,
alpha=0.95, color=vis.COLORS['gray'], bbox=None)
save = vis.savefig('{0}/overview.png'.format(base_dir), tight_layout=True)
def stamp_imgs(img_fp_list):
dt_list = []
for img_fp in img_fp_list:
try:
dt = get_dt_from_img(img_fp)
dt_list.append(dt)
dt_rounded = round_to_nearest_minute(dt)
ds = df.loc[dt_rounded]
mi = sci.convert(ds.distance_cumsum, km2mi=True)
img_txt_fmtd = IMG_TXT_FMT.format(dt=dt_rounded,
month=dt.strftime('%b'),
hour=dt.strftime('%I'),
ampm=dt.strftime('%p'),
steps=ds.steps_cumsum,
km=ds.distance_cumsum,
mi=mi,
bpm=ds.bpm
)
img = Image.open(img_fp)
draw = ImageDraw.Draw(img)
x, y, z = np.shape(img)
draw.text((30, 20),
img_txt_fmtd,
SHADOW_COLOR,
font=FONT)
draw.text((25, 15),
img_txt_fmtd,
COLOR,
font=FONT)
img_stamped_fp = ext.append_to_fn(img_fp, '_stamped')
img.save(img_stamped_fp)
_ = os.system('mv {0} {1}'.format(img_fp, orig_dir))
except:
ext.report_err()
return dt_list
def plot_steps_bpm(df, dts=None):
if dts is not None:
df = df.loc[df.index <= dts[-1]]
mark = True
elif dts is None:
dts = pd.date_range(START_DT_STR, END_DT_STR, freq='1Min')
mark = False
title_fmtd = OVERVIEW_TITLE_FMT.format(hr1=dts[0].strftime('%I'),
min1=dts[0].strftime('%M'),
ampm1=dts[0].strftime('%p'),
hr2=dts[-1].strftime('%I'),
min2=dts[-1].strftime('%M'),
ampm2=dts[-1].strftime('%p')
)
ax = vis.plot_line(df.index, df['bpm'], label='Beats per Minute', xlabel=XLABEL,
ylabel='Heart Rate / Pace', length_scale=False)
ax = vis.plot_bar(df.index, df['steps'], alpha=0.5, bar_vals=False, label='Steps per Minute',
ylim=(0, 175), xlim=(dts[0], dts[-1] + datetime.timedelta(minutes=10)),
color=vis.COLORS['blue'], width=0.0001, figsize='na', title=title_fmtd, length_scale=False)
if mark:
for i, dt in enumerate(dts):
vis.annotate_point(ax, dt,
df['bpm'][round_to_nearest_minute(dt)],
'<-- Pic {0}'.format(i + 1), size=12,
alpha=0.95, color=vis.COLORS['gray'], bbox=None)
save = vis.savefig('{0}/detailed.png'.format(base_dir), tight_layout=True)
In [45]:
df1 = pre.read_csv('data/activity_070817.csv', date='ACTIVITY DATE/TIME').resample('1Min').mean()
df2 = pre.read_csv('data/heart_070817.csv', date='HEART RATE DATE/TIME').resample('1Min').mean()
df = pre.merge(df1, df2).dropna(axis=0)
df.columns = 'calories, steps, distance, floors, bpm'.split(', ')
df = df.loc[df.index > START_DT_STR]
df['distance_cumsum'] = np.cumsum(df.distance)
df['steps_cumsum'] = np.cumsum(df.steps)
In [46]:
base_dir = 'aftermath'
plot_summary(df, base_dir)
In [47]:
base_dir = 'inopportune_start'
orig_dir = pre.mkdir(os.path.join(base_dir, 'original'))
img_fp_list = ext.glob('{0}/*.jpg'.format(base_dir))
dt_list = stamp_imgs(img_fp_list)
dts = pd.DatetimeIndex(dt_list)
plot_summary(df, base_dir, dts)
plot_steps_bpm(df, dts=dts)
In [ ]:
In [ ]: