Exemplo n.º 1
0
    def predict(self, image_file=None, location=None):
        if location:
            lon, lat = location
        else:
            metadata = PhotoMetadata(image_file)
            location = metadata.get('GPS Position') and parse_gps_location(
                metadata.get('GPS Position')) or None
            if location:
                lon, lat = location
            else:
                return {
                    'country': None,
                    'city': None,
                }

        country = self.get_country(lon=lon, lat=lat)
        if country:
            city = self.get_city(lon=lon,
                                 lat=lat,
                                 country_code=country['code'])
        else:
            city = self.get_city(lon=lon, lat=lat)

        if not country and city:
            country = {
                'name': city['country_name'],
            }

        return {
            'country': country,
            'city': city,
        }
Exemplo n.º 2
0
 def resolve_photo_file_metadata(self, info, **kwargs):
     """Return metadata for photofile."""
     photo_file = PhotoFile.objects.filter(id=kwargs.get('photo_file_id'))
     if photo_file and os.path.exists(photo_file[0].path):
         metadata = PhotoMetadata(photo_file[0].path)
         return {'data': metadata.get_all(), 'ok': True}
     return {'ok': False}
Exemplo n.º 3
0
def get_thumbnail(photo,
                  width=256,
                  height=256,
                  crop='cover',
                  quality=75,
                  return_type='path',
                  force_regenerate=False):
    if not isinstance(photo, Photo):
        photo = Photo.objects.get(id=photo)

    # If thumbnail image was previously generated and we weren't told to re-generate, return that one
    output_path = get_thumbnail_path(photo, width, height, crop, quality)
    if os.path.exists(output_path):
        if return_type == 'bytes':
            return open(output_path, 'rb').read()
        else:
            return output_path

    # Read base image and metadata
    input_path = photo.base_image_path
    im = Image.open(input_path)

    if im.mode != 'RGB':
        im = im.convert('RGB')

    metadata = PhotoMetadata(input_path)

    # Perform rotations if decalared in metadata
    if metadata.get('Orientation') in ['Rotate 90 CW', 'Rotate 270 CCW']:
        im = im.rotate(-90, expand=True)
    elif metadata.get('Orientation') in ['Rotate 90 CCW', 'Rotate 270 CW']:
        im = im.rotate(90, expand=True)

    # Crop / resize
    if crop == 'cover':
        im = ImageOps.fit(im, (width, height), Image.ANTIALIAS)
    else:
        im.thumbnail((width, height), Image.ANTIALIAS)

    # Save to disk (keeping the bytes in memory if we need to return them)
    if return_type == 'bytes':
        img_byte_array = io.BytesIO()
        im.save(img_byte_array, format='JPEG', quality=quality)
        with open(output_path, 'wb') as f:
            f.write(img_byte_array.getvalue())
    else:
        im.save(output_path, format='JPEG', quality=quality)

    # Update Photo DB model
    photo.last_thumbnailed_version = 0
    photo.last_thumbnailed_at = timezone.now()
    photo.save()

    # Return accordingly
    if return_type == 'bytes':
        return img_byte_array.getvalue()
    return output_path
Exemplo n.º 4
0
def test_metadata():
    # General EXIF metadata
    photo_path = str(Path(__file__).parent / 'photos' / 'snow.jpg')
    metadata = PhotoMetadata(photo_path)
    assert metadata.get('Image Size') == '800x600'
    assert metadata.get('Date Time') == '2018:02:28 07:16:25'
    assert metadata.get('Make') == 'Xiaomi'
    assert metadata.get('ISO') == '100'

    # Ignore invalid UTF-8 that might be in the metadata
    photo_path = str(Path(__file__).parent / 'photos' / 'invalid_utf8.jpg')
    metadata = PhotoMetadata(photo_path)
    assert len(metadata.get_all().keys()) > 30
    assert metadata.get('Artist') == ''
