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_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 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 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_from_point(self): rect = Rect.from_point(100, 200, 50, 20) self.assertEqual(rect, Rect(75, 190, 125, 210))
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))