How to remove EXIF data without recompressing the JPEG?

UnixImagemagickJpegExif

Unix Problem Overview


I want to remove the EXIF information (including thumbnail, metadata, camera info... everything!) from JPEG files, but I don't want to recompress it, as recompressing the JPEG will degrade the quality, as well as usually increasing the file size.

I'm looking for a Unix/Linux solution, even better if using the command-line. If possible, using ImageMagick (convert tool). If that's not possible, a small Python, Perl, PHP (or other common language on Linux) script would be ok.

There is a similar question, but related to .NET.

Unix Solutions


Solution 1 - Unix

exiftool does the job for me, it's written in perl so should work for you on any o/s

https://exiftool.org/

usage :

exiftool -all= image.jpg

UPDATED - as PeterCo explains below this will remove ALL of the tags. if you just want to remove the EXIF tags then you should use

exiftool -EXIF= image.jpg

Solution 2 - Unix

With imagemagick:

convert <input file> -strip <output file>

Solution 3 - Unix

ImageMagick has the -strip parameter, but it recompresses the image before saving. Thus, this parameter is useless for my need.

This topic from ImageMagick forum explains that there is no support for JPEG lossless operations in ImageMagick (whenever this changes, please post a comment with a link!), and suggests using jpegtran (from libjpeg):

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

(If you are unsure about me answering my own question, read this and this and this)

Solution 4 - Unix

You might also want to look into Exiv2 -- it's really fast (C++ and no recompression), it's command line, and it also provides a library for EXIF manipulation you can link against. I don't know how many Linux distros make it available, but in CentOS it's currently available in the base repo.

Usage:

exiv2 rm image.jpg

Solution 5 - Unix

I'd propose jhead:

man jhead
jhead -purejpg image.jpg

Only 123Kb on debian/ubuntu, it's fast, and it only touches EXIF keeping the image itself intact. Note that you need to create a copy if you want to preserve the original file with EXIF in it.

Solution 6 - Unix

I recently undertook this project in C. The code below does the following:

  1. Gets the current orientation of the image.

  2. Removes all data contained in APP1 (Exif data) and APP2 (Flashpix data) by blanking.

  3. Recreates the APP1 orientation marker and sets it to the original value.

  4. Finds the first EOI marker (End of Image) and truncates the file if nessasary.

Some things to note first are:

  1. This program is used for my Nikon camera. Nikon's JPEG format adds somthing to the very end of each file it creates. They encode this data on to the end of the image file by creating a second EOI marker. Normally image programs read up to the first EOI marker found. Nikon has information after this which my program truncates.

  2. Because this is for Nikon format, it assumes big endian byte order. If your image file uses little endian, some adjustments need to be made.

  3. When trying to use ImageMagick to strip exif data, I noticed that I ended up with a larger file than what I started with. This leads me to believe that Imagemagick is encoding the data you want stripped away, and is storing it somewhere else in the file. Call me old fashioned, but when I remove something from a file, I want a file size the be smaller if not the same size. Any other results suggest data mining.

And here is the code:

#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;
}

Hope this helps someone!

Solution 7 - Unix

Hint for convenience: If you are on Windows, you can apply a REG file to the registry, to install an entry in the context menu, so you can easily remove metadata by right-clicking the file and selecting the command.

For example (remember to edit the paths to point to where the executables are installed on your computer):


For JPEG,JPG,JPE,JFIF files: command "Remove metadata"
(using ExifTool, preserves original file as backup)
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"

For PNG files: command "Convert to minified PNG"
(using ImageMagick, changes data overwriting original file)
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"

Related: convert PNGs to ICO in context menu.

Solution 8 - Unix

We used this to remove latitude data from TIFF file:

exiv2 mo -M"del Exif.GPSInfo.GPSLatitude" IMG.TIF where you can use exiv2 -pa IMG.TIF to list all metadata.

Solution 9 - Unix

For lossless EXIF strip you can use libexif, which is available with cygwin. Remove both EXIF and thumbnail to anonymize an image:

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

Drag-n-drop .bat file for use with cygwin:

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

Solution 10 - Unix

If you already use jpegoptim you can use it to remove the exif, too.

jpegoptim -s *

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDenilson S&#225; MaiaView Question on Stackoverflow
Solution 1 - UnixchrisView Answer on Stackoverflow
Solution 2 - UnixJayMView Answer on Stackoverflow
Solution 3 - UnixDenilson Sá MaiaView Answer on Stackoverflow
Solution 4 - UnixBogdan StăncescuView Answer on Stackoverflow
Solution 5 - UnixVasiliNovikovView Answer on Stackoverflow
Solution 6 - UnixDeanieView Answer on Stackoverflow
Solution 7 - UnixgeekleyView Answer on Stackoverflow
Solution 8 - UnixMuhammet Ali AsanView Answer on Stackoverflow
Solution 9 - UnixradioxomaView Answer on Stackoverflow
Solution 10 - UnixstrifelView Answer on Stackoverflow