一切的起因是因为换了新手机 (-,-),旧手机上的照片实在是太多了所以想腾出大部分到PC上面来释放一些存储空间。但是首先出现的问题就是照片有6000多个文件而且全都集中在一起,没有进行任何的分类。
所以我初步的想法,是按照日期时间进行分类,而且对应到的是【拍摄时间】而非【创建时间、修改时间、访问时间】这三个。如:照片是2019.6.20照的,就根据他的日期给分类到 "盘符:\某文件夹\2019\2019-06\"这个目录下。
那么接下来就直接动工了。首先我使用的还是自己熟悉的Python语言来进行,因为毕竟python三方所提供的各类库能够提供许多方便。
那么先要安装PIL库(Python Image Library):Pillow库是一个用于图像处理的第三方库,它支持多种图像格式,并提供了一系列图像处理功能,如图像存储、显示、缩放、裁剪、叠加以及图像的基本处理(如像素操作)、颜色处理等。调出CMD命令行执行安装:
pip install pillow
-
处理PNG, JPG, GIF为扩展名的文件
在PIL库里面我们主要是要应用到里面的“ExifTags”模块,这个模块公开了几个 enum.IntEnum
类,这些类为各种常见的EXIF标记提供常量和明文名称。而照片的各类元信息(Exif)头部插入了数码照片的信息,包括拍摄时的光圈、快门、白平衡、ISO、焦距、日期时间等各种和拍摄条件以及相机品牌、型号、色彩编码、拍摄时录制的声音以及GPS全球定位系统数据、缩略图等。
接下来就开始编码了,我们先导入几个库再编写一个函数
from PIL import Image
from PIL.ExifTags import TAGS
import os
# image_path为传入的路径字符参数
def get_datetime_original(image_path):
# 打开文件
image = Image.open(image_path)
# 读取exif内容
exif_data = image._getexif()
if exif_data is None:
return None
# 设置两个变量tag_id和value,分别获取exif里面对应的‘信息名称’和‘具体数据’
for tag_id, value in exif_data.items():
tag_name = TAGS.get(tag_id, tag_id)
# 获取里面的“DateTimeOriginal”信息名称,该元数据就是“拍摄日期”
if tag_name == 'DateTimeOriginal':
return value
image.close()
return None
image.close()
通过这个函数我们就可以返回某个照片文件的“拍摄日期”。
再往下,我们设置路径folder_path,和一个空列表file_names。并且通过os库来相应地读取在folder_path路径下所有的文件名称(含扩展名)。并且把它们append到列表file_names上. 再设置一个循环来遍历列表内的文件名称, 判断文件的扩展名是否是 PNG, JPG, GIF 结尾的文件
之后根据创建好的函数get_datetime_original获取对应文件的拍摄日期, 使用os库来创建和判断路径是否存在, 再使用os.path.join()函数来拼接路径, 根据完整路径month_dir,利用os.rename()函数把原路径下的文件移动到new_file_path新路径下.
# 设置照片所在路径,和一个空列表file_names
folder_path = "E:/照片备份/"
file_names = []
# 通过os库来相应地读取在folder_path路径下所有的文件名称(含扩展名)
# 并且把它们append到列表file_names上
for file in os.listdir(folder_path):
if not os.path.isdir(os.path.join(folder_path, file)):
file_names.append(file)
print("该文件夹下的所有文件名如下:")
# 计数变量x
x = 0
# 设置一个循环来遍历列表内的文件名称
for name in file_names:
# 判断当前文件的扩展名是否是 PNG, JPG, GIF 结尾的文件
if any([name.endswith(ext) for ext in [".PNG", ".png", ".JPG", ".jpg", ".GIF", ".gif"]]):
x += 1
# 根据照片名称来完整一下路径
datetime_original = get_datetime_original("E:/照片备份/"+ name)
if datetime_original:
# 如果文件没问题那就print出【文件名,拍摄时间,计数x】
print(name,"【拍摄时间】", datetime_original,x)
# 再print出对应的拍摄时间,由于时间字符都是固定格式的,所以我直接利用字符切分方法切分出年月
print('Year:',datetime_original[0:4],'Month:',datetime_original[5:7])
# 接下来就是设置按年月分出的文件夹路径了
# 我们使用os库来创建和判断路径是否存在,先设置年月的字符变量year和month
year = datetime_original[0:4]
month = year + "-" + datetime_original[5:7]
# 使用os.path.join()函数来拼接路径,路径应该以字符串形式传递
year_dir = os.path.join(folder_path, year)
month_dir = os.path.join(year_dir, month)
# 如果路径不存在则使用os.mkdir()创建路径
if not os.path.exists(year_dir):
os.mkdir(year_dir)
if not os.path.exists(month_dir):
os.mkdir(month_dir)
# 如果路径没问题则根据完整路径month_dir,利用os.rename()函数把原路径下的文件移动到new_file_path新路径下.
new_file_path = os.path.join(month_dir, os.path.basename(name))
os.rename("E:/照片备份/"+ name, new_file_path)
else:
print('未找到拍摄日期和时间信息')
else:
# 跳过异常
continue
至此如果你的代码可以完整运行, 那么对于PNG, JPG, GIF格式的文件分类应该已经完成了。那接下来就是要处理 HEIC 这个鬼屎的照片格式了。
-
处理HEIC格式文件
为什么单独把HEIC格式文件单拎出来讲, 是因为windows上python的各类第三方库都不能够完整兼容地处理HEIC. 在MacOS上有pyheif这个库可以处理但是windows上不行,所以需要单独找一种方法来处理。
我所使用的方法大致与刚才无异,但是需要多做几个步骤。第一还是先要安装两个库“pillow_heif”和“exifread”。
pip install pillow_heif
pip install exifread
然后我们测试着做一下如下代码
from PIL import Image
from pillow_heif import register_heif_opener
import io
import exifread
# 创建register_heif_opener模块里的一个类然后再Image.open打开HEIC文件
register_heif_opener()
# 打开你自己的HEIC照片文件
image = Image.open("E:/iPhone照片备份/2018/2018-04/IMG_0186.HEIC")
# image.info里存放着照片的各类信息数据,当然也包括exif信息
print(image.info)
之后可以得到如下的输出内容:
{'primary': True, 'bit_depth': 8, 'exif': b'Exif\x00\x00MM\x00*\x00\x00\x00\x08\x00\x0b\x01\x0f\x00\x02\x00\x00\x00\x06\x00\x00\x00\x92\x01\x10\x00\x02\x00\x00\x00\t\x00\x00\x00\x98\x01\x12\x00\x03\x00\x00\x00\x01\x00\x01\x00\x00\x01\x1a\x00\x05\x00\x00\x00\x01\x00\x00\x00\xa2\x01\x1b\x00\x05\x00\x00\x00\x01\x00\x00\x00\xaa\x01(\x00\x03\x00\x00\x00\x01\x00\x02\x00\x00\x011\x00\x02\x00\x00\x00\x07\x00\x00\x00\xb2\x012\x00\x02\x00\x00\x00\x14\x00\x00\x00\xba\x02\x13\x00\x03\x00\x00\x00\x01\x00\x01\x00\x00\x87i\x00\x04\x00\x00\x00\x01\x00\x00\x00\xce\x88%\x00\x04\x00\x00\x00\x01\x00\x00\x06\xc2\x00\x00\x00\x00Apple\x00iPhone X\x00\x00\x00\x00\x00H\x00\x00\x00\x01\x00\x00\x00H\x00\x00\x00\x0111.2.6\x00\x002018:04:29 13:20:43\x00\x00!\x82\x9a\x00\x05\x00\x00\x00\x01\x00\x00\x02`\x82\x9d\x00\x05\x00\x00\x00\x01\x00\x00\x02h\x88"\x00\x03\x00\x00\x00\x01\x00\x02\x00\x00\x88\'\x00\x03\x00\x00\x00\x01\x00\x14\x00\x00\x90\x00\x00\x07\x00\x00\x00\x040221\x90\x03\x00\x02\x00\x00\x00\x14\x00\x00\x02p\x90\x04\x00\x02\x00\x00\x00\x14\x00\x00\x02\x84\x91\x01\x00\x07\x00\x00\x00\x04\x01\x02\x03\x00\x92\x01\x00\n\x00\x00\x00\x01\x00\x00\x02\x98\x92\x02\x00\x05\x00\x00\x00\x01\x00\x00\x02\xa0\x92\x03\x00\n\x00\x00\x00\x01\x00\x00\x02\xa8\x92\x04\x00\n\x00\x00\x00\x01\x00\x00\x02\xb0\x92\x07\x00\x03\x00\x00\x00\x01\x00\x05\x00\x00\x92\t\x00\x03\x00\x00\x00\x01\x00\x10\x00\x00\x92\n\x00\x05\x00\x00\x00\x01\x00\x00\x02\xb8\x92\x14\x00\x03\x00\x00\x00\x04\x00\x00\x02\xc0\x92|\x00\x07\x00\x00\x03\xb0\x0