import numpy as np from datetime import datetime, timedelta import sys from time import strptime from PIL import Image, ImageFont import pandas as pd import openpyxl def divide_chunks(l, n): """ Splits a list into chunks of length n. Used to process the images in chunks. """ for i in range(0, len(l), n): yield l[i : i + n] # Sourced from https://gist.github.com/victorkristof/b9d794fe1ed12e708b9d def datenum_to_datetime(datenum): """ Convert Matlab datenum into Python datetime. :param datenum: Date in datenum format :return: Datetime object corresponding to datenum. """ days = datenum % 1 hours = days % 1 * 24 minutes = hours % 1 * 60 seconds = np.round(minutes % 1 * 60) return datetime.fromordinal(int(datenum)) \ + timedelta(days=int(days)) \ + timedelta(hours=int(hours)) \ + timedelta(minutes=int(minutes)) \ + timedelta(seconds=int(seconds)) \ - timedelta(days=366) # Sourced from https://stackoverflow.com/questions/32237862/find-the-closest-date-to-a-given-date def nearest(all_dates, target_date_raw): abs_deltas_from_target_date = np.absolute(all_dates - target_date_raw) index_of_min_delta_from_target_date = np.argmin(abs_deltas_from_target_date) closest_date = all_dates[index_of_min_delta_from_target_date] return closest_date def progressbar(it, prefix="", size=60, out=sys.stdout): # Python3.3+ count = len(it) def show(j): x = int(size*j/count) print("{}[{}{}] {}/{}".format(prefix, u"#"*x, "."*(size-x), j, count), end='\r', file=out, flush=True) show(0) for i, item in enumerate(it): yield item show(i+1) print("\n", flush=True, file=out) def get_site_tide_data(images_parent_dir, site): print("Retrieving tide data") # Retrieve tide data for the given site db = openpyxl.load_workbook(images_parent_dir + "/Database/CoastSnapDB.xlsx") beach_data = db[site] tide_filename = beach_data["B24"].value if tide_filename == 'NO_TIDE.mat': return False tide_filename_pkl = tide_filename[:-4] + '.pkl' tides_path = images_parent_dir + '/Tide Data/Tide_Data_Python/' + tide_filename_pkl tides_df = pd.read_pickle(tides_path) return tides_df class RegisteredImage(): def __init__(self, pathname, filename, registered_pathname): self.pathname = pathname self.filename = filename self.registered_pathname = registered_pathname filename_list = filename.split(".") date = filename_list[3].split("_") if 'snap' in filename_list: self.contributor = filename_list[8] # Mitch filename format else: self.contributor = filename_list[6] # Leaman filename format self.site = filename_list[6] self.posix_time = filename_list[0] self.timezone = filename_list[4] self.year = filename_list[5] self.month = '{:02d}'.format(strptime(filename_list[2],'%b').tm_mon) # Ensure 2-digit format self.day = date[0] self.hour = date[1] self.minute = date[2] self.second = date[3] self.tide = None self.tag = None self.width = None self.height = None self.font = None self.large_filename = False def get_tide(self, tides_df): # Account for daylight savings # ASSUMPTION: All .mat tide files are either AEST or AEDT if self.timezone == 'AEDT': self.hour = str(int(self.hour) - 1) date_string = self.year + '-' + self.month + '-' + self.day + ' ' + self.hour + ':' + self.minute + ':' + self.second img_datetime = datetime.strptime(date_string, "%Y-%m-%d %H:%M:%S") # Image date/time as a datetime object tide_date = nearest(tides_df['time'], img_datetime) tide = tides_df.loc[tides_df['time'] == tide_date, 'tide level'] tide = tide.iloc[0] self.tide = "{:.2f}".format(tide) def create_tag(self): # Create image tag hour = self.hour.zfill(2) minute = self.minute.zfill(2) if self.tide: # Tag with tide data tide = self.tide if float(self.tide) >= 0: tide = '+' + self.tide tag = ('Date:' + self.year + '/' + self.month + '/' + self.day + ' Time:' + hour + ':' + minute + ' Tide:' + tide + 'm AHD' + ' Contributor:' + self.contributor) if(self.font.getsize(tag)[0] > self.width): # Check if text is wider than the image self.large_filename = True tag = ('Date:' + self.year + '/' + self.month + '/' + self.day + ' Time:' + hour + ':' + minute + ' Tide:' + tide + 'm AHD' + '\n' + 'Contributor:' + self.contributor) self.tag = tag else: # Tag without tide data tag = ('Date:' + self.year + '/' + self.month + '/' + self.day + ' Time:' + hour + ':' + minute + ' Contributor:' + self.contributor) if(self.font.getsize(tag)[0] > self.width): # Check if text is wider than the image self.large_filename = True tag = ('Date:' + self.year + '/' + self.month + '/' + self.day + ' Time:' + hour + ':' + minute + '\n' + 'Contributor:' + self.contributor) self.tag = tag def get_dimensions(self): image = Image.open(self.pathname) # White Text Box self.width, self.height = image.size def get_font(self): image = Image.open(self.pathname) fontsize = 1 if self.tide: img_fraction = 0.95 else: img_fraction = 0.85 font = ImageFont.truetype("fonts/LiberationMono.ttf", fontsize) generic_large_tag = "1641775682.Mon.Jan.10_11_48_02.AEDT.2022.byron.snap.KateThornborough123" while font.getsize(generic_large_tag)[0] < img_fraction*image.size[0]: # iterate until the text size is just larger than the criteria fontsize += 1 font = ImageFont.truetype("fonts/LiberationMono.ttf", fontsize) self.font = ImageFont.truetype("fonts/LiberationMono.ttf", fontsize)