Пример #1
0
    def parse_exif_values(self, _path_file, _force_focal, _force_ccd):
        # 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 'EXIF FocalLength' in tags:
                    self.focal_length = self.float_values(
                        tags['EXIF FocalLength'])[0]
                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
        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')
Пример #2
0
    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
Пример #3
0
    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]
Пример #4
0
    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)
Пример #5
0
    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')
Пример #6
0
    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)
Пример #7
0
    def georeference_with_gps(self, images_path, output_coords_file, rerun=False):
        try:
            if not io.file_exists(output_coords_file) or rerun:
                location.extract_utm_coords(self.photos, images_path, output_coords_file)
            else:
                log.ODM_INFO("Coordinates file already exist: %s" % output_coords_file)
            
            self.georef = ODM_GeoRef.FromCoordsFile(output_coords_file)
        except:
            log.ODM_WARNING('Could not generate coordinates file. The orthophoto will not be georeferenced.')

        self.gcp = GCPFile(None)
        return self.georef
Пример #8
0
    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)
Пример #9
0
    def FromCoordsFile(coords_file):
        # check for coordinate file existence
        if not io.file_exists(coords_file):
            log.ODM_WARNING('Could not find file %s' % coords_file)
            return

        srs = None

        with open(coords_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()
            srs = location.parse_srs_header(line)

        return ODM_GeoRef(srs)
Пример #10
0
    def get_xmp(self, file):
        img_str = str(file.read())
        xmp_start = img_str.find('<x:xmpmeta')
        xmp_end = img_str.find('</x:xmpmeta')

        if xmp_start < xmp_end:
            xmp_str = img_str[xmp_start:xmp_end + 12]
            try:
                xdict = x2d.parse(xmp_str)
            except ExpatError:
                from bs4 import BeautifulSoup
                xmp_str = str(BeautifulSoup(xmp_str, 'xml'))
                xdict = x2d.parse(xmp_str)
                log.ODM_WARNING("%s has malformed XMP XML (but we fixed it)" %
                                self.filename)
            xdict = xdict.get('x:xmpmeta', {})
            xdict = xdict.get('rdf:RDF', {})
            xdict = xdict.get('rdf:Description', {})
            if isinstance(xdict, list):
                return xdict
            else:
                return [xdict]
        else:
            return []
Пример #11
0
    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_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, e.message))

            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.encode(
                        'utf8')
                    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, e.message))

            # 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)

                    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, e.message))

                # 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)
Пример #12
0
    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)