Skip to content

Instantly share code, notes, and snippets.

@gustavohenrique
Last active May 5, 2023 11:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gustavohenrique/b316951ca8ce5636754523f074ae3fdd to your computer and use it in GitHub Desktop.
Save gustavohenrique/b316951ca8ce5636754523f074ae3fdd to your computer and use it in GitHub Desktop.
Organize my photos taken by my smartphone
# SETUP
# pip install exif geopy
# python photo-organize.py ~/Pictures/Camera
#
# What happens?
# Organize all photos in Camera dir to dirs according to datetime and location. Ex: ~/Pictures/Camera/2023-05-06_rj-cabo-frio
import sys
import os
import re
import shutil
import unicodedata
from datetime import date
from exif import Image
from geopy.geocoders import Nominatim
def get_grouped_photos(dir_path):
res = {}
for path in os.listdir(dir_path):
file = os.path.join(dir_path, path)
if not os.path.isfile(file):
continue
year = str(date.today().year)
dt = re.findall('_' + year + '[0-9]{4}_', path)[0].replace('_', '')
files = []
if dt in res:
files = res[dt]
files.append(file)
res[dt] = files
return res
def decimal_coords(coords, ref):
decimal_degrees = coords[0] + coords[1] / 60 + coords[2] / 3600
if ref == 'S' or ref =='W' :
decimal_degrees = -decimal_degrees
return decimal_degrees
def get_gps_from(photo):
empty = {'lat': 0, 'long': 0}
jpg = photo.lower()
if not jpg.endswith('.jpg') or jpg.endswith('.jpeg'):
return empty
with open(photo, 'rb') as src:
img = Image(src)
if not img.has_exif:
return empty
try:
img.gps_longitude
lat = decimal_coords(img.gps_latitude, img.gps_latitude_ref)
lgt = decimal_coords(img.gps_longitude, img.gps_longitude_ref)
return {
'lat': lat,
'long': lgt
}
except:
return empty
def normalize(s):
return ''.join(c for c in unicodedata.normalize('NFD', s)
if unicodedata.category(c) != 'Mn')
def get_location_from(coords):
geolocator = Nominatim(user_agent='Fotos')
coordinates = '{0}, {1}'.format(coords['lat'], coords['long'])
location = geolocator.reverse(coordinates)
address = location.raw['address']
city = address.get('city', '')
if not city:
city = address.get('village', '')
state = address.get('ISO3166-2-lvl4', '')
return {
'city': normalize(city.lower().replace(' ', '-')),
'state': state.lower().replace('br-', '')
}
def move(photos, dist_dir):
for photo in photos:
new_photo = os.path.join(dist_dir, os.path.basename(photo))
shutil.move(photo, new_photo)
if __name__ == '__main__':
source_photos = sys.argv[1]
if not os.path.isdir(source_photos):
print(f'The source dir {source_photos} does not exists')
exit(1)
dist_photos = sys.argv[2] if len(sys.argv) == 3 else source_photos
if not os.path.isdir(dist_photos):
os.mkdir(dist_photos, 0o777)
print(f'The dist dir {dist_photos} had created')
grouped = get_grouped_photos(source_photos)
if len(grouped) == 0:
print('No photos found. Exiting...')
exit(1)
for key in grouped:
photos = grouped[key]
dist_dir = dist_photos
if len(photos) > 1:
coords = get_gps_from(photos[0])
print('coords', coords)
location = get_location_from(coords)
print('location', location)
folder_name = '{0}-{1}-{2}_{3}-{4}'.format(key[0:4], key[4:6], key[6:8], location['state'], location['city'])
dist_dir = os.path.join(dist_photos, folder_name)
if not os.path.isdir(dist_dir):
os.mkdir(dist_dir, 0o777)
print(f'Created dir {folder_name}')
move(photos, dist_dir)
print(dist_photos)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment