Beispiel #1
0
    def add_image(self, image: ImageFile, encodings: List[ndarray]) -> int:
        """
        Adds one image entry to the database.
        :param image: ImageFile to add
        :return: Id of the created image record
        """
        # Check for existence
        exist_check = self.get_image_id_by_attributes(image)
        if exist_check:
            return exist_check

        # Insert images
        insert_image = """
        INSERT INTO Image 
        (filename, date_modified, size_bytes, aperture, shutter_speed, iso, date_taken, path) 
        values (?, ?, ?, ?, ?, ?, ?, ?)
        --      0  1  2  3  4  5  6  7  
        """
        dbresponse = self.connection.execute(insert_image,
                                             self.adapt_ImageFile(image))
        image.dbid = dbresponse.lastrowid

        # Insert associated encodings
        for enc in encodings:
            self.add_encoding(enc, image.dbid, image=True)

        self.connection.commit()
        image.in_database = True
        return image.dbid
Beispiel #2
0
def get_all_compatible_files(folderpath: str) -> List[ImageFile]:
    all_matching: List[str] = []
    extensions = ["*" + ext for ext in valid_extensions]
    for extension in extensions:
        all_matching.extend(
            glob(folderpath + "/**/" + extension, recursive=True))
    wrapped = [ImageFile(file) for file in all_matching]
    return wrapped
Beispiel #3
0
class TestImage(unittest.TestCase):
    test_image = ImageFile(test_data_path + "known.jpg")

    def setUp(self):
        self.test_image.clear_keywords()

    def test_clear_keywords(self):
        self.test_image.append_keywords(["TESTING keyword clearing"])
        self.test_image.clear_keywords()
        self.assertEqual(0, len(self.test_image.get_keywords()),
                         "keywords should be blank, but aren't")

    def test_append_single(self):
        rand_keyword = str(
            uuid.uuid4()
        )  # In case the clear fails, guid should prevent tests passing falsely
        self.test_image.append_keywords([rand_keyword])
        readout = self.test_image.get_keywords()
        self.assertEqual(
            1, len(readout),
            "Appending a single keyword resulted in multiple keywords written")
        self.assertEqual(rand_keyword, readout[0])

    def test_append_multiple(self):
        rand_keywords = [str(uuid.uuid4()) for i in range(3)]
        self.test_image.append_keywords(rand_keywords)
        readout = self.test_image.get_keywords()
        self.assertEqual(len(rand_keywords), len(readout))
        self.assertListEqual(rand_keywords, readout)

    def test_append_duplicate(self):
        rand_keyword = str(uuid.uuid4())
        self.test_image.append_keywords([rand_keyword])
        self.test_image.append_keywords([rand_keyword])
        self.test_image.append_keywords([rand_keyword])
        readout = self.test_image.get_keywords()
        self.assertEqual(
            1, len(readout),
            "Adding the same keyword multiple times wasn't de-duplicated.")

    def test_exif_extract(self):
        exif_data = self.test_image.get_salient_exif_data()
        self.assertEqual(1.8, exif_data["aperture"])
        self.assertEqual(1 / 200, exif_data["shutter_speed"])
        self.assertEqual(100, exif_data["iso"])
        self.assertEqual(
            datetime.strptime("2020:07:23 07:47:08",
                              ImageFile.exif_timestamp_format),
            exif_data["date_taken"])
Beispiel #4
0
def ensure_image_in_database(db: Database, image: ImageFile) -> int:
    image_id = db.get_image_id_by_attributes(image)
    if image_id:
        encodings: List[FaceEncoding] = db.get_encodings_by_image_id(image_id)
        logging.debug(
            f"File {image.filepath} already in database (image_id: {image_id}) with {len(encodings)} faces(s)."
        )
    else:  # Encode and save
        new_encodings: List[ndarray] = encode_faces(image.filepath)
        image_id = db.add_image(image, new_encodings)
        logging.debug(
            f"File {image.filepath} added to database (image_id: {image_id}) with {len(new_encodings)} face(s)."
        )
    image.dbid = image_id
    return image_id
