예제 #1
0
 def getMeta(self, files):
     et = ExifTool()
     et.start()
     a = et.get_metadata_batch(files)
     et.terminate()
     meta = self._removeErrors(a)
     return meta
예제 #2
0
class FlirExtractor:
    """Extracts thermal data from FLIR images using ExifTool.

    Attributes:
        exiftoolpath: The path to the ExifTool executable.

    Example:
        with FlirExtractor(exiftoolpath="/usr/bin/exiftool") as extractor:
            # get a single thermal data point
            thermal_data = extractor.get_thermal("./path/to/FLIR.jpg")
            # get multiple thermal data
            thermal_d_list = extractor.get_thermal_batch(
                ["./FLIR1.jpg", "./FLIR2.jpg"]
            )
    """

    exiftoolpath: Optional[Path]
    _exiftool: Optional[ExifTool]

    def __init__(self, exiftoolpath: Path = exiftool_default_exe):
        self.exiftoolpath = exiftoolpath
        self._exiftool = None

    @property
    def exiftool(self) -> ExifTool:
        _exiftool = self._exiftool
        if _exiftool is None:
            raise AttributeError(
                "ExifTool was not initialized. "
                "Use FlirExtractor in a context manager, e.g. \n"
                "with FlirExtractor() as e:\n"
                "    e.do_magic()")
        return _exiftool

    def open(self):
        """Creates an Exiftool process.

        Not recommended, use `with:` context manager instead.
        """
        if self._exiftool is not None:
            raise Exception("ExifTool was already initialized.")
        self._exiftool = ExifTool(executable_=str(self.exiftoolpath))
        self._exiftool.start()

    def close(self):
        """Closes the Exiftool process.

        Not recommended, use `with:` context manager instead.
        """
        if self._exiftool is None:
            return  # already closed, do nothing
        self._exiftool.terminate()
        self._exiftool = None

    def __enter__(self):
        self.open()
        return self

    def __exit__(self, exception_type, exception_value, traceback):
        self.close()

    def get_thermal(self, filepath: Path) -> "np.ndarray":
        """Gets a thermal image from a FLIR file.

        Parameters:
            filepath: The path to the FLIR file.

        Returns:
            The thermal data in Celcius as a 2-D numpy array.
        """
        return get_thermal(self.exiftool, filepath)

    def get_thermal_batch(self,
                          filepaths: Iterable[Path]) -> Iterable["np.ndarray"]:
        """Gets thermal images from a list of FLIR files.

        Parameters:
            filepaths: The paths to the FLIR files.

        Returns:
            A list of the thermal data in Celcius as 2-D numpy arrays.
        """
        return get_thermal_batch(self.exiftool, filepaths)
예제 #3
0
class TestExifManager(OPennTestCase):

    staging_dir      = os.path.join(os.path.dirname(__file__), 'staging')
    source_dir       = os.path.join(os.path.dirname(__file__), 'data/mscodex1223')
    test_image_names = [ 'mscodex1223_wk1_body0001.tif', 'mscodex1223_wk1_body0002.tif', 'mscodex1223_wk1_body0003.tif' ]
    test_images      = [ os.path.join(source_dir, img) for img in test_image_names ]
    staged_images    = [ os.path.join(staging_dir, img) for img in test_image_names ]
    img_desc         = { 'ImageDescription': 'This is the image description' }
    img_desc_nl      = { 'ImageDescription': """This is the two-line
description""" }
    xmp_marked       = { 'xmpRights:Marked': 'true' }
    # the following has a unicode (c) symbol
    dc_rights        = { 'dc:Rights': 'The dc rights ©2014' }
    tag_dict         = { 'Marked': True, 'rights': 'The dc rights ©2014' }

    def setUp(self):
        if not os.path.exists(TestExifManager.staging_dir):
            os.mkdir(TestExifManager.staging_dir)
        self._exiftool = ExifTool()

    def tearDown(self):
        if os.path.exists(TestExifManager.staging_dir):
           shutil.rmtree(TestExifManager.staging_dir)
        self._exiftool.terminate()

    def get_metadata(self,img):
        with ExifTool() as et:
            md = et.get_metadata(img)
        return md

    def stage_images(self):
        for f in TestExifManager.test_images:
            shutil.copy(f, TestExifManager.staging_dir)

    def stage_image(self, index=0):
        shutil.copy(TestExifManager.test_images[index],
                TestExifManager.staged_images[index])
        return TestExifManager.staged_images[index]

    def test_init(self):
        self.assertIsInstance(ExifManager(), ExifManager)

    def test_add_tag(self):
        img = self.stage_image(0)
        xman = ExifManager()
        xman.add_metadata([img], TestExifManager.img_desc)
        md = self.get_metadata(img)
        self.assertIn('EXIF:ImageDescription', md)
        self.assertIn('the image description', md['EXIF:ImageDescription'])

    def test_add_nl_tag(self):
        img = self.stage_image(0)
        xman = ExifManager()
        xman.add_metadata([img], TestExifManager.img_desc_nl)
        md = self.get_metadata(img)
        self.assertIn('EXIF:ImageDescription', md)
        self.assertIn(TestExifManager.img_desc_nl.values()[0], md['EXIF:ImageDescription'])

    def test_add_xmp_metadata(self):
        self.stage_images()
        xman = ExifManager()
        xman.add_metadata(TestExifManager.staged_images, TestExifManager.xmp_marked)
        for img in TestExifManager.staged_images:
            md = self.get_metadata(img)
            self.assertIn('XMP:Marked', md)
            self.assertTrue(os.path.exists(img + "_original"))

    def test_add_json_metadata(self):
        self.stage_images()
        xman = ExifManager()
        xman.add_json_metadata(TestExifManager.staged_images, TestExifManager.tag_dict)
        for img in TestExifManager.staged_images:
            md = self.get_metadata(img)
            self.assertIn('XMP:Marked', md)
            self.assertIn('XMP:Rights', md)
            self.assertTrue(os.path.exists(img + "_original"))

    def test_add_xmp_metadata_overwrite(self):
        self.stage_images()
        xman = ExifManager()
        xman.add_metadata(TestExifManager.staged_images, TestExifManager.xmp_marked,
               overwrite_original=True)
        for img in TestExifManager.staged_images:
            md = self.get_metadata(img)
            self.assertIn('XMP:Marked', md)
            self.assertFalse(os.path.exists(img + "_original"))

    def test_add_json_metadata_overwrite(self):
        self.stage_images()
        xman = ExifManager()
        xman.add_json_metadata(TestExifManager.staged_images, TestExifManager.tag_dict,
                overwrite_original=True)
        for img in TestExifManager.staged_images:
            md = self.get_metadata(img)
            self.assertIn('XMP:Marked', md)
            self.assertIn('XMP:Rights', md)
            self.assertFalse(os.path.exists(img + "_original"))
