def test_transplant_m(self): """'transplant' on memory. """ o = io.BytesIO() piexif.transplant(I1, I2, o) self.assertEqual(piexif.load(I1), piexif.load(o.getvalue())) Image.open(o).close()
def test_piexif( input_file_name, output_file_name, args ): """ Tests Exif manipulation using Piexif performing JPEG I/O with buffered reads and writes. Takes 3 arguments: input_file_name - Path of input JPEG file. output_file_name - Path of output JPEG file. args - Dictionary of arguments provided to the test. This is unused. Returns nothing. """ import piexif jpeg_data = open( input_file_name, "rb" ).read() o = io.BytesIO() # use piexif to extract our Exif data and insert it back into the raw JPEG # byte stream. exif_dict = piexif.load( input_file_name ) exif_dict['GPS'][2] = ((1, 1), (2, 1), (3, 1)) exif_dict['GPS'][4] = ((4, 1), (5, 1), (6, 1)) exif_bytes = piexif.dump( exif_dict ) piexif.insert( exif_bytes, jpeg_data, o ) with open( output_file_name, "wb" ) as file: file.write( o.getvalue() ) munged_jpeg_data = open( output_file_name, "rb" ).read() munged_exif_dict = piexif.load( output_file_name ) munged_exif_bytes = piexif.dump( munged_exif_dict )
def test_load_fail(self): with self.assertRaises(ValueError): exif = piexif.load(os.path.join("tests", "images", "note.txt")) with self.assertRaises(ValueError): exif = piexif.load(os.path.join("tests", "images", "notjpeg.jpg")) with self.assertRaises(ValueError): exif = piexif.load(os.path.join("Oh", "My", "God"))
def test_load_tif(self): exif = piexif.load(INPUT_FILE_TIF) zeroth_ifd = exif["0th"] exif_bytes = piexif.dump({"0th":zeroth_ifd}) im = Image.new("RGBA", (8, 8)) o = io.BytesIO() im.save(o, format="jpeg", exif=exif_bytes) im.close() exif2 = piexif.load(o.getvalue()) zeroth_ifd2 = exif2["0th"] self.assertDictEqual(zeroth_ifd, zeroth_ifd2)
def test_transplant(self): piexif.transplant(INPUT_FILE1, INPUT_FILE_PEN, "transplant.jpg") i = Image.open("transplant.jpg") i.close() exif_src = piexif.load(INPUT_FILE1) img_src = piexif.load(INPUT_FILE_PEN) generated = piexif.load("transplant.jpg") self.assertEqual(exif_src, generated) self.assertNotEqual(img_src, generated) piexif.transplant(INPUT_FILE1, "transplant.jpg") self.assertEqual(piexif.load(INPUT_FILE1), piexif.load("transplant.jpg")) os.remove("transplant.jpg")
def test_remove(self): piexif.remove(INPUT_FILE1, "remove.jpg") exif_dict = piexif.load("remove.jpg") none_dict = {"0th":{}, "Exif":{}, "GPS":{}, "Interop":{}, "1st":{}, "thumbnail":None} self.assertEqual(exif_dict, none_dict) piexif.remove("remove.jpg") exif_dict = piexif.load("remove.jpg") self.assertEqual(exif_dict, none_dict) os.remove("remove.jpg")
def imread(filepath): image = Image.open(filepath) try: exf = piexif.load(image.info['exif']) except: exf = None return image, exf
def get_exif_datetimeorig_tag(fname): """ Returns the EXIF Tag 36867 (DateTimeOriginal) from a file The return string will have the format 'YYYY:MM:DD HH:MM:SS' or if no EXIF tag is found or the file is not valid or doesn't exist, an exception will be thrown :param fname: Name of file to read EXIF tag from :returns: EXIF tag in specified format :Example: >>> import fileops >>> print fileops.get_exif_datetimeorig_tag("IMG_1234.JPG") '2013:09:30 15:21:42' """ try: exif_dict = piexif.load(fname) except ValueError: raise EXIFTagError, "Not a valid picture file" else: if exif_dict['Exif']: try: date_string = exif_dict['Exif'][36867] except KeyError: raise EXIFTagError, "No DateTimeOriginal EXIF Tag found" else: return date_string else: raise EXIFTagError, "No EXIF information found"
def manipulate_exif(img, settings): try: exif = piexif.load(img.info['exif']) except Exception: logger.debug('EXIF information not found') exif = {} if settings['PHOTO_EXIF_AUTOROTATE']: img, exif = rotate_image(img, exif) if settings['PHOTO_EXIF_REMOVE_GPS']: exif.pop('GPS') if settings['PHOTO_EXIF_COPYRIGHT']: # We want to be minimally destructive to any preset exif author or copyright information. # If there is copyright or author information prefer that over everything else. if not exif['0th'].get(piexif.ImageIFD.Artist): exif['0th'][piexif.ImageIFD.Artist] = settings['PHOTO_EXIF_COPYRIGHT_AUTHOR'] author = settings['PHOTO_EXIF_COPYRIGHT_AUTHOR'] if not exif['0th'].get(piexif.ImageIFD.Copyright): license = build_license(settings['PHOTO_EXIF_COPYRIGHT'], author) exif['0th'][piexif.ImageIFD.Copyright] = license return (img, piexif.dump(exif))
def _get_exif_orientation(exif): """ Return the orientation value for the given Image object, or None if the value is not set. """ exif_dict = piexif.load(exif) return exif_dict['0th'].get(piexif.ImageIFD.Orientation)
def setgeoimage(data,lat,lon,imagedescription="",usercomment="",dt=None): exif_dict = piexif.load(data) #for ifd in ("0th", "Exif", "GPS", "1st"): # for tag in exif_dict[ifd]: # print tag # print(piexif.TAGS[ifd][tag]["name"], exif_dict[ifd][tag]) # #lat,lon=get_geo(exif_dict) #print "lat lon",lat,lon if dt is None: exif_dict["0th"][piexif.ImageIFD.DateTime]=datetime.utcnow().strftime("%Y:%m:%d %H:%M:%S") else: exif_dict["0th"][piexif.ImageIFD.DateTime]=dt.strftime("%Y:%m:%d %H:%M:%S") set_geo(exif_dict, lat, lon) exif_dict["Exif"][piexif.ExifIFD.UserComment]=chr(0x55)+chr(0x4E)+chr(0x49)+chr(0x43)+chr(0x4F)+chr(0x44)+chr(0x45)+chr(0x00)+usercomment exif_dict["0th"][piexif.ImageIFD.ImageDescription]=imagedescription exif_bytes = piexif.dump(exif_dict) new_data=io.BytesIO(data) piexif.insert(exif_bytes, data, new_data) return new_data.getvalue()
def normalize_image_orientation(img_file): """Reads orientation tag from the exif data attached to a JPEG image. Returns the image rotated to normal orientation and with corresponding normal exif orientation tag. Note: horizontal (normal) exif value = 1 """ ### Open image and read orientation tag im = Image.open(img_file) exif_dict = piexif.load(im.info["exif"]) orientation = exif_dict["0th"][piexif.ImageIFD.Orientation] print(orientation) ## Rotate image im = rotate_image_to_normal(im, orientation) ## Create new exif dict with base orientation value of 1 exif_dict["0th"][piexif.ImageIFD.Orientation] = 1 exif_bytes = piexif.dump(exif_dict) ## Return file in a buffer buf = BytesIO() im.save(buf, "jpeg", exif=exif_bytes) buf.seek(0) return(buf)
def test_piexif_with_pil( input_file_name, output_file_name, args ): """ Tests Exif manipulation using Piexif performing JPEG I/O with PIL. Note that PIL performs image decompression and recompression which will likely alter the contents of image. Takes 3 arguments: input_file_name - Path of input JPEG file. output_file_name - Path of output JPEG file. args - Dictionary of arguments provided to the test. This may contain a "quality" key that specifies a quality parameter suitable for use when saving a JPEG with PIL. Returns nothing. """ import piexif from PIL import Image # use piexif to extract our Exif data. exif_dict = piexif.load( input_file_name ) exif_dict['GPS'][2] = ((1, 1), (2, 1), (3, 1)) exif_dict['GPS'][4] = ((4, 1), (5, 1), (6, 1)) exif_bytes = piexif.dump( exif_dict ) # use PIL to specify raw Exif data when saving. # # NOTE: this will reencode the file that has been opened which is likely # not what a user wants. im = Image.open( input_file_name ) im.save( output_file_name, exif=exif_bytes, **args )
def get_exif_tags(image_data): try: def decode_tag(tag): if type(tag) == bytes: tag = tag.rstrip(b'\x00') try: return tag.decode('utf-8') except UnicodeDecodeError: return tag.decode('ascii', 'ignore') else: return tag exif_dict = piexif.load(image_data) return { piexif.TAGS[ifd][tag]["name"]: decode_tag(exif_dict[ifd][tag]) for ifd in ["0th", "1st", "Exif", "GPS"] for tag in exif_dict[ifd] if tag != piexif.ExifIFD.MakerNote # Exclude proprietary MakerNote tag } except Exception: # Don't let any exceptions bubble (ignore exif data silently), but log a warning logger.warning( "Kunne ikke parse exif data", exc_info=sys.exc_info(), )
def create_duplicateOLD(source, target, batch, frame): if config['overlay-text'] or config['resize']: img = Image.open(source) exif_bytes = None if config['resize']: img = img.resize( (config['resize_width'], config['resize_height']), Image.ANTIALIAS) if config['write_exif']: exif_dict = piexif.load(img.info['exif']) # from pprint import pprint # pprint(exif_dict) exif_dict["0th"][piexif.ImageIFD.Orientation] = 4 exif_bytes = piexif.dump(exif_dict) if config['overlay-text']: # text overlay if config['image_format'] == "XXXX_XX": text = "%04d-%02d" % (batch, frame) else: text = "%04d" % (batch) draw = ImageDraw.Draw(img) draw.text( (60, 60), text, (255, 14, 179), font=config['font'] ) if config['write_exif']: img.save(target, quality=config['image-quality'], exif=exif_bytes) else: img.save(target, quality=config['image-quality']) else: shutil.copy2(source, target) time.sleep(config['delay_between_frames'])
def add_gps_tags(path: str, gps_tags: {str: any}): """This method will add gps tags to the photo found at path""" exif_dict = piexif.load(path) for tag, tag_value in gps_tags.items(): exif_dict["GPS"][tag] = tag_value exif_bytes = piexif.dump(exif_dict) piexif.insert(exif_bytes, path)
def test_load_m(self): """'load' on memory. """ exif = piexif.load(I1) e = load_exif_by_PIL(INPUT_FILE1) print("********************\n\n" + INPUT_FILE1 + "\n") self._compare_piexifDict_PILDict(exif, e)
def _rotate_exif_image(self, im): if VERSION_AUTOROTATE_EXIF and 'exif' in im.info: import piexif x, y = im.size try: e_dict = piexif.load(im.info['exif']) if piexif.ImageIFD.Orientation in e_dict['0th']: orientation = e_dict['0th'].pop(piexif.ImageIFD.Orientation) if orientation == 2: im = im.transpose(Image.FLIP_LEFT_RIGHT) elif orientation == 3: im = im.rotate(180) elif orientation == 4: im = im.rotate(180).transpose(Image.FLIP_LEFT_RIGHT) elif orientation == 5: im = im.rotate(-90, expand=1).transpose(Image.FLIP_LEFT_RIGHT) im.size = (y, x) elif orientation == 6: im = im.rotate(-90, expand=1) im.size = (y, x) elif orientation == 7: im = im.rotate(90, expand=1).transpose(Image.FLIP_LEFT_RIGHT) im.size = (y, x) elif orientation == 8: im = im.rotate(90, expand=1) im.size = (y, x) except: pass return im
def test_load(self): files = glob.glob(os.path.join("tests", "images", "r_*.jpg")) for input_file in files: exif = piexif.load(input_file) e = load_exif_by_PIL(input_file) print("********************\n" + input_file + "\n") self._compare_piexifDict_PILDict(exif, e, p=False)
def test_dump_and_load2(self): thumbnail_io = io.BytesIO() thumb = Image.open(INPUT_FILE2) thumb.thumbnail((40, 40)) thumb.save(thumbnail_io, "JPEG") thumb.close() thumb_data = thumbnail_io.getvalue() exif_dict = {"0th":ZEROTH_IFD, "Exif":EXIF_IFD, "GPS":GPS_IFD, "Interop":INTEROP_IFD, "1st":FIRST_IFD, "thumbnail":thumb_data} exif_bytes = piexif.dump(exif_dict) im = Image.new("RGBA", (80, 80)) o = io.BytesIO() im.save(o, format="jpeg", exif=exif_bytes) im.close() o.seek(0) exif = piexif.load(o.getvalue()) exif["0th"].pop(ImageIFD.ExifTag) # pointer to exif IFD exif["0th"].pop(ImageIFD.GPSTag) # pointer to GPS IFD exif["Exif"].pop(ExifIFD.InteroperabilityTag) self.assertDictEqual(ZEROTH_IFD, exif["0th"]) self.assertDictEqual(EXIF_IFD, exif["Exif"]) self.assertDictEqual(GPS_IFD, exif["GPS"]) self.assertDictEqual(INTEROP_IFD, exif["Interop"]) exif["1st"].pop(513) # pointer to exif IFD exif["1st"].pop(514) # pointer to GPS IFD self.assertDictEqual(FIRST_IFD, exif["1st"]) Image.open(io.BytesIO(exif["thumbnail"])).close()
def __init__(self, location: Path): self.location = location metadata_store = MetadataFile(self.location.with_name('_metadata.yaml')) self.metadata = metadata_store.get_section(self.location.stem) info = piexif.load(str(self.location)) self.exif = info['Exif'] self.zeroth = info['0th']
def test_rotation(self): tu = TestUtils() assert tu.is_env_clean(tu.conf['lycheepath']), "env not clean" # load 1 album with same photo under different name tu.load_photoset("rotation") # launch lycheesync src = tu.conf['testphotopath'] lych = tu.conf['lycheepath'] conf = tu.conf['conf'] # run runner = CliRunner() result = runner.invoke(main, [src, lych, conf, '-v']) # no crash assert result.exit_code == 0, "process result is ok" photos = tu.get_photos(tu.get_album_id('rotation')) for p in photos: # rotation tag is gone pfullpath = os.path.join(lych, "uploads", "big", p['url']) img = Image.open(pfullpath) assert "exif" in img.info, "Pas d'info exif" exif_dict = piexif.load(img.info["exif"]) assert exif_dict["0th"][piexif.ImageIFD.Orientation] == 1, "Exif rotation should be 1" img.close()
def run(filepath): try: exifdata = piexif.load(filepath) except piexif.InvalidImageDataError: return {} # How to present? # exifdata['GPS'][piexif.GPSIFD.GPSLatitudeRef], # N # exifdata['GPS'][piexif.GPSIFD.GPSLatitude], # ((59, 1), (50, 1), (1111, 100)) # exifdata['GPS'][piexif.GPSIFD.GPSLongitudeRef], # E # exifdata['GPS'][piexif.GPSIFD.GPSLongitude], # (10, 1), (50, 1), (3000, 100)) gpslatlon = None return { 'ImageLength': exifdata['0th'].get(piexif.ImageIFD.ImageLength, b''), 'ImageWidth': exifdata['0th'].get(piexif.ImageIFD.ImageWidth, b''), 'Make': exifdata['0th'].get(piexif.ImageIFD.Make, b'').decode('utf-8'), 'Model': exifdata['0th'].get(piexif.ImageIFD.Model, b'').decode('utf-8'), 'Orientation': exifdata['0th'].get(piexif.ImageIFD.Orientation, b''), 'Flash': exifdata['Exif'].get(piexif.ExifIFD.Flash, b''), 'GPSAltitudeRef': exifdata['GPS'].get(piexif.GPSIFD.GPSAltitudeRef, b''), 'GPSLatLon': gpslatlon, }
def hide(input_image_file, img_enc, secret_message = None, secret_file = None): """ Hide a message (string) in an image. """ from zlib import compress from base64 import b64encode if secret_file != None: with open(secret_file, "r") as f: secret_message = f.read() try: text = compress(b64encode(bytes(secret_message, "utf-8"))) except: text = compress(b64encode(secret_message)) img = Image.open(input_image_file) if "exif" in img.info: exif_dict = piexif.load(img.info["exif"]) else: exif_dict = {} exif_dict["0th"] = {} exif_dict["0th"][piexif.ImageIFD.ImageDescription] = text exif_bytes = piexif.dump(exif_dict) img.save(img_enc, exif=exif_bytes) img.close() return img
def get_data(self): """ Get picture exif'GPS information :return: """ file_dialog = QtGui.QFileDialog() file_name = file_dialog.getOpenFileName() # self.line_edit set text self.line_edit.setText(str(file_name)) # load picture and get exif data as dict exif_data = piexif.load(str(file_name)) if exif_data['GPS']: print exif_data['GPS'] for key, value in exif_data['GPS'].items(): print key, value try: self.latitude = self.d2l(exif_data['GPS'].get(7)) self.longitude = self.d2l(exif_data['GPS'].get(7)) print 'latitude: %s' % self.latitude print 'longitude: %s' % self.longitude except: QtGui.QMessageBox.information(self, 'Message', 'Get latitude and longitude error!') else: QtGui.QMessageBox.information(self, 'Message', 'This picture has not GPS information!')
def download_image_with_retry(image, bucket, tmp_file, tmp_file_index, zip_archive, file_count, tries=5): """ Tries to download an image from S3, and if an SSLError occurs, resets the boto connection and retries the download. This was implemented because we experienced this error occasionally for large album downloads. The idea was originally to use funcy[1] with the @retry decorator, but we discovered that we need to change state on error (i.e. reset the boto connection), which AFAIK couldn't be done with funcy; hence this custom implementation. [1] See https://github.com/Suor/funcy and http://hackflow.com/blog/2014/06/22/why-every-language-needs-its-underscore/ """ try: image_key = bucket.get_key("%s/%s.%s" % (settings.AWS_S3_FOLDERS['imagearchive'], image.key, image.extension)) image_data = image_key.get_contents_as_string() pil_image = PIL.Image.open(BytesIO(image_data)) image_data_buffer = BytesIO() # Write relevant exif data try: exif = piexif.load(image_data) exif['0th'][piexif.ImageIFD.ImageDescription] = image.description exif['0th'][piexif.ImageIFD.Artist] = image.photographer exif['0th'][piexif.ImageIFD.Copyright] = image.licence pil_image.save(image_data_buffer, standardize_extension(image.extension), exif=piexif.dump(exif)) except ValueError: # piexiv will throw ValueError if the file type isn't JPEG or the file contains invalid exif data; if so # skip writing exif pil_image.save(image_data_buffer, standardize_extension(image.extension)) # And add the modified image to the zip archive if image.photographer == '': image_filename = '%s-%s.%s' % (image_set_name, file_count, image.extension) else: image_filename = '%s-%s-%s.%s' % (image_set_name, file_count, image.photographer, image.extension) # Rather than encoding for a specific platform and yielding encoding errors on other platforms, use the # ascii encoder to filter out non-ascii chars image_filename = image_filename.encode('ascii', 'ignore').decode() zip_archive.writestr(image_filename, image_data_buffer.getvalue()) # Rewind the memory file back, read the written data, and yield it to our response, # while we'll go fetch the next file from S3 next_index = tmp_file.tell() tmp_file.seek(tmp_file_index) return next_index, tmp_file.read() except Exception: logger.warning( "Feil ved albumnedlasting (prøver igjen automatisk)", exc_info=sys.exc_info(), extra={'request': request} ) if tries <= 0: raise # Reset the conncetion and try again conn = boto.connect_s3(settings.AWS_ACCESS_KEY_ID, settings.AWS_SECRET_ACCESS_KEY) bucket = conn.get_bucket(settings.AWS_S3_BUCKET) return download_image_with_retry(image, bucket, tmp_file, tmp_file_index, zip_archive, file_count, tries=tries-1)
def resize_image(self, src, dst, max_size, bigger_panoramas=True, preserve_exif_data=False, exif_whitelist={}): """Make a copy of the image in the requested size.""" if not Image or os.path.splitext(src)[1] in ['.svg', '.svgz']: self.resize_svg(src, dst, max_size, bigger_panoramas) return im = Image.open(src) if hasattr(im, 'n_frames') and im.n_frames > 1: # Animated gif, leave as-is utils.copy_file(src, dst) return size = w, h = im.size if w > max_size or h > max_size: size = max_size, max_size # Panoramas get larger thumbnails because they look *awful* if bigger_panoramas and w > 2 * h: size = min(w, max_size * 4), min(w, max_size * 4) try: exif = piexif.load(im.info["exif"]) except KeyError: exif = None # Inside this if, we can manipulate exif as much as # we want/need and it will be preserved if required if exif is not None: # Rotate according to EXIF value = exif['0th'].get(piexif.ImageIFD.Orientation, 1) if value in (3, 4): im = im.transpose(Image.ROTATE_180) elif value in (5, 6): im = im.transpose(Image.ROTATE_270) elif value in (7, 8): im = im.transpose(Image.ROTATE_90) if value in (2, 4, 5, 7): im = im.transpose(Image.FLIP_LEFT_RIGHT) exif['0th'][piexif.ImageIFD.Orientation] = 1 try: im.thumbnail(size, Image.ANTIALIAS) if exif is not None and preserve_exif_data: # Put right size in EXIF data w, h = im.size if '0th' in exif: exif["0th"][piexif.ImageIFD.ImageWidth] = w exif["0th"][piexif.ImageIFD.ImageLength] = h if 'Exif' in exif: exif["Exif"][piexif.ExifIFD.PixelXDimension] = w exif["Exif"][piexif.ExifIFD.PixelYDimension] = h # Filter EXIF data as required exif = self.filter_exif(exif, exif_whitelist) im.save(dst, exif=piexif.dump(exif)) else: im.save(dst) except Exception as e: self.logger.warn("Can't process {0}, using original " "image! ({1})".format(src, e)) utils.copy_file(src, dst)
def _update_exif_orientation(exif, orientation): """ Given an exif value and an integer value 1-8, reflecting a valid value for the exif orientation, return a new exif with the orientation set. """ exif_dict = piexif.load(exif) if orientation: exif_dict['0th'][piexif.ImageIFD.Orientation] = orientation return piexif.dump(exif_dict)
def test_no_exif_load(self): exif_dict = piexif.load(NOEXIF_FILE) none_dict = {"0th":{}, "Exif":{}, "GPS":{}, "Interop":{}, "1st":{}, "thumbnail":None} self.assertEqual(exif_dict, none_dict)
import sys import json import csv from pathlib import Path import piexif try: csv_path = sys.argv[-2] root = sys.argv[-1] except Exception: print('Usage: python addmask.py <csv> <root>') exit() labels = {} with open(csv_path) as fp: for row in csv.DictReader(fp): label = [int(v) for v in row[' EncodedPixels'].split(' ') if v != ''] labels.setdefault(row['ImageId'], []).append(label) for p in Path(root).glob('*.jpg'): if p.stem in labels: exif_dict = piexif.load(str(p)) exif = json.loads(exif_dict["Exif"][piexif.ExifIFD.MakerNote].decode("ascii")) exif["Masks"] = labels[p.stem] exif_dict["Exif"][piexif.ExifIFD.MakerNote] = json.dumps(exif).encode("ascii") piexif.insert(piexif.dump(exif_dict), str(p))
def extract_exif_bytes(pil_original_image): try: exif_dict = piexif.load(pil_original_image.info["exif"]) return piexif.dump(exif_dict) except KeyError: return None
def set_file_mod_time_from_jpeg_time(file): exif_dict = piexif.load(file) dt = datetime.strptime(exif_dict["Exif"][piexif.ExifIFD.DateTimeOriginal], "%Y:%m:%d %H:%M:%S") timestamp = (dt - datetime(1970, 1, 1)).total_seconds() os.utime(file, (timestamp, timestamp))
def optimize_jpg(t: Task) -> TaskResult: """ Try to reduce file size of a JPG image. Expects a Task object containing all the parameters for the image processing. If file reduction is successful, this function will replace the original file with the optimized version and return some report data (file path, image format, image color mode, original file size, resulting file size, and resulting status of the optimization. :param t: A Task object containing all the parameters for the image processing. :return: A TaskResult object containing information for single file report. """ img = Image.open(t.src_path) orig_format = img.format orig_mode = img.mode folder, filename = os.path.split(t.src_path) temp_file_path = os.path.join(folder + "/~temp~" + filename) orig_size = os.path.getsize(t.src_path) orig_colors, final_colors = 0, 0 result_format = "JPEG" try: had_exif = True if piexif.load(t.src_path)['Exif'] else False except piexif.InvalidImageDataError: # Not a supported format had_exif = False except ValueError: # No exif info had_exif = False if t.max_w or t.max_h: img, was_downsized = downsize_img(img, t.max_w, t.max_h) else: was_downsized = False if t.grayscale: img = make_grayscale(img) # only use progressive if file size is bigger use_progressive_jpg = orig_size > 10000 if t.fast_mode: quality = t.quality else: quality, jpgdiff = jpeg_dynamic_quality(img) try: img.save(temp_file_path, quality=quality, optimize=True, progressive=use_progressive_jpg, format=result_format) except IOError: ImageFile.MAXBLOCK = img.size[0] * img.size[1] img.save(temp_file_path, quality=quality, optimize=True, progressive=use_progressive_jpg, format=result_format) if t.keep_exif and had_exif: try: piexif.transplant(os.path.expanduser(t.src_path), temp_file_path) has_exif = True except ValueError: has_exif = False else: has_exif = False # Only replace the original file if compression did save any space final_size = os.path.getsize(temp_file_path) if t.no_size_comparison or (orig_size - final_size > 0): shutil.move(temp_file_path, os.path.expanduser(t.src_path)) was_optimized = True else: final_size = orig_size was_optimized = False try: os.remove(temp_file_path) except OSError as e: print(f"\nError while removing temporary file.\n{e}\n") return TaskResult(t.src_path, orig_format, result_format, orig_mode, img.mode, orig_colors, final_colors, orig_size, final_size, was_optimized, was_downsized, had_exif, has_exif)
except ImportError: bEXIFOptionEnabled = False from PIL import Image, ImageDraw, ImageFont # Loop through jpg files in current folder sCurrentPath = os.getcwd() for file in os.listdir(sCurrentPath): if file.endswith(".jpg"): sCurrentFileName = os.path.join(sCurrentPath, file) print "- File " + sCurrentFileName # get EXIF from file if not bEXIFOptionEnabled: print " EXIF not enabled -- Cannot rename file " + sCurrentFileName else: try: exif_dict = piexif.load(sCurrentFileName) sNewFileName = exif_dict['0th'][315] except: sNewFileName = "" if sNewFileName == "": print " ERROR - Cannot rename file " + sCurrentFileName + " ... can't retrieve EXIF" else: sNewFileName = exif_dict['0th'][315] # Remove Name and parenthesis sNewFileName = sNewFileName[sNewFileName.find("(") + 1:] sNewFileName = sNewFileName[:len(sNewFileName) - 1] sNewFileName = os.path.join(sCurrentPath, sNewFileName.strip()) # rename file try: os.rename(sCurrentFileName, sNewFileName) print " SUCCESS - File " + sCurrentFileName + " --> " + sNewFileName
if os.path.exists(NEWPATH): shutil.rmtree(NEWPATH) shutil.copytree(PATH, NEWPATH) logger.info(f'Copy from {PATH} to {NEWPATH}') # In[9]: temp = [] for f in os.listdir(NEWPATH): if not f.endswith('.jpg'): os.remove(os.path.join(NEWPATH, f)) logger.info(f'Remove {f}') else: img = Image.open(os.path.join(NEWPATH, f)) exif_dict = piexif.load(os.path.join(NEWPATH, f)) if 274 not in exif_dict['0th']: logger.info(f'No image metadata: {f}') elif exif_dict['0th'][274] == 3: exif_dict['0th'][274] = 1 exif_bytes = piexif.dump(exif_dict) PIL.ImageOps.mirror(PIL.ImageOps.flip(img)).save( os.path.join(NEWPATH, f), 'jpeg') piexif.insert(exif_bytes, os.path.join(NEWPATH, f)) logger.warning(f'Flipped: {f}') elif exif_dict['0th'][274] != 1: logger.warning(f'Check orientation: {f}') logger.info('Done with preprocessing') # In[ ]:
#------------------------------------- r = results[0] myData = [list(r.keys())] #make csv template with timestamps for i in results: srtlong = i['longitude'] srtlat = i['latitude'] correctimglist = [] for j in imagelist: file_path = ('images/' + j) data = piexif.load(file_path) for key in ['Exif', '0th', '1st', 'GPS', 'Interop']: key = 'GPS' subdata = data[key] if (piexif.GPSIFD.GPSLatitude in subdata and piexif.GPSIFD.GPSLongitude in subdata): coords_1 = (_convert_to_degress( subdata[piexif.GPSIFD.GPSLatitude]), _convert_to_degress( subdata[piexif.GPSIFD.GPSLongitude])) coords_2 = (srtlat, srtlong) distance_in_m = geopy.distance.vincenty( coords_1, coords_2).m
def optimize_jpeg( src: Union[pathlib.Path, io.BytesIO], dst: Optional[pathlib.Path] = None, quality: Optional[int] = 85, fast_mode: Optional[bool] = True, keep_exif: Optional[bool] = True, **options, ) -> Union[pathlib.Path, io.BytesIO]: """method to optimize JPEG files using a pure python external optimizer quality: JPEG quality (integer between 1 and 100) values: 50 | 55 | 35 | 100 | XX keep_exif: Whether to keep EXIF data in JPEG (boolean) values: True | False fast_mode: Use the supplied quality value. If turned off, optimizer will get dynamic quality value to ensure better compression values: True | False""" ensure_matches(src, "JPEG") img = Image.open(src) orig_size = (os.path.getsize(src) if isinstance(src, pathlib.Path) else src.getbuffer().nbytes) had_exif = False if (isinstance(src, io.BytesIO) and piexif.load(src.getvalue())["Exif"]) or (isinstance( src, pathlib.Path) and piexif.load(str(src))["Exif"]): had_exif = True # only use progressive if file size is bigger use_progressive_jpg = orig_size > 10240 # 10KiB if fast_mode: quality_setting = quality else: quality_setting, _ = jpeg_dynamic_quality(img) if dst is None: dst = io.BytesIO() img.save( dst, quality=quality_setting, optimize=True, progressive=use_progressive_jpg, format="JPEG", ) if isinstance(dst, io.BytesIO): dst.seek(0) if keep_exif and had_exif: piexif.transplant( exif_src=str(src.resolve()) if isinstance(src, pathlib.Path) else src.getvalue(), image=str(dst.resolve()) if isinstance(dst, pathlib.Path) else dst.getvalue(), new_file=dst, ) return dst
#!/usr/bin/python import os import re import piexif import shutil exif_dict = piexif.load("/Users/dagmar/Pictures/test/DSCF4890-6.JPG") for ifd in ("0th", "GPS", "Exif"): for tag in exif_dict[ifd]: print(piexif.TAGS[ifd][tag]["name"], exif_dict[ifd][tag]) #print (exif_dict["GPS"]["GPSDateStamp"])
output_file_name) command_list.append(command) print("Running: {}".format(command)) sp.check_output(command, shell=True) if USE_MESHROOM_RIG_FOLDER_FORMAT and EXPORT_JPG: if "png" in file_name: date_number = int( file_name.replace(".png", "").replace('converted_', '')) else: date_number = int( file_name.replace(".jpg", "").replace('section_', '')) print(date_number) exif_dict = piexif.load(output_file_name) exif_dict['Exif'][piexif.ExifIFD.BodySerialNumber] = bytes( '00{}'.format(rig_index), 'utf-8') exif_dict['Exif'][piexif.ExifIFD.FocalLength] = (3, 1) exif_dict['0th'][piexif.ImageIFD.Make] = bytes( 'GoPro', 'utf-8') exif_dict['0th'][piexif.ImageIFD.Model] = bytes( 'FUSION', 'utf-8') exif_dict['0th'][piexif.ImageIFD.DateTime] = bytes( '2020:01:01 01:00:{:02d}'.format(date_number), 'utf-8') exif_bytes = piexif.dump(exif_dict) piexif.insert(exif_bytes, output_file_name) #print(command) if vertical_offset_degree > 0: vertical_offset_degree = -vertical_offset_degree
def set_file_geo_data(file: Path, json): """ Reads the geoData from google and saves it to the EXIF. This works assuming that the geodata looks like -100.12093, 50.213143. Something like that. Written by DalenW. :param file: :param json: :return: """ # prevents crashes try: exif_dict = _piexif.load(str(file)) except (_piexif.InvalidImageDataError, ValueError): exif_dict = {'0th': {}, 'Exif': {}} # fetches geo data from the photos editor first. longitude = float(json['geoData']['longitude']) latitude = float(json['geoData']['latitude']) altitude = float(json['geoData']['altitude']) # fallbacks to GeoData Exif if it wasn't set in the photos editor. # https://github.com/TheLastGimbus/GooglePhotosTakeoutHelper/pull/5#discussion_r531792314 longitude = float(json['geoData']['longitude']) latitude = float(json['geoData']['latitude']) altitude = json['geoData']['altitude'] # Prioritise geoData set from GPhotos editor if longitude == 0 and latitude == 0: longitude = float(json['geoDataExif']['longitude']) latitude = float(json['geoDataExif']['latitude']) altitude = json['geoDataExif']['altitude'] # latitude >= 0: North latitude -> "N" # latitude < 0: South latitude -> "S" # longitude >= 0: East longitude -> "E" # longitude < 0: West longitude -> "W" if longitude >= 0: longitude_ref = 'E' else: longitude_ref = 'W' longitude = longitude * -1 if latitude >= 0: latitude_ref = 'N' else: latitude_ref = 'S' latitude = latitude * -1 # referenced from https://gist.github.com/c060604/8a51f8999be12fc2be498e9ca56adc72 gps_ifd = {_piexif.GPSIFD.GPSVersionID: (2, 0, 0, 0)} # skips it if it's empty if latitude != 0 or longitude != 0: gps_ifd.update({ _piexif.GPSIFD.GPSLatitudeRef: latitude_ref, _piexif.GPSIFD.GPSLatitude: degToDmsRational(latitude), _piexif.GPSIFD.GPSLongitudeRef: longitude_ref, _piexif.GPSIFD.GPSLongitude: degToDmsRational(longitude) }) if not isinstance(altitude, str) and altitude != 0: gps_ifd.update({ _piexif.GPSIFD.GPSAltitudeRef: 1, _piexif.GPSIFD.GPSAltitude: change_to_rational(round(altitude)) }) gps_exif = {"GPS": gps_ifd} exif_dict.update(gps_exif) try: _piexif.insert(_piexif.dump(exif_dict), str(file)) except Exception as e: print("Couldn't insert geo exif!") # local variable 'new_value' referenced before assignment means that one of the GPS values is incorrect print(e)
import easygui import piexif print( "Select the image files which have JPEG/JPG or TIFF as their extensions, selecting other types will raise errors\n" ) #for selecting the file exif_dict = piexif.load(easygui.fileopenbox()) thumbnail = exif_dict.pop("thumbnail") if thumbnail is not None: with open("thumbnail.jpg", "wb+") as f: f.write(thumbnail) #for printing the whole exif_dict """ for ifd_name in exif_dict: print("\n{0} IFD:".format(ifd_name)) for key in exif_dict[ifd_name]: try: print(key, exif_dict[ifd_name][key][:10]) except: print(key, exif_dict[ifd_name][key]) """ gps_info = exif_dict['GPS'] #for converting to decimal from degrees minutes seconds def get_decimal_from_dms(dms, ref): degrees = dms[0][0] / dms[0][1] minutes = dms[1][0] / dms[1][1] / 60.0 seconds = dms[2][0] / dms[2][1] / 3600.0 if ref in ['S', 'W']:
def exif_editor(endereco, titulo): exif_dict = piexif.load(endereco) exif_dict['0th'][270] = titulo exif_bytes = piexif.dump(exif_dict) piexif.insert(exif_bytes, endereco)
def writeToImageExif(self, childName, image): user_comment = piexif.helper.UserComment.dump(childName) exif_dict = piexif.load(image) exif_dict["Exif"][piexif.ExifIFD.UserComment] = user_comment exif_bytes = piexif.dump(exif_dict) piexif.insert(exif_bytes, image)
sys.exit() file_path = file_root + file_name + '.jpg' #openedFile = open(file_path) #readFile = openedFile.read() #sha1Hash = hashlib.sha1(readFile) #sha1Hashed = sha1Hash.hexdigest() img = cv.imread(file_path) pil_img = Image.open(file_path) # load exif data try: exif_dict = piexif.load(pil_img.info["exif"]) exif_dict.pop("thumbnail") exif_dict.pop("1st") except: #print('No exif data') #sys.exit() zeroth_ifd = {} exif_ifd = {} gps_ifd = {} exif_dict = {"0th": zeroth_ifd, "Exif": exif_ifd, "GPS": gps_ifd} pil_img.close() # for ifd_name in exif_dict: # print("\n{0} IFD:".format(ifd_name))
import piexif, piexif.helper, json, pprint filename = "photos/cbindonesia_1.jpg" exif_dict = piexif.load(filename) user_comment = piexif.helper.UserComment.load( exif_dict["Exif"][piexif.ExifIFD.UserComment]) data = json.loads(user_comment) pprint.pprint(data)
def create_image(path): try: filetype = os.path.splitext(path)[1] if filetype == ".mp4" or filetype == ".MP4" or filetype == ".MOV" or filetype == ".mov": media_info = MediaInfo.parse(path) for track in media_info.tracks: if track.bit_rate is not None and track.track_type == "Video": # get the videos recording date date_time = track.tagged_date year = date_time[4:8] month = date_time[9:11] day = date_time[12:14] hour = date_time[15:17] minute = date_time[18:20] second = date_time[21:23] # if there is no metadata in the video file the tagged_date is UTC 1904-01-01 00:00:00 if year == "1904" and use_creation_date == False: print( bcolors.WARNING + "[ WARNING ] " + "Could not retrieve metadata information for: " + bcolors.ENDC + os.path.split(path)[1]) if handle_nodate: move_or_copy_nodate(path) return False # FALLBACK elif year == "1904" and use_creation_date: date_time = get_time_from_creation_date(path) year = date_time[0:4] month = date_time[5:7] day = date_time[8:10] hour = date_time[11:13] minute = date_time[14:16] second = date_time[17:19] elif filetype == ".jpg" or filetype == ".JPG" or filetype == ".jpeg" or filetype == ".JPEG": # get exif information exif_dict = piexif.load(path) # exif36867 is the creation timestamp date_time = bytes.decode(exif_dict["Exif"][36867], "utf-8") year = date_time[0:4] month = date_time[5:7] day = date_time[8:10] hour = date_time[11:13] minute = date_time[14:16] second = date_time[17:19] # FALLBACK elif use_creation_date: date_time = get_time_from_creation_date(path) year = date_time[0:4] month = date_time[5:7] day = date_time[8:10] hour = date_time[11:13] minute = date_time[14:16] second = date_time[17:19] else: return False image = Image(year, month, day, hour, minute, second, path, filetype) return image except (KeyError, ValueError): print(bcolors.WARNING + "[ WARNING ] " + "Could not retrieve exif information for: " + bcolors.ENDC + os.path.split(path)[1]) # FALLBACK if use_creation_date: date_time = get_time_from_creation_date(path) year = date_time[0:4] month = date_time[5:7] day = date_time[8:10] hour = date_time[11:13] minute = date_time[14:16] second = date_time[17:19] image = Image(year, month, day, hour, minute, second, path, filetype) return image if handle_nodate: move_or_copy_nodate(path) return False except TypeError: print(bcolors.WARNING + "[ WARNING ] " + "Could not retrieve metadata information for: " + bcolors.ENDC + os.path.split(path)[1]) # FALLBACK if use_creation_date: date_time = get_time_from_creation_date(path) year = date_time[0:4] month = date_time[5:7] day = date_time[8:10] hour = date_time[11:13] minute = date_time[14:16] second = date_time[17:19] image = Image(year, month, day, hour, minute, second, path, filetype) return image if handle_nodate: move_or_copy_nodate(path) return False
def start(pass_index): # update the status in daily_passes.json with open("/home/pi/website/weather/scripts/daily_passes.json", "r") as f: data = json.load(f) with open("/home/pi/website/weather/scripts/daily_passes.json", "w") as f: data[pass_index]["status"] = "CURRENT" json.dump(data, f, indent=4, sort_keys=True) # get info about the pass from the daily_passes.json with open("/home/pi/website/weather/scripts/daily_passes.json") as f: p = json.load(f)[pass_index] print( "STATUS: {} - ".format(datetime.now().strftime("%Y/%m/%d %H:%M:%S")) + "Started processing {}° {} pass at {}".format( p['max_elevation'], p['satellite'], datetime.fromtimestamp(p['aos']).strftime( "%B %-d, %Y at %-H:%M:%S"))) # assign variables with open("/home/pi/website/weather/scripts/secrets.json") as f: data = json.load(f) lat = data['lat'] lon = data['lon'] elev = data['elev'] sat = p['satellite'] frequency = p['frequency'] duration = p['duration'] max_elevation = p['max_elevation'] # string used for naming the files (aos in %Y-%m-%d %H.%M.%S format) local_time = datetime.fromtimestamp(p['aos']).strftime("%Y-%m-%d_%H.%M.%S") day = str(local_time)[:10] # the name of the folder containing all the passes for the day (aos in %Y-%m-%d format) outfile = "/home/pi/drive/weather/images/{}/{}/{}".format( day, local_time, local_time) # the name of the json file containing all the info about the pass pass_file = "/home/pi/website/weather/images/{}/{}/{}.json".format( day, local_time, local_time) try: # if this is the first pass of the day, create a new folder for all the images of the day if not os.path.exists("/home/pi/drive/weather/images/{}".format(day)): os.makedirs("/home/pi/website/weather/images/{}".format(day)) os.makedirs("/home/pi/drive/weather/images/{}".format(day)) # create new directory for this pass if not os.path.exists("/home/pi/drive/weather/images/{}/{}".format( day, local_time)): os.makedirs("/home/pi/drive/weather/images/{}/{}".format( day, local_time)) os.makedirs("/home/pi/website/weather/images/{}/{}".format( day, local_time)) except: print("Failed creating new directories for the pass. Aborting") exit() # send console output to log file stdout_old = sys.stdout log_file = "/home/pi/drive/weather/images/{}/{}/{}.log".format( day, local_time, local_time) sys.stdout = open(log_file, "a") # compute sun elevation obs = ephem.Observer() obs.lat = str(lat) obs.long = str(lon) obs.date = datetime.utcfromtimestamp(p['tca']) sun = ephem.Sun(obs) sun.compute(obs) sun_elev = round(float(sun.alt) * 57.2957795, 1) # convert to degrees from radians # write sun elevation to pass file with open(pass_file, "w") as f: pass_info = p pass_info["sun_elev"] = sun_elev json.dump(pass_info, f, indent=4, sort_keys=True) # process depending on the satellite if sat[:4] == "NOAA": images, main_tag = process_satellite.NOAA(pass_file, outfile, log_file) elif sat == "METEOR-M 2": images, main_tag = process_satellite.METEOR(pass_file, outfile, log_file) # upload each image to the internet links = {} with open(pass_file) as f: data = json.load(f) for image in images: # add metadata to image exif_dict = piexif.load(image) exif_dict["Exif"][ piexif.ExifIFD.UserComment] = piexif.helper.UserComment.dump( json.dumps(data), encoding="unicode") piexif.insert(piexif.dump(exif_dict), image) # upload image and get a link tag = image.split(".")[-2] link = share.imgbb(image) if tag == main_tag: main_image = link links[tag] = link # write pass info to json file with open(pass_file) as fr: pass_info = json.load(fr) with open(pass_file, "w") as fw: pass_info['links'] = links pass_info["main_image"] = main_image json.dump(pass_info, fw, indent=4, sort_keys=True) # send discord webhook with open("/home/pi/website/weather/scripts/secrets.json") as f: data = json.load(f) for webhook in data["discord_webhook_urls"]: result = share.discord_webhook(pass_file, webhook) print(result) # update the status in daily_passes.json with open("/home/pi/website/weather/scripts/daily_passes.json", "r") as f: data = json.load(f) with open("/home/pi/website/weather/scripts/daily_passes.json", "w") as f: data[pass_index]["status"] = "PASSED" json.dump(data, f, indent=4, sort_keys=True) # append the pass to the passes list with open("/home/pi/website/weather/images/passes.json", "r+") as rf: data = json.load(rf) with open("/home/pi/website/weather/images/passes.json", "w") as f: data.append("/weather/images/{}/{}/{}.json".format( day, local_time, local_time)) json.dump(data, f, indent=4, sort_keys=True) # commit changes to git repository # print("STATUS {} - ".format(datetime.now().strftime("%Y/%m/%d %H:%M:%S")) + # "Commiting changes to github") # os.system( # "/home/pi/website/weather/scripts/commit.sh 'Automatic commit for satellite pass' >> {}" # .format(log_file)) # set console output back to default sys.stdout = stdout_old # send status to console print( "STATUS: {} - ".format(datetime.now().strftime("%Y/%m/%d %H:%M:%S")) + "Finished processing {}° {} pass at {}".format( p['max_elevation'], p['satellite'], datetime.fromtimestamp(p['aos']).strftime( "%B %-d, %Y at %-H:%M:%S"))) # get info about next pass next_pass = {} for p in json.load( open("/home/pi/website/weather/scripts/daily_passes.json")): if p["status"] == "INCOMING": next_pass = p break if next_pass == {}: print("STATUS: {} - ".format(datetime.now().strftime( "%Y/%m/%d %H:%M:%S")) + "No more passes to process. Rescheduling...") else: print("STATUS: {} - ".format(datetime.now().strftime( "%Y/%m/%d %H:%M:%S")) + "Waiting until {} for {}° {} pass...".format( datetime.fromtimestamp(next_pass['aos']).strftime( "%B %-d, %Y at %-H:%M:%S"), next_pass['max_elevation'], next_pass['satellite']))
from PIL import Image import piexif from pprint import pprint from PIL import ExifTags im = Image.open("../../_images/rose.jpg") exif_dict = piexif.load(im.info["exif"]) pprint(exif_dict) pprint(ExifTags.TAGS)
def read(self): """ Read EXIF of pictures in media library """ return piexif.load(self.mediadir_path + self.kwargs["filename"])
def add_exif_data(self, filename): print "----------------------------------------------------------------------------" # pop top gps coordinate from queue try: print "Size of Queue: " + str( len(ArduinoUSBconn.connection.gps_queue)) print ArduinoUSBconn.connection.gps_queue location = ArduinoUSBconn.connection.gps_queue.popleft() except Exception as e: print "Queue is empty" print e return try: im = Image.open(FOLDER + "/" + filename) exif_dict = piexif.load(im.info["exif"]) print "AddExif: " + filename except Exception as e: print "Couldn't open file " + filename print "Error: " + e print "----------------------------------------------------------------------------" return """ image_time_s = exif_dict["Exif"][piexif.ExifIFD.DateTimeOriginal] image_time_ms = int(exif_dict["Exif"][piexif.ExifIFD.SubSecTimeOriginal]) image_time = time.strptime(image_time_s,"%Y:%m:%d %H:%M:%S") image_unix = time.mktime(image_time) image_unix = image_unix + image_time_ms/int(len(str(image_time_ms))) # copy dict for use in case of modification gps_dict = ArduinoUSBconn.connection.gps_dict.copy() if self.adds == 0: lowest = 10000000000000000000 for t in gps_dict: if t < lowest: lowest = t self.beg_obc_clock = lowest self.beg_cam_clock = image_unix self.adds = 1 print gps_dict print image_unix # offset camera time to obc time image_offset = abs(image_unix - self.beg_cam_clock) closest = 0 smallest = -1 for k in gps_dict: obc_offset = abs(k - self.beg_obc_clock) diff = abs(k - image_unix) if smallest == -1: if diff < 10: closest = k smallest = diff else: if diff < smallest and diff < 5: closest = k smallest = diff if closest not in gps_dict: print "ERROR NO GPS TAG FOR THIS IMAGE" + filename print "IMAGE TIME = "+str(image_unix) print "----------------------------------------------------------------------------" return print "Image Time: "+ str(image_unix) print "Closest GPS Time: "+ str(closest) + " diff: "+str(smallest) print "Location: "+ str(gps_dict[closest]) #location = gps_dict[closest] """ absolute_alt = location["alt"] relative_alt = location["rel_alt"] calc_ground = abs(absolute_alt - relative_alt) lat = location["lat"] lon = location["lng"] heading = location["heading"] # two precision absolute_alt = int(round(absolute_alt * 100)) calc_ground = int(round(calc_ground * 100)) relative_alt = int(round(relative_alt * 100)) # three precision heading = int(round(heading * 1000)) lat = int(round(abs(lat * 1000))) lon = int(round(abs(lon * 1000))) exif_dict["GPS"][piexif.GPSIFD.GPSSpeed] = (calc_ground, 100) exif_dict["GPS"][piexif.GPSIFD.GPSAltitude] = (absolute_alt, 100) exif_dict["GPS"][piexif.GPSIFD.GPSImgDirection] = (heading, 1000) exif_dict["GPS"][piexif.GPSIFD.GPSLongitude] = (lon, 1000) exif_dict["GPS"][piexif.GPSIFD.GPSLatitude] = (lat, 1000) exif_dict["GPS"][piexif.GPSIFD.GPSTrack] = (relative_alt, 100) try: exif_bytes = piexif.dump(exif_dict) im.save(OUTPUTFOLDER + "/" + filename, "jpeg", exif=exif_bytes, quality=90) except Exception as e: print "--------" print "ERROR Exif data could not be saved" print e print "Lat " + str(lat) print "Long " + str(lon) print "Heading " + str(heading) print "AAlt " + str(absolute_alt) print "RAlt " + str(relative_alt) print "CGround " + str(calc_ground) print "--------" print "exif data added" print "----------------------------------------------------------------------------"
def rotate_image(image_data, method=None, REQUEST=None): """Rotate Image if it has Exif Orientation Informations other than 1. Do not use PIL.Image.rotate function as this did not transpose the image, rotate keeps the image width and height and rotates the image around a central point. PIL.Image.transpose also changes Image Orientation. """ orientation = 1 # if not set assume correct orrinetation --> 1 data = _ensure_data(image_data) img = PIL.Image.open(StringIO(data)) exif_data = None if 'exif' in img.info: try: exif_data = piexif.load(img.info['exif']) except ValueError: log.warn('Exif information currupt') pass if exif_data and piexif.ImageIFD.Orientation in exif_data['0th']: orientation = exif_data['0th'][piexif.ImageIFD.Orientation] if exif_data and \ (not exif_data['0th'].get(piexif.ImageIFD.XResolution) or not exif_data['0th'].get(piexif.ImageIFD.YResolution)): exif_data['0th'][piexif.ImageIFD.XResolution] = (img.width, 1) exif_data['0th'][piexif.ImageIFD.YResolution] = (img.height, 1) if exif_data is None: width, height = img.size exif_data = { '0th': { piexif.ImageIFD.XResolution: (width, 1), piexif.ImageIFD.YResolution: (height, 1), } } if method is not None: orientation = method log.debug('Rotate image with input orientation: %s', orientation) fmt = img.format if orientation == 1: # not transform necessary # img = img pass elif orientation == 2: img = img.transpose(PIL.Image.FLIP_LEFT_RIGHT) elif orientation == 3: img = img.transpose(PIL.Image.ROTATE_180) elif orientation == 4: img = img.transpose(PIL.Image.ROTATE_180).transpose( PIL.Image.FLIP_LEFT_RIGHT) elif orientation == 5: img = img.transpose(PIL.Image.ROTATE_270).transpose( PIL.Image.FLIP_LEFT_RIGHT) elif orientation == 6: img = img.transpose(PIL.Image.ROTATE_270) elif orientation == 7: img = img.transpose(PIL.Image.ROTATE_90).transpose( PIL.Image.FLIP_LEFT_RIGHT) elif orientation == 8: img = img.transpose(PIL.Image.ROTATE_90) if orientation in [5, 6, 7, 8]: if exif_data['0th'][piexif.ImageIFD.XResolution] and \ exif_data['0th'][piexif.ImageIFD.YResolution]: exif_data['0th'][piexif.ImageIFD.XResolution], \ exif_data['0th'][piexif.ImageIFD.YResolution] = \ exif_data['0th'][piexif.ImageIFD.YResolution], \ exif_data['0th'][piexif.ImageIFD.XResolution] else: exif_data['0th'][piexif.ImageIFD.XResolution], \ exif_data['0th'][piexif.ImageIFD.YResolution] = \ (img.width, 1), (img.height, 1) # set orientation to normal exif_data['0th'][piexif.ImageIFD.Orientation] = 1 try: exif_bytes = piexif.dump(exif_data) except Exception as e: log.warn(e) del (exif_data['Exif'][piexif.ExifIFD.SceneType]) # This Element piexif.ExifIFD.SceneType cause error on dump exif_bytes = piexif.dump(exif_data) output_image_data = StringIO() img.save(output_image_data, format=fmt, exif=exif_bytes) width, height = img.size return output_image_data.getvalue(), width, height, exif_data
str(image_location)) extensions = ("*.jpg", "*.JPG", "*.JPEG", "*.jpeg") folder_contents = [] for extension in extensions: folder_contents.extend(glob.glob(image_location + "/**/" + extension)) if len(folder_contents) > 0: print( str(datetime.now()) + " [INFO] Found " + str(len(folder_contents)) + " Images to process") for image_location in folder_contents: if os.path.isfile(image_location): exif_dict = piexif.load(image_location) for ifd in ("0th", "Exif", "GPS", "1st"): for tag in exif_dict[ifd]: if piexif.TAGS[ifd][tag]["name"] == "DateTime": date_of_image["DateTime"] = exif_dict[ifd][ tag].decode("utf-8") if piexif.TAGS[ifd][tag]["name"] == "DateTimeOriginal": date_of_image["DateTimeOriginal"] = exif_dict[ifd][ tag].decode("utf-8") if "DateTimeOriginal" in date_of_image: img_date = datetime.strptime( date_of_image["DateTimeOriginal"], "%Y:%m:%d %H:%M:%S")
def __init__(self, file_dir, floatview_info, raw_info): self.file_dir = file_dir self.floatview_info = floatview_info self.raw_info = raw_info self.exif_dict = piexif.load(self.file_dir) self.is_dirty = False
print('读取 image') if not os.path.exists('image'): print('image 文件夹不存在') exit(0) print('清理dist') if os.path.exists('dist'): shutil.rmtree('dist') os.mkdir('dist') props = readprops('reformat.props') tSize = (160, 120) finalSize = (960, 1280) exif = piexif.load(open('exif.bin', 'rb').read()) # 根据props修改信息 exif['0th'][305] = struct.pack('%ds' % (len(props['productVersion'])), bytes(props['productVersion'], 'utf-8')) exif['0th'][306] = struct.pack( '19s', bytes(props['time_str'].replace('-', ':'), 'utf-8')) exif['Exif'][36867] = struct.pack( '19s', bytes(props['time_str'].replace('-', ':'), 'utf-8')) exif['Exif'][36868] = struct.pack( '19s', bytes(props['time_str'].replace('-', ':'), 'utf-8')) pt = props['productType'] + ' front camera 2.15mm f/2.4' exif['Exif'][42036] = struct.pack('%ds' % (len(pt)), bytes(pt, 'utf-8')) def handleDir(sub): currDir = os.path.join('image', sub)
def load_exif(self): print("LOAD EXIF") try: self.exif_dict = piexif.load(self.file_path) except Exception as e: print(e)
# from https://piexif.readthedocs.io/en/latest/functions.html#load import pickle import piexif exifDict = piexif.load("image_mod.jpg") for ifdName in exifDict: print("\n{0} IFD:".format(ifdName)) if isinstance(exifDict[ifdName], dict): for key in exifDict[ifdName]: try: data = exifDict[ifdName][key] if isinstance(data, bytes): if key == piexif.ExifIFD.MakerNote: d = pickle.loads(data) print(key, d) elif key == piexif.ExifIFD.UserComment: if data[0:8] == b"UNICODE\0": print(key, data[8:].decode("utf-8")) else: print(f"{key} type {type(data)}: {data[:10]}") else: print(key, exifDict[ifdName][key][:10]) except: print(key, exifDict[ifdName][key]) else: print(f"{ifdName} is type {type(exifDict[ifdName])}")
def batch(self, should_rename=True, should_add_exif=True): # re constants p_date = re.compile( r"(\d{4})(0[1-9]|1[0-2])(0[1-9]|[1-2]\d|3[0-1])[ _]([0-1]\d|2[0-3])([0-5]\d)([0-5]\d)" ) p_floatview_json = re.compile(r"^floatview_photo_\d{5}-\d{5}.json$") p_raw_json = re.compile(r"^photo_\d{5}-\d{5}.json$") target_dir = os.path.join(os.getcwd(), self.target_uin, "photo") if not os.path.exists(target_dir): print("路径不存在,请确认照片已下载,并在本文件尾部添加目标QQ号") # form album list album_info_dir = os.path.join(target_dir, "album_info.json") with open(album_info_dir, "r", encoding="utf-8") as album_info_f: album_info = json.load(album_info_f) album_list = get_album_list_data(album_info['data']) # No album at all! if len(album_list) <= 0: print("【json记录中无相册!】") return # do for every album for album in album_list: album_dir = "" files_in_target_dir = os.listdir(target_dir) album_id_purged = purge_file_name(album["id"]) # find album folder for file_name_in_target_dir in files_in_target_dir: if album_id_purged in file_name_in_target_dir: album_dir = os.path.join(target_dir, file_name_in_target_dir) # rename album folder if should_rename: if not re.search(p_date, file_name_in_target_dir): album_create_timestamp = int( album["createtime"]) # 取相册创建时间 album_create_date = time.strftime( '%Y%m%d %H%M%S', time.localtime(album_create_timestamp)) file_name_in_target_dir_new = album_create_date + " " + file_name_in_target_dir album_dir_new = os.path.join( target_dir, file_name_in_target_dir_new) os.rename(album_dir, album_dir_new) album_dir = album_dir_new break if album_dir == "": print("相册文件夹缺失:", os.path.join(target_dir, album["name"])) continue # find floatview and raw json (500+ json文件会分裂。。) files_in_album_dir = os.listdir(album_dir) floatview_json_dir_list = [] raw_json_dir_list = [] for file_name_in_album_dir in files_in_album_dir: if re.search(p_floatview_json, file_name_in_album_dir): floatview_json_dir_list.append( os.path.join(album_dir, file_name_in_album_dir)) elif re.search(p_raw_json, file_name_in_album_dir): raw_json_dir_list.append( os.path.join(album_dir, file_name_in_album_dir)) # floatview or raw json is missing! if len(floatview_json_dir_list) == 0 or len( raw_json_dir_list) == 0: print("【相册中照片json数据缺失】:", album_dir) continue floatview_list = [] raw_list = [] for floatview_json_dir in floatview_json_dir_list: with open(floatview_json_dir, "r", encoding="utf-8") as floatview_json_f: floatview_json = json.load(floatview_json_f) for _floatview_info in floatview_json["data"]["photos"]: floatview_list.append(_floatview_info) for raw_json_dir in raw_json_dir_list: with open(raw_json_dir, "r", encoding="utf-8") as raw_json_f: raw_json = json.load(raw_json_f) for _raw_info in raw_json["data"]["photoList"]: raw_list.append(_raw_info) # find downloaded folder and file list within downloaded_dir = os.path.join(album_dir, "downloaded") # downloaded folder is missing! if not os.path.exists(downloaded_dir): print("【无downloaded文件夹】:", downloaded_dir) continue photos_in_album_downloaded_dir = os.listdir(downloaded_dir) # start to handle every photo within an album # floatview_info for floatview_info in floatview_list: lloc = floatview_info["lloc"] # find raw_info raw_info = None for _raw_info in raw_list: if _raw_info["lloc"] == lloc: raw_info = _raw_info break # find photo_dir photo_dir = "" lloc_purged = purge_file_name(lloc) for photo_name in photos_in_album_downloaded_dir: if lloc_purged in photo_name: photo_dir = os.path.join(downloaded_dir, photo_name) break if photo_dir == "": print("照片缺失:", os.path.join(downloaded_dir, lloc_purged)) continue # recover EXIF if should_add_exif: try: photoExifRecover = PhotoExifRecover( photo_dir, floatview_info, raw_info) photoExifRecover.recover() except Exception as e: error_message = "EXIF写入失败:" + photo_dir + "\n↘失败原因:" + str( e) print(error_message) self.e.append(error_message) continue # 对于EXIF写入发生异常的文件跳过重命名步骤 # rename photo if should_rename: [dir_name, photo_name] = os.path.split(photo_dir) if not re.search(p_date, photo_name): exif_in_file = piexif.load(photo_dir) if "Exif" in exif_in_file.keys() \ and piexif.ExifIFD.DateTimeOriginal in exif_in_file["Exif"].keys() \ and exif_in_file["Exif"][piexif.ExifIFD.DateTimeOriginal]: photo_create_date = \ bytes.decode(exif_in_file["Exif"][piexif.ExifIFD.DateTimeOriginal]).replace(":", "") photo_name_new = photo_create_date + " " + photo_name photo_dir_new = os.path.join( dir_name, photo_name_new) os.rename(photo_dir, photo_dir_new) photoExifRecover.file_dir = photo_dir_new photo_dir = photo_dir_new
def __init__(self, filename): self.filename = filename self.img = Image.open(filename) self.exif = piexif.load(filename) self._datetime_original = ''
def write_gps(filename, lat, longit, altitude): #lat and long are decimals gps = {} #create array #Altitude if altitude<0: gps[piexif.GPSIFD.GPSAltitudeRef] = 1; #Below see level else: gps[piexif.GPSIFD.GPSAltitudeRef] = 0; #Above see level gps[piexif.GPSIFD.GPSAltitude]= (int(abs(altitude)*100),100) #Latitude if lat<0: gps[piexif.GPSIFD.GPSLatitudeRef] = "S" else: gps[piexif.GPSIFD.GPSLatitudeRef] = "N" latd,latm,lats = ExifWriter._decdeg2dms(lat) gps[piexif.GPSIFD.GPSLatitude] = [(latd,1),(latm,1),(lats*100,100)]; #Longitude if longit<0: gps[piexif.GPSIFD.GPSLongitudeRef] = "W" else: gps[piexif.GPSIFD.GPSLongitudeRef] = "E" longd,longm,longs = ExifWriter._decdeg2dms(longit) gps[piexif.GPSIFD.GPSLongitude] = [(longd,1),(longm,1),(longs*100,100)]; exifdict = piexif.load(filename) exifdict["GPS"] = gps exif_bytes = piexif.dump(exifdict) piexif.insert(exif_bytes, filename) exifdict = piexif.load(filename)