Last active
May 5, 2023 11:31
-
-
Save gustavohenrique/b316951ca8ce5636754523f074ae3fdd to your computer and use it in GitHub Desktop.
Organize my photos taken by my smartphone
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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