惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
罗磊的独立博客
博客园 - 【当耐特】
M
MIT News - Artificial intelligence
月光博客
月光博客
博客园_首页
博客园 - 叶小钗
T
Tailwind CSS Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
I
InfoQ
量子位
小众软件
小众软件
爱范儿
爱范儿
The GitHub Blog
The GitHub Blog
IT之家
IT之家
Jina AI
Jina AI
阮一峰的网络日志
阮一峰的网络日志
G
Google Developers Blog
WordPress大学
WordPress大学
人人都是产品经理
人人都是产品经理
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
J
Java Code Geeks
云风的 BLOG
云风的 BLOG
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报

Allen Hua 的网络博客

由于 Linux 桌面没有一个好用的天气程序就写了一个跨平台的CheckitoutWeather 给 caesium-image-compressor 图片压缩程序构建了 Linux AppImage v2.8.5 最新版 debian13(debian trixie)安装了nvidia闭源驱动后从x11切换到wayland的方法 山间摩旅追风,偶遇一场绚烂晚霞 最近使用debian系统的一些心得 机械革命无界14Pro笔记本debian forky成功驱动内置扬声器和麦克风 记录 typecho 1.2.0 升级到 1.3.0 过程 开发了一款openwrt插件:文本剪贴板 解决机械革命笔记本内屏高刷240Hz闪屏问题 使用 Java 写了一个局域网端口扫描器 - Allen Hua 的网络博客 openwrt使用外置根extroot机制扩展根分区大小 - Allen Hua 的网络博客 将机场ss节点批量转换成ss字符串链接批量添加到passwall - Allen Hua 的网络博客 给机械革命钛钽plus换屏:NY2换成NZ2 - Allen Hua 的网络博客 记录一次pve宿主机和上面的debian虚机无故down机事件 - Allen Hua 的网络博客 给图床部署cdn腾讯云的edgeone并排查Cache-Control max-age 3600的问题 - Allen Hua 的网络博客 从高山草甸到徽派古村:武功山反穿与皖浙赣自驾行记 - Allen Hua 的网络博客 增程器就是充电宝?别被忽悠了 - Allen Hua 的网络博客 浦口龙虎巷扫街,记录人间真实 博客图片压缩方案更新|AVIF|WebP|MozJPEG|标准JPEG Windows 电脑使用 Obs Studio 录制各个网站视频/桌面画面教程 为typecho博客添加latex支持 新能源汽车之纯电车使用交流慢充和直流快充的充电损耗对比 2025年3月更新全国5A景区名录 2025最新查看小米/红米手机电池健康度和循环次数方法 完美解决 seafile FILE_SERVER_ROOT 配置导致的内网外网不能同时访问和上传下载的问题 纯css实现typecho博客文章文字spoiler剧透效果 我对Typecho Facile主题的一些修改,图片懒加载优化,样式定制 2024年8月我的宜昌 - 重庆 - 川西小环线自驾旅行分享 2022年打卡南京市区人防工程纳凉点 2024年带着A7C2+腾龙28-200 再次来到红山森林动物园
使用 exiftool 方便快捷写入图片的 exif metadata
Allen Hua · 2024-07-15 · via Allen Hua 的网络博客
阅读量:3291

warning: 这篇文章距离上次修改已过639天,其中的内容可能已经有所变动。

我现在都在用 squoosh 批量压缩照片。但是 squoosh 压缩出来的图片丢失了 exif 数据,不论是网页还是终端 squoosh-cli。关于使用终端 squoosh-cli 批量压缩图片,你可以阅读我写的 [可能是]最好的压缩图片程序,使用 squoosh-cli 批量压缩图片

关于在得到压缩后的图片不丢失exif元数据这个问题,我尝试了其他压缩工具,比如 tinypng.com 也一样无法实现目的。

tinypng.com 的 web,压缩后的图片无法得到元数据。然后我关注到了其 API,https://tinypng.com/developers/reference/java

保留元数据.jpg

我写了段Java代码试了一下,通过调用方法 source.preserve("copyright", "creation", "location"); 可以保存元数据

System.out.println("start to tiny your images...");
        try {
            String path = "c:\\users\\dk\\downloads\\IMG_1989.jpg";
            Source source = Tinify.fromFile(path);
            String lowercasedPath = path.toLowerCase(Locale.ROOT);
            Source sourceWithMetadata;
            if (lowercasedPath.endsWith(".jpg") || lowercasedPath.endsWith(".jpeg")) {
                //location
                sourceWithMetadata = source.preserve("copyright", "creation", "location");
            }
            else {
                sourceWithMetadata = source.preserve("copyright", "creation");
            }
            sourceWithMetadata.toFile("c:\\users\\dk\\downloads\\IMG_1989-dk-compressed.jpg");
        }
        catch (IOException e) {
            throw new RuntimeException(e);
        }

但是经过实践发现只保留下来了图片拍摄时间。我想要的相机信息,镜头信息,拍摄参数(比如快门时间、ISO大小、光圈大小)等还是丢失了。