Exemplo n.º 5
0
    def predict(self, image_file, min_score=0.99):
        # Detects face bounding boxes
        image = Image.open(image_file)

        if image.mode != 'RGB':
            image = image.convert('RGB')

        # Perform rotations if decalared in metadata
        metadata = PhotoMetadata(image_file)
        if metadata.get('Orientation') in ['Rotate 90 CW', 'Rotate 270 CCW']:
            image = image.rotate(-90, expand=True)
        elif metadata.get('Orientation') in ['Rotate 90 CCW', 'Rotate 270 CW']:
            image = image.rotate(90, expand=True)

        image = np.asarray(image)
        results = self.graph['mtcnn'].detect_faces(image)
        return list(filter(lambda f: f['confidence'] > min_score, results))
Exemplo n.º 6
0
    def predict(self, image_file, min_score=0.1):
        image = Image.open(image_file)

        if image.mode != 'RGB':
            image = image.convert('RGB')

        # Perform rotations if decalared in metadata
        metadata = PhotoMetadata(image_file)
        if metadata.get('Orientation') in ['Rotate 90 CW', 'Rotate 270 CCW']:
            image = image.rotate(-90, expand=True)
        elif metadata.get('Orientation') in ['Rotate 90 CCW', 'Rotate 270 CW']:
            image = image.rotate(90, expand=True)

        # the array based representation of the image will be used later in order to prepare the
        # result image with boxes and labels on it.
        image_np = self.load_image_into_numpy_array(image)
        # Expand dimensions since the model expects images to have shape: [1, None, None, 3]
        np.expand_dims(image_np, axis=0)
        # Actual detection.
        output_dict = self.run_inference_for_single_image(image_np)
        return self.format_output(output_dict, min_score)
Exemplo n.º 7
0
 def predict(self, image_file):
     metadata = PhotoMetadata(image_file)
     date_taken = None
     possible_date_keys = ['Date/Time Original', 'Date Time Original', 'Date/Time', 'Date Time', 'GPS Date/Time', 'Modify Date', 'File Modification Date/Time']
     for date_key in possible_date_keys:
         date_taken = parse_datetime(metadata.get(date_key))
         if date_taken:
             events = {
                 datetime.date(date_taken.year, 12, 25): "Christmas Day",
                 datetime.date(date_taken.year, 10, 31): "Halloween",
                 datetime.date(date_taken.year, 2, 14): "Valentine's Day",
                 datetime.date(date_taken.year, 12, 31): "New Year Start",
                 datetime.date(date_taken.year, 1, 1): "New Year End",
             }
             if events.get(date_taken.date()):
                 if events.get(date_taken.date()).startswith("New Year"):
                     start_of_day = datetime.datetime.combine(datetime.date(date_taken.year, 12, 31), datetime.datetime.min.time())
                     end_of_day = start_of_day + datetime.timedelta(days=1)
                     if start_of_day <= date_taken.replace(tzinfo=None) <= end_of_day:
                         return ['New Year']
                 return [events.get(date_taken.date())]
     return []
Exemplo n.º 8
0
def test_metadata():
    # General exif metadata
    photo_path = str(Path(__file__).parent / 'photos' / 'snow.jpg')
    metadata = PhotoMetadata(photo_path)
    assert metadata.get('Image Size') == '800x600'
    assert metadata.get('Date Time') == '2018:02:28 07:16:25'
    assert metadata.get('Make') == 'Xiaomi'
    assert metadata.get('ISO') == '100'
