吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 11244|回复: 72
收起左侧

[原创工具] Python练习小工具——根据Exif的拍摄时间和设备名批量重命名照片

    [复制链接]
似水流年2015 发表于 2020-8-24 23:50
本帖最后由 似水流年2015 于 2020-8-25 00:00 编辑

image.png

觉得还行的朋友麻烦评一下分,每天每位朋友都有1次评分机会,不用白不用,用了也不亏,谢谢。

初衷:
朋友有大量的照片,因为某些原因文件名和实际拍摄时间不一致,而实际拍摄时间存在exif中,所以我想了一下,可以通过读取照片中exif中的拍摄时间来重命名照片。这工具是三个月前写的吧,在朋友后续的试用建议下,加上了三种重命名方式:
第一种:优先判断exif信息是否存在,存在则用exif时间重命名
第二种:exif信息不存在,则进行判断文件名是否包含有时间信息,例如564-201206091615.jpg ,此情况就通过正则提取,重命名为2012-06-09-16-15.jpg
第三种:如果前两种都不满足,则通过读取照片文件在电脑中的创建时间来重命名。

很遗憾,上面说的是原版本,没有窗口,纯命令行,有一些小问题,懒得修复了,也就不放出来了,免得因bug,弄坏大家电脑(代码里有一行是当文件名存在有 . 符号,就替换为- 号),因为没加判断,自己的电脑c盘很多带.号的文件路径全换成-了,导致软件失效。  猜测是在调试时选择照片路径,取消了选择,系统默认返回C盘根目录,导致遍历,把C盘的文件夹的 . 号全换成 -号了。此版本就不放出来了,谅解一下,也不修复了。

所以今天抽空重新弄了一个有窗口的,但只有
第一种重命名方式,即exif方式,懒得加其它2种重命名方式了,调调试试再修修用户使用时可能因误操作会出现的bug,搞得写了快三小时,怪自己学艺不精咯。

功能如下:
第一:选择照片所在的目录,子目录会自动遍历出照片的绝对路径并显示到列表框。
第二:点击开始重命名,然后耐心等待即可,为了省时间,重命名成功的提示没有写出到黑窗口,只输出因拍摄时间同样(即那种1秒拍了十几张照片的,exif中的时间信息一模一样)而导致重命名失败的提示,此时,会自动加上后缀数字1到50,循环50次重命名,当某次重命名成功,会跳出循环,继续下一个照片重命名(之所以循环50次,就是防止有些用户用设备1秒拍出了50张照片)。
第三:实际使用发现,有些照片的格式不支持读取exif信息,忘记哪些了,jpg和dng支持,其它自测。


已打包为exe文件,因为要输出错误信息,打开会带有黑窗口:
https://www.lanzoux.com/iAmGbg0s97a


源码如下,尽量写得很详细了:
[Python] 纯文本查看 复制代码
import exifread
from tkinter.filedialog import *   #别问我怎么不导入os模块就能 重命名,我也不知道,可能这个tkinter模块包含有os了吧,哈哈

Files_Names={                      #全局变量,存储照片绝对路径  和  目录路径(即不包含照片名的路径)
    "all_path":[],
    "ml_name":""
}

def mainapp():     #创建窗体
    app=Tk()
    app.title("批量根据exif信息自动重命名照片")
    Label(app, text="批量根据exif信息自动重命名照片", font=("微软雅黑", 25, "bold")).pack()  # 画标签并设置显示的文字,字体,加粗
    Listbox(app, name='l_b', bg="#f2f2f2", fg="red").pack(fill=BOTH,expand=True)  # 画列表框并指定名字,方便后续根据名字调用获取Listbox实例,指定背景,前景,显示方式
    Button(app, text="选择照片路径", command=ui_update).pack(fill=BOTH)  # command指定了当按下按钮,会调用哪个方法
    Button(app, text="开始Exif命命名", command=rename).pack(fill=BOTH)  # left

    app_width = 500  # 设置窗口宽度
    app_height = 500  # 设置窗口高度
    win_width = int((
                                app.winfo_screenwidth() - app_width) / 2)  # 设置窗口距离左上角原点的 宽度  屏幕总宽度-软件窗口宽度  再除2,即软件距离左侧窗口的宽度  转为整数,因为下面geometry只接受字符串型整数
    win_height = int((
                                 app.winfo_screenheight() - app_height) / 2)  # 设置窗口距离左上角原点的 高度  屏幕总宽度-软件窗口高度  再除2,即软件距离顶边的高度  转为整数,因为下面geometry只接受字符串型整数
    app.geometry("%sx%s+%s+%s" % (app_width, app_height, win_width, win_height))  # 设置窗口显示的宽高度 及 距离原点的长宽度
    return app