Beispiel #5
0
    def adapt_ImageFile(cls, image: ImageFile, include_path: bool = True):
        # Date and time stamp of the last time the file was modified
        mtime = cls.get_formatted_date_modified(image.filepath)

        exif_data = image.get_salient_exif_data()
        date_taken = exif_data.get("date_taken")
        date_taken = date_taken and date_taken.strftime(
            cls.datetime_format_string)  # Null conditional

        # Relative path is last because we won't always use it
        output = [
            os.path.basename(image.filepath),  # 0
            mtime,  # 1
            os.path.getsize(image.filepath),  # 2
            exif_data.get("aperture"),  # 3
            exif_data.get("shutter_speed"),  # 4
            exif_data.get("iso"),  # 5
            date_taken  # 6
        ]
        if include_path:
            output.append(image.filepath)  # 7

        return output
Beispiel #6
0
class TestFaceRecognizer(unittest.TestCase):
    my_dir = path.dirname(__file__)

    known_image = ImageFile(test_data_path + "known.jpg", skip_md_init=True)
    test_face = FaceEncoding(-1, encode_faces(known_image.filepath)[0])
    test_person = Person(-1, "will", [test_face])

    will_as_unknown = ImageFile(test_data_path + "man.jpg", skip_md_init=True)
    mushroom = ImageFile(test_data_path + "mushroom.jpg", skip_md_init=True)
    multiple_people = ImageFile(test_data_path + "people.jpg",
                                skip_md_init=True)
    different_person = ImageFile(test_data_path + "woman right.jpg",
                                 skip_md_init=True)

    # Should match when same person
    def test_one_to_one_match(self):
        self.will_as_unknown.encodings_in_image = [
            FaceEncoding(-1, fe)
            for fe in encode_faces(self.will_as_unknown.filepath)
        ]
        best_matches = match_best([self.test_person],
                                  self.will_as_unknown.encodings_in_image)
        self.assertEqual(len(best_matches), 1,
                         "face didn't match itself in another picture")

    @unittest.skip("Not yet implemented")
    def test_multiple_pictures_per_known(self):
        # Requires more convoluted test setup for the known person
        known_images = []
        encode_faces(known_images)
        test_person = Person("will")
        test_person.encodings = [
            enc for im in known_images for enc in im.encodings_in_image
        ]  # Flat?

        unknown_images = encode_faces(self.will_as_unknown.filepath)
        best_matches = match_best([test_person],
                                  unknown_images[0].encodings_in_image)
        self.assertEqual(
            1, len(best_matches),
            "couldn't match a face using multiple images for known person")

        unknown_images = encode_faces(self.sam_will_trail.filepath)
        best_matches = match_best([test_person],
                                  unknown_images[0].encodings_in_image)
        self.assertEqual(
            1, len(best_matches),
            "couldn't match a face using multiple images for known person")

    # Should work with multiple matches in picture
    def test_multiple_unknown_in_picture(self):
        unknown_faces = [
            FaceEncoding(-1, fe)
            for fe in encode_faces(self.multiple_people.filepath)
        ]
        best_matches = match_best([self.test_person], unknown_faces)
        self.assertEqual(
            1, len(best_matches),
            "face didn't match with itself in a picture with other people as well"
        )

    # Shouldn't match on different person
    def test_no_match(self):
        unknown_faces = [
            FaceEncoding(-1, fe)
            for fe in encode_faces(self.different_person.filepath)
        ]
        best_matches = match_best([self.test_person], unknown_faces)
        self.assertEqual(0, len(best_matches),
                         "face matched against a different face")

    # Shouldn't match on a mushroom
    def test_not_a_person(self):
        unknown_faces = [
            FaceEncoding(-1, fe) for fe in encode_faces(self.mushroom.filepath)
        ]
        faces_found = len(unknown_faces)
        self.assertEqual(0, faces_found,
                         "found a face match when looking at a mushroom")