Exemplo n.º 9
0
Arquivo: db.py Projeto: zdqf/photonix
def record_photo(path):
    file_modified_at = datetime.fromtimestamp(os.stat(path).st_mtime, tz=utc)

    try:
        photo_file = PhotoFile.objects.get(path=path)
    except PhotoFile.DoesNotExist:
        photo_file = PhotoFile()

    if photo_file and photo_file.file_modified_at == file_modified_at:
        return False

    metadata = PhotoMetadata(path)
    date_taken = parse_datetime(metadata.get('Date/Time Original'))

    camera = None
    camera_make = metadata.get('Make')
    camera_model = metadata.get('Camera Model Name')
    if camera_model:
        camera_model = camera_model.replace(camera_make, '').strip()
    if camera_make and camera_model:
        try:
            camera = Camera.objects.get(make=camera_make, model=camera_model)
            if date_taken < camera.earliest_photo:
                camera.earliest_photo = date_taken
                camera.save()
            if date_taken > camera.latest_photo:
                camera.latest_photo = date_taken
                camera.save()
        except Camera.DoesNotExist:
            camera = Camera(make=camera_make,
                            model=camera_model,
                            earliest_photo=date_taken,
                            latest_photo=date_taken)
            camera.save()

    lens = None
    lens_name = metadata.get('Lens ID')
    if lens_name:
        try:
            lens = Lens.objects.get(name=lens_name)
            if date_taken < lens.earliest_photo:
                lens.earliest_photo = date_taken
                lens.save()
            if date_taken > lens.latest_photo:
                lens.latest_photo = date_taken
                lens.save()
        except Lens.DoesNotExist:
            lens = Lens(name=lens_name,
                        earliest_photo=date_taken,
                        latest_photo=date_taken)
            lens.save()

    photo = None
    if date_taken:
        try:
            # TODO: Match on file number/file name as well
            photo = Photo.objects.get(taken_at=date_taken)
        except Photo.DoesNotExist:
            pass

    latitude = None
    longitude = None
    if metadata.get('GPS Position'):
        latitude, longitude = parse_gps_location(metadata.get('GPS Position'))

    if not photo:
        # Save Photo
        photo = Photo(
            taken_at=date_taken,
            taken_by=metadata.get('Artist') or None,
            aperture=metadata.get('Aperture')
            and Decimal(metadata.get('Aperture')) or None,
            exposure=metadata.get('Exposure Time') or None,
            iso_speed=metadata.get('ISO') and int(metadata.get('ISO')) or None,
            focal_length=metadata.get('Focal Length')
            and metadata.get('Focal Length').split(' ', 1)[0] or None,
            flash=metadata.get('Flash')
            and 'on' in metadata.get('Flash').lower() or False,
            metering_mode=metadata.get('Metering Mode') or None,
            drive_mode=metadata.get('Drive Mode') or None,
            shooting_mode=metadata.get('Shooting Mode') or None,
            camera=camera,
            lens=lens,
            latitude=latitude,
            longitude=longitude,
            altitude=metadata.get('GPS Altitude')
            and metadata.get('GPS Altitude').split(' ')[0])
        photo.save()

    # Save PhotoFile
    photo_file.photo = photo
    photo_file.path = path
    photo_file.width = metadata.get('Image Width')
    photo_file.height = metadata.get('Image Height')
    photo_file.mimetype = mimetypes.guess_type(path)[0]
    photo_file.file_modified_at = file_modified_at
    photo_file.bytes = os.stat(path).st_size
    photo_file.preferred = False  # TODO
    photo_file.save()

    # Create task to ensure JPEG version of file exists (used for thumbnailing, analysing etc.)
    Task(type='ensure_raw_processed',
         subject_id=photo.id,
         complete_with_children=True).save()

    return photo
