随着数字相机和智能手机的普及,我们拍摄的照片数量越来越多。但是,如何有效地管理这些照片成为了一个问题。有时候,我们希望按照照片拍摄的地点进行分类,这就需要提取照片中的GPS信息并进行地理编码。
下面是一个使用Python实现的脚本,可以从照片中提取GPS信息,并根据位置将照片分类到相应的文件夹中。 依赖安装:
pip install Pillow
pip install geopy
整理代码:
import os
from PIL import Image, UnidentifiedImageError
from PIL.ExifTags import TAGS, GPSTAGS
import shutil
from geopy.geocoders import Nominatim
from fractions import Fraction
from geopy.point import Point
from geopy.exc import GeocoderTimedOut
import time
def get_exif_data(image_path):
"""获取图片的EXIF数据"""
try:
image = Image.open(image_path)
exif_data = image.getexif()
if exif_data:
exif_data_decoded = {
TAGS.get(tag, tag): value for tag, value in exif_data.items()}
if 'GPSInfo' in exif_data_decoded and isinstance(exif_data_decoded['GPSInfo'], dict):
gps_info = {GPSTAGS.get(gps_tag, gps_tag): gps_value for gps_tag,
gps_value in exif_data_decoded['GPSInfo'].items()}
for key in ['GPSLatitude', 'GPSLongitude']:
if key in gps_info:
coordinates = gps_info[key]
if isinstance(coordinates, tuple):
gps_info[key] = tuple([rational_to_float(Fraction(coord))
for coord in coordinates])
return gps_info
except (IOError, SyntaxError, UnidentifiedImageError) as e:
print(f"文件 {image_path} 损坏,已删除。")
os.remove(image_path)
return None
def rational_to_float(rational):
"""将有理数转换为浮点数"""
return float(rational.numerator) / float(rational.denominator)
def get_location_name(latitude, longitude):
"""获取坐标对应的位置名称"""
geolocator = Nominatim(user_agent="geo_locator")
latitude_degrees, latitude_minutes, latitude_seconds = latitude
longitude_degrees, longitude_minutes, longitude_seconds = longitude
latitude_decimal = latitude_degrees + \
latitude_minutes / 60 + latitude_seconds / 3600
longitude_decimal = longitude_degrees + \
longitude_minutes / 60 + longitude_seconds / 3600
point = Point(latitude_decimal, longitude_decimal)
try:
location = geolocator.reverse(point, language="zh", timeout=30)
return location.address
except GeocoderTimedOut:
print("地理编码服务超时,忽略此请求。")
return None
def create_folder_for_location(gps_info, base_dir):
"""根据GPS信息创建对应位置的文件夹"""
if gps_info is None:
unknown_folder = os.path.join(base_dir, "未知位置")
return unknown_folder
latitude_ref = gps_info.get('GPSLatitudeRef')
longitude_ref = gps_info.get('GPSLongitudeRef')
latitude = gps_info.get('GPSLatitude')
longitude = gps_info.get('GPSLongitude')
if latitude and longitude:
location_name = get_location_name(latitude, longitude)
if location_name:
# 按逗号分割地址,保留第一个逗号之前的内容作为文件名
location_parts = location_name.split(',')
filename = location_parts[0]
if not filename: # 如果第一个位置为空,则取第二个位置
filename = location_parts[1] if len(
location_parts) > 1 else "未知位置"
filename = filename.replace("/", "_")
filename = filename.replace(":", "_")
# 检查文件夹是否已存在,若存在,则直接使用
folder_path = os.path.join(base_dir, filename)
return folder_path
return None
def move_image_to_folder(image_path, folder_path, base_dir, force_overwrite=False):
"""将图片移动到对应的文件夹中"""
if not os.path.exists(image_path):
print(f"文件 {image_path} 不存在,跳过处理。")
return
if folder_path:
os.makedirs(folder_path, exist_ok=True)
# 构建目标文件路径
destination_file = os.path.join(
folder_path, os.path.basename(image_path))
if os.path.exists(destination_file) and not force_overwrite:
filename = os.path.basename(image_path)
timestamp = int(time.time())
filename_parts = os.path.splitext(filename)
new_filename = f"{filename_parts[0]}_{timestamp}{filename_parts[1]}"
destination_file = os.path.join(folder_path, new_filename)
# 判断是否已存在同名文件,如果存在则加上时间戳
while os.path.exists(destination_file):
timestamp = int(time.time())
new_filename = f"{filename_parts[0]}_{timestamp}{filename_parts[1]}"
destination_file = os.path.join(folder_path, new_filename)
# 移动文件
try:
shutil.move(image_path, destination_file)
except FileNotFoundError:
print(f"文件 {image_path} 不存在,跳过处理。")
else:
# 如果无法确定目标文件夹,将图片移动到未知位置文件夹
unknown_folder = os.path.join(base_dir, "未知位置")
os.makedirs(unknown_folder, exist_ok=True)
# 生成唯一的文件名
filename = os.path.basename(image_path)
timestamp = int(time.time())
filename_parts = os.path.splitext(filename)
new_filename = f"{filename_parts[0]}_{timestamp}{filename_parts[1]}"
# 构建目标文件路径
destination_file = os.path.join(unknown_folder, new_filename)
if os.path.exists(destination_file) and not force_overwrite:
# 判断是否已存在同名文件,如果存在则加上时间戳
while os.path.exists(destination_file):
timestamp = int(time.time())
new_filename = f"{filename_parts[0]}_{timestamp}{filename_parts[1]}"
destination_file = os.path.join(unknown_folder, new_filename)
# 移动文件
try:
shutil.move(image_path, destination_file)
except FileNotFoundError:
print(f"文件 {image_path} 不存在,跳过处理。")
def main():
base_dir = r"D:\照片"
os.makedirs(base_dir, exist_ok=True)
total_files = 0
processed_files = 0
# 允许处理的图像文件格式
ALLOWED_IMAGE_EXTENSIONS = [".jpg", ".jpeg",
".png", ".bmp", ".gif", ".tiff", ".tif"]
image_paths = [] # 收集图片路径
for filename in os.listdir(base_dir):
# 检查文件扩展名是否在允许的列表中
if any(filename.lower().endswith(ext) for ext in ALLOWED_IMAGE_EXTENSIONS):
total_files += 1
image_paths.append(os.path.join(base_dir, filename)) # 添加图片路径
print(f"总共有 {total_files} 个文件需要处理.")
for image_path in image_paths: # 使用收集到的图片路径
gps_info = get_exif_data(image_path)
folder_path = create_folder_for_location(gps_info, base_dir)
move_image_to_folder(image_path, folder_path,
base_dir, force_overwrite=True)
processed_files += 1
print(
f"已处理 {processed_files}/{total_files} 个文件.正在处理文件:{image_path} 到:{folder_path}。")
# 创建“其他格式”文件夹并移动其他格式文件
other_formats_folder = os.path.join(base_dir, "其他格式")
os.makedirs(other_formats_folder, exist_ok=True)
for filename in os.listdir(base_dir):
file_path = os.path.join(base_dir, filename)
if os.path.isfile(file_path) and file_path not in image_paths: # 使用收集到的图片路径
destination_file = os.path.join(
other_formats_folder, os.path.basename(file_path))
if os.path.exists(destination_file):
# 如果目标文件已存在,生成新的唯一文件名
timestamp = int(time.time())
filename_parts = os.path.splitext(filename)
new_filename = f"{filename_parts[0]}_{timestamp}{filename_parts[1]}"
destination_file = os.path.join(
other_formats_folder, new_filename)
# 移动文件
try:
shutil.move(file_path, destination_file)
except FileNotFoundError:
print(f"文件 {file_path} 不存在,跳过处理。")
if __name__ == "__main__":
main()
整理结果:
发表评论 取消回复