Beispiel #7
0
class TestDatabase(unittest.TestCase):
    # DB Info
    test_db_path = "test.db"

    # Set up a basic image to test with
    test_image_path = test_data_path + "known.jpg"
    test_image = ImageFile(test_image_path)
    base_test_image = ImageFile(
        test_image_path
    )  # Will be copied, since many DB calls are writing to the input image
    base_test_image.encodings_in_image = [
        FaceEncoding(-1, enc) for enc in encode_faces(base_test_image.filepath)
    ]

    test_person = Person(-1, "Will", base_test_image.encodings_in_image)

    base_test_image.matched_people = [test_person]
    test_person.encodings = [base_test_image.encodings_in_image[0]]

    def setUp(self):
        self.test_db = Database(self.test_db_path)

        # Clean out the database
        self.test_db.connection.executescript("DELETE FROM Image")
        self.test_db.connection.executescript("DELETE FROM Person")
        self.test_db.connection.executescript("DELETE FROM Encoding")
        self.test_db.connection.executescript("DELETE FROM PersonEncoding")
        self.test_db.connection.executescript("DELETE FROM ImageEncoding")

        # Clone the test image to reduce time spent encoding
        self.this_test_image: ImageFile = deepcopy(self.base_test_image)

    def test_add_image(self):
        # This will fail if anything else does, which isn't ideal, but I want to make sure it's thorough
        new_image_id = self.test_db.add_image(self.this_test_image, [])
        dbresponse = self.test_db.connection.execute("SELECT * FROM Image")
        result = dbresponse.fetchall()
        test_row = result[0]

        self.assertEqual(
            1, len(result),
            "Wrong number of images inserted when trying to add one")
        second_image_id = self.test_db.add_image(self.this_test_image, [])
        self.assertEqual(new_image_id, second_image_id,
                         "Inserted same file twice, got different Ids")
        self.assertEqual(os.path.getsize(self.this_test_image.filepath),
                         test_row["size_bytes"])

    def test_encoding_ops(self):
        self.test_db.connection.executescript("DELETE FROM Encoding")
        self.assertRaises(
            Exception,
            self.test_db.add_encoding,
            [self.this_test_image.encodings_in_image],
            msg=
            "Expected a failure while trying to add an unassociated encoding, but succeeded."
        )

        # Strip and store an encoding
        test_encoding = self.this_test_image.encodings_in_image[0].encoding
        self.this_test_image.encodings_in_image = []
        id_of_partial_image = self.test_db.add_image(self.this_test_image,
                                                     [test_encoding])
        self.test_db.connection.commit()
        new_enc = self.test_db.add_encoding(test_encoding,
                                            associate_id=id_of_partial_image,
                                            image=True)

        dbresponse = self.test_db.connection.execute("SELECT * FROM Encoding")
        result = dbresponse.fetchall()

        retrieved_encoding_bytes = result[0]["encoding"]
        retrieved_encoding: numpy.ndarray = numpy.frombuffer(
            retrieved_encoding_bytes, "float64")

        self.assertTrue(numpy.array_equal(test_encoding, retrieved_encoding),
                        "Encoding didn't survive being stored and retrieved")
Beispiel #8
0
from os import listdir
from os import path
import pyexiv2 as pe2
from pprint import pprint as pp
from Model.ImageFile import ImageFile

scan_path = r"..\test-data\unknown"
images_to_scan = [
    ImageFile(path.join(scan_path, f)) for f in listdir(scan_path)
    if path.isfile(path.join(scan_path, f))
]

image = ImageFile(r"..\test-data\unknown\sam tongue out.jpg")

for image in images_to_scan:
    image.clear_keywords()

    print("Reading file ", image.filepath)
    with open(image.filepath, 'rb+') as file:
        with pe2.ImageData(file.read()) as imdat:
            data = imdat.read_exif(encoding=ImageFile.normal_encoding)
            print(data.get("Exif.Photo.ISOSpeedRatings"))