Exemplo n.º 10
0
def record_photo(path, library, inotify_event_type=None):
    logger.info(f'Recording photo {path}')

    mimetype = get_mimetype(path)

    if not imghdr.what(
            path) and not mimetype in MIMETYPE_WHITELIST and subprocess.run(
                ['dcraw', '-i', path]).returncode:
        logger.error(f'File is not a supported type: {path} ({mimetype})')
        return None

    if type(library) == Library:
        library_id = library.id
    else:
        library_id = str(library)
    try:
        photo_file = PhotoFile.objects.get(path=path)
    except PhotoFile.DoesNotExist:
        photo_file = PhotoFile()

    if inotify_event_type in ['DELETE', 'MOVED_FROM']:
        if PhotoFile.objects.filter(path=path).exists():
            return delete_photo_record(photo_file)
        else:
            return True

    file_modified_at = datetime.fromtimestamp(os.stat(path).st_mtime, tz=utc)

    if photo_file and photo_file.file_modified_at == file_modified_at:
        return True

    metadata = PhotoMetadata(path)
    date_taken = None
    possible_date_keys = [
        'Create Date', 'Date/Time Original', 'Date Time Original', 'Date/Time',
        'Date Time', 'GPS Date/Time', 'File Modification Date/Time'
    ]
    for date_key in possible_date_keys:
        date_taken = parse_datetime(metadata.get(date_key))
        if date_taken:
            break
    # If EXIF data not found.
    date_taken = date_taken or datetime.strptime(
        time.ctime(os.path.getctime(path)), "%a %b %d %H:%M:%S %Y")

    camera = None
    camera_make = metadata.get('Make', '')[:Camera.make.field.max_length]
    camera_model = metadata.get('Camera Model Name', '')
    if camera_model:
        camera_model = camera_model.replace(camera_make, '').strip()
    camera_model = camera_model[:Camera.model.field.max_length]
    if camera_make and camera_model:
        try:
            camera = Camera.objects.get(library_id=library_id,
                                        make=camera_make,
                                        model=camera_model)
            if date_taken < camera.earliest_photo:
                camera.earliest_photo = date_taken
                camera.save()
            if date_taken > camera.latest_photo:
                camera.latest_photo = date_taken
                camera.save()
        except Camera.DoesNotExist:
            camera = Camera(library_id=library_id,
                            make=camera_make,
                            model=camera_model,
                            earliest_photo=date_taken,
                            latest_photo=date_taken)
            camera.save()

    lens = None
    lens_name = metadata.get('Lens ID')
    if lens_name:
        try:
            lens = Lens.objects.get(name=lens_name)
            if date_taken < lens.earliest_photo:
                lens.earliest_photo = date_taken
                lens.save()
            if date_taken > lens.latest_photo:
                lens.latest_photo = date_taken
                lens.save()
        except Lens.DoesNotExist:
            lens = Lens(library_id=library_id,
                        name=lens_name,
                        earliest_photo=date_taken,
                        latest_photo=date_taken)
            lens.save()

    photo = None
    if date_taken:
        try:
            # TODO: Match on file number/file name as well
            photo = Photo.objects.get(taken_at=date_taken)
        except Photo.DoesNotExist:
            pass

    latitude = None
    longitude = None
    if metadata.get('GPS Position'):
        latitude, longitude = parse_gps_location(metadata.get('GPS Position'))

    iso_speed = None
    if metadata.get('ISO'):
        try:
            iso_speed = int(re.search(r'[0-9]+', metadata.get('ISO')).group(0))
        except AttributeError:
            pass
    if not photo:
        # Save Photo
        aperture = None
        aperturestr = metadata.get('Aperture')
        if aperturestr:
            try:
                aperture = Decimal(aperturestr)
                if aperture.is_infinite():
                    aperture = None
            except:
                pass

        photo = Photo(
            library_id=library_id,
            taken_at=date_taken,
            taken_by=metadata.get(
                'Artist', '')[:Photo.taken_by.field.max_length] or None,
            aperture=aperture,
            exposure=metadata.get(
                'Exposure Time', '')[:Photo.exposure.field.max_length] or None,
            iso_speed=iso_speed,
            focal_length=metadata.get('Focal Length')
            and metadata.get('Focal Length').split(' ', 1)[0] or None,
            flash=metadata.get('Flash')
            and 'on' in metadata.get('Flash').lower() or False,
            metering_mode=metadata.get(
                'Metering Mode', '')[:Photo.metering_mode.field.max_length]
            or None,
            drive_mode=metadata.get(
                'Drive Mode', '')[:Photo.drive_mode.field.max_length] or None,
            shooting_mode=metadata.get(
                'Shooting Mode', '')[:Photo.shooting_mode.field.max_length]
            or None,
            camera=camera,
            lens=lens,
            latitude=latitude,
            longitude=longitude,
            altitude=metadata.get('GPS Altitude')
            and metadata.get('GPS Altitude').split(' ')[0],
            star_rating=metadata.get('Rating'))
        photo.save()

        for subject in metadata.get('Subject', '').split(','):
            subject = subject.strip()
            if subject:
                tag, _ = Tag.objects.get_or_create(library_id=library_id,
                                                   name=subject,
                                                   type="G")
                PhotoTag.objects.create(photo=photo, tag=tag, confidence=1.0)
    else:
        for photo_file in photo.files.all():
            if not os.path.exists(photo_file.path):
                photo_file.delete()

    width = metadata.get('Image Width')
    height = metadata.get('Image Height')
    if metadata.get('Orientation') in [
            'Rotate 90 CW', 'Rotate 270 CCW', 'Rotate 90 CCW', 'Rotate 270 CW'
    ]:
        old_width = width
        width = height
        height = old_width

    # Save PhotoFile
    photo_file.photo = photo
    photo_file.path = path
    photo_file.width = width
    photo_file.height = height
    photo_file.mimetype = mimetype
    photo_file.file_modified_at = file_modified_at
    photo_file.bytes = os.stat(path).st_size
    photo_file.preferred = False  # TODO
    photo_file.save()

    # Create task to ensure JPEG version of file exists (used for thumbnailing, analysing etc.)
    Task(type='ensure_raw_processed',
         subject_id=photo.id,
         complete_with_children=True,
         library=photo.library).save()

    return photo