def ui_update():   #此方法用于把照片所有的路径读到 列表框,当点击按钮《选择照片路径》,就会调用此方法
    list_box = a.children["l_b"]  # 读取出列表框对象
    list_box.delete(0, END)  # 删除全部列表内容,防止你点了几次《选择照片路径》,重复添加了几次照片。
    Files_Names["ml_name"]=askdirectory()  #选择照片所在的目录并存到变量里,后面重命名完成后,调用这个变量,显示文件夹出来
    Files_Names["all_path"]=[]      #把存了照片的绝对路径的变量清空,防止你批量重命名完一个文件夹后(此时没关闭软件),继续使用去选择下一个文件夹来重命名,如果不清空,将会出错,因为残留有上一次批量重命名的路径名。
    if Files_Names["ml_name"]!="":  #此判断为了防止你点了《选择照片路径》按钮,又不选择文件夹,直接点了取消,此时返回空,就不执行遍历照片功能。
        Files_Names["ml_name"] += "/"
        list_name(Files_Names["ml_name"])  # 把选择的目录传入到list_name方法中,进行递归遍历照片文件路径

def list_name(ml_name):
    file_names = os.listdir(ml_name)  # 根据选择的目录列出里面的照片及目录 返回列表
    if file_names != "":               # 只要返回的照片和目录路径不为空,防止你犯二,选择了一个没有照片的空目录
        list_box = a.children["l_b"]   # 读取出列表框对象,下面在循环插入路径到列表框要用对象调用
        for i in range(0, len(file_names)):  # 列表转换为循环数。
            path = os.path.join(ml_name, file_names[i])  # 把目录名ml_name拼接上文件名file_names[i],获得完整路径。
            if os.path.isfile(path):   # 如果为真,则是文件
                list_box.insert(END, path)  # 循环插入文件名到列表框
                Files_Names["all_path"].append(path)   # 完整的照片路径追加到 全局变量列表中
            else:  # 如果不是文件,那应该是目录,就加上/,再调用自身方法,进行递归列出下一层的文件,很多人在递归这里搞不明白。。。
                try:
                    list_name(path + "/")
                except:
                    print("访问目录出错,可能不是一个目录,或者是奇奇怪怪的文件名")


