def test_no_app1_segment(self): """Verify behavior of an image without an APP1 segment marker. Assert the ``has_exif`` attribute is false. Verify non-EXIF ``dir()`` list contents. Then, check the ``get_file()`` hexadecimal. """ image_path = os.path.join(os.path.dirname(__file__), "no_app1.png") with open(image_path, "rb") as image_file: my_image = Image(image_file) self.assertFalse(my_image.has_exif) self.assertEqual( str(dir(my_image)), Baseline(""" ['_segments', 'delete', 'delete_all', 'get', 'get_all', 'get_file', 'get_thumbnail', 'has_exif', 'list_all'] """), ) with pytest.raises(RuntimeError, match="image does not contain thumbnail"): my_image.get_thumbnail() self.assertEqual( "\n".join(textwrap.wrap(str(my_image.get_file()), 90)), NO_APP1_PNG)
def set_description(filename, description): with open(filename, 'rb') as img: imgobj = Image(img) print(imgobj.list_all()) imgobj.image_description = description print(imgobj.list_all()) with open(filename, 'wb') as writeimg: writeimg.write(imgobj.get_file())
def create_exif(self, img, coords): with open(img, 'rb') as file: file = Exif(file) file.gps_latitude = coords['lat'] file.gps_longitude = coords['lon'] with open(img, 'wb') as new_image_file: new_image_file.write(file.get_file())
def test_get_file(): """Verify that an image is writable to a file after modifying its EXIF metadata. Assert the produced file is equivalent to a known baseline. """ image = Image(os.path.join(os.path.dirname(__file__), "noise.jpg")) image.software = "Python" file_hex = binascii.hexlify(image.get_file()).decode("utf-8") assert "\n".join(textwrap.wrap(file_hex, 90)) == MODIFIED_NOISE_FILE_HEX_BASELINE
def write_new_date(filename: Path, new_date: datetime): logging.info('update file ' + str(filename) + ' with ' + str(new_date)) with open(filename, 'rb') as image_file: image = Image(image_file) new_date_str = new_date.strftime(DATETIME_STR_FORMAT) image.datetime_original = new_date_str image.datetime = new_date_str with open(filename, 'wb') as new_image_file: new_image_file.write(image.get_file())
def main(): # Open JSON file # Parser JSON headers = {"Content-type": "application/x-www-form-urlencoded"} fd = open("memories_history.json") memories = json.load(fd) for x in memories["Saved Media"]: if x["Media Type"] == "PHOTO": url = x["Download Link"] split_url = url.split("?") r = requests.post( split_url[0], headers=headers, data=split_url[1], ) if r.status_code == 200: # 200 means that we got HTTP success aws_resp = requests.get(r.text) aws_url = r.text.split("?") print(aws_url[0]) last_occur = aws_url[0].rfind("/", 0) + 1 print(last_occur) filename = aws_url[0][last_occur:] i = Image.open(BytesIO(aws_resp.content)) i = i.save(filename, quality="keep") time.sleep(1) # Prepare the datetime info date_datetime = datetime.strptime(x["Date"], "%Y-%m-%d %H:%M:%S %Z") with open(filename, "rb") as image_file_thing: image_file = EImage(image_file_thing) image_file.datetime_original = date_datetime.strftime( DATETIME_STR_FORMAT) image_file.datetime_scanned = date_datetime.strftime( DATETIME_STR_FORMAT) image_file.datetime_digitized = date_datetime.strftime( DATETIME_STR_FORMAT) with open("{}_akk.jpg".format(filename), "wb") as new_image_file: new_image_file.write(image_file.get_file()) else: print(r.status_code) exit() fd.close()
def test_no_app1_segment(self): """Verify behavior of an image without an APP1 segment marker. Assert the ``has_exif`` attribute is false. Verify non-EXIF ``dir()`` list contents. Then, check the ``get_file()`` hexadecimal. """ image_path = os.path.join(os.path.dirname(__file__), 'no_app1.png') with open(image_path, 'rb') as image_file: my_image = Image(image_file) self.assertFalse(my_image.has_exif) self.assertEqual(str(dir(my_image)), Baseline(""" ['_segments', 'get', 'get_file', 'has_exif'] """)) self.assertEqual('\n'.join(textwrap.wrap(str(my_image.get_file()), 90)), NO_APP1_PNG)
def clear_img(filename): file_mod = filename.split('.') file_mod = file_mod[0]+_cleaned+'.'+file_mod[1] with open(filename, 'rb') as image_file: my_image = Image(image_file) for element in dir(my_image): try: print(f"{element}: {my_image[element]}") del my_image[element] except: print(f"{element} unknown") with open(file_mod, 'wb') as new_image_file: new_image_file.write(my_image.get_file()) print(f'File {filename} cleared and saved as a {file_mod}')
def change_datetimes(image_path, time_delta): """Take in image file and time change as a time delta object. Change exif datetime, datetime_digitized, and datetime_original Save file with updated exif datetimes.""" # Open image. with open(image_path, 'rb') as image_file: image = Image(image_file) # Generate new datetime. old_datetime = datetime.datetime.strptime(image.datetime, '%Y:%m:%d %H:%M:%S') new_datetime = (old_datetime + time_delta).strftime("%Y:%m:%d %H:%M:%S") # Replace all exif datetimes with new datetime. image.datetime = new_datetime image.datetime_digitized = new_datetime image.datetime_original = new_datetime # Rewrite image file. with open(image_path, 'wb') as new_image_file: new_image_file.write(image.get_file()) print(f"{image_path[7::]} datetime fixed!")
class TestGetFile(unittest.TestCase): """Test cases for modifying EXIF attributes and getting new file contents.""" def setUp(self): """Open sample image file in binary mode for use in test cases.""" noise = os.path.join(os.path.dirname(__file__), 'noise.jpg') with open(noise, 'rb') as image_file: self.image = Image(image_file) assert self.image.has_exif def test_get_file(self): """Verify that an image is writable to a file after modifying its EXIF metadata. Assert the produced file is equivalent to a known baseline. """ self.image.software = "Python" file_hex = binascii.hexlify(self.image.get_file()) if sys.version_info[0] == 3: file_hex = file_hex.decode("utf8") self.assertEqual('\n'.join(textwrap.wrap(file_hex, 90)), MODIFIED_NOISE_FILE_HEX_BASELINE)
def geotag_photo(self, file, lat, lon): exif_lat = (int(lat), int(lat % 1 * 60), (lat % 1 * 60) % 1 * 60) exif_lon = (int(lon), int(lon % 1 * 60), (lon % 1 * 60) % 1 * 60) if int(lat) < 0: exif_lat_ref = 'S' else: exif_lat_ref = 'N' if int(lon) < 0: exif_lon_ref = 'W' else: exif_lon_ref = 'E' with open(file, 'rb') as image_file: my_image = Image(image_file) my_image.make = "Python" my_image.gps_latitude_ref = exif_lat_ref my_image.gps_latitude = exif_lat my_image.gps_longitude_ref = exif_lon_ref my_image.gps_longitude = exif_lon with open(file, 'wb') as new_image_file: new_image_file.write(my_image.get_file())
def process_one_file( in_file: Path, out_file: Path, strategies: List[str], formats: List[str] ): if not in_file.is_file(): return if in_file.name.startswith("."): return if in_file.suffix == ".json": return print(f"{esc(1)}{esc(96)}{in_file.name}{esc(0)} =>\t", end="") date: Optional[datetime] = None location: Optional[CoordinatesDMS] = None image: Optional[Image] = None for strategy in reversed(strategies): strategy = strategy.lower() if strategy == "exif": try: with in_file.open("rb") as fo: image = Image(fo) except Exception: image = None continue e_date, e_location = get_info_from_exif(image) date = e_date or date location = e_location or location elif strategy == "json": j_date, j_location = get_info_from_json(in_file) date = j_date or date location = j_location or location elif strategy == "filename": f_date, f_location = get_info_from_filename(in_file, formats) date = f_date or date location = f_location or location if date is None: print(f"{esc(91)}no date{esc(0)}, ", end="") else: print(f"{esc(92)}{date.isoformat()}{esc(0)}, ", end="") if image is not None: image.datetime = date.strftime(DATETIME_STR_FORMAT) if location is None: print(f"{esc(91)}no location{esc(0)}") else: print(f"{esc(92)}{coords_dms2dec(location)}{esc(0)}") if image is not None: try: ( (image.gps_latitude, image.gps_latitude_ref), (image.gps_longitude, image.gps_longitude_ref), ) = location except TypeError: pass if image is not None: out_file.write_bytes(image.get_file()) else: out_file.write_bytes(in_file.read_bytes()) if date is not None: os.utime(out_file, (date.timestamp(), date.timestamp()))
def strip_exifdates(myfile, date_now, dryrun): """Function to remove known EXIF date data that affects sorting""" if dryrun == False: local_exceptions = {} with open(myfile, "rb") as image_file: try: my_image = Image(image_file) except AssertionError as err: exc_type, value, traceback = sys.exc_info() local_exceptions[myfile] = exc_type logger.exception( f"exif library exception caught with {myfile}", exc_info=True) return try: logger.debug( f"Attempting to modify EXIF datetime for {myfile}") logger.debug(f"Before my_image.datetime: {my_image.datetime}") my_image.datetime = f"{date_now.year}:{date_now.month}:{date_now.day} {date_now.hour}:{date_now.minute}:{date_now.second}" logger.debug(f"After my_image.datetime: {my_image.datetime}") except (AttributeError, KeyError) as err: exc_type, value, traceback = sys.exc_info() local_exceptions[myfile] = exc_type logger.exception( f"Exception caught modifying datetime with {myfile}", exc_info=True) pass try: logger.debug( f"Attempting to modify EXIF datetime_original for {myfile}" ) logger.debug( f"Before my_image.datetime_original: {my_image.datetime_original}" ) my_image.datetime_original = f"{date_now.year}:{date_now.month}:{date_now.day} {date_now.hour}:{date_now.minute}:{date_now.second}" logger.debug( f"After my_image.datetime_original: {my_image.datetime_original}" ) except (AttributeError, KeyError) as err: exc_type, value, traceback = sys.exc_info() local_exceptions[myfile] = exc_type logger.exception( f"Exception caught modifying datetime_original with {myfile}", exc_info=True) pass try: logger.debug( f"Attempting to modify EXIF datetime_digitized for {myfile}" ) logger.debug( f"Before my_image.datetime_digitized: {my_image.datetime_digitized}" ) my_image.datetime_digitized = f"{date_now.year}:{date_now.month}:{date_now.day} {date_now.hour}:{date_now.minute}:{date_now.second}" logger.debug( f"After my_image.datetime_digitized: {my_image.datetime_digitized}" ) except (AttributeError, KeyError) as err: exc_type, value, traceback = sys.exc_info() local_exceptions[myfile] = exc_type logger.exception( f"Exception caught modifying datetime_digitized with {myfile}", exc_info=True) pass try: logger.debug( f"Attempting to modify EXIF gps_datestamp for {myfile}") logger.debug( f"Before my_image.gps_datestamp: {my_image.gps_datestamp}") my_image.gps_datestamp = f"{date_now.year}:{date_now.month}:{date_now.day}" logger.debug( f"After my_image.gps_datestamp: {my_image.gps_datestamp}") except (AttributeError, KeyError) as err: exc_type, value, traceback = sys.exc_info() local_exceptions[myfile] = exc_type logger.exception( f"Exception caught modifying gps_datestamp with {myfile}", exc_info=True) pass with open(myfile, "wb") as new_image_file: new_image_file.write(my_image.get_file()) if dryrun == True: print(f"DRYRUN: Would have removed EXIF date data on {myfile}") logger.debug(f"Iteration exceptions: {local_exceptions}") return local_exceptions
import exif from exif import Image #https://pypi.org/project/exif/ # open image as my_image with open('1.jpg', 'rb') as image_file: my_image = Image(image_file) # show all image attributes print(my_image.list_all()) ### show model attribute from my_image ##print(my_image.model) # set image attributes my_image.focal_length = 99 my_image.model = "iPhone 13" # print for testing print(">>", my_image.focal_length) # save modified image as modified_image.jpg with open('modified_image.jpg', 'wb') as new_image_file: new_image_file.write(my_image.get_file())
def processFile(args): argob, path = args path = Path(path) file_info = { "creation": None, "file_prefix": "", "file_suffix": "", "file_content_type": "", "old_conflict": "", "dtpattern_in_path": False, "conflict_count": 0, "cleaned_file_name": "" } creation = None image_exif = None image_exif_date_error = False lower_suffix = path.suffix.lower() if lower_suffix in img_suffixes: file_info["file_content_type"] = "IMG" try: with path.open(mode='rb') as f: image_exif = Image(f) except Exception as exc: logger.debug("Exception while reading exif: %s" % exc) if not image_exif or not image_exif.has_exif: logger.debug("image has no exif data/is not compatible: %s", path) image_exif = None elif hasattr(image_exif, "datetime"): try: creation = \ dt.strptime(image_exif.datetime, "%Y:%m:%d %H:%M:%S") except ValueError: logger.warning("Invalid format: %s", image_exif.datetime) image_exif_date_error = True elif hasattr(image_exif, "datetime_original"): try: creation = \ dt.strptime( image_exif.datetime_original, "%Y:%m:%d %H:%M:%S" ) except ValueError: logger.warning("Invalid format: %s", image_exif.datetime_original) image_exif_date_error = True elif lower_suffix in mov_suffixes: file_info["file_content_type"] = "MOV" else: if not argob.prune: logger.info("unrecognized file: %s", path) elif argob.dry_run: logger.info("Would remove: %s (unrecognized)", path) else: path.unlink() return 1 if argob.conflict == "ignore": file_info["cleaned_file_name"] = path.stem else: # find conflict counter and hash conflictmatch = extract_conflict_pattern.match(path.stem).groupdict() file_info["cleaned_file_name"] = conflictmatch["unrelated"] if conflictmatch["conflict_hash"]: if argob.conflict == "hash": # if hash is found, one conflict is indicated file_info["conflict_count"] = 1 else: file_info["cleaned_file_name"] = "%s-%s" % ( file_info["cleaned_file_name"], conflictmatch["conflict_hash"]) # if hash and counter: conflict_count = counter + 1 # if counter: conflict_count = counter # if hash: conflict_count = 1 file_info["conflict_count"] += \ int(conflictmatch["conflict_counter"] or 0) # check and potential extract datetime info from filename dtnamematch = extraction_pattern.match(file_info["cleaned_file_name"]) if dtnamematch: dtnamematchg = dtnamematch.groupdict() file_info["file_prefix"] = dtnamematchg["prefix"] or "" file_info["file_suffix"] = dtnamematchg["suffix"] or "" file_info["dtpattern_in_path"] = True if not creation: logger.debug("extract time from path: %s", path) creation = dt(year=int(dtnamematchg["year"]), month=int(dtnamematchg["month"]), day=int(dtnamematchg["day"]), hour=int(dtnamematchg["hour"] or 0), minute=int(dtnamematchg["minute"] or 0), second=int(dtnamematchg["second"] or 0)) # still no creation time if not creation: logger.debug("extract time from st_ctime: %s", path) creation = dt.fromtimestamp(path.stat().st_ctime) file_info["creation"] = creation if image_exif_date_error: file_info["old_file_hash"] = generate_hash(path) if argob.dry_run: logger.info("Would fix: %s to %s, of %s", image_exif.datetime, creation.strftime("%Y:%m:%d %H:%M:%S"), path) else: image_exif.datetime = creation.strftime("%Y:%m:%d %H:%M:%S") image_exif.datetime_original = image_exif.datetime with path.open(mode='wb') as f: f.write(image_exif.get_file()) file_info["file_hash"] = generate_hash(path) if rename_file(argob, path, file_info): return 1 return 0
class TestModifyExif(unittest.TestCase): """Test cases for deleting EXIF attributes.""" def setUp(self): """Open sample image file in binary mode for use in test cases.""" grand_canyon = os.path.join(os.path.dirname(__file__), 'grand_canyon.jpg') with open(grand_canyon, 'rb') as image_file: self.image = Image(image_file) assert self.image.has_exif def test_delete_all_tags(self): """Verify deleting all EXIF tags from the Image object.""" self.image.delete_all() segment_hex = self.image._segments['APP1'].get_segment_hex() self.assertEqual('\n'.join(textwrap.wrap(segment_hex, 90)), DELETE_ALL_HEX_BASELINE) with TemporaryFile("w+b") as temporary_file_stream: temporary_file_stream.write(self.image.get_file()) temporary_file_stream.seek(0) reloaded_image = Image(temporary_file_stream) dunder_dir_text = '\n'.join(textwrap.wrap(repr(sorted(dir(reloaded_image))), 90)) self.assertEqual(dunder_dir_text, Baseline(""" ['<unknown EXIF tag 59932>', '_segments', 'delete', 'delete_all', 'get', 'get_file', 'get_thumbnail', 'has_exif', 'resolution_unit', 'x_resolution', 'y_resolution'] """)) def test_delete_ascii_tags(self): """Verify deleting EXIF ASCII from the Image object and the hexadecimal equivalent.""" del self.image.make del self.image.model with self.assertRaisesRegex(AttributeError, "image does not have attribute make"): self.image.make with self.assertRaisesRegex(AttributeError, "image does not have attribute model"): self.image.model segment_hex = self.image._segments['APP1'].get_segment_hex() self.assertEqual('\n'.join(textwrap.wrap(segment_hex, 90)), DELETE_ASCII_TAGS_HEX_BASELINE) def test_delete_gps_tags(self): """Verify deleting EXIF geotags from the Image object and the hexadecimal equivalent.""" del self.image.gps_latitude del self.image.gps_longitude del self.image.gps_altitude with self.assertRaisesRegex(AttributeError, "image does not have attribute gps_latitude"): self.image.gps_latitude with self.assertRaisesRegex(AttributeError, "image does not have attribute gps_longitude"): self.image.gps_longitude with self.assertRaisesRegex(AttributeError, "image does not have attribute gps_altitude"): self.image.gps_altitude segment_hex = self.image._segments['APP1'].get_segment_hex() self.assertEqual('\n'.join(textwrap.wrap(segment_hex, 90)), DELETE_GEOTAG_HEX_BASELINE) def test_delete_method(self): """Test behavior when setting tags using the ``delete()`` method.""" self.image.delete("model") with self.assertRaisesRegex(AttributeError, "image does not have attribute model"): self.image.model def test_handle_unset_attribute(self): """Verify that accessing an attribute not present in an image raises an AttributeError.""" with self.assertRaisesRegex(AttributeError, "image does not have attribute light_source"): del self.image.light_source def test_index_deleter(self): """Test deleting attributes using index syntax.""" del self.image["model"] with self.assertRaisesRegex(AttributeError, "image does not have attribute model"): self.image.model def test_standard_delete(self): """Verify that writing and deleting non-EXIF attributes behave normally.""" self.image.dummy_attr = 123 assert self.image.dummy_attr == 123 del self.image.dummy_attr with self.assertRaisesRegex(AttributeError, "unknown image attribute dummy_attr"): self.image.dummy_attr
17: (1017664, 4813), 23: 'T', 24: (1017664, 4813), 29: '2019:01:11', 31: (65, 1) } with open(im_path, 'rb') as image_file: im = Image(image_file) im.make = data['make'] im.model = data['model'] im.camera = data['camera'] im.GPSInfo = GPSInfo with open(new_im_name, 'wb') as new_image_file: new_image_file.write(im.get_file()) with open(new_im_name, 'rb') as image_file: imtest = Image(image_file) os.system('rm ' + im_path) os.system('cp ' + exif_list[0] + ' ' + exif_list[0] + '1') os.system('mv ' + exif_list[0] + '1' + ' ' + exif_folder + '/' + im_name + 'exif.jpg.exif')
print('USAGE: exifGPSimplant filename latitude [0-360) longitude [0 - 180)') exit(-1) else: (recipient, latitude, longitude) = names[1:4] with open(recipient, 'rb') as image_file: img = Image(image_file) img.gps_latitude = dd_GPS_dms(latitude) img.gps_longitude = dd_GPS_dms(longitude) #img.gps_altitude = 1200 # An orphan... print(img.gps_latitude, img.gps_longitude) with open(recipient, 'wb') as image_file: image_file.write(img.get_file()) ## Note the GPS tags format # 34°56'43.386"N 109°46'32.447"W ## Other locations... # https://www.gps-coordinates.net/gps-coordinates-converter # Whitewater, CA: 33.923685, -116.640324 # Yosemite Valley: 37° 43′ 18″ N, 119° 38′ 47″ W # Mocassin (Tuolumne Count, CA): 37° 48′ 39″ N, 120° 18′ 0″ W # Hollywood Sign Puzzle View: 34°06'18.3"N 118°19'52.0"W # Hoover Dam: 36° 0′ 56″ N, 114° 44′ 16″ W # Rainbow Canyon: 36° 21′ 56.88″ N, 117° 30′ 5.4″ W # Route 66 (AZ): 35°14'15.5"N 113°12'22.6"W # Las Vegas' Replica of the Statue of Liberty 36°6'3.58"N 115°10'23.029"W # The Tepees in Petrified Forest 34°56'43.386"N 109°46'32.447"W