165

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

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

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

4

10 回答 10

205

exiftool 为我完成了这项工作,它是用 perl 编写的,因此应该适用于任何操作系统

https://exiftool.org/

用法 :

exiftool -all= image.jpg

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

exiftool -EXIF= image.jpg
于 2010-04-16T15:46:19.983 回答
106

使用 imagemagick:

convert <input file> -strip <output file>
于 2010-04-16T15:54:54.253 回答
50

ImageMagick 有-strip参数,但它会在保存之前重新压缩图像。因此,这个参数对我的需要没有用。

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

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

(如果您不确定我是否会回答我自己的问题,请阅读

于 2010-04-16T16:06:17.213 回答
38

您可能还想研究一下Exiv2——它非常快(C++无需重新压缩),它是命令行,它还提供了一个可以链接的 EXIF 操作库。我不知道有多少 Linux 发行版提供了它,但在 CentOS 中,它目前在基本存储库中可用。

用法:

exiv2 rm image.jpg
于 2014-02-28T21:49:30.803 回答
23

我建议jhead

man jhead
jhead -purejpg image.jpg

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

于 2013-07-30T21:05:40.790 回答
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;
}

希望这对某人有帮助!

于 2016-10-27T22:23:48.917 回答
1

为方便起见:如果您在 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-缩小.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

于 2019-04-03T01:35:47.270 回答
1

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

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

于 2019-04-29T19:07:00.033 回答
0

对于无损 EXIF 条,您可以使用cygwin提供的libexif。删除 EXIF 和缩略图以匿名化图像:

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

.bat用于 cygwin的拖放文件:

@ECHO OFF
exif --remove --tag=0 --remove-thumbnail %~1
于 2019-05-17T16:43:18.537 回答
0

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

jpegoptim -s *
于 2020-05-23T19:08:16.773 回答