Skip to content

Instantly share code, notes, and snippets.

@2minchul
Last active December 23, 2020 09:27
Show Gist options
  • Save 2minchul/ad3c750caceb8342e18e1deed0d076da to your computer and use it in GitHub Desktop.
Save 2minchul/ad3c750caceb8342e18e1deed0d076da to your computer and use it in GitHub Desktop.
remove exif from jpg without PIL
"""
This code is based on https://github.com/AzadKurt/pexif written by AzadKurt
"""
def remove_exif(img_bytes: bytes) -> bytes:
"""
returns the bytes of the image without exif
:param img_bytes: image bytes including exif
:return: image bytes without exif
"""
exif_start = get_exif_sig_start(img_bytes)
if not exif_start:
return img_bytes
img_size = len(img_bytes)
exif_data_size = ((img_bytes[exif_start + 2] << 8) | img_bytes[exif_start + 3]) + 2
# + 2 to include exif marker 0xFFE1
i = exif_start + exif_data_size
while not (img_bytes[i] == 0xFF and img_bytes[i + 1] == 0xDB):
i += ((img_bytes[i + 2] << 8) | img_bytes[i + 3]) + 2 # skip to next marker
new_size = img_size - i + 20 # + 20 because of the 20 jfif bytes
purged = init_basic_jfif(new_size)
jfif_difference = i - 20
for ii in range(i, img_size):
purged[ii - jfif_difference] = img_bytes[ii]
return bytes(purged)
def get_exif_sig_start(img_bytes: bytes) -> int:
"""
find the starting position of exif
if there is no exif returns 0
:param img_bytes: image bytes
:return: index of image
"""
if not (img_bytes[0] == 0xFF and img_bytes[1] == 0xD8):
return 0
for i in range(2, len(img_bytes)):
if img_bytes[i] == 0xFF:
marker = img_bytes[i + 1]
if marker == 0xDB: # 0xFFDB marks the beginning of the quantization table
return 0
if marker == 0xE1:
return i
# if another marker that is not the exif marker is present,
# jump forward by the specified data size of the current marker
if (marker & 0xF0) == 0xE0:
i += ((img_bytes[i + 2] << 8) | img_bytes[i + 3]) + 1
# + 1 because the two bytes that specify the data size are
# included and + 1 together with i++ results in an increase of 2
return 0
def init_basic_jfif(size):
"""
these bytes make up the beginning of a basic jpg in the jfif format
for an explanation as to what these specific bytes mean, visit
https://www.ccoderun.ca/programming/2017-01-31_jpeg/ or
http://web.archive.org/web/20200929185628/https://www.ccoderun.ca/programming/2017-01-31_jpeg/ if necessary
:param size: size of image
:return: allocated list of image bytes
"""
purged = [0] * size
purged[0] = 0xFF
purged[1] = 0xD8
purged[2] = 0xFF
purged[3] = 0xE0
purged[4] = 0x00
purged[5] = 0x10
purged[6] = 0x4A
purged[7] = 0x46
purged[8] = 0x49
purged[9] = 0x46
purged[10] = 0x00
purged[11] = 0x01
purged[12] = 0x01
purged[13] = 0x00
purged[14] = 0x00
purged[15] = 0x01
purged[16] = 0x00
purged[17] = 0x01
purged[18] = 0x00
purged[19] = 0x00
return purged
if __name__ == '__main__':
with open('in.jpg', 'rb') as f:
raw = f.read()
with open('out.jpg', 'wb') as f:
f.write(remove_exif(raw))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment