def get_suggested_focal_point(self): with self.get_willow_image() as willow: faces = willow.detect_faces() if faces: # Create a bounding box around all faces left = min(face[0] for face in faces) top = min(face[1] for face in faces) right = max(face[2] for face in faces) bottom = max(face[3] for face in faces) focal_point = Rect(left, top, right, bottom) else: features = willow.detect_features() if features: # Create a bounding box around all features left = min(feature[0] for feature in features) top = min(feature[1] for feature in features) right = max(feature[0] for feature in features) bottom = max(feature[1] for feature in features) focal_point = Rect(left, top, right, bottom) else: return None # Add 20% to width and height and give it a minimum size x, y = focal_point.centroid width, height = focal_point.size width *= 1.20 height *= 1.20 width = max(width, 100) height = max(height, 100) return Rect.from_point(x, y, width, height)
def get_suggested_focal_point(self, backend_name='default'): backend = get_image_backend(backend_name) image_file = self.file.file # Make sure image is open and seeked to the beginning image_file.open('rb') image_file.seek(0) # Load the image image = backend.open_image(self.file.file) image_data = backend.image_data_as_rgb(image) # Make sure we have image data # If the image is animated, image_data_as_rgb will return None if image_data is None: return # Use feature detection to find a focal point feature_detector = FeatureDetector(image.size, image_data[0], image_data[1]) faces = feature_detector.detect_faces() if faces: # Create a bounding box around all faces left = min(face.left for face in faces) top = min(face.top for face in faces) right = max(face.right for face in faces) bottom = max(face.bottom for face in faces) focal_point = Rect(left, top, right, bottom) else: features = feature_detector.detect_features() if features: # Create a bounding box around all features left = min(feature[0] for feature in features) top = min(feature[1] for feature in features) right = max(feature[0] for feature in features) bottom = max(feature[1] for feature in features) focal_point = Rect(left, top, right, bottom) else: return None # Add 20% to width and height and give it a minimum size x, y = focal_point.centroid width, height = focal_point.size width *= 1.20 height *= 1.20 width = max(width, 100) height = max(height, 100) return Rect.from_point(x, y, width, height)
def test_getitem(self): rect = Rect(100, 150, 200, 250) self.assertEqual(rect[0], 100) self.assertEqual(rect[1], 150) self.assertEqual(rect[2], 200) self.assertEqual(rect[3], 250) self.assertRaises(IndexError, rect.__getitem__, 4)
def test_centroid(self): rect = Rect(100, 150, 200, 350) self.assertIsInstance(rect.centroid, Vector) self.assertEqual(rect.centroid, (150, 250)) self.assertEqual(rect.x, 150) self.assertEqual(rect.y, 250) self.assertEqual(rect.centroid_x, 150) self.assertEqual(rect.centroid_y, 250)
def get_focal_point(self): if ( self.focal_point_x is not None and self.focal_point_y is not None and self.focal_point_width is not None and self.focal_point_height is not None ): return Rect.from_point( self.focal_point_x, self.focal_point_y, self.focal_point_width, self.focal_point_height )
def get_focal_point(self): if self.focal_point_x is not None and \ self.focal_point_y is not None and \ self.focal_point_width is not None and \ self.focal_point_height is not None: return Rect.from_point( self.focal_point_x, self.focal_point_y, self.focal_point_width, self.focal_point_height, )
def test_fill_filter_with_focal_point(self): image = Image.objects.create( title="Test image", file=get_test_image_file(filename='test_rf3.png'), ) image.set_focal_point(Rect(100, 100, 200, 200)) image.save() rendition = image.get_rendition('fill-100x100') self.assertEqual(rendition.file.name, 'images/test_rf3.15ee4958.fill-100x100.png')
def test_filter_with_pipe_gets_dotted(self): image = Image.objects.create( title="Test image", file=get_test_image_file(filename='test_rf4.png'), ) image.set_focal_point(Rect(100, 100, 200, 200)) image.save() rendition = image.get_rendition('fill-200x200|height-150') self.assertEqual(rendition.file.name, 'images/test_rf4.15ee4958.fill-200x200.height-150.png')
def test_get_focal_point(self): self.assertEqual(self.image.get_focal_point(), None) # Add a focal point to the image self.image.focal_point_x = 100 self.image.focal_point_y = 200 self.image.focal_point_width = 50 self.image.focal_point_height = 20 # Get it self.assertEqual(self.image.get_focal_point(), Rect(75, 190, 125, 210))
def test_set_focal_point(self): self.assertEqual(self.image.focal_point_x, None) self.assertEqual(self.image.focal_point_y, None) self.assertEqual(self.image.focal_point_width, None) self.assertEqual(self.image.focal_point_height, None) self.image.set_focal_point(Rect(100, 150, 200, 350)) self.assertEqual(self.image.focal_point_x, 150) self.assertEqual(self.image.focal_point_y, 250) self.assertEqual(self.image.focal_point_width, 100) self.assertEqual(self.image.focal_point_height, 200) self.image.set_focal_point(None) self.assertEqual(self.image.focal_point_x, None) self.assertEqual(self.image.focal_point_y, None) self.assertEqual(self.image.focal_point_width, None) self.assertEqual(self.image.focal_point_height, None)
def detect_faces(self): if opencv_available: cascade_filename = os.path.join(os.path.dirname(__file__), 'face_detection', 'haarcascade_frontalface_alt2.xml') cascade = cv.Load(cascade_filename) image = self.opencv_grey_image() cv.EqualizeHist(image, image) min_size = (40, 40) haar_scale = 1.1 min_neighbors = 3 haar_flags = 0 faces = cv.HaarDetectObjects( image, cascade, cv.CreateMemStorage(0), haar_scale, min_neighbors, haar_flags, min_size ) if faces: return [Rect(face[0][0], face[0][1], face[0][0] + face[0][2], face[0][1] + face[0][3]) for face in faces] return []
def test_set_centroid_with_vector(self): rect = Rect(100, 150, 200, 350) rect.centroid = Vector(500, 500) self.assertEqual(rect, (450, 400, 550, 600))
def test_as_tuple(self): rect = Rect(100, 150, 200, 250) self.assertEqual(rect.as_tuple(), (100, 150, 200, 250))
def test_set_size_with_vector(self): rect = Rect(100, 150, 200, 350) rect.size = Vector(200, 400) self.assertEqual(rect, (50, 50, 250, 450))
def test_repr(self): rect = Rect(100, 150, 200, 250) self.assertEqual(repr(rect), "Rect(left: 100, top: 150, right: 200, bottom: 250)")
def test_from_point(self): rect = Rect.from_point(100, 200, 50, 20) self.assertEqual(rect, Rect(75, 190, 125, 210))
def test_get_key(self): rect = Rect(100, 150, 200, 250) self.assertEqual(rect.get_key(), '150-200-100x100')
def test_size(self): rect = Rect(100, 150, 200, 350) self.assertIsInstance(rect.size, Vector) self.assertEqual(rect.size, (100, 200)) self.assertEqual(rect.width, 100) self.assertEqual(rect.height, 200)
def test_centroid(self): rect = Rect(100, 150, 200, 350) self.assertEqual(rect.centroid, (150, 250)) self.assertEqual(rect.centroid_x, 150) self.assertEqual(rect.centroid_y, 250)
def run(self, willow, image): image_width, image_height = willow.get_size() focal_point = image.get_focal_point() # Get crop aspect ratio crop_aspect_ratio = self.width / self.height # Get crop max crop_max_scale = min(image_width, image_height * crop_aspect_ratio) crop_max_width = crop_max_scale crop_max_height = crop_max_scale / crop_aspect_ratio # Initialise crop width and height to max crop_width = crop_max_width crop_height = crop_max_height # Use crop closeness to zoom in if focal_point is not None: # Get crop min crop_min_scale = max(focal_point.width, focal_point.height * crop_aspect_ratio) crop_min_width = crop_min_scale crop_min_height = crop_min_scale / crop_aspect_ratio # Sometimes, the focal point may be bigger than the image... if not crop_min_scale >= crop_max_scale: # Calculate max crop closeness to prevent upscaling max_crop_closeness = max( 1 - (self.width - crop_min_width) / (crop_max_width - crop_min_width), 1 - (self.height - crop_min_height) / (crop_max_height - crop_min_height)) # Apply max crop closeness crop_closeness = min(self.crop_closeness, max_crop_closeness) if 1 >= crop_closeness >= 0: # Get crop width and height crop_width = crop_max_width + ( crop_min_width - crop_max_width) * crop_closeness crop_height = crop_max_height + ( crop_min_height - crop_max_height) * crop_closeness # Find focal point UV if focal_point is not None: fp_x, fp_y = focal_point.centroid else: # Fall back to positioning in the centre fp_x = image_width / 2 fp_y = image_height / 2 fp_u = fp_x / image_width fp_v = fp_y / image_height # Position crop box based on focal point UV crop_x = fp_x - (fp_u - 0.5) * crop_width crop_y = fp_y - (fp_v - 0.5) * crop_height # Convert crop box into rect rect = Rect.from_point(crop_x, crop_y, crop_width, crop_height) # Make sure the entire focal point is in the crop box if focal_point is not None: rect = rect.move_to_cover(focal_point) # Don't allow the crop box to go over the image boundary rect = rect.move_to_clamp(Rect(0, 0, image_width, image_height)) # Crop! willow.crop(rect.round()) # Get scale for resizing # The scale should be the same for both the horizontal and # vertical axes aftercrop_width, aftercrop_height = willow.get_size() scale = self.width / aftercrop_width # Only resize if the image is too big if scale < 1.0: # Resize! willow.resize((self.width, self.height))
def test_init(self): rect = Rect(100, 150, 200, 250) self.assertEqual(rect.left, 100) self.assertEqual(rect.top, 150) self.assertEqual(rect.right, 200) self.assertEqual(rect.bottom, 250)
def test_get_rect(self): self.assertTrue(self.image.get_rect(), Rect(0, 0, 640, 480))
def get_rect(self): return Rect(0, 0, self.width, self.height)
def test_equality(self): self.assertEqual(Rect(100, 150, 200, 250), Rect(100, 150, 200, 250)) self.assertNotEqual(Rect(100, 150, 200, 250), Rect(10, 15, 20, 25))
def resize_to_fill(self, image, arg, focal_point=None): """ Resize down and crop image to fill the given dimensions. Most suitable for thumbnails. (The final image will match the requested size, unless one or the other dimension is already smaller than the target size) """ size = arg[:2] # Get crop closeness if it's set if len(arg) > 2 and arg[2] is not None: crop_closeness = arg[2] / 100 # Clamp it if crop_closeness > 1: crop_closeness = 1 else: crop_closeness = 0 # Get image width and height (im_width, im_height) = image.size # Get filter width and height fl_width = size[0] fl_height = size[1] # Get crop aspect ratio crop_aspect_ratio = fl_width / fl_height # Get crop max crop_max_scale = min(im_width, im_height * crop_aspect_ratio) crop_max_width = crop_max_scale crop_max_height = crop_max_scale / crop_aspect_ratio # Initialise crop width and height to max crop_width = crop_max_width crop_height = crop_max_height # Use crop closeness to zoom in if focal_point is not None: fp_width = focal_point.width fp_height = focal_point.height # Get crop min crop_min_scale = max(fp_width, fp_height * crop_aspect_ratio) crop_min_width = crop_min_scale crop_min_height = crop_min_scale / crop_aspect_ratio # Sometimes, the focal point may be bigger than the image... if not crop_min_scale >= crop_max_scale: # Calculate max crop closeness to prevent upscaling max_crop_closeness = max( 1 - (fl_width - crop_min_width) / (crop_max_width - crop_min_width), 1 - (fl_height - crop_min_height) / (crop_max_height - crop_min_height) ) # Apply max crop closeness crop_closeness = min(crop_closeness, max_crop_closeness) if 1 >= crop_closeness >= 0: # Get crop width and height crop_width = crop_max_width + (crop_min_width - crop_max_width) * crop_closeness crop_height = crop_max_height + (crop_min_height - crop_max_height) * crop_closeness # Find focal point UV if focal_point is not None: fp_x, fp_y = focal_point.centroid else: # Fall back to positioning in the centre fp_x = im_width / 2 fp_y = im_height / 2 fp_u = fp_x / im_width fp_v = fp_y / im_height # Position crop box based on focal point UV crop_x = fp_x - (fp_u - 0.5) * crop_width crop_y = fp_y - (fp_v - 0.5) * crop_height # Convert crop box into rect left = crop_x - crop_width / 2 top = crop_y - crop_height / 2 right = crop_x + crop_width / 2 bottom = crop_y + crop_height / 2 # Make sure the entire focal point is in the crop box if focal_point is not None: focal_point_left = focal_point.left focal_point_top = focal_point.top focal_point_right = focal_point.right focal_point_bottom = focal_point.bottom if left > focal_point_left: right -= left - focal_point_left left = focal_point_left if top > focal_point_top: bottom -= top - focal_point_top top = focal_point_top if right < focal_point_right: left += focal_point_right - right right = focal_point_right if bottom < focal_point_bottom: top += focal_point_bottom - bottom bottom = focal_point_bottom # Don't allow the crop box to go over the image boundary if left < 0: right -= left left = 0 if top < 0: bottom -= top top = 0 if right > im_width: left -= right - im_width right = im_width if bottom > im_height: top -= bottom - im_height bottom = im_height # Crop! return self.resize_to_min(self.crop(image, Rect(left, top, right, bottom)), size)
def run(self, willow, image): image_width, image_height = willow.get_size() focal_point = image.get_focal_point() # Get crop aspect ratio crop_aspect_ratio = self.width / self.height # Get crop max crop_max_scale = min(image_width, image_height * crop_aspect_ratio) crop_max_width = crop_max_scale crop_max_height = crop_max_scale / crop_aspect_ratio # Initialise crop width and height to max crop_width = crop_max_width crop_height = crop_max_height # Use crop closeness to zoom in if focal_point is not None: # Get crop min crop_min_scale = max(focal_point.width, focal_point.height * crop_aspect_ratio) crop_min_width = crop_min_scale crop_min_height = crop_min_scale / crop_aspect_ratio # Sometimes, the focal point may be bigger than the image... if not crop_min_scale >= crop_max_scale: # Calculate max crop closeness to prevent upscaling max_crop_closeness = max( 1 - (self.width - crop_min_width) / (crop_max_width - crop_min_width), 1 - (self.height - crop_min_height) / (crop_max_height - crop_min_height) ) # Apply max crop closeness crop_closeness = min(self.crop_closeness, max_crop_closeness) if 1 >= crop_closeness >= 0: # Get crop width and height crop_width = crop_max_width + (crop_min_width - crop_max_width) * crop_closeness crop_height = crop_max_height + (crop_min_height - crop_max_height) * crop_closeness # Find focal point UV if focal_point is not None: fp_x, fp_y = focal_point.centroid else: # Fall back to positioning in the centre fp_x = image_width / 2 fp_y = image_height / 2 fp_u = fp_x / image_width fp_v = fp_y / image_height # Position crop box based on focal point UV crop_x = fp_x - (fp_u - 0.5) * crop_width crop_y = fp_y - (fp_v - 0.5) * crop_height # Convert crop box into rect rect = Rect.from_point(crop_x, crop_y, crop_width, crop_height) # Make sure the entire focal point is in the crop box if focal_point is not None: rect = rect.move_to_cover(focal_point) # Don't allow the crop box to go over the image boundary rect = rect.move_to_clamp(Rect(0, 0, image_width, image_height)) # Crop! willow = willow.crop(rect.round()) # Get scale for resizing # The scale should be the same for both the horizontal and # vertical axes aftercrop_width, aftercrop_height = willow.get_size() scale = self.width / aftercrop_width # Only resize if the image is too big if scale < 1.0: # Resize! willow = willow.resize((self.width, self.height)) return willow
def test_get_key(self): rect = Rect(100, 150, 200, 250) self.assertEqual(rect.get_key(), "150-200-100x100")