• 【python初学者日记】用PIL批量给HEIC格式的照片,添加拍摄日期、拍摄地点的水印戳


    最近在整理手机相册,发现以前在拍摄的时候,不喜欢给照片带水印,现在想在照片上面打上拍摄时间+地点的水印戳。之前有写过将exl中的内容,添加到照片的功能,所以这次的就简单多了。

    问题合集

    1、读取 HEIC 格式照片的拍摄信息

    2、将已知坐标转码成具体省市地址的文字信息

    3、将文字添加到HEIC格式的照片上

    问题解决

    一、问题分析

    1、读取HEIC格式照片的拍摄信息:
    现有版本python3.11,用exifread库即可将苹果导出的HEIC格式照片的拍摄信息。
    2、将已知坐标转码成具体省市地址的文字信息:
    这里我使用的是百度地图调起API,参考说明如图:
    在这里插入图片描述传送门: https://lbsyun.baidu.com/index.php?title=uri/api/web
    注:此方法无需注册开发者,可以直接使用。
    3、将文字添加到HEIC格式的照片上:
    这里偷个懒,我之前有写过将exl中的内容,添加到照片的功能,此处稍作修改即可实现功能。

    二、代码实现

    1、代码如下:

    # -*- coding: utf-8 -*-
    # 读取图片属性中的拍摄日期,并给图片添加拍摄日期、地点
    import datetime
    import os
    
    import exifread
    import requests
    import json
    
    from PIL import Image, ImageDraw, ImageFont, ImageFilter
    from pillow_heif import register_heif_opener
    
    register_heif_opener()
    
    # 示例已知经纬度,利用百度地图,输出当前省市等信息
    # http://api.map.baidu.com/geocoder?location=30.204891666666665,120.19970833333333&coord_type=gcj02&output=json
    
    
    my_font = r"C:\Windows\Fonts\SIMYOU.TTF"
    
    startTime_program = datetime.datetime.now()  # 开始时间
    
    
    # 定义一个换算函数,功能:将输入的度(时)分秒,经过换算,返回度(时)。例如输入[30, 12, 1761/100],返回30.204891666666665
    def Conversion(data):
        temp_data = [ele.strip() for ele in (str(data).replace('[', '').replace(']', '').split(','))]
        new_data = eval(temp_data[-1]) / 3600 + int(temp_data[1]) / 60 + int(temp_data[0])
        return new_data
    
    
    # 在给定的照片中,提取时间戳:仅保留年月日;经纬度:通过在百度api上查找到省市,然后返回”年/月/日 省 市“的值。
    def get_stamp(pic):
        # exifread读出的经纬度是列表[度,分,秒],需要转换成单位为度的小数
        # 获取图片拍摄时间
        time = pic['Image DateTime']
        # print(time) # 打印时间戳
        time_stamp = str(time)[0:4] + "/" + str(time)[5:7] + "/" + str(time)[8:10]  # 从拍摄时间中,提取日期
        # print(time_stamp) # 打印新格式的时间戳
        latitude = Conversion(pic['GPS GPSLatitude'])  # 从图片提取纬度并将单位度分秒换算成度
        # print(latitude)  # 打印纬度
        longitude = Conversion(pic['GPS GPSLongitude'])  # 从图片提取经度并将单位度分秒换算成度
        # print(longitude) # 打印经度
        coordinates = str(latitude) + ',' + str(longitude)
        # print(coordinates) # 打印坐标
        query_adStr = "http://api.map.baidu.com/geocoder?location=" + coordinates + "&coord_type=gcj02&output=json"  # 注意此链接,根据百度开发者平台,此处改为以json格式输出,更方便后续解码
        resp = requests.get(query_adStr)  # 向服务器请求数据
        url_text = resp.content.decode()  # 读取源文件字节流,随后将该字节流编码为Unicode编码
        location_data = json.loads(url_text)  # 用于解码 JSON 数据。该函数返回 Python 字段的数据类型
        address = location_data.get('result').get(
            'addressComponent')  # 在代码中找到addressComponent字段,此条可去浏览器粘贴链接查看:http://api.map.baidu.com/geocoder?location=30.204891666666665,120.19970833333333&coord_type=gcj02
        province = address.get('province')  # 从获取的地址信息中,找到省份的值
        city = address.get('city')  # 从获取的地址信息中,找到城市的值
        stamp = str(time_stamp) + '\n' + ' ' + str(province[:-1]) + ' ' + str(city[:-1])  # 将时间戳、地址戳按格式整合
        return stamp  # 返回新戳
    
    
    # 定义在已给地址批量读图的信息,并存到数组里,并返回数组.修正:改为字典方式输出,即文件路径名:stamp
    def pics_add_stamps(path):
        stamps = {}
        for each_pic in os.listdir(path):
            if os.path.splitext(each_pic)[1] == '.heic':
                each_pic_Path = path + "\\" + each_pic
                pic_data = exifread.process_file(open(each_pic_Path, 'rb'))
                temp_stamp = get_stamp(pic_data)
                stamp_key = each_pic_Path
                stamp_data = temp_stamp
                stamps[stamp_key] = stamp_data
        return stamps
    
    
    # 读照片,并将提取到的时间位置信息,加到照片上,并存到新的文件夹
    def deal_pic_1by1(pic, text, pic_newPath):
        # 打开初始图片
        image = Image.open(pic)
        # 设置字体
        size = 100  # 定义字体大小
        font = ImageFont.truetype(my_font, size)  # 设置字体的格式、大小
        # 创建Draw对象:
        draw = ImageDraw.Draw(image)
        # 计算文字要放置的位置:image.size:像素(宽,高);image.size[0]是指照片的宽,即距离左边最大水平距离;image.size[1]是指照片的高,即距离上边最大垂直距离
        site_x = image.size[0] - 650  # 距离左边水平距离:650像素(自定义,可根据自己喜好调整)
        site_y = image.size[1] - 320  # 距离上边垂直距离:320像素(自定义,可根据自己喜好调整)
        site = (site_x, site_y)  # 距离左上角距离
    
        # 输出文字(可以连续写入):
        draw.text(site, text, font=font, fill='#fff')  # 设置放置位置、文本内容、字体、颜色
        # 模糊并保存:
        image.filter(ImageFilter.BLUR)
    
        namePath = os.path.basename(pic)
    
        if not os.path.exists(pic_newPath):  # 判断存放图片的文件夹是否存在
            os.makedirs(pic_newPath)  # 若图片文件夹不存在就创建
        # 两种方式保存:
        # image.save(pic_newPath + "/" + namePath)  # 以默认的图片格式来存储:保留原图所有信息,但是会压缩文件大小,文件保存时间和原图拍摄保存的时间一致
        image.save(pic_newPath + "/" + namePath,
                   "PNG")  # 缺省原图大部分信息,如拍照时间地点等,生成新的图片,由于save不能直接将heic转换为其他格式,所以保存的格式仍然是和原图一样,heic
    
    
    if __name__ == "__main__":
    
        pic_path = r"F:\qin_ipad_pic"  # 待处理的图片所在路径
        pic_path_new = f"F:\\qin_ipad_pic\\qin_ipad_pic-NEW"  # 处理好的图片要保存的路径
    
        n = 0
        i = 1
        # 将pic_path路径下的heic照片提取信息,返回值保存到pic_stamps,类型是字典{图片1路径:图片1时间戳+地点戳;图片2路径:图片2时间戳+地点戳,...}
        pic_stamps = pics_add_stamps(pic_path)
        # print("pic_stamps的类型是:", type(pic_stamps))
        for item in pic_stamps:
            data = pic_stamps.get(item)  # dict.get(item)
            deal_pic_1by1(item, data, pic_path_new)
            n += 1
    
        print('共有{}个HEIC文件'.format(n))
    
    endTime_program = datetime.datetime.now()  # 结束时间
    print('本次处理照片一共用了:%0.2f秒' % (endTime_program - startTime_program).seconds)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119

    2、运行结果如下:
    在这里插入图片描述

  • 相关阅读:
    小米官网(难点分析)
    记一次在amd架构打包arm64架构的镜像的试错经历
    视频编码-码率控制CQP/CRF/ABR/CBR/VBV
    Unity UGUI的CanvasScaler(画布缩放器)组件的介绍及使用
    jupyter崩溃OOM,out of memory,jupyter代码写不进去,保存不了。
    LeetCode 2359. 找到离给定两个节点最近的节点 基环树
    2023牛客OI赛前集训营-提高组(第二场)B.出租
    AI算法优缺点
    Linux时间相关C库函数
    antdv使用a-cascader联级选择器实现自定义浮层样式
  • 原文地址:https://blog.csdn.net/weixin_44834325/article/details/127861795