Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to keep JPEG metadata using PIL?

I am using the library PIL to resize an image to automate the inputs for other software in my job and this software needs this metadata.

The image is correctly resized, but loses all metadata contained in it.

I used the code below to resize.

from PIL import Image
filename = r'input.jpg'
ratio=0.2
im = Image.open(filename)
out = im.resize([int(ratio * s) for s in im.size], Image.ANTIALIAS)
out.save("out.jpg", format=im.format, optimize=True)

I can see all metadata into the dict of image using:

im.__dict__

Libraries like pyexif, pyexif2, etc return Null values. I check the tag exif into im.dict and this tag is empty.

Is there any method of adding this dictionary to my resized image?

link to image download: Image

Thank you very much.

EDIT: Results for im.dict (Inside this dictionary has a XML code. It is responsible for transfer metadata for the software that my company uses). We can see The xml script in 3 tags info-->comment, app-->com and applist1.

{'im': None,
'mode': 'RGB',
'_size': (4096, 2304),
'palette': None,
'info': {'jfif': 258,
     'jfif_version': (1, 2),
         'jfif_unit': 0, 'jfif_density': (1, 1),
         'comment': b'<?xml version="1.0" encoding="utf-8"?>\n<image time="09:04:10.271222" date="2020.06.15" acq_index="7666">\n\t<Position time="20200615T090410.834" received="2020-Jun-15 09:04:10.267082" extrapolated="false" age="4" transponder_id="0">\n\t\t<Coords long="-40.9273182" lat="-22.7837963"/>\n\t\t<Depth altitude="5.14" depth="85.57"/>\n\t\t<Direction pitch="-3.09" roll="0.03" yaw="180.02"/>\n\t</Position>\n\t<acquisition>\n\t\t<exposure>5000</exposure>\n\t\t<digital_gain>1.19</digital_gain>\n\t\t<analog_gain>6</analog_gain>\n\t\t<sensor_gain>4</sensor_gain>\n\t\t<aperture>1.4</aperture>\n\t\t<focus>498</focus>\n\t\t<name>ColorCamera</name>\n\t\t<camera_session_name>start_1</camera_session_name>\n\t\t<camera_sub_session_name/>\n\t\t<focus_enc>3945</focus_enc>\n\t\t<width>4096</width>\n\t\t<height>2304</height>\n\t\t<seq_slot>0</seq_slot>\n\t\t<dequeue_time>2020-06-15T09:04:10.887848</dequeue_time>\n\t</acquisition>\n\t<errors/>\n\t<versions>\n\t\t<software>0.968s4</software>\n\t\t<fpga>0x02d1</f                    pga>\n\t\t<pic>210</pic>\n\t\t<serial_number>191</serial_number>\n\t</versions>\n\t<ntp>\n\t\t<ntpq>*192.168.99.100                   1 u   69  128  377    0.196   -3.329   0.521</ntpq>\n\t\t<state>within_limits</state>\n\t\t<sync_level>excellent_sync</sync_level>\n\t</ntp>\n\t<pps/>\n</image>\n'}, 'category': 0, 'readonly': 1, 'pyaccess': None, '_exif': None, '_min_frame': 0, 'custom_mimetype': None, 'tile': [('jpeg', (0, 0, 4096, 2304), 0, ('RGB', ''))], 
'decoderconfig': (),
'decodermaxblock': 65536,
'fp': <_io.BufferedReader name='input.jpg'>,
'filename': 'input.jpg',
'_exclusive_fp': True, 'bits': 8,
'layers': 3,
'layer': [(1, 2, 2, 0), (2, 1, 1, 1), (3, 1, 1, 1)],
'huffman_dc': {},
'huffman_ac': {},
'quantization': {0: array('B', [3, 2, 2, 3, 2, 2, 3, 3, 3, 3, 4, 3, 3, 4, 5, 8, 5, 5, 4, 4, 5, 10, 7, 7, 6, 8, 12, 10, 12, 12, 11, 10, 11, 11, 13, 14, 18, 16, 13, 14, 17, 14, 11, 11, 16, 22, 16, 17, 19, 20, 21, 21, 21, 12, 15, 23, 24, 22, 20, 24, 18, 20, 21, 20]), 
                 1: array('B', [3, 4, 4, 5                    , 4, 5, 9, 5, 5, 9, 20, 13, 11, 13, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20])}, 

'app': {'APP0': b'JFIF\x00\x01\x02\x00\x00\x01\x00\x01\x00\x00', 
        'COM': b'<?xml version="1.0" encoding="utf-8"?>\n<image time="09:04:10.271222" date="2020.06.15" acq_index="7666">\n\t<Position time="20200615T090410.834" received="2020-Jun-15 09:04:10.267082" extrapolated="false" age="4" transponder_id="0">\n\t\t<Coords long="-40.9273182" lat="-22.7837963"/>\n\t\t<Depth altitude="5.14" depth="85.57"/>\n\t\t<Direction pitch="-3.09" roll="0.03" yaw="180.02"/>\n\t</Position>\n\t<acquisition>\n\t\t<exposure>5000</exposure>\n\t\t<digital_gain>1.19</digital_gain>\n\t\t<analog_gain>6</analog_gain>\n\t\t<sensor_gain>4</sensor_gain>\n\t\t<aperture>1.4</aperture>\n\t\t<focus>498</focus>\n\t\t<name>ColorCamera</name>\n\t\t<camera_session_name>start_1</camera_session_name>\n\t\t<camera_sub_session_name/>\n\t\t<focus_enc>3945</focus_enc>\n\t\t<width>4096</width>\n\t\t<height>2304</height>\n\t\t<seq_slot>0</seq_slot>\n\t\t<dequeue_time>2020-06-15T09:04:10.887848</dequeue_time>\n\t</acquisition>\n\t<errors/>\n\t<versions>\n\t\t<software>0.968s4</software>\n\t\t<fpga>0x02d1</fpga>\               n\t\t<pic>210</pic>\n\t\t<serial_number>191</serial_number>\n\t</versions>\n\t<ntp>\n\t\t<ntpq>*192.168.99.100                   1 u   69  128  377    0.196   -3.329   0.521</ntpq>\n\t\t<state>within_limits</state>\n\t\t<sync_level>excellent_sync</sync_level>\n\t</ntp>\n\t<pps/>\n</image>\n'},

'applist': [('APP0', b'JFIF\x00\x01\x02\x00\x00\x01\x00\x01\x00\x00'), ('COM', b'<?xml version="1.0" encoding="utf-8"?>\n<image time="09:04:10.271222" date="2020.06.15" acq_index="7666">\n\t<Position time="20200615T090410.834" received="2020-Jun-15 09:04:10.267082" extrapolated="false" age="4" transponder_id="0">\n\t\t<Coords long="-40.9273182" lat="-22.7837963"/>\n\t\t<Depth altitude="5.14" depth="85.57"/>\n\t\t<Direction pitch="-3.09" roll="0.03" yaw="180.02"/>\n\t</Position>\n\t<acquisition>\n\t\t<exposure>5000</exposure>\n\t\t<digital_gain>1.19</digital_gain>\n\t\t<analog_gain>6</analog_gain>\n\t\t<sensor_gain>4</sensor_gain>\n\t\t<aperture>1.4</aperture>\n\t\t<focus>498</focus>\n\t\t<name>ColorCamera</name>\n\t\t<camera_session_name>start_1</camera_session_name>\n\t\t<camera_sub_session_name/>\n\t\t<focus_enc>3945</focus_enc>\n\t\t<width>4096</width>\n\t\t<height>2304</height>\n\t\t<seq_slot>0</seq_slot>\n\t\t<dequeue_time>2020-06-15T09:04:10.887848</dequeue_time>\n\t</acquisition>\n\t<errors/>\n\t<versions>\n\t\t<software>0.968s4</software>\n\t\t<fpga>0x02d1</fpga>\n\t\t<pic>210</pic>\n\t\t<serial_number>191</serial_number>\n\t</versions>\n\t<ntp>\n\t\t<ntpq>*192.168.99.100                   1 u   69  128  377    0.196   -3.329   0.521</ntpq>\n\t\t<state>within_limits</state>\n\t\t<sync_level>excellent_sync</sync_level>\n\t</ntp>\n\t<pps/>\n</image>\n')],

'icclist': []}
like image 631
Vinicius Nogueira Avatar asked Oct 28 '25 10:10

