V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
cshlxm
V2EX  ›  问与答

电脑里的照片如何归类整理?

  •  
  •   cshlxm · 2019-09-11 09:05:51 +08:00 · 9102 次点击
    这是一个创建于 1683 天前的主题,其中的信息可能已经有所发展或是发生改变。
    相机和 iphone 拍了很多照片,之前备份都是 windows 照片应用导入是按照月份分的,也有很多没有按照月份堆在一个文件夹下,现在几万图和视频(有 jpg、HEIC、HEVC、MOV、mp4、cr2 等等),分布在各个盘,一直想整理下,写了个 python 脚本用 md5 摘要把重复的图片、视频先删了,下一步想还没想好该怎么办?
    各位大佬,有什么好的建议,比如好用的软件,或者怎么归类比较适合?
    34 条回复    2020-02-29 19:50:54 +08:00
    Ettup
        1
    Ettup  
       2019-09-11 09:14:46 +08:00 via iPhone
    Adobe Bridge、Lightroom、Capture One
    pilgrim_kevin
        2
    pilgrim_kevin  
       2019-09-11 09:17:22 +08:00
    嗯,大量的照片管理一向是个问题。我给你讲一下我的解决方案:

    1. 用 python 写一个程序,将从任何设备里导出的照片自动化整理,按照统一的规则,按日期创建文件夹,按拍摄时间(精确到秒)自动命名文件名,并拷贝汇总到 NAS 里;一般来说在同一秒拍摄的照片是不同的照片的概率是极低的,我这里默认如果是同一秒就末尾添加 01、02、03 来区分了,然后在 Photo Station 里浏览的时候,如果看到是同一张照片就随手删掉重复的。如果省事,可以直接默认为同一秒的照片就是同一张。这样的话我的不管任何设备(相机,手机还是啥)拍摄的照片最终都汇总到一处;即使各设备里有重复的照片也没关系。
    2. 我用群晖 NAS 管理我的计算机文件和数据。利用 DSM 系统里的 Photo Station 查看和管理照片,还是比较方便的。也利用 DSM 的自动备份和同步等功能去备份照片,也安全很多。将 DSM 映射到外网也可以远程用手机等访问,不过这个必要性不大。
    pilgrim_kevin
        3
    pilgrim_kevin  
       2019-09-11 09:20:05 +08:00
    群晖 NAS,值得花钱买一个。相比无价的照片等数据而言,买个群晖的钱值得花。
    pilgrim_kevin
        4
    pilgrim_kevin  
       2019-09-11 09:21:10 +08:00
    利用脚本自动删除重复文件的做法很好,回头我也增加一下这个功能,照片太多了,经常重复性地汇集,难免有重复的
    JRay
        5
    JRay  
       2019-09-11 09:26:39 +08:00
    Lightroom
    Tezos
        6
    Tezos  
       2019-09-11 09:31:28 +08:00
    google photos?
    chztv
        7
    chztv  
       2019-09-11 09:38:57 +08:00
    Google Photos
    越来越觉得这个比 自带的 Photos 要好用太多,价格也便宜,包括分享给其他人都非常好用。
    当然唯一的缺点必须要有科学的渠道。
    cshlxm
        8
    cshlxm  
    OP
       2019-09-11 09:42:55 +08:00
    @pilgrim_kevin python 读取“媒体创建时间”这个属性,来获取拍摄时间么? 不知道对 HEIC HEVC 支持怎样,我没搞 NAS,重要的照片、视频是 win 主机一份,移动硬盘一份,mac mini 主机一份,这样好处是挺安全,坏处也显而易见,整理起来费劲。 你说这个方案不错,我去瞅瞅看
    cshlxm
        9
    cshlxm  
    OP
       2019-09-11 09:44:28 +08:00
    @chztv 都是家人的照片、视频,不想上云,对远程查看这种需求基本没有。
    cshlxm
        10
    cshlxm  
    OP
       2019-09-11 09:46:15 +08:00
    @chztv 分享的话,进本就是家庭内,iphone 的共享即可。朋友的话怕麻烦也是原图发送微信群了,自己挑着保存。
    cshlxm
        11
    cshlxm  
    OP
       2019-09-11 09:47:39 +08:00
    @JRay
    @Ettup LR 没怎么用过,以前印象中都是用来简单修图的,不知道整理功能如何,最早用过 google Picasa,其实我一直觉得这个挺好用
    jaylong
        12
    jaylong  
       2019-09-11 09:50:31 +08:00
    @pilgrim_kevin 能否分享一下 python 脚本?
    loveour
        13
    loveour  
       2019-09-11 09:51:49 +08:00
    @cshlxm #11 我也一直觉得 Picasa 很好用,可惜被谷歌砍掉了,而且,Picasa 不能支持数量特别多的文件,会很卡。也许放在固态上会不卡?
    我现在照片是备份在 GoogleDrive,上传到 GooglePhotos,同时在电脑上,在 NAS 上各有一份。其实群晖的 Moments 也挺好的,和 GooglePhotos 类似,也会给照片打标签,识别人脸什么的。
    pilgrim_kevin
        14
    pilgrim_kevin  
       2019-09-11 09:55:19 +08:00
    照片文件都有 EXIF 信息,里面各种信息都有,包括 GPS 定位(如果打开了的话),用 python 读取就好了。
    视频我还没仔细研究过。目前主要是针对照片文件,视频的数量不是特别大,目前是养成了不定时手动整理的习惯。

    手动备份很繁琐,而且移动硬盘之类的介质并不可靠(以年为时间单位衡量的话),备份的话一定要多重备份,但是手动备份以后整理和检查都非常繁琐。我的备份和存储方案比较靠谱,NAS 是磁盘阵列,可以有效降低磁盘损坏风险,有磁盘损坏就及时更换。备份是双机热备份,就是说我有两台 NAS,定时自动同步,以及带版本管理的自动定时备份。用于备份的 NAS 性能要求可以低一些。这样双机备份的话数据彻底损毁的概率很小,还有部分非敏感数据我还会用云盘同步一份以防万一。我使用计算机二十多年,过去从磁盘、光盘、硬盘等各种数据备份方式经历太多,几乎每种方式都遇到过数据彻底损毁找不回来的,只有目前的方式最靠谱。
    greatdancing
        15
    greatdancing  
       2019-09-11 09:57:15 +08:00 via Android   ❤️ 1
    droplt 文件自动分类
    http://www.dropitproject.com/
    JackieMe
        16
    JackieMe  
       2019-09-11 09:57:21 +08:00 via Android
    同喜欢 Picasa,很好用的,可惜最后被砍
    pilgrim_kevin
        17
    pilgrim_kevin  
       2019-09-11 10:00:23 +08:00
    @jaylong 可以,不过最近出差,需要回去着给你。这个其实很简单的,无非就是遍历文件,挨个读取 exif 信息,然后给它创建文件夹,重命名,mv 到新文件夹里。估计你自己写也是很简单。
    pilgrim_kevin
        18
    pilgrim_kevin  
       2019-09-11 10:01:18 +08:00
    待整理的文件放到 source 目录,mv 到 target 目录里。这样 source 目录空了,照片也就整理完了。
    cshlxm
        19
    cshlxm  
    OP
       2019-09-11 10:05:11 +08:00
    @pilgrim_kevin exif 我试过,主要是对 iphone 新的 HEIC HEVC 支持不好,所以一直没弄,看来有必要再研究下。自己习惯还好,主要是家人没这种意识,每次忙起来就忘了,想起来时就是一股脑先拷贝进去,久而久之就更不想整了。
    joiejia
        20
    joiejia  
       2019-09-11 10:07:46 +08:00
    Eagle
    jaylong
        21
    jaylong  
       2019-09-11 10:12:04 +08:00
    @pilgrim_kevin 不会编程 等你的代码了 到时候就在此贴回复吧 记得 @我一下 谢谢啦
    pilgrim_kevin
        22
    pilgrim_kevin  
       2019-09-11 10:12:13 +08:00
    @cshlxm 哦,我没注意过 iphone 的问题,可能我的 iPhone 老一点,没注意有这个问题。
    pilgrim_kevin
        23
    pilgrim_kevin  
       2019-09-11 10:13:40 +08:00
    @jaylong 好。
    chztv
        24
    chztv  
       2019-09-11 10:44:38 +08:00
    @cshlxm 有时间的话,我觉得本地照片只能自己手动整理,印象里还没有哪个软件能完全代替人手。Adobe Bridge、Lightroom 之类的,还是需要手工整理文件夹,但如果是摄影发烧友,这二款肯定是必需的。
    没有时间的话又不想上云的,我也想知道有啥办法,毕竟我自己也是从人手本地整理过渡到了云相册,毕竟当你的照片有 6 位数以上时,整理太费时间了,先在 Google Photos 上面把部分有用的照片整理成相册,然后打包下载相册存到 NAS 上。其实主要是看中了 Google Photos 的人工智能人脸识别,能把所有和小朋友的有关的照片全部标注成一个相册。
    hensy
        25
    hensy  
       2019-09-11 11:15:16 +08:00
    每次看到这样的帖子都忍不住要安利一下这个 APP:时光相册。连接 WIFI 自动备份,有人脸识别,支持分人物、地点、事物等自动归类,好用得一批。。
    cskeleton
        26
    cskeleton  
       2019-09-11 11:57:39 +08:00
    个人还是习惯手动整理,按年建文件夹,年以下就按事件建文件夹,比如我出去玩了一趟,就会按这种格式建一个文件夹 20190511_Chongqing,里面再分俩,raw 和 output,raw 下面可能会再按设备分,例如不同的相机和手机,output 可能会放这次事件别人拍的照片。不过我用手机拍得少,基本上一年一个手机相册的子文件夹就够了,如果手机拍的也属于某个事件,那也归类进去。

    这样找起来也比较容易,我会先回忆我是在什么时候、哪次旅游、哪个活动、谁拍过某张照片。备份就是 GoodSync 定时同步到另一块硬盘,也不需要 RAID。后面计划把新的照片从身边这台星际蜗牛也定时同步回老家的 Gen8 上去做个异地备份。
    JRay
        27
    JRay  
       2019-09-11 14:33:26 +08:00
    @cshlxm LR 的照片管理功能很强大
    akimotofeng996
        28
    akimotofeng996  
       2019-09-11 17:24:32 +08:00 via Android
    上传到某云,利用云服务只能分类,还能从相似照片中取优选,节省空间
    manxiaqu
        29
    manxiaqu  
       2019-09-12 14:16:23 +08:00   ❤️ 1
    ptsa
        30
    ptsa  
       2019-09-13 21:38:27 +08:00
    @manxiaqu 还不算完善 关注下
    Huangenius
        31
    Huangenius  
       2019-09-16 03:46:32 +08:00
    推荐一个个人觉得很好用的软件 Zoner Photo Studio X,比 Lr 对位置信息兼容更好,比 Picasa 整理大量图片有优势,本人 15 万左右张的照片可以按位置、时间、标签 等等分类(暂无人脸识别),支持各种格式包括 HEIC,没有 Google photos 的搜索引擎是个很大缺陷,管理本地文件还是很方便的。
    pilgrim_kevin
        32
    pilgrim_kevin  
       2019-10-18 09:21:40 +08:00
    @jaylong

    src/handler_photo_by_date.py

    #!/usr/bin/python
    # -*- coding:utf-8 -*-
    __author__ = 'Barry'

    from PIL import Image
    from PIL.ExifTags import TAGS
    from datetime import datetime
    from hashlib import md5
    import time
    import os
    import shutil
    import fnmatch
    import random

    global Count

    def IterFindFiles(path, fnexp):
    for root, dirs, files in os.walk(path):
    for filename in fnmatch.filter(files, fnexp):
    yield os.path.join(root, filename)

    def GetPicPath():
    pic_full_path = []
    if os.path.isdir(input_path):
    for i in os.listdir(input_path):
    sub_dir = input_path + '/' + i
    if os.path.isdir(sub_dir):
    for n in os.listdir(sub_dir):
    pic_full_path.append(sub_dir + '/' + n)
    return pic_full_path


    def print_all_know_exif_tags():
    for k in sorted(TAGS):
    print k, TAGS[k]

    def FormatTime(date):
    try:
    ts = time.mktime(time.strptime(date,'%Y:%m:%d %H:%M:%S'))
    return time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(ts))
    except Exception, e:
    return False

    def TrimTime(mtime):
    ts = time.mktime(time.strptime(mtime,'%Y-%m-%d %H:%M:%S'))
    return time.strftime('%Y%m%d',time.localtime(ts))

    def TrimTime2(mtime):
    ts = time.mktime(time.strptime(mtime,'%Y-%m-%d %H:%M:%S'))
    return time.strftime('%Y%m%d-%H%M%S',time.localtime(ts))

    def GenMd5(filename):
    file_tmp = open(filename,'rb')
    md5_value = md5(file_tmp.read())
    file_tmp.close()
    return md5_value.hexdigest()

    def GetPicExif():
    pic_date = {}
    #pic_path = GetPicPath()
    for filename in IterFindFiles(input_path,fnexp):
    mtime = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(os.stat(filename).st_mtime))
    try:
    img = Image.open(filename)
    except Exception, e:
    print filename,"skipping due to ",e
    try:
    exif_data = img._getexif()
    except Exception, e:
    print filename,"skipping due to ",e
    finally:
    if exif_data:
    if exif_data.has_key(36867):
    pic_date[filename] = FormatTime(exif_data[36867]) and FormatTime(exif_data[36867]) or mtime
    elif exif_data.has_key(36868):
    pic_date[filename] = FormatTime(exif_data[36868]) and FormatTime(exif_data[36868]) or mtime
    elif exif_data.has_key(306):
    pic_date[filename] = FormatTime(exif_data[306]) and FormatTime(exif_data[306]) or mtime
    else:
    pic_date[filename] = mtime
    else:
    pic_date[filename] = mtime

    print filename, pic_date[filename]

    return pic_date

    def ArchivePic():
    pic_date = GetPicExif()
    Count = 0
    new_dir_file_md5_list = []
    for pic,mtime in pic_date.items():
    std_out = "\033[1;33mAdd new picture \033[1;31m%s\033[0m\033[1;33m to dest path:%s\033[0m"%(os.path.split(pic)[1],output_path)
    new_dir = "%s/%s"%(output_path,TrimTime(mtime))
    if not os.path.exists(new_dir):
    os.mkdir(new_dir)
    new_file_name = '%s-%s%s'%(pic_prefix,TrimTime2(mtime),pic_suffix)
    new_file_path = '%s/%s'%(new_dir,new_file_name)

    print "processing", pic, "to", new_file_name

    if not os.path.exists(new_file_path):
    try:
    if os.listdir(new_dir):
    for i in os.listdir(new_dir):
    hashvalue = GenMd5(os.path.join(new_dir,i))
    new_dir_file_md5_list.append(hashvalue)
    if GenMd5(pic) not in new_dir_file_md5_list:
    shutil.move(pic,new_file_path)
    #shutil.copy2(pic,new_file_path)
    #os.remove(pic)
    Count+=1
    print Count, std_out
    else:
    shutil.move(pic,new_file_path)
    #shutil.copy2(pic,new_file_path)
    #os.remove(pic)
    Count+=1
    print Count, std_out
    except Exception, e:
    print e
    else:
    while True:
    new_file_name = '%s-%s_%s'%(pic_prefix,TrimTime2(mtime),pic_suffix)
    if new_file_name not in os.listdir(new_dir):
    break
    else:
    continue
    new_file_path = '%s/%s'%(new_dir,new_file_name)
    for i in os.listdir(new_dir):
    hashvalue = GenMd5(os.path.join(new_dir,i))
    new_dir_file_md5_list.append(hashvalue)
    if GenMd5(pic) not in new_dir_file_md5_list:
    try:
    shutil.move(pic,new_file_path)
    #shutil.copy2(pic,new_file_path)
    #os.remove(pic)
    Count+=1
    print Count, std_out
    except Exception, e:
    print e
    else:
    os.remove(pic)

    return


    if __name__ == "__main__":
    input_path = r"/home/xxx/Camera"
    output_path = r"/home/xxx/New"
    fnexp = "*.jpg"
    pic_suffix = ".jpg" #如果查找的是 jpg 文件,那文件后缀名也要匹配
    pic_prefix = "IMG"
    birth_day = '20191018'
    if not os.path.exists(output_path):
    os.mkdir(output_path)
    if not os.path.exists(input_path):
    print "some of path not found!"
    else:
    ArchivePic()
    #print_all_know_exif_tags()
    pilgrim_kevin
        33
    pilgrim_kevin  
       2019-10-18 09:24:13 +08:00
    不好意思不知道如何保持格式贴代码, 楼上有耐心的话自己整理下缩进,python 是缩进敏感的
    higkoo
        34
    higkoo  
       2020-02-29 19:50:54 +08:00
    刚刚用 Shell 整理了近 5 万张 10 多年的照片,供参考:
    https://my.oschina.net/higkoo/blog/3179534
    (视频文件和 HEIC 格式的图片 待处理)
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2587 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 01:42 · PVG 09:42 · LAX 18:42 · JFK 21:42
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.