Exemplo n.º 11
0
def record_photo(path, library, inotify_event_type=None):
    if type(library) == Library:
        library_id = library.id
    else:
        library_id = str(library)

    try:
        photo_file = PhotoFile.objects.get(path=path)
    except PhotoFile.DoesNotExist:
        photo_file = PhotoFile()

    if inotify_event_type in ['DELETE', 'MOVED_FROM']:
        if PhotoFile.objects.filter(path=path).exists():
            return delete_photo_record(photo_file)
        else:
            return False

    file_modified_at = datetime.fromtimestamp(os.stat(path).st_mtime, tz=utc)

    if photo_file and photo_file.file_modified_at == file_modified_at:
        return False

    metadata = PhotoMetadata(path)
    date_taken = None
    possible_date_keys = [
        'Date/Time Original', 'Date Time Original', 'Date/Time', 'Date Time',
        'GPS Date/Time', 'Modify Date', 'File Modification Date/Time'
    ]
    for date_key in possible_date_keys:
        date_taken = parse_datetime(metadata.get(date_key))
        if date_taken:
            break

    camera = None
    camera_make = metadata.get('Make')
    if camera_make is None:
        camera_make = ''
    camera_model = metadata.get('Camera Model Name')
    if camera_model:
        camera_model = camera_model.replace(camera_make, '').strip()
    if camera_make and camera_model:
        try:
            camera = Camera.objects.get(library_id=library_id,
                                        make=camera_make,
                                        model=camera_model)
            if date_taken < camera.earliest_photo:
                camera.earliest_photo = date_taken
                camera.save()
            if date_taken > camera.latest_photo:
                camera.latest_photo = date_taken
                camera.save()
        except Camera.DoesNotExist:
            camera = Camera(library_id=library_id,
                            make=camera_make,
                            model=camera_model,
                            earliest_photo=date_taken,
                            latest_photo=date_taken)
            camera.save()

    lens = None
    lens_name = metadata.get('Lens ID')
    if lens_name:
        try:
            lens = Lens.objects.get(name=lens_name)
            if date_taken < lens.earliest_photo:
                lens.earliest_photo = date_taken
                lens.save()
            if date_taken > lens.latest_photo:
                lens.latest_photo = date_taken
                lens.save()
        except Lens.DoesNotExist:
            lens = Lens(library_id=library_id,
                        name=lens_name,
                        earliest_photo=date_taken,
                        latest_photo=date_taken)
            lens.save()

    photo = None
    if date_taken:
        try:
            # TODO: Match on file number/file name as well
            photo = Photo.objects.get(taken_at=date_taken)
        except Photo.DoesNotExist:
            pass

    latitude = None
    longitude = None
    if metadata.get('GPS Position'):
        latitude, longitude = parse_gps_location(metadata.get('GPS Position'))

    iso_speed = None
    if metadata.get('ISO'):
        try:
            iso_speed = int(re.search(r'[0-9]+', metadata.get('ISO')).group(0))
        except AttributeError:
            pass

    if not photo:
        # Save Photo
        aperture = None
        aperturestr = metadata.get('Aperture')
        if aperturestr:
            try:
                aperture = Decimal(aperturestr)
                if aperture.is_infinite():
                    aperture = None
            except:
                pass

        photo = Photo(library_id=library_id,
                      taken_at=date_taken,
                      taken_by=metadata.get('Artist') or None,
                      aperture=aperture,
                      exposure=metadata.get('Exposure Time') or None,
                      iso_speed=iso_speed,
                      focal_length=metadata.get('Focal Length')
                      and metadata.get('Focal Length').split(' ', 1)[0]
                      or None,
                      flash=metadata.get('Flash')
                      and 'on' in metadata.get('Flash').lower() or False,
                      metering_mode=metadata.get('Metering Mode') or None,
                      drive_mode=metadata.get('Drive Mode') or None,
                      shooting_mode=metadata.get('Shooting Mode') or None,
                      camera=camera,
                      lens=lens,
                      latitude=latitude,
                      longitude=longitude,
                      altitude=metadata.get('GPS Altitude')
                      and metadata.get('GPS Altitude').split(' ')[0])
        photo.save()

    width = metadata.get('Image Width')
    height = metadata.get('Image Height')
    if metadata.get('Orientation') in [
            'Rotate 90 CW', 'Rotate 270 CCW', 'Rotate 90 CCW', 'Rotate 270 CW'
    ]:
        old_width = width
        width = height
        height = old_width

    # Save PhotoFile
    photo_file.photo = photo
    photo_file.path = path
    photo_file.width = width
    photo_file.height = height
    photo_file.mimetype = mimetypes.guess_type(path)[0]
    photo_file.file_modified_at = file_modified_at
    photo_file.bytes = os.stat(path).st_size
    photo_file.preferred = False  # TODO
    photo_file.save()

    # Create task to ensure JPEG version of file exists (used for thumbnailing, analysing etc.)
    Task(type='ensure_raw_processed',
         subject_id=photo.id,
         complete_with_children=True).save()

    return photo
