如何在不重新压缩JPEG的情况下删除EXIF数据?

183
我想从JPEG文件中删除EXIF信息(包括缩略图、元数据、相机信息等一切!),但我不想重新压缩它,因为重新压缩JPEG会降低质量,并且通常会增加文件大小。
我正在寻找一个Unix/Linux解决方案,最好使用命令行。如果可能的话,使用ImageMagick(convert工具)。如果不可能,一个小的Python、Perl、PHP(或其他Linux上常见的语言)脚本也可以。
有一个类似的问题,但与.NET相关

2
相关:https://askubuntu.com/questions/260810/how-can-i-read-and-remove-meta-exif-data-from-my-photos-using-the-command-line - Ciro Santilli OurBigBook.com
1
您确定要删除所有EXIF信息吗?EXIF方向信息对于正确显示图像的旋转非常重要。如果您将其删除,您可能会发现图像显示的方向与您预期的相差90度。此外,颜色配置文件存储在EXIF元数据中,这会影响图像颜色的显示方式。 - Flimm
10个回答

237

exiftool对我来说是一种适合的工具,它是用perl编写的,因此在任何操作系统上都应该能够使用。

https://exiftool.org/

用法:

exiftool -all= image.jpg

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

exiftool -EXIF= image.jpg

8
一些其他有趣的选项:"-o outfile.jpg" 或 "-out outfile.jpg","-overwrite_original" 或 "-overwrite_original_in_place","-P" 或 "-preserve","-r" 或 "-recurse"。 - Denilson Sá Maia
8
我刚看到这个帖子,也在寻找同样的事情。我想为Ubuntu用户补充一下:该脚本在Ubuntu仓库中可用,名为libimage-exiftool-perl:sudo apt-get install libimage-exiftool-perl - user605331
9
使用homebrew命令brew install exiftool安装。 - cwd
7
如果您真的信任该程序,请添加-overwrite_original开关,该程序将不会创建备份文件。 - Salman A
15
使用选项-all=会删除图像.jpg中的所有标记!正确的答案应该是exiftool -EXIF= image.jpg - PeterCo
显示剩余5条评论

106

使用ImageMagick:

convert <input file> -strip <output file>

28
抱歉,但是-strip并没有像预期的那样起作用,因为ImageMagick仍然会重新压缩JPEG文件。 - Denilson Sá Maia
5
顺便提一下,如果有人正在对文件进行其他转换,使用“-strip”可能会很有用。还要注意,“-thumbnail 123x456”几乎等同于“-strip -resize 123x456”。 - Denilson Sá Maia
25
+1是因为这比下载一个新工具容易得多。 - Dave Dopson
19
注意:-strip 会移除颜色配置文件。如果有人想要去除 EXIF 数据但不丢失配置文件信息,请参考我的解决方案,链接在这里:https://dev59.com/3WYr5IYBdhLWcg3wdqGw#17516878 - Robbert
5
OP应该更新这篇答案,因为它错了两次。 - berbt
显示剩余2条评论

51
ImageMagick有-strip参数,但是它会在保存之前重新压缩图像。因此,这个参数对我的需求来说是无用的。 这个来自ImageMagick论坛的话题解释了ImageMagick不支持JPEG无损操作(如果有任何变化,请发表评论并附上链接!),并建议使用jpegtran(来自libjpeg):
jpegtran -copy none -progressive image.jpg > newimage.jpg
jpegtran -copy none -progressive -outfile newimage.jpg image.jpg