예제 #4
0
class ExifManager(object):
    xmp_re = re.compile(':')
    newline_re = re.compile('\n')
    logger = logging.getLogger(__name__)

    def __init__(self):
        self._exiftool = ExifTool()
        self._tempfiles = set()

    def __del__(self):
        self.stop()

    def serialize_xmp(self, file_list, **kwargs):
        args = {'keep_open': True}
        args.update(kwargs)
        counter = CountLogger(self.logger, file_list)
        counter.count(msg='Serialize XMP', inc=False)
        for path in file_list:
            self.serialize_xmp_one_file(path, **args)
            counter.count(msg="XMP for %s" % (os.path.basename(path), ))
        self.stop()

    def serialize_xmp_one_file(self, path, **kwargs):
        keep_open = kwargs.get('keep_open', False)
        kwargs.pop('keep_open', None)

        if not self._exiftool.running:
            self.start()
        xmp = "%s.xmp" % (path, )
        tags = ['-tagsFromFile', path, xmp]
        self._exiftool.execute(*tags)

        if not keep_open:
            self.stop()

    def get_tag(self, tag, file):
        self.start()
        value = self._exiftool.get_tag(tag, file)
        self.stop()

        return value

    def add_metadata(self, file_list, prop_dict, overwrite_original=False):
        self.start()
        for file in file_list:
            self._add_md_to_file(file, prop_dict, overwrite_original)
        self.stop()

    def add_json_metadata(self,
                          file_list,
                          prop_dict,
                          overwrite_original=False):
        self.start()
        for file in file_list:
            self._add_json_md_to_file(file, prop_dict, overwrite_original)
        self.stop()

    def add_json_one_file(self, filename, prop_dict, **kwargs):
        """
        kwargs can be:

            overwrite_original : True|[False]
            keep_open          : True|[False] (keep exiftool instance running)
        """
        keep_open = kwargs.get('keep_open', False)
        kwargs.pop('keep_open', None)

        if not self._exiftool.running:
            self.start()

        self._add_json_md_to_file(filename, prop_dict, **kwargs)

        if not keep_open:
            self.stop()

    def add_md_one_file(self, filename, prop_dict, **kwargs):
        """
        kwargs can be:

            overwrite_original : True|[False]
            keep_open          : True|[False] (keep exiftool instance running)
        """
        keep_open = kwargs.get('keep_open', False)
        kwargs.pop('keep_open', None)

        if not self._exiftool.running:
            self.start()

        self._add_md_to_file(filename, prop_dict, **kwargs)

        if not keep_open:
            self.stop()

    def _add_json_md_to_file(self, file, prop_dict, overwrite_original=False):
        dct = {'SourceFile': file}
        dct.update(**prop_dict)
        path = self._to_json_file(dct)
        tags = ['-json=%s' % path, file]
        if overwrite_original:
            tags.insert(0, '-overwrite_original')
        return self._exiftool.execute(*tags)

    def _add_md_to_file(self, file, prop_dict, overwrite_original=False):
        tags = self._build_tags(prop_dict)
        if overwrite_original:
            tags.insert(0, '-overwrite_original')
        tags.append(file)
        self._exiftool.execute(*tags)

    def start(self):
        self._exiftool.start()

    def stop(self):
        self._exiftool.terminate()
        self._cleanup()

    def _build_tags(self, prop_dict):
        tags = []
        for key in prop_dict:
            value = prop_dict.get(key)
            if isinstance(value, list) or isinstance(value, tuple):
                for v in value:
                    tags.append(self._build_tag(key, v))
            else:
                tags.append(self._build_tag(key, value))
        return tags

    def _build_tag(self, name, value):
        if ExifManager.newline_re.search(value):
            path = self._value_to_file(value)
            return "%s<=%s" % (self._tag(name), path)
        else:
            return "%s=%s" % (self._tag(name), value.encode('utf-8'))

    def _tag(self, name):
        if ExifManager.xmp_re.search(name):
            return "-xmp-%s" % name
        else:
            return "-%s" % name

    def _to_json_file(self, prop_dict):
        return self._value_to_file(json.dumps(prop_dict))

    def _value_to_file(self, value):
        f = tempfile.NamedTemporaryFile(delete=False)
        f.write(value.encode('utf-8'))
        f.flush()
        f.seek(0)
        name = f.name
        self._tempfiles.add(name)
        return name

    def _cleanup(self):
        for f in self._tempfiles:
            try:
                os.remove(f)
            except OSError:
                pass
        self._tempfiles.clear()