def rename():
    for index,j_file_name in enumerate(Files_Names["all_path"]):  #准备循环重命名
        with open(j_file_name, 'rb') as f:  # 打开照片文件并读取到f中
            tags =exifread.process_file(f)  # 从f中通过exifread.process_file获取exif信息(返回一个字典类型)
            image_exif_time=""   #清空exif拍摄时间信息,为下一个照片重命名做准备,不残留上一个照片的拍照时间
            image_Make_mode=""   #清空exif拍摄时间信息,为下一个照片重命名做准备,不残留上一个照片的手机型号
            if "EXIF DateTimeOriginal" in tags.keys():   #如果这个存着拍摄时间的键 存在 上面取出的exif字典里
                image_exif_time=tags["EXIF DateTimeOriginal"]  #把时间取出来,赋值给变量,准备拿它来当新文件名,下面两个同样道理,都是存着时间的,当判断这个键不存在时,就判断下面两个键有没有时间存着。

            elif "EXIF DateTimeDigitized" in tags.keys():    #实践发现,这也是一个拍摄时间
                image_exif_time = tags["EXIF DateTimeDigitized"]
            elif "Image DateTime" in tags.keys():             #实践发现,这也是一个拍摄时间
                image_exif_time = tags["Image DateTime"]

            if ":" in str(image_exif_time):  #取出来的拍摄时间是 2020:01:01 18:18 这种形式,难看
                image_exif_time = str(image_exif_time).replace(":", "-")  #换成2020-01-01 18-18 形式

            if "Image Make" in tags.keys() and "Image Model" in tags.keys() :  #如果存在品牌和 型号,就取出来
                image_Make_mode="-"+str(tags["Image Make"])+str(tags["Image Model"])

            if image_exif_time!="":  #如果有拍摄时间,就进行 拆分目录 和 文件名
                img_path="/".join(j_file_name.split("/")[0:-1])  #取出照片目录    D:/ZM/2019.05.11社团周年庆
                img_name=j_file_name.split("/")[-1]              #取出文件名。    656566.jpg
                hz=img_name.split(".")[-1]                         #取出后缀。     .jpg

            else:  #如果没有拍摄时间,直接跳到循环尾,进行下一个照片exif时间的提取,不进行重命名了。
                continue

        try:  # try一下,防止出现未知错误    这里要注意,不能写在with里,因为会占用照片文件,导致重命名失败。
            new_file_name = ("%s/%s%s.%s") % (img_path, image_exif_time, image_Make_mode, hz)
            os.renames(j_file_name, new_file_name)
        except:  # 当出现未知错误,或者是照片里的exif拍摄时间重复,比如1秒拍了10张,这类照片因为时间一模一样,重命名不了,只能在时间一样的照片后面按顺序加上 数字 来重命名。
            for o in range(50):  # 防止你相机设备太牛逼,1秒拍了50张,导致时间一模一样的照片,所以设置循环50次,在文件名后面依次加上数字来重命名。
                try:
                    new_file_name = ("%s/%s%s-%s.%s") % (img_path, image_exif_time, image_Make_mode, str(o+ 1), hz)
                    print("第%d张图片重命名中,疑似名字重复或照片文件损坏,重命名失败,将添加后缀%s继续重命名!50次内若重命名还是失败,将忽略些图片!" % (index, str(o + 1)))
                    print("原文件名:%s" % j_file_name)
                    print("预计新文件名:%s\n\n" % new_file_name)
                    os.renames(j_file_name, new_file_name)
                    break  # 当重命名成功后,立即跳出循环,不用笨笨的循环到50次。
                except:
                    pass
    os.startfile(Files_Names["ml_name"])   #所有文件重命名完成后,弹出照片所有的窗口
    print("重命名完成!")
a=mainapp()
a.mainloop()







免费评分