(如果您对我回答自己的问题感到不确定,请阅读这个这个这个


1
尝试了jpegtran方法,但在大多数情况下它会增加文件大小而不是减小。在大多数情况下,您希望这样做是为了减小文件大小。 - Codebeat
1
当尝试使用ImageMagick去除exif数据时,我注意到最终得到的文件比原始文件更大。这让我相信Imagemagick正在编码您想要删除的数据,并将其存储在文件的其他位置。虽然有人可能认为这是数据挖掘,但我还是比较传统,认为如果从文件中删除了某些内容,那么文件大小应该会变小或者保持不变。 - Deanie
1
一个小问题:对我来说,列出的两个命令都不起作用,而以下命令可以正常工作:jpegtran -copy none image.jpg newimage.jpg - ibic
@Codebeat 你需要添加参数 -progressive。这将减小文件大小。 - Tom
请注意,jpegtran -copy none file.jpg将会删除“方向”元数据,这将导致一些照片显示为错误的旋转方向。 - Flimm

41

你可能还想查看 Exiv2 -- 它非常快速(使用 C++,且不重新压缩),它是命令行工具,并且还提供了一个可供链接的 EXIF 操作库。我不知道有多少 Linux 发行版可以使用它,但在 CentOS 中,它目前可以在基础库中获取。

使用方法:

exiv2 rm image.jpg

1
谢谢,伙计!这是第一个能够在不损失质量的情况下快速完成工作的程序!你应该得到+100的评价!但是为了删除所有类型的头文件,我必须指定-da选项,否则它将无法从JPG中删除Adobe Photoshop/Creator信息。反正我用的是Windows系统。 - Codebeat
1
谢谢!我想确认一下exiv2是否会显示GPS位置信息,以便我在之后可以看到它是否已经消失。打印的默认选项是“摘要”,不包括GPS信息。要查看所有信息,您必须使用: exiv2 -pa pr image.jpg - Rob Russell
请注意,这个工具破坏了我的一些JPEG图像的质量,幸运的是我有备份。 - Steel Brain
1
@SteelBrain,这真的很奇怪 - 你能分享一张受此影响的JPEG图像吗? - Bogdan Stăncescu
请注意,exiv2 rm image.jpg将删除“方向”元数据,这会导致一些照片看起来旋转了错误的方向。 - Flimm
显示剩余2条评论

24

我建议使用 jhead:

man jhead
jhead -purejpg image.jpg

在Debian/Ubuntu上只有123Kb,速度快,仅会触及EXIF信息而不会影响图片本身。请注意,如果你想保留带有EXIF信息的原始文件,你需要创建一个副本。


3

我最近在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;
}

希望这能对某些人有所帮助!

1
我们使用以下命令从TIFF文件中删除纬度数据: exiv2 mo -M"del Exif.GPSInfo.GPSLatitude" IMG.TIF 您可以使用exiv2 -pa IMG.TIF列出所有元数据。

OP要求从JPEG中删除_所有内容_,而不是仅从tiff文件中删除GPSLatitude。因此,这并没有回答问题。 - maxschlepzig

0

方便提示:如果您使用的是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”(使用ImageMagick,更改数据并覆盖原始文件)
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


OP明确要求UNIX/Linux解决方案,因此这与问题无关。 - maxschlepzig
尽管这个问题有Unix标签,但这仍然是SO,而不是Unix SE或Ask Ubuntu。我可以想到很多次,在别人的问题中找到了一个(并不完全特定于该问题的)答案,这帮助了我。我们不仅仅为了提问者的利益而给出答案,你知道的...在我看来,没有必要对规则如此严格;这个问题已经有了很好的答案。 - geekley
这不是SO的工作方式。这是一个问答网站,答案应该实际回答问题。而不是一个网络论坛,在任何主题中发布随意的恰当评论。对于您的用例,在SO上,更好的方法是发布一个关于Windows注册表细节的新问题,可能直接与您的答案一起发布。然后,您可以添加一个相关问题的评论作为参考。 - maxschlepzig

0

如果要进行无损的EXIF剥离,您可以使用libexif,该库可以在cygwin上使用。删除EXIF和缩略图以匿名化图像:

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

拖放 .bat 文件以与Cygwin一起使用:

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

当我尝试时,这并没有删除 GPS 位置数据。 - Flimm

0
如果您已经使用jpegoptim,您也可以使用它来删除exif信息。
jpegoptim -s *

这个不起作用,它修改了图像(似乎没有“复制”选项,并且使用“-n”开关不优化图像也无法去除EXIF数据)。 - Synetech

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接