Last active
December 23, 2020 09:27
-
-
Save 2minchul/ad3c750caceb8342e18e1deed0d076da to your computer and use it in GitHub Desktop.
remove exif from jpg without PIL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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