如何删除 EXIF 数据而不重新压缩 JPEG?

我想删除 EXIF 信息(包括缩略图,元数据,相机信息... 一切!)从 JPEG 文件,但我不想重新压缩它,因为重新压缩的 JPEG 会降低质量,以及通常增加文件大小。

我正在寻找一个 Unix/Linux 解决方案,如果使用命令行更好。如果可能,使用 ImageMagick (转换工具)。如果这不可能,一个小的 Python、 Perl、 PHP (或 Linux 上的其他公共语言)脚本也可以。

还有一个类似的问题,但是 与.NET 有关

114599 次浏览

Exiftool 为我做这个工作,它是用 perl 写的,所以在任何操作系统上都可以为你工作

Https://exiftool.org/

用途:

exiftool -all= image.jpg

更新-正如 PeterCo 下面解释的那样,这将删除所有的标签。如果你只是想删除 EXIF 标签,那么你应该使用

exiftool -EXIF= image.jpg

图像魔术:

convert <input file> -strip <output file>

ImageMagick 有 脱衣服参数,但是它在保存之前重新压缩了图像。

这个来自 ImageMagick 论坛 的主题解释了 ImageMagick 中不支持 JPEG 无损操作(无论何时发生这种变化,请发表带链接的评论!),并建议使用 JPEGTRAN(来自 libjpeg) :

jpegtran -copy none -progressive image.jpg > newimage.jpg
jpegtran -copy none -progressive -outfile newimage.jpg image.jpg

(如果你不确定我是否能回答自己的问题,请阅读 这个这个这个)

我建议 jhead:

man jhead
jhead -purejpg image.jpg

在 debian/ubuntu 上只有123Kb,速度很快,而且只接触 EXIF 保持图像本身完整。请注意,如果要保留包含 EXIF 的原始文件,则需要创建一个副本。

您可能还想研究一下 出版物2——它非常快(C + + 还有没有重新压缩) ,它是命令行,并且它还提供了一个可以链接的 EXIF 操作库。我不知道有多少 Linux 发行版可以使用它,但是在 CentOS 中,它目前可以在基础回购中使用。

用法:

exiv2 rm image.jpg

我最近在 C 中进行了这个项目。下面的代码执行以下操作:

1)获取图像的当前方向。

2)删除包含在 APP1(Exif 数据)和 APP2(Flashpix 数据)中的所有数据。

3)重新创建 APP1方向标记并将其设置为原始值。

4)找到第一个 EOI标记(图像结束) ,并在必要时截断文件。

首先要注意的是:

1)这个程序是用于我的尼康相机。尼康的 JPEG 格式在它创建的每个文件的末尾都添加了一些东西。他们通过创建第二个 EOI标记将这些数据编码到图像文件的末尾。正常情况下,图像程序读取到发现的第一个 EOI标记。尼康有此后的信息,我的程序截断。

2)因为这是尼康格式,它假定 big endian字节顺序。如果您的图像文件使用 little endian,一些调整需要作出。

3)当尝试使用 ImageMagick去除 exif 数据时,我注意到我得到了一个比开始时更大的文件。这使我相信,Imagemagick正在编码您想要剥离的数据,并将其存储在文件的其他地方。你可以说我是老古董,但是当我从文件中删除一些东西时,我希望文件的大小要小一些,如果不是相同大小的话。任何其他结果都表明需要进行数据挖掘。

这是密码:

#include <stdio.h>
#include <stdlib.h>
#include <libgen.h>
#include <string.h>
#include <errno.h>


// Declare constants.
#define COMMAND_SIZE     500
#define RETURN_SUCCESS     1
#define RETURN_FAILURE     0
#define WORD_SIZE         15


int check_file_jpg (void);
int check_file_path (char *file);
int get_marker (void);
char * ltoa (long num);
void process_image (char *file);


// Declare global variables.
FILE *fp;
int orientation;
char *program_name;


int main (int argc, char *argv[])
{
// Set program name for error reporting.
program_name = basename(argv[0]);


// Check for at least one argument.
if(argc < 2)
{
fprintf(stderr, "usage: %s IMAGE_FILE...\n", program_name);
exit(EXIT_FAILURE);
}


// Process all arguments.
for(int x = 1; x < argc; x++)
process_image(argv[x]);


exit(EXIT_SUCCESS);
}


