如何删除EXIF数据,而无需重新压缩JPEG?

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

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

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

exiftool为我做的工作,它是用perl写的,所以应该在任何o / s上为你工作

http://www.sno.phy.queensu.ca/~phil/exiftool

用法:

exiftool -all= image.jpg 

随着imagemagick:

 convert <input file> -strip <output file> 

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

这个来自ImageMagick论坛的主题解释说,在ImageMagick中不支持JPEG无损操作(只要发生这种变化,请发表评论和链接!),并build议使用jpegtran (来自libjpeg):

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

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

你可能也想看看Exiv2 – 它真的很快(C ++ 不重新压缩),它是命令行,它还提供了一个EXIF操作库,你可以链接。 我不知道有多lessLinux发行版可以使用,但在CentOS中,它目前在基本回购协议中可用。

用法:

 exiv2 rm image.jpg 

我会build议:

 man jhead jhead -purejpg image.jpg 
  • debian(/ ubuntu)上的软件包只有123Kb的大小
  • 它不会因为不重新压缩而丢失质量
  • 该程序会改变图像,所以你最好做一个备份,如果你想

相邻软件:

MetAbility QuickFix

“MetabilityQuickFix只需点击一下鼠标,即可从所有照片中删除所有个人信息和GPS位置数据,从JPEG文件中安全地清除所有Exif,Iptc和XMP数据块的元数据项,并自动备份原始文件“

JPEG和PNG剥离器

“一个从JPG / JPEG / JFIF&PNG文件中剥离/清除/删除不必要的元数据(垃圾)的工具,图像质量不受影响,包括命令行支持,只需在命令行中指定一个文件夹或文件(允许使用通配符)”

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

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

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

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

4)find第一个EOI标记(图像的结尾),并截断文件,如果nessasary。

有些事情要先注意的是:

1)该程序用于我的尼康相机。 尼康的JPEG格式在每个创build的文件的最后添加了一些东西。 他们通过创build第二个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; } 

希望这可以帮助别人!