Exemplo n.º 12
0
def get_thumbnail(photo_file=None,
                  photo=None,
                  width=256,
                  height=256,
                  crop='cover',
                  quality=75,
                  return_type='path',
                  force_regenerate=False,
                  force_accurate=False):
    if not photo_file:
        if not isinstance(photo, Photo):
            photo = Photo.objects.get(id=photo)
        photo_file = photo.base_file
    elif not isinstance(photo_file, PhotoFile):
        photo_file = PhotoFile.objects.get(id=photo_file)

    # If thumbnail image was previously generated and we weren't told to re-generate, return that one
    output_path = get_thumbnail_path(photo_file.id, width, height, crop,
                                     quality)
    output_url = get_thumbnail_url(photo_file.id, width, height, crop, quality)

    if os.path.exists(output_path):
        if return_type == 'bytes':
            return open(output_path, 'rb').read()
        elif return_type == 'url':
            return output_url
        else:
            return output_path

    # Read base image and metadata
    input_path = photo_file.base_image_path
    ImageFile.LOAD_TRUNCATED_IMAGES = True
    im = Image.open(input_path)

    if im.mode != 'RGB':
        im = im.convert('RGB')

    metadata = PhotoMetadata(input_path)

    # Perform rotations if decalared in metadata
    if force_regenerate:
        im = im.rotate(photo_file.rotation, expand=True)
    elif metadata.get('Orientation') in ['Rotate 90 CW', 'Rotate 270 CCW']:
        im = im.rotate(-90, expand=True)
    elif metadata.get('Orientation') in ['Rotate 90 CCW', 'Rotate 270 CW']:
        im = im.rotate(90, expand=True)

    # Crop / resize
    if force_accurate:
        im = srgbResize(im, (width, height), crop, Image.BICUBIC)
    else:
        if crop == 'cover':
            im = ImageOps.fit(im, (width, height), Image.BICUBIC)
        else:
            im.thumbnail((width, height), Image.BICUBIC)

    # Save to disk (keeping the bytes in memory if we need to return them)
    if return_type == 'bytes':
        img_byte_array = io.BytesIO()
        im.save(img_byte_array, format='JPEG', quality=quality)
        with open(output_path, 'wb') as f:
            f.write(img_byte_array.getvalue())
    else:
        im.save(output_path, format='JPEG', quality=quality)

    # Update PhotoFile DB model with version of thumbnailer
    if photo_file.thumbnailed_version != THUMBNAILER_VERSION:
        photo_file.thumbnailed_version = THUMBNAILER_VERSION
        photo_file.save()

    # Return accordingly
    if return_type == 'bytes':
        return img_byte_array.getvalue()
    elif return_type == 'url':
        return output_url
    return output_path