2010-04-16 181 views
78

我想從JPEG文件中刪除EXIF信息(包括縮略圖,元數據,相機信息...一切!),但我不想重新壓縮它,因爲重新壓縮JPEG會降低質量,以及通常增加文件大小。如何刪除EXIF數據而無需重新壓縮JPEG?

我正在尋找一種Unix/Linux解決方案,如果使用命令行的話更好。如果可能的話,使用ImageMagick(轉換工具)。如果這是不可能的,一個小的Python,Perl,PHP(或Linux上的其他通用語言)腳本就可以。

還有一個類似的問題,但是related to .NET

回答

114

exiftool做這項工作對我來說,它是用Perl編寫所以應該爲你工作在任何O/S

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

用法:

exiftool -all= image.jpg 
+0

小心編輯您的文章並添加示例命令行? – 2010-04-16 16:09:40

+0

肯定:)雖然它很簡單 – oedo 2010-04-16 16:30:11

+3

一些其他有趣的選項:「-o outfile.jpg」或「-out outfile.jpg」,「-overwrite_original」或「-overwrite_original_in_place」,「-P」或「-preserve」, 「-r」或「-recurse」 – 2010-04-17 02:05:30

67

隨着ImageMagick的:

convert <input file> -strip <output file> 
+10

對不起,但-strip無法正常工作,因爲ImageMagick仍然重新壓縮JPEG文件。 – 2010-04-16 16:06:53

+4

請注意,順便說一句,如果有人正在使用文件進行其他轉換,「-strip」可能會有用。另請注意,「-thumbnail 123x456」幾乎等同於「-strip-resize 123x456」。 – 2010-04-17 02:08:09

+10

+1,因爲這比下載新工具更容易。 – 2012-02-12 02:17:35

35

ImageMagick有-strip參數,但它推薦在保存之前按下圖像。因此,這個參數對我的需要沒用。

This topic from ImageMagick forum解釋說,對於在ImageMagick的JPEG無損操作不支持(只要這種變化,請張貼帶有鏈接的評論!),並使用jpegtran(從libjpeg的)提示:

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

(如果您不知道我的回答我的問題,閱讀thisthisthis

+1

嘗試了jpegtran方法,但在大多數情況下,它會增加文件大小而不是減小文件大小。在大多數情況下,您希望這樣做以減少文件大小。 – Codebeat 2015-05-27 02:06:09

+1

當試圖使用ImageMagick去除exif數據時,我注意到我最終以比我開始的文件更大的文件。這使我相信Imagemagick正在編碼您想要刪除的數據,並將其存儲在文件的其他位置。打電話給我老式的,但是當我從文件中刪除某些東西時,如果文件大小不一樣,我希望文件大小更小。任何其他結果都表明數據挖掘。 – Deanie 2017-04-06 19:59:30

+0

一個小點:對我而言,列出的兩個命令都不起作用,而不是以下工作: 'jpegtran -copy none image.jpg newimage.jpg' – ibic 2017-06-11 10:10:56

16

我建議jhead

man jhead 
jhead -purejpg image.jpg 
  • 包在Debian(/ Ubuntu的)只有123KB大小
  • 它不會失去質量,因爲它不會重新壓縮
  • 程序變異的圖像,所以你最好做一個備份,如果你想
21

您可能還需要尋找到Exiv2 - 這是非常快(C++ 沒有再壓縮),它的命令行,它也提供了EXIF操作,你可以對鏈接庫。我不知道有多少Linux發行版可以使用它,但在CentOS中,它目前可用於基本回購。

用法:

exiv2 rm image.jpg 
+0

謝謝你,這是第一個完成這項工作的人沒有質量損失和快速發展!喲應得的+100!但要刪除所有類型的標題,我必須指定-da選項,否則它將不會從jpg中刪除adobe photoshop/creator信息。無論如何,我在Windows上。 – Codebeat 2015-05-27 02:38:00

+0

謝謝!我想確認exiv2會顯示GPS位置信息,以便我可以看到它已經消失。打印的默認選項是「摘要」,不包括GPS信息。要查看您必須使用的所有信息: exiv2 -pa pr image.jpg.jpg – 2015-10-24 20:36:33

+0

請注意,此工具會破壞我的一些JPEG文件的質量,幸運的是我有一個備份 – 2016-03-01 20:32:41

0

其他軟件:

MetAbility QuickFix

「MetabilityQuickFix條你所有的個人信息和您的所有照片GPS定位數據,只是一個單一的點擊鼠標。它擦洗所有元數據項從您的JPEG文件安全地形成的Exif,IPTC和XMP數據塊,並自動使原始文件「

JPEG & PNG Stripper

」爲剝離/清洗工具/刪除uncessary元數據的備份副本(垃圾)從JPG/JPEG/JFIF & PNG文件。圖像質量不受影響。包括命令行支持。只要指定命令行上的一個文件夾或文件(支持通配符)」

1

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

希望這可以幫助別人!