def parse_exif_values(self, _path_file): # Disable exifread log logging.getLogger('exifread').setLevel(logging.CRITICAL) with open(_path_file, 'rb') as f: tags = exifread.process_file(f, details=False) try: if 'Image Make' in tags: self.camera_make = tags['Image Make'].values.encode('utf8') if 'Image Model' in tags: self.camera_model = tags['Image Model'].values.encode( 'utf8') if 'GPS GPSAltitude' in tags: self.altitude = self.float_values( tags['GPS GPSAltitude'])[0] if 'GPS GPSAltitudeRef' in tags and self.int_values( tags['GPS GPSAltitudeRef'])[0] > 0: self.altitude *= -1 if 'GPS GPSLatitude' in tags and 'GPS GPSLatitudeRef' in tags: self.latitude = self.dms_to_decimal( tags['GPS GPSLatitude'], tags['GPS GPSLatitudeRef']) if 'GPS GPSLongitude' in tags and 'GPS GPSLongitudeRef' in tags: self.longitude = self.dms_to_decimal( tags['GPS GPSLongitude'], tags['GPS GPSLongitudeRef']) except IndexError as e: log.ODM_WARNING("Cannot read EXIF tags for %s: %s" % (_path_file, e.message)) if self.camera_make and self.camera_model: self.make_model = sensor_string(self.camera_make, self.camera_model) self.width, self.height = get_image_size.get_image_size(_path_file)
def parse_exif_values(self, _path_file): # Disable exifread log logging.getLogger('exifread').setLevel(logging.CRITICAL) with open(_path_file, 'rb') as f: tags = exifread.process_file(f, details=False) try: if 'Image Make' in tags: self.camera_make = tags['Image Make'].values.encode('utf8') if 'Image Model' in tags: self.camera_model = tags['Image Model'].values.encode('utf8') if 'GPS GPSAltitude' in tags: self.altitude = self.float_values(tags['GPS GPSAltitude'])[0] if 'GPS GPSAltitudeRef' in tags and self.int_values(tags['GPS GPSAltitudeRef'])[0] > 0: self.altitude *= -1 if 'GPS GPSLatitude' in tags and 'GPS GPSLatitudeRef' in tags: self.latitude = self.dms_to_decimal(tags['GPS GPSLatitude'], tags['GPS GPSLatitudeRef']) if 'GPS GPSLongitude' in tags and 'GPS GPSLongitudeRef' in tags: self.longitude = self.dms_to_decimal(tags['GPS GPSLongitude'], tags['GPS GPSLongitudeRef']) except IndexError as e: log.ODM_WARNING("Cannot read EXIF tags for %s: %s" % (_path_file, e.message)) if self.camera_make and self.camera_model: self.make_model = sensor_string(self.camera_make, self.camera_model) # needed to do that since sometimes metadata contains wrong data try: self.width, self.height = get_image_size.get_image_size(_path_file) except get_image_size.UnknownImageFormat: # Fallback to slower cv2 img = cv2.imread(_path_file) self.width = img.shape[1] self.height = img.shape[0]
def compute_cutline(orthophoto_file, crop_area_file, destination, max_concurrency=1, tmpdir=None, scale=1): if io.file_exists(orthophoto_file) and io.file_exists(crop_area_file): from opendm.grass_engine import grass log.ODM_DEBUG("Computing cutline") if tmpdir and not io.dir_exists(tmpdir): system.mkdir_p(tmpdir) scale = max(0.0001, min(1, scale)) scaled_orthophoto = None if scale < 1: log.ODM_DEBUG("Scaling orthophoto to %s%% to compute cutline" % (scale * 100)) scaled_orthophoto = os.path.join(tmpdir, os.path.basename(io.related_file_path(orthophoto_file, postfix=".scaled"))) # Scale orthophoto before computing cutline system.run("gdal_translate -outsize {}% 0 " "-co NUM_THREADS={} " "--config GDAL_CACHEMAX {}% " "{} {}".format( scale * 100, max_concurrency, concurrency.get_max_memory(), orthophoto_file, scaled_orthophoto )) orthophoto_file = scaled_orthophoto try: ortho_width,ortho_height = get_image_size.get_image_size(orthophoto_file, fallback_on_error=False) log.ODM_DEBUG("Orthophoto dimensions are %sx%s" % (ortho_width, ortho_height)) number_lines = int(max(8, math.ceil(min(ortho_width, ortho_height) / 256.0))) except: log.ODM_DEBUG("Cannot compute orthophoto dimensions, setting arbitrary number of lines.") number_lines = 32 log.ODM_DEBUG("Number of lines: %s" % number_lines) gctx = grass.create_context({'auto_cleanup' : False, 'tmpdir': tmpdir}) gctx.add_param('orthophoto_file', orthophoto_file) gctx.add_param('crop_area_file', crop_area_file) gctx.add_param('number_lines', number_lines) gctx.add_param('max_concurrency', max_concurrency) gctx.add_param('memory', int(concurrency.get_max_memory_mb(300))) gctx.set_location(orthophoto_file) cutline_file = gctx.execute(os.path.join("opendm", "grass", "compute_cutline.grass")) if cutline_file != 'error': if io.file_exists(cutline_file): shutil.move(cutline_file, destination) log.ODM_INFO("Generated cutline file: %s --> %s" % (cutline_file, destination)) gctx.cleanup() return destination else: log.ODM_WARNING("Unexpected script result: %s. No cutline file has been generated." % cutline_file) else: log.ODM_WARNING("Could not generate orthophoto cutline. An error occured when running GRASS. No orthophoto will be generated.") else: log.ODM_WARNING("We've been asked to compute cutline, but either %s or %s is missing. Skipping..." % (orthophoto_file, crop_area_file))
def compute_cutline(orthophoto_file, crop_area_file, destination, max_concurrency=1, tmpdir=None): if io.file_exists(orthophoto_file) and io.file_exists(crop_area_file): from opendm.grass_engine import grass log.ODM_DEBUG("Computing cutline") if tmpdir and not io.dir_exists(tmpdir): system.mkdir_p(tmpdir) try: ortho_width, ortho_height = get_image_size.get_image_size( orthophoto_file) log.ODM_DEBUG("Orthophoto dimensions are %sx%s" % (ortho_width, ortho_height)) number_lines = int( max(8, math.ceil(min(ortho_width, ortho_height) / 256.0))) except get_image_size.UnknownImageFormat: log.ODM_DEBUG( "Cannot compute orthophoto dimensions, setting arbitrary number of lines." ) number_lines = 32 log.ODM_DEBUG("Number of lines: %s" % number_lines) gctx = grass.create_context({'auto_cleanup': False, 'tmpdir': tmpdir}) gctx.add_param('orthophoto_file', orthophoto_file) gctx.add_param('crop_area_file', crop_area_file) gctx.add_param('number_lines', number_lines) gctx.add_param('max_concurrency', max_concurrency) gctx.add_param('memory', int(concurrency.get_max_memory_mb(300))) gctx.set_location(orthophoto_file) cutline_file = gctx.execute( os.path.join("opendm", "grass", "compute_cutline.grass")) if cutline_file != 'error': if io.file_exists(cutline_file): shutil.move(cutline_file, destination) log.ODM_INFO("Generated cutline file: %s --> %s" % (cutline_file, destination)) gctx.cleanup() return destination else: log.ODM_WARNING( "Unexpected script result: %s. No cutline file has been generated." % cutline_file) else: log.ODM_WARNING( "Could not generate orthophoto cutline. An error occured when running GRASS. No orthophoto will be generated." ) else: log.ODM_WARNING( "We've been asked to compute cutline, but either %s or %s is missing. Skipping..." % (orthophoto_file, crop_area_file))
def parse_exif_values(self, _path_file): # Disable exifread log logging.getLogger('exifread').setLevel(logging.CRITICAL) with open(_path_file, 'rb') as f: tags = exifread.process_file(f, details=False) try: if 'Image Make' in tags: try: self.camera_make = tags['Image Make'].values except UnicodeDecodeError: log.ODM_WARNING("EXIF Image Make might be corrupted") self.camera_make = "unknown" if 'Image Model' in tags: try: self.camera_model = tags['Image Model'].values except UnicodeDecodeError: log.ODM_WARNING("EXIF Image Model might be corrupted") self.camera_model = "unknown" if 'GPS GPSAltitude' in tags: self.altitude = self.float_value(tags['GPS GPSAltitude']) if 'GPS GPSAltitudeRef' in tags and self.int_value( tags['GPS GPSAltitudeRef']) > 0: self.altitude *= -1 if 'GPS GPSLatitude' in tags and 'GPS GPSLatitudeRef' in tags: self.latitude = self.dms_to_decimal( tags['GPS GPSLatitude'], tags['GPS GPSLatitudeRef']) if 'GPS GPSLongitude' in tags and 'GPS GPSLongitudeRef' in tags: self.longitude = self.dms_to_decimal( tags['GPS GPSLongitude'], tags['GPS GPSLongitudeRef']) except IndexError as e: log.ODM_WARNING("Cannot read basic EXIF tags for %s: %s" % (_path_file, str(e))) try: if 'Image Tag 0xC61A' in tags: self.black_level = self.list_values( tags['Image Tag 0xC61A']) elif 'BlackLevel' in tags: self.black_level = self.list_values(tags['BlackLevel']) if 'EXIF ExposureTime' in tags: self.exposure_time = self.float_value( tags['EXIF ExposureTime']) if 'EXIF FNumber' in tags: self.fnumber = self.float_value(tags['EXIF FNumber']) if 'EXIF ISOSpeed' in tags: self.iso_speed = self.int_value(tags['EXIF ISOSpeed']) elif 'EXIF PhotographicSensitivity' in tags: self.iso_speed = self.int_value( tags['EXIF PhotographicSensitivity']) elif 'EXIF ISOSpeedRatings' in tags: self.iso_speed = self.int_value( tags['EXIF ISOSpeedRatings']) if 'Image BitsPerSample' in tags: self.bits_per_sample = self.int_value( tags['Image BitsPerSample']) if 'EXIF DateTimeOriginal' in tags: str_time = tags['EXIF DateTimeOriginal'].values utc_time = datetime.strptime(str_time, "%Y:%m:%d %H:%M:%S") subsec = 0 if 'EXIF SubSecTime' in tags: subsec = self.int_value(tags['EXIF SubSecTime']) negative = 1.0 if subsec < 0: negative = -1.0 subsec *= -1.0 subsec = float('0.{}'.format(int(subsec))) subsec *= negative ms = subsec * 1e3 utc_time += timedelta(milliseconds=ms) timezone = pytz.timezone('UTC') epoch = timezone.localize(datetime.utcfromtimestamp(0)) self.utc_time = (timezone.localize(utc_time) - epoch).total_seconds() * 1000.0 except Exception as e: log.ODM_WARNING("Cannot read extended EXIF tags for %s: %s" % (_path_file, str(e))) # Extract XMP tags f.seek(0) xmp = self.get_xmp(f) for tags in xmp: try: band_name = self.get_xmp_tag( tags, ['Camera:BandName', '@Camera:BandName']) if band_name is not None: self.band_name = band_name.replace(" ", "") self.set_attr_from_xmp_tag( 'band_index', tags, [ 'DLS:SensorId', # Micasense RedEdge '@Camera:RigCameraIndex', # Parrot Sequoia, Sentera 21244-00_3.2MP-GS-0001 'Camera:RigCameraIndex', # MicaSense Altum ]) self.set_attr_from_xmp_tag( 'radiometric_calibration', tags, [ 'MicaSense:RadiometricCalibration', ]) self.set_attr_from_xmp_tag('vignetting_center', tags, [ 'Camera:VignettingCenter', 'Sentera:VignettingCenter', ]) self.set_attr_from_xmp_tag('vignetting_polynomial', tags, [ 'Camera:VignettingPolynomial', 'Sentera:VignettingPolynomial', ]) self.set_attr_from_xmp_tag('horizontal_irradiance', tags, ['Camera:HorizontalIrradiance'], float) self.set_attr_from_xmp_tag( 'irradiance_scale_to_si', tags, ['Camera:IrradianceScaleToSIUnits'], float) self.set_attr_from_xmp_tag('sun_sensor', tags, [ 'Camera:SunSensor', ], float) self.set_attr_from_xmp_tag('spectral_irradiance', tags, [ 'Camera:SpectralIrradiance', 'Camera:Irradiance', ], float) # Phantom 4 RTK if '@drone-dji:RtkStdLon' in tags: y = float( self.get_xmp_tag(tags, '@drone-dji:RtkStdLon')) x = float( self.get_xmp_tag(tags, '@drone-dji:RtkStdLat')) self.gps_xy_stddev = max(x, y) if '@drone-dji:RtkStdHgt' in tags: self.gps_z_stddev = float( self.get_xmp_tag(tags, '@drone-dji:RtkStdHgt')) else: self.set_attr_from_xmp_tag( 'gps_xy_stddev', tags, ['@Camera:GPSXYAccuracy', 'GPSXYAccuracy'], float) self.set_attr_from_xmp_tag( 'gps_z_stddev', tags, ['@Camera:GPSZAccuracy', 'GPSZAccuracy'], float) if 'DLS:Yaw' in tags: self.set_attr_from_xmp_tag('dls_yaw', tags, ['DLS:Yaw'], float) self.set_attr_from_xmp_tag('dls_pitch', tags, ['DLS:Pitch'], float) self.set_attr_from_xmp_tag('dls_roll', tags, ['DLS:Roll'], float) except Exception as e: log.ODM_WARNING("Cannot read XMP tags for %s: %s" % (_path_file, str(e))) # self.set_attr_from_xmp_tag('center_wavelength', tags, [ # 'Camera:CentralWavelength' # ], float) # self.set_attr_from_xmp_tag('bandwidth', tags, [ # 'Camera:WavelengthFWHM' # ], float) self.width, self.height = get_image_size.get_image_size(_path_file) # Sanitize band name since we use it in folder paths self.band_name = re.sub('[^A-Za-z0-9]+', '', self.band_name)
def parse_exif_values(self, _path_file): # Disable exifread log logging.getLogger('exifread').setLevel(logging.CRITICAL) try: self.width, self.height = get_image_size.get_image_size(_path_file) except Exception as e: raise PhotoCorruptedException(str(e)) tags = {} xtags = {} with open(_path_file, 'rb') as f: tags = exifread.process_file(f, details=True, extract_thumbnail=False) try: if 'Image Make' in tags: try: self.camera_make = tags['Image Make'].values self.camera_make = self.camera_make.strip() except UnicodeDecodeError: log.ODM_WARNING("EXIF Image Make might be corrupted") self.camera_make = "unknown" if 'Image Model' in tags: try: self.camera_model = tags['Image Model'].values self.camera_model = self.camera_model.strip() except UnicodeDecodeError: log.ODM_WARNING("EXIF Image Model might be corrupted") self.camera_model = "unknown" if 'GPS GPSAltitude' in tags: self.altitude = self.float_value(tags['GPS GPSAltitude']) if 'GPS GPSAltitudeRef' in tags and self.int_value( tags['GPS GPSAltitudeRef'] ) is not None and self.int_value( tags['GPS GPSAltitudeRef']) > 0: self.altitude *= -1 if 'GPS GPSLatitude' in tags and 'GPS GPSLatitudeRef' in tags: self.latitude = self.dms_to_decimal( tags['GPS GPSLatitude'], tags['GPS GPSLatitudeRef']) elif 'GPS GPSLatitude' in tags: log.ODM_WARNING( "GPS position for %s might be incorrect, GPSLatitudeRef tag is missing (assuming N)" % self.filename) self.latitude = self.dms_to_decimal( tags['GPS GPSLatitude'], GPSRefMock('N')) if 'GPS GPSLongitude' in tags and 'GPS GPSLongitudeRef' in tags: self.longitude = self.dms_to_decimal( tags['GPS GPSLongitude'], tags['GPS GPSLongitudeRef']) elif 'GPS GPSLongitude' in tags: log.ODM_WARNING( "GPS position for %s might be incorrect, GPSLongitudeRef tag is missing (assuming E)" % self.filename) self.longitude = self.dms_to_decimal( tags['GPS GPSLongitude'], GPSRefMock('E')) if 'Image Orientation' in tags: self.orientation = self.int_value( tags['Image Orientation']) except (IndexError, ValueError) as e: log.ODM_WARNING("Cannot read basic EXIF tags for %s: %s" % (self.filename, str(e))) try: if 'Image Tag 0xC61A' in tags: self.black_level = self.list_values( tags['Image Tag 0xC61A']) elif 'BlackLevel' in tags: self.black_level = self.list_values(tags['BlackLevel']) if 'EXIF ExposureTime' in tags: self.exposure_time = self.float_value( tags['EXIF ExposureTime']) if 'EXIF FNumber' in tags: self.fnumber = self.float_value(tags['EXIF FNumber']) if 'EXIF ISOSpeed' in tags: self.iso_speed = self.int_value(tags['EXIF ISOSpeed']) elif 'EXIF PhotographicSensitivity' in tags: self.iso_speed = self.int_value( tags['EXIF PhotographicSensitivity']) elif 'EXIF ISOSpeedRatings' in tags: self.iso_speed = self.int_value( tags['EXIF ISOSpeedRatings']) if 'Image BitsPerSample' in tags: self.bits_per_sample = self.int_value( tags['Image BitsPerSample']) if 'EXIF DateTimeOriginal' in tags: str_time = tags['EXIF DateTimeOriginal'].values utc_time = datetime.strptime(str_time, "%Y:%m:%d %H:%M:%S") subsec = 0 if 'EXIF SubSecTime' in tags: subsec = self.int_value(tags['EXIF SubSecTime']) negative = 1.0 if subsec < 0: negative = -1.0 subsec *= -1.0 subsec = float('0.{}'.format(int(subsec))) subsec *= negative ms = subsec * 1e3 utc_time += timedelta(milliseconds=ms) timezone = pytz.timezone('UTC') epoch = timezone.localize(datetime.utcfromtimestamp(0)) self.utc_time = (timezone.localize(utc_time) - epoch).total_seconds() * 1000.0 if 'MakerNote SpeedX' in tags and \ 'MakerNote SpeedY' in tags and \ 'MakerNote SpeedZ' in tags: self.speed_x = self.float_value(tags['MakerNote SpeedX']) self.speed_y = self.float_value(tags['MakerNote SpeedY']) self.speed_z = self.float_value(tags['MakerNote SpeedZ']) except Exception as e: log.ODM_WARNING("Cannot read extended EXIF tags for %s: %s" % (self.filename, str(e))) # Warn if GPS coordinates are suspiciously wrong if self.latitude is not None and self.latitude == 0 and \ self.longitude is not None and self.longitude == 0: log.ODM_WARNING( "%s has GPS position (0,0), possibly corrupted" % self.filename) # Extract XMP tags f.seek(0) xmp = self.get_xmp(f) for xtags in xmp: try: band_name = self.get_xmp_tag( xtags, ['Camera:BandName', '@Camera:BandName']) if band_name is not None: self.band_name = band_name.replace(" ", "") self.set_attr_from_xmp_tag( 'band_index', xtags, [ 'DLS:SensorId', # Micasense RedEdge '@Camera:RigCameraIndex', # Parrot Sequoia, Sentera 21244-00_3.2MP-GS-0001 'Camera:RigCameraIndex', # MicaSense Altum ]) self.set_attr_from_xmp_tag( 'radiometric_calibration', xtags, [ 'MicaSense:RadiometricCalibration', ]) self.set_attr_from_xmp_tag('vignetting_center', xtags, [ 'Camera:VignettingCenter', 'Sentera:VignettingCenter', ]) self.set_attr_from_xmp_tag( 'vignetting_polynomial', xtags, [ 'Camera:VignettingPolynomial', 'Sentera:VignettingPolynomial', ]) self.set_attr_from_xmp_tag('horizontal_irradiance', xtags, ['Camera:HorizontalIrradiance'], float) self.set_attr_from_xmp_tag( 'irradiance_scale_to_si', xtags, ['Camera:IrradianceScaleToSIUnits'], float) self.set_attr_from_xmp_tag('sun_sensor', xtags, [ 'Camera:SunSensor', ], float) self.set_attr_from_xmp_tag('spectral_irradiance', xtags, [ 'Camera:SpectralIrradiance', 'Camera:Irradiance', ], float) self.set_attr_from_xmp_tag( 'capture_uuid', xtags, [ '@drone-dji:CaptureUUID', # DJI 'MicaSense:CaptureId', # MicaSense Altum '@Camera:ImageUniqueID', # sentera 6x ]) # Camera make / model for some cameras is stored in the XMP if self.camera_make == '': self.set_attr_from_xmp_tag('camera_make', xtags, ['@tiff:Make']) if self.camera_model == '': self.set_attr_from_xmp_tag('camera_model', xtags, ['@tiff:Model']) # DJI GPS tags self.set_attr_from_xmp_tag('longitude', xtags, ['@drone-dji:Longitude'], float) self.set_attr_from_xmp_tag('latitude', xtags, ['@drone-dji:Latitude'], float) self.set_attr_from_xmp_tag('altitude', xtags, ['@drone-dji:AbsoluteAltitude'], float) # Phantom 4 RTK if '@drone-dji:RtkStdLon' in xtags: y = float( self.get_xmp_tag(xtags, '@drone-dji:RtkStdLon')) x = float( self.get_xmp_tag(xtags, '@drone-dji:RtkStdLat')) self.gps_xy_stddev = max(x, y) if '@drone-dji:RtkStdHgt' in xtags: self.gps_z_stddev = float( self.get_xmp_tag(xtags, '@drone-dji:RtkStdHgt')) else: self.set_attr_from_xmp_tag( 'gps_xy_stddev', xtags, ['@Camera:GPSXYAccuracy', 'GPSXYAccuracy'], float) self.set_attr_from_xmp_tag( 'gps_z_stddev', xtags, ['@Camera:GPSZAccuracy', 'GPSZAccuracy'], float) # DJI Speed tags if '@drone-dji:FlightXSpeed' in xtags and \ '@drone-dji:FlightYSpeed' in xtags and \ '@drone-dji:FlightZSpeed' in xtags: self.set_attr_from_xmp_tag('speed_x', xtags, ['@drone-dji:FlightXSpeed'], float) self.set_attr_from_xmp_tag('speed_y', xtags, [ '@drone-dji:FlightYSpeed', ], float) self.set_attr_from_xmp_tag('speed_z', xtags, [ '@drone-dji:FlightZSpeed', ], float) # Account for over-estimation if self.gps_xy_stddev is not None: self.gps_xy_stddev *= 2.0 if self.gps_z_stddev is not None: self.gps_z_stddev *= 2.0 if 'DLS:Yaw' in xtags: self.set_attr_from_xmp_tag('dls_yaw', xtags, ['DLS:Yaw'], float) self.set_attr_from_xmp_tag('dls_pitch', xtags, ['DLS:Pitch'], float) self.set_attr_from_xmp_tag('dls_roll', xtags, ['DLS:Roll'], float) camera_projection = self.get_xmp_tag( xtags, ['@Camera:ModelType', 'Camera:ModelType']) if camera_projection is not None: camera_projection = camera_projection.lower() if camera_projection in projections: self.camera_projection = camera_projection # OPK self.set_attr_from_xmp_tag('yaw', xtags, [ '@drone-dji:FlightYawDegree', '@Camera:Yaw', 'Camera:Yaw' ], float) self.set_attr_from_xmp_tag('pitch', xtags, [ '@drone-dji:GimbalPitchDegree', '@Camera:Pitch', 'Camera:Pitch' ], float) self.set_attr_from_xmp_tag('roll', xtags, [ '@drone-dji:GimbalRollDegree', '@Camera:Roll', 'Camera:Roll' ], float) # Normalize YPR conventions (assuming nadir camera) # Yaw: 0 --> top of image points north # Yaw: 90 --> top of image points east # Yaw: 270 --> top of image points west # Pitch: 0 --> nadir camera # Pitch: 90 --> camera is looking forward # Roll: 0 (assuming gimbal) if self.has_ypr(): if self.camera_make.lower() in ['dji', 'hasselblad']: self.pitch = 90 + self.pitch if self.camera_make.lower() == 'sensefly': self.roll *= -1 except Exception as e: log.ODM_WARNING("Cannot read XMP tags for %s: %s" % (self.filename, str(e))) # self.set_attr_from_xmp_tag('center_wavelength', xtags, [ # 'Camera:CentralWavelength' # ], float) # self.set_attr_from_xmp_tag('bandwidth', xtags, [ # 'Camera:WavelengthFWHM' # ], float) # Sanitize band name since we use it in folder paths self.band_name = re.sub('[^A-Za-z0-9]+', '', self.band_name) self.compute_focal(tags, xtags) self.compute_opk()
def parse_exif_values(self, _path_file): # Disable exifread log logging.getLogger('exifread').setLevel(logging.CRITICAL) with open(_path_file, 'rb') as f: tags = exifread.process_file(f, details=False) try: if 'Image Make' in tags: self.camera_make = tags['Image Make'].values.encode('utf8') if 'Image Model' in tags: self.camera_model = tags['Image Model'].values.encode( 'utf8') if 'GPS GPSAltitude' in tags: self.altitude = self.float_values( tags['GPS GPSAltitude'])[0] if 'GPS GPSAltitudeRef' in tags and self.int_values( tags['GPS GPSAltitudeRef'])[0] > 0: self.altitude *= -1 if 'GPS GPSLatitude' in tags and 'GPS GPSLatitudeRef' in tags: self.latitude = self.dms_to_decimal( tags['GPS GPSLatitude'], tags['GPS GPSLatitudeRef']) if 'GPS GPSLongitude' in tags and 'GPS GPSLongitudeRef' in tags: self.longitude = self.dms_to_decimal( tags['GPS GPSLongitude'], tags['GPS GPSLongitudeRef']) except IndexError as e: log.ODM_WARNING("Cannot read EXIF tags for %s: %s" % (_path_file, e.message)) # Extract XMP tags f.seek(0) xmp = self.get_xmp(f) # Find band name and camera index (if available) camera_index_tags = [ 'DLS:SensorId', # Micasense RedEdge '@Camera:RigCameraIndex', # Parrot Sequoia 'Camera:RigCameraIndex', # MicaSense Altum ] for tags in xmp: if 'Camera:BandName' in tags: cbt = tags['Camera:BandName'] band_name = None if isinstance(cbt, string_types): band_name = str(tags['Camera:BandName']) elif isinstance(cbt, dict): items = cbt.get('rdf:Seq', {}).get('rdf:li', {}) if items: band_name = " ".join(items) if band_name is not None: self.band_name = band_name.replace(" ", "") else: log.ODM_WARNING( "Camera:BandName tag found in XMP, but we couldn't parse it. Multispectral bands might be improperly classified." ) for cit in camera_index_tags: if cit in tags: self.band_index = int(tags[cit]) self.width, self.height = get_image_size.get_image_size(_path_file) # Sanitize band name since we use it in folder paths self.band_name = re.sub('[^A-Za-z0-9]+', '', self.band_name)