void process_image (char *file)
{
char command[COMMAND_SIZE + 1];


// Check that file exists.
if(check_file_path(file) == RETURN_FAILURE)
return;


// Check that file is an actual JPEG file.
if(check_file_jpg() == RETURN_FAILURE)
{
fclose(fp);
return;
}


// Jump to orientation marker and store value.
fseek(fp, 55, SEEK_SET);
orientation = fgetc(fp);


// Recreate the APP1 marker with just the orientation tag listed.
fseek(fp, 21, SEEK_SET);
fputc(1, fp);


fputc(1, fp);
fputc(18, fp);
fputc(0, fp);
fputc(3, fp);
fputc(0, fp);
fputc(0, fp);
fputc(0, fp);
fputc(1, fp);
fputc(0, fp);
fputc(orientation, fp);


// Blank the rest of the APP1 marker with '\0'.
for(int x = 0; x < 65506; x++)
fputc(0, fp);


// Blank the second APP1 marker with '\0'.
fseek(fp, 4, SEEK_CUR);


for(int x = 0; x < 2044; x++)
fputc(0, fp);


// Blank the APP2 marker with '\0'.
fseek(fp, 4, SEEK_CUR);


for(int x = 0; x < 4092; x++)
fputc(0, fp);


// Jump the the SOS marker.
fseek(fp, 72255, SEEK_SET);


while(1)
{
// Truncate the file once the first EOI marker is found.
if(fgetc(fp) == 255 && fgetc(fp) == 217)
{
strcpy(command, "truncate -s ");
strcat(command, ltoa(ftell(fp)));
strcat(command, " ");
strcat(command, file);
fclose(fp);
system(command);
break;
}
}
}


int get_marker (void)
{
int c;


// Check to make sure marker starts with 0xFF.
if((c = fgetc(fp)) != 0xFF)
{
fprintf(stderr, "%s: get_marker: invalid marker start (should be FF, is %2X)\n", program_name, c);
return(RETURN_FAILURE);
}


// Return the next character.
return(fgetc(fp));
}


int check_file_jpg (void)
{
// Check if marker is 0xD8.
if(get_marker() != 0xD8)
{
fprintf(stderr, "%s: check_file_jpg: not a valid jpeg image\n", program_name);
return(RETURN_FAILURE);
}


return(RETURN_SUCCESS);
}


int check_file_path (char *file)
{
// Open file.
if((fp = fopen(file, "rb+")) == NULL)
{
fprintf(stderr, "%s: check_file_path: fopen failed (%s) (%s)\n", program_name, strerror(errno), file);
return(RETURN_FAILURE);
}


return(RETURN_SUCCESS);
}


char * ltoa (long num)
{
// Declare variables.
int ret;
int x = 1;
int y = 0;
static char temp[WORD_SIZE + 1];
static char word[WORD_SIZE + 1];


// Stop buffer overflow.
temp[0] = '\0';


// Keep processing until value is zero.
while(num > 0)
{
ret = num % 10;
temp[x++] = 48 + ret;
num /= 10;
}


// Reverse the word.
while(y < x)
{
word[y] = temp[x - y - 1];
y++;
}


return word;
}

希望这对谁有帮助!

方便提示: 如果您在 Windows 上,您可以将一个 REG 文件应用到注册表中,以便在上下文菜单中安装一个条目,这样您就可以通过右键单击该文件并选择命令轻松地删除元数据。

例如(记住编辑指向计算机上安装可执行文件的路径) :


对于 JPEG,JPG,JPE,JFIF 文件: 命令“ 删除元数据
(使用 ExifTool,保留原始文件作为备份)
exiftool -all= image.jpg

JPG-RemoveExif.reg

Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Classes\jpegfile\shell\RemoveMetadata]
@="Remove metadata"
[HKEY_CURRENT_USER\Software\Classes\jpegfile\shell\RemoveMetadata\command]
@="\"C:\\Path to\\exiftool.exe\" -all= \"%1\""
[HKEY_CURRENT_USER\Software\Classes\jpegfile\shell\RemoveMetadata]
"Icon"="C:\\Path to\\exiftool.exe,0"

对于 PNG 文件: 命令“ 转换为小型 PNG
(使用 图像魔术,改变数据覆盖原始文件)
convert -background none -strip -set filename:n "%t" image.png "%[filename:n].png"

PNG-Minify. reg

Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Classes\pngfile\shell\ConvertToMinifiedPNG]
@="Convert to minified PNG"
[HKEY_CURRENT_USER\Software\Classes\pngfile\shell\ConvertToMinifiedPNG\command]
@="\"C:\\Path to\\convert.exe\" -background none -strip -set filename:n \"%%t\" \"%1\" \"%%[filename:n].png\""
[HKEY_CURRENT_USER\Software\Classes\pngfile\shell\ConvertToMinifiedPNG]
"Icon"="C:\\Path to\\convert.exe,0"

相关阅读: 在上下文菜单中将 PNG 转换为 ICO

我们使用它来删除 TIFF 文件中的纬度数据:

exiv2 mo -M"del Exif.GPSInfo.GPSLatitude" IMG.TIF 其中可以使用 exiv2 -pa IMG.TIF列出所有元数据。

对于无损的 EXIF 条,你可以使用 Libexif,也就是 和 Cygwin 一起。删除 EXIF 和缩略图来匿名一张图片:

$ exif --remove --tag=0 --remove-thumbnail exif.jpg -o anonymized.jpg

用于 cygwin 的拖放 .bat文件:

@ECHO OFF
exif --remove --tag=0 --remove-thumbnail %~1

如果您已经使用了 jpegoptim,那么也可以使用它来删除 exif。

jpegoptim -s *