我正在尝试将EXIF标记从一个JPEG复制到另一个没有元数据的JPEG。我试着做this comment中描述的事情。
我的想法是从标签源文件复制所有内容,直到第一个ffdb
被排除,然后从图像源文件(没有标签)复制所有内容,从第一个ffdb
开始。生成的文件已损坏(缺少SOS标记)。
一个完整的复制器,包括Luatic的建议,可以在https://go.dev/play/p/9BLjuZk5qlr上找到。只需在包含带有标记的test.jpg文件的目录中运行它。
这是Go代码草案。
func copyExif(from, to string) error {
os.Rename(to, to+"~")
//defer os.Remove(to + "~")
tagsSrc, err := os.Open(from)
if err != nil {
return err
}
defer tagsSrc.Close()
imageSrc, err := os.Open(to + "~")
if err != nil {
return err
}
defer imageSrc.Close()
dest, err := os.Create(to)
if err != nil {
return err
}
defer dest.Close()
// copy from tagsSrc until ffdb, excluded
buf := make([]byte, 1000000)
n, err := tagsSrc.Read(buf)
if err != nil {
return err
}
x := 0
for i := 0; i < n-1; i++ {
if buf[i] == 0xff && buf[i+1] == 0xdb {
x = i
break
}
}
_, err = dest.Write(buf[:x])
if err != nil {
return err
}
// skip ffd8 from imageSrc, then copy the rest (there are no tags here)
skip := []byte{0, 0}
_, err = imageSrc.Read(skip)
if err != nil {
return err
}
_, err = io.Copy(dest, imageSrc)
if err != nil {
return err
}
return nil
}
字符串
检查结果文件,看起来代码做了我之前描述的事情。
左上角是标记源。左下角是图像的源代码。右边是结果。
x1c 0d1x的数据
有人知道我错过了什么吗?- 谢谢-谢谢
1条答案
按热度按时间fzwojiic1#
这比预期的要困难得多。我提到了这个资源,它解释了JPEG作为片段流的一般结构,唯一的例外是保存实际图像数据的“熵编码片段”(ECS)。
方法问题
我的想法是从标签源文件复制所有内容,直到第一个
ffdb
被排除,然后从图像源文件(没有标签)复制所有内容,从第一个ffdb
开始。生成的文件已损坏(缺少SOS标记)。这使得非常强烈的假设JPEG文件将不会举行。首先,
ffdb
可以很好地出现在 * 段内的某个地方 *。段的顺序也很松散,所以你不能保证ffdb
(定义量化表的段)之前或之后是什么。即使它在大部分时间里都能工作,它仍然是一个非常脆弱,不可靠的解决方案。正确方法
正确的方法是 * 迭代所有片段 *,仅从提供元数据的文件复制元数据片段,并且仅从提供图像数据的文件复制非元数据片段。
使这复杂化的是,出于某种原因,ECS不遵循段约定。因此,在阅读SOS(扫描开始)之后,我们需要通过查找下一个段标签跳到ECS的结尾:
0xFF
,后跟一个字节,该字节可能既不是数据(零),也不是“重启标记”(0xD0
-0xD7
)。为了测试,我使用了this image with EXIF metadata。我的测试命令如下所示:
字符串
我使用
exiftool
剥离EXIF元数据,然后通过重新读取它来测试Go程序。然后,使用exiftool exif_stripped.jpg
(或您选择的图像查看器)查看元数据,并与exiftool exif.jpg
的输出进行比较(附注:你可以简单地使用exiftool
来完全废弃这个Go程序)。我编写的程序取代了EXIF元数据、注解和版权声明。我添加了一个简单的命令行界面用于测试。如果只想保留EXIF元数据,只需将
isMetaTagType
函数更改为型
完整程序
型