def parse_pyexiv2_values(self, _path_file, _force_focal, _force_ccd): # read image metadata metadata = pyexiv2.ImageMetadata(_path_file) metadata.read() # loop over image tags for key in metadata: # try/catch tag value due to weird bug in pyexiv2 # ValueError: invalid literal for int() with base 10: '' GPS = 'Exif.GPSInfo.GPS' try: # parse tag names if key == 'Exif.Image.Make': self.camera_make = metadata[key].value elif key == 'Exif.Image.Model': self.camera_model = metadata[key].value elif key == 'Exif.Photo.FocalLength': self.focal_length = float(metadata[key].value) elif key == GPS + 'Latitude': self.latitude = self.dms_to_decimal(*metadata[key].value + [metadata[GPS + 'LatitudeRef'].value]) elif key == GPS + 'Longitude': self.longitude = self.dms_to_decimal(*metadata[key].value + [metadata[GPS + 'LongitudeRef'].value]) elif key == GPS + 'Altitude': self.altitude = float(metadata[key].value) if metadata[GPS + 'AltitudeRef'] and int(metadata[GPS + 'AltitudeRef'].value) > 0: self.altitude *= -1. except (pyexiv2.ExifValueError, ValueError) as e: pass except KeyError as e: log.ODM_DEBUG('Tag not set') except NotImplementedError as e: pass 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 img = cv2.imread(_path_file) self.width = img.shape[1] self.height = img.shape[0] # force focal and ccd_width with user parameter if _force_focal: self.focal_length = _force_focal if _force_ccd: self.ccd_width = _force_ccd # find ccd_width from file if needed if self.ccd_width is None and self.camera_model is not None: # load ccd_widths from file ccd_widths = system.get_ccd_widths() # search ccd by camera model key = [x for x in ccd_widths.keys() if self.make_model in x] # convert to float if found if key: self.ccd_width = float(ccd_widths[key[0]]) else: log.ODM_WARNING('Could not find ccd_width in file. Use --force-ccd or edit the sensor_data.json ' 'file to manually input ccd width')
def __init__(self, path_file, force_focal, force_ccd): # general purpose self.path_file = path_file self.filename = io.extract_file_from_path_file(path_file) # useful attibutes self.width = None self.height = None self.ccd_width = None self.focal_length = None self.focal_length_px = None # other attributes self.camera_make = '' self.camera_model = '' self.make_model = '' self.latitude = None self.longitude = None self.altitude = None # parse values from metadata self.parse_pyexiv2_values(self.path_file, force_focal, force_ccd) # compute focal length into pixels self.update_focal() # print log message log.ODM_DEBUG('Loaded {} | camera: {} | dimensions: {} x {} | focal: {} | ccd: {} | lat: {} | lon: {} | alt: {}' .format(self.filename, self.make_model, self.width, self.height, self.focal_length, self.ccd_width, self.latitude, self.longitude, self.altitude))
def parse_coordinate_system(self, _file): """Write attributes to jobOptions from coord file""" # check for coordinate file existence if not io.file_exists(_file): log.ODM_WARNING('Could not find file %s' % _file) return with open(_file) as f: # extract reference system and utm zone from first line. # We will assume the following format: # 'WGS84 UTM 17N' or 'WGS84 UTM 17N \n' line = f.readline().rstrip() log.ODM_DEBUG('Line: %s' % line) ref = line.split(' ') # match_wgs_utm = re.search('WGS84 UTM (\d{1,2})(N|S)', line, re.I) if ref[0] == 'WGS84' and ref[1] == 'UTM': # match_wgs_utm: datum = ref[0] utm_pole = ref[2][len(ref[2]) - 1] utm_zone = int(ref[2][:len(ref[2]) - 1]) return Proj(proj="utm", zone=utm_zone, datum=datum, no_defs=True) elif '+proj' in line: return Proj(line.strip('\'')) elif 'epsg' in line.lower(): return Proj(init=line) else: raise log.ODM_ERROR('Could not parse coordinates. Bad CRS supplied: %s' % line)
def georeference_with_gcp(self, gcp_file, output_coords_file, output_gcp_file, rerun=False): if not io.file_exists(output_coords_file) or not io.file_exists( output_gcp_file) or rerun: gcp = GCPFile(gcp_file) if gcp.exists(): # Create coords file, we'll be using this later # during georeferencing with open(output_coords_file, 'w') as f: coords_header = gcp.wgs84_utm_zone() f.write(coords_header + "\n") log.ODM_DEBUG("Generated coords file from GCP: %s" % coords_header) # Convert GCP file to a UTM projection since the rest of the pipeline # does not handle other SRS well. rejected_entries = [] utm_gcp = GCPFile( gcp.create_utm_copy( output_gcp_file, filenames=[p.filename for p in self.photos], rejected_entries=rejected_entries, include_extras=False)) if not utm_gcp.exists(): raise RuntimeError( "Could not project GCP file to UTM. Please double check your GCP file for mistakes." ) for re in rejected_entries: log.ODM_WARNING("GCP line ignored (image not found): %s" % str(re)) if utm_gcp.entries_count() > 0: log.ODM_INFO( "%s GCP points will be used for georeferencing" % utm_gcp.entries_count()) else: raise RuntimeError( "A GCP file was provided, but no valid GCP entries could be used. Note that the GCP file is case sensitive (\".JPG\" is not the same as \".jpg\")." ) self.gcp = utm_gcp else: log.ODM_WARNING("GCP file does not exist: %s" % gcp_file) return else: log.ODM_INFO("Coordinates file already exist: %s" % output_coords_file) log.ODM_INFO("GCP file already exist: %s" % output_gcp_file) self.gcp = GCPFile(output_gcp_file) self.georef = ODM_GeoRef.FromCoordsFile(output_coords_file) return self.georef
def __init__(self, path_file): # Standard tags (virtually all photos have these) self.filename = io.extract_file_from_path_file(path_file) self.width = None self.height = None self.camera_make = '' self.camera_model = '' # Geo tags self.latitude = None self.longitude = None self.altitude = None # Multi-band fields self.band_name = 'RGB' self.band_index = 0 # Multi-spectral fields self.fnumber = None self.radiometric_calibration = None self.black_level = None # Capture info self.exposure_time = None self.iso_speed = None self.bits_per_sample = None self.vignetting_center = None self.vignetting_polynomial = None self.spectral_irradiance = None self.horizontal_irradiance = None self.irradiance_scale_to_si = None self.utc_time = None # DLS self.sun_sensor = None self.dls_yaw = None self.dls_pitch = None self.dls_roll = None # self.center_wavelength = None # self.bandwidth = None # RTK self.gps_xy_stddev = None # Dilution of Precision X/Y self.gps_z_stddev = None # Dilution of Precision Z # parse values from metadata self.parse_exif_values(path_file) # print log message log.ODM_DEBUG('Loaded {}'.format(self))
def parse_coordinate_system(self, _file): """Write attributes to jobOptions from coord file""" # check for coordinate file existence if not io.file_exists(_file): log.ODM_WARNING('Could not find file %s' % _file) return with open(_file) as f: # extract reference system and utm zone from first line. # We will assume the following format: # 'WGS84 UTM 17N' or 'WGS84 UTM 17N \n' line = f.readline().rstrip() log.ODM_DEBUG('Line: %s' % line) ref = line.split(' ') # match_wgs_utm = re.search('WGS84 UTM (\d{1,2})(N|S)', line, re.I) try: if ref[0] == 'WGS84' and ref[1] == 'UTM': # match_wgs_utm: datum = ref[0] utm_pole = (ref[2][len(ref[2]) - 1]).upper() utm_zone = int(ref[2][:len(ref[2]) - 1]) proj_args = { 'proj': "utm", 'zone': utm_zone, 'datum': datum, 'no_defs': True } if utm_pole == 'S': proj_args['south'] = True return Proj(**proj_args) elif '+proj' in line: return Proj(line.strip('\'')) elif 'epsg' in line.lower(): return Proj(init=line) else: log.ODM_ERROR( 'Could not parse coordinates. Bad CRS supplied: %s' % line) except RuntimeError as e: log.ODM_ERROR( 'Uh oh! There seems to be a problem with your GCP file.\n\n' 'The line: %s\n\n' 'Is not valid. Projections that are valid include:\n' ' - EPSG:*****\n' ' - WGS84 UTM **(N|S)\n' ' - Any valid proj4 string (for example, +proj=utm +zone=32 +north +ellps=WGS84 +datum=WGS84 +units=m +no_defs)\n\n' 'Modify your GCP file and try again.' % line) raise RuntimeError(e)
def __init__(self, path_file): # general purpose self.filename = io.extract_file_from_path_file(path_file) self.width = None self.height = None # other attributes self.camera_make = '' self.camera_model = '' self.make_model = '' self.latitude = None self.longitude = None self.altitude = None # parse values from metadata self.parse_exif_values(path_file) # print log message log.ODM_DEBUG('Loaded {}'.format(self))
def parse_coordinate_system(self, _file): """Write attributes to jobOptions from coord file""" # check for coordinate file existence if not io.file_exists(_file): log.ODM_ERROR('Could not find file %s' % _file) return with open(_file) as f: # extract reference system and utm zone from first line. # We will assume the following format: # 'WGS84 UTM 17N' line = f.readline() log.ODM_DEBUG('Line: %s' % line) ref = line.split(' ') # match_wgs_utm = re.search('WGS84 UTM (\d{1,2})(N|S)', line, re.I) if ref[0] == 'WGS84' and ref[1] == 'UTM': # match_wgs_utm: self.datum = ref[0] self.utm_pole = ref[2][len(ref) - 1] self.utm_zone = int(ref[2][:len(ref) - 1]) # extract east and west offsets from second line. # We will assume the following format: # '440143 4588391' # update EPSG self.epsg = self.calculate_EPSG(self.utm_zone, self.utm_pole) # If the first line looks like "EPSG:n" or "epsg:n" elif ref[0].split(':')[0].lower() == 'epsg': self.epsg = line.split(':')[1] else: log.ODM_ERROR( 'Could not parse coordinates. Bad CRS supplied: %s' % line) return offsets = f.readline().split(' ') self.utm_east_offset = int(offsets[0]) self.utm_north_offset = int(offsets[1]) # parse coordinates lines = f.readlines() for l in lines: xyz = l.split(' ') if len(xyz) == 3: x, y, z = xyz[:3] elif len(xyz) == 2: x, y = xyz[:2] z = 0 self.gcps.append(ODM_GCPoint(float(x), float(y), float(z)))
def __init__(self, path_file, force_focal, force_ccd): # general purpose self.path_file = path_file self.filename = io.extract_file_from_path_file(path_file) # useful attibutes self.width = None self.height = None self.ccd_width = None self.focal_length = None self.focal_length_px = None # other attributes self.camera_make = None self.camera_model = None self.make_model = None # parse values from metadata self.parse_pyexiv2_values(self.path_file, force_focal, force_ccd) # compute focal length into pixels self.update_focal() # print log message log.ODM_DEBUG( 'Loaded %s | camera: %s | dimensions: %s x %s | focal: %s | ccd: %s' % (self.filename, self.make_model, self.width, self.height, self.focal_length, self.ccd_width))
def utm_to_latlon(self, _file, _photo, idx): gcp = self.gcps[idx] kwargs = { 'epsg': self.epsg, 'file': _file, 'x': gcp.x + self.utm_east_offset, 'y': gcp.y + self.utm_north_offset, 'z': gcp.z } latlon = system.run_and_return( 'echo {x} {y} {z} '.format(**kwargs), 'gdaltransform -s_srs \"EPSG:{epsg}\" ' '-t_srs \"EPSG:4326\"'.format(**kwargs)).split() # Example: 83d18'16.285"W # Example: 41d2'11.789"N # Example: 0.998 if len(latlon) == 3: lon_str, lat_str, alt_str = latlon elif len(latlon) == 2: lon_str, lat_str = latlon alt_str = '' else: log.ODM_ERROR('Something went wrong %s' % latlon) lat_frac = self.coord_to_fractions(latlon[1], ['N', 'S']) lon_frac = self.coord_to_fractions(latlon[0], ['E', 'W']) # read image metadata metadata = pyexiv2.ImageMetadata(_photo.path_file) metadata.read() # set values # GPS latitude key = 'Exif.GPSInfo.GPSLatitude' value = lat_frac[0].split(' ') log.ODM_DEBUG('lat_frac: %s %s %s' % (value[0], value[1], value[2])) metadata[key] = pyexiv2.ExifTag( key, [Fraction(value[0]), Fraction(value[1]), Fraction(value[2])]) key = 'Exif.GPSInfo.GPSLatitudeRef' value = lat_frac[1] metadata[key] = pyexiv2.ExifTag(key, value) # GPS longitude key = 'Exif.GPSInfo.GPSLongitude' value = lon_frac[0].split(' ') metadata[key] = pyexiv2.ExifTag( key, [Fraction(value[0]), Fraction(value[1]), Fraction(value[2])]) key = 'Exif.GPSInfo.GPSLongitudeRef' value = lon_frac[1] metadata[key] = pyexiv2.ExifTag(key, value) # GPS altitude altitude = abs(int(float(latlon[2]) * 100)) key = 'Exif.GPSInfo.GPSAltitude' value = Fraction(altitude, 1) metadata[key] = pyexiv2.ExifTag(key, value) if latlon[2] >= 0: altref = '0' else: altref = '1' key = 'Exif.GPSInfo.GPSAltitudeRef' metadata[key] = pyexiv2.ExifTag(key, altref) # write values metadata.write()