经过一番研究,我最终转变了思路。 使用 exiftool 工具写入照片的原始exif metadata,我之前居然不知道 exiftool 这么好用的工具。

官网 https://exiftool.org/

ExifTool 由 Phil Harvey 开发,是一款免费、跨平台的开源软件,用于读写和处理图像(主要)、音视频和PDF等文件的元数据(metadata)。ExifTool可以作为Perl库(Image::ExifTool)使用,也有功能齐全的命令行版本。ExifTool支持很多类型的元数据,包括Exif、IPTC、XMP、JFIF、GeoTIFF、ICC配置文件、Photoshop IRB、FlashPix、AFCP和ID3,以及众多品牌的数码相机的私有格式的元数据。

本段介绍source

我在 squoosh 容器当中继续操作。

squoosh 容器基于 alpine 镜像制作,所以使用 apk 安装 exiftool 即可。

# apk add exiftool

然后编写一段 shell 脚本。

2024-08-02 19:18:18 新增一些说明

  • 我做的 dko0/squoosh:v1.12.0 镜像是支持amd64, arm64 and arm v7 arch这三个架构的,但是只有在 arm64 机器上运行的容器安装了vim之后才能正常运行。在amd64机器上pull了此镜像后启动一个容器然后通过apk安装vim会无法运行,都会报错Segmentation fault (core dumped)。关于这个问题我检查了很多东西,发现可能是 alpine linux 和不同架构的相关依赖实现的问题
  • 由于 Alpine Linux 使用的是轻量级的 musl libc,而不是更常见的 glibc,这可能会导致某些软件在运行时出现兼容性问题
  • alpine linux 默认使用 /bin/ash 作为 default shell,而且内置的 vi 等命令都是来自 busybox,简体中文不太好支持。所以下面的 shell 我最终改用 /bin/bash 编写。我放在宿主机 debian 执行了
  • debian 需要通过 apt-get update && apt-get install -y exiftool 安装 exiftool 工具
#!/bin/bash

# 检查是否提供了必要的参数
if [ -z "$1" ] || [ -z "$2" ]; then
    echo "Usage: $0 <input_dir> <output_dir>"
    exit 1
fi

# 从命令行参数中读取输入目录和输出目录
input_dir="$1"
output_dir="$2"

# 检查输入目录是否存在
if [ ! -d "$input_dir" ]; then
    echo "Input directory '$input_dir' does not exist."
    exit 1
fi

# 检查输出目录是否存在
if [ ! -d "$output_dir" ]; then
    echo "Output directory '$output_dir' does not exist."
    exit 1
fi

# 如果 output_dir 以 / 结尾,删除末尾的 /
if [ "${output_dir: -1}" = "/" ]; then
    output_dir="${output_dir%/}"
fi

# 定义要处理的文件扩展名
exts=("jpg" "JPG" "jpeg" "JPEG")

# 遍历所有指定文件扩展名的图片文件
for ext in "${exts[@]}"; do
    for input_file in "$input_dir"/*."$ext"; do
        # 检查文件是否存在
        if [ ! -f "$input_file" ]; then
            continue
        fi

        # 提取文件名,不带路径
        filename=$(basename "$input_file")

        # 构建目标文件路径
        output_file="${output_dir}/${filename%.*}-dk-compressed.jpg"

        # 检查压缩后的文件是否存在,以防万一
        if [ ! -f "$output_file" ]; then
            echo "File $output_file does not exist, skipping..."
            continue
        fi

        # 复制 EXIF 元数据
        exiftool -TagsFromFile "$input_file" -all:all "$output_file"
        echo "$output_file exif metadata write success."

        # 删除生成的备份文件(-TagsFromFile 操作会创建一个备份文件)
        rm "${output_file}_original"
    done
done

将此 shell 文件保存为 /home/dk/copy_exif.sh

使用时的命令:~/copy_exif.sh . ./output/

解释说明:

  1. 该脚本从命令行参数中读取输入目录和输出目录,可以是相对路径。会将输入路径下的所有 xxx.jpg 的元数据读取出来,写入当前文件夹下的 output 目录中的 xxx-dk-compressed.jpg 文件中,这样刚好能确保 exif metadata 有正确的归属关系
  2. 记住添加可执行权限 chmod +x /home/dk/copy_exif.sh
  3. 我使用 dk 这个用户执行,因为 squoosh-cli 也是在 dk 这个用户下执行的。不在 root 下操作是不希望破坏文件权限和所有者,宿主机内的文件系统还通过 samba 挂载了,其他设备访问时如果是 root 用户操作的会无法删除文件(没有写权限)

经过 exiftool 写入exif元数据之后,文件会变大一丢丢。通过下面一个case 粗略估计文件体积会增大5%。

写入exif metadata元数据前后文件大小对比.jpg

可以看到压缩后的照片也具有了元数据了,这对我将文件存放在 immich 里时可以有更好的读写性能,并且在浏览照片时也能看到照片 exif 信息,这对我很重要。

保留了元数据.jpg

当然具备完整细节的原图还在SD卡/电脑里备份着。