参与人数 58吾爱币 +53 热心值 +51 收起 理由
jjjhgz + 1 + 1 很实用的小工具,谢谢
lcg2014 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
bd7mcj + 1 + 1 谢谢@Thanks!
图样图森破 + 1 + 1 谢谢@Thanks!
jnez112358 + 1 + 1 谢谢@Thanks!
jerry9801 + 1 我很赞同!
走音 + 1 我很赞同!
zz1119463380 + 1 + 1 我很赞同!
是否还能放弃 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
noask + 1 + 1 谢谢@Thanks!
wuhua2001 + 1 + 1 热心回复!
ytmfndcw + 1 谢谢@Thanks!
jxcllj + 1 + 1 我很赞同!
52pjie25 + 1 + 1 谢谢@Thanks!
Adger027 + 1 + 1 热心回复!谢谢
七个八个九个 + 1 + 1 我很赞同!
jackl + 1 我很赞同!
bandishui + 1 谢谢@Thanks!
libai_svp + 1 + 1 我很赞同!
zhoujing169 + 1 + 1 谢谢@Thanks!
御用马甲 + 1 + 1 用心讨论,共获提升!
jollna + 1 谢谢@Thanks!
songso + 1 + 1 我很赞同!
hqboy + 1 + 1 谢谢@Thanks!
咖啡一杯闷 + 1 楼主以前用的WP手机?
萌新一枚 + 1 谢谢@Thanks!
lcjinhying + 1 + 1 谢谢@Thanks!
keeynelly + 1 + 1 谢谢@Thanks!
xiaoxiaot2 + 1 我很赞同!
lbllevchin + 1 + 1 用心讨论,共获提升!
daixingren007 + 1 + 1 谢谢@Thanks!
nanzhiman + 1 + 1 热心回复!
suifengtec + 1 + 1 我很赞同!
同行皆狼狈 + 1 + 1 我很赞同!
你吾識我 + 1 + 1 大佬,是否能根据文件名(日期格式)写入拍照时间啊。
huaxia8848 + 1 谢谢@Thanks!
JKBR + 1 热心回复!
风之暇想 + 7 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
ftfwjft + 1 + 1 我很赞同!
peterwoo + 1 + 1 谢谢@Thanks!
DrDing + 1 + 1 用心讨论,共获提升!
zj7344426 + 1 + 1 真的好用!32个赞!!!
netspirit + 1 + 1 以后再学习
guyuchao3 + 1 + 1 谢谢@Thanks!
刀大喵 + 1 注释很详细 爱了 爱了
江南小公子 + 1 + 1 喜欢 让我这个小白也看懂了
mdf3192078 + 1 + 1 我很赞同!
h357746 + 1 我很赞同!
493564970 + 1 + 1 很不错,确实有时候会用到
fxd680126 + 1 我很赞同!
andyyang886 + 1 用心讨论,共获提升!
zsz1204 + 1 果然是高手,我学爬虫永远在第一课
leonca + 1 + 1 我很赞同!
mybabyon + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
zecore + 1 + 1 谢谢@Thanks!
lapumengde + 1 + 1 用心讨论,共获提升!
莫利花 + 1 + 1 热心回复!
macolma + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

netspirit 发表于 2020-8-25 00:04
我今天已经评了好多次分了 已经没了 你的文章我收藏了 我下次看 最近我也在学python..........

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
似水流年2015 + 1 + 1 我很赞同!

查看全部评分

 楼主| 似水流年2015 发表于 2020-8-28 14:08
你吾識我 发表于 2020-8-27 16:55
大佬,是否能根据文件名(日期格式)写入拍照时间。  
有很多照片(很老的)文件名 2019-12-01 12.12.12  ...

找到教程,有写入exif信息到照片文件里的 py模块。有兴趣的可以按这个来。https://blog.csdn.net/iteye_17686/article/details/82410075
第七只海东青 发表于 2020-8-25 00:03
Mr-Golden-Week 发表于 2020-8-25 00:06
支持一下
 楼主| 似水流年2015 发表于 2020-8-25 00:09
netspirit 发表于 2020-8-25 00:04
我今天已经评了好多次分了 已经没了 你的文章我收藏了 我下次看 最近我也在学python..........

突然想起凌晨 了,我回到3分了
netspirit 发表于 2020-8-25 00:10
似水流年2015 发表于 2020-8-25 00:09
突然想起凌晨 了,我回到3分了

看了一下没看懂 你导入的两个模块我都没见过 下面几个函数我都没见过

你这是gui编程么 我还没学到那里.........



还有200积分可以评分3次嘛?
 楼主| 似水流年2015 发表于 2020-8-25 00:22
netspirit 发表于 2020-8-25 00:10
看了一下没看懂 你导入的两个模块我都没见过 下面几个函数我都没见过

你这是gui编程么 我 ...

一个是exif模块,读取照片exif信息的,一个是窗体的模块,是的,算gui了。我是在免费评分那里看的,显示我还有3分可以评。
zecore 发表于 2020-8-25 00:28
有意思的程序
yangcg8 发表于 2020-8-25 05:39
详细的图解,谢谢分享
demigodswl 发表于 2020-8-25 07:58
感谢分享,支持一下
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 22:02

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表