Vinicius Nogueira


2 Answers

You could add the metadata extracted from your original file to the resized one by means of the exif keyword argument. If you modify your code like this:

from PIL import Image

filename = r'input.jpg'
ratio = 0.2

im = Image.open(filename)
EXIF = im.getexif()

out = im.resize([int(ratio * s) for s in im.size], Image.ANTIALIAS)
out.save("out.jpg", format=im.format, optimize=True, exif=EXIF)

The metadata should be now transferred to the new image.

like image 111
panadestein Avatar answered Oct 30 '25 00:10

panadestein


Maybe you could use wand instead of PIL as it propagates the comment forward for you automagically:

#!/usr/bin/env python3

from wand.image import Image

with Image(filename='input.jpg') as img: 
    img.save(filename='result.jpg')

Or, here's a possible work-around. You can extract the comment from input.jpg into a file called comment.txt like this:

jhead -cs comment.txt input.jpg

You can then write that comment into a different file called result.jpg like this:

jhead -ci comment.txt result.jpg

I assume you could use the Python subprocess module to copy forward your data using something like:

import subprocess

# Propagate JPEG comment forward from "input.jpg" to "result.jpg"
subprocess.run('jhead -cs - input.jpg | jhead -ci - result.jpg', shell=True)
like image 20
Mark Setchell Avatar answered Oct 29 '25 23:10

Mark Setchell



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!