Adding Drop Shadows to Images

One simple way to enhance the aesthetic appeal of a photo is to add a drop shadow. A drop shadow creates a floating effect, adding depth and dimensionality to your images.

The Process

Adding a drop shadow involves several steps using the alpha channel of the image:

  • Extract the alpha channel from the foreground image to use as a mask
  • Apply a blur effect to the extracted alpha channel
  • Composite the blurred shadow onto the background
  • Place the original image on top of the composition

Process Visualization

Drop shadow process visualization

Implementation with OpenCV

Here is how to implement drop shadows using OpenCV in Python:

OpenCV Implementation

Python implementation using OpenCV for adding drop shadows

PYTHON
1import cv2
2import numpy as np
3
4FG_IMG_PATH = "fg.png"
5BG_IMG_PATH = "bg.jpeg"
6BLUR_AMOUNT = 32
7
8def load_image(path, color_conversion=None):
9    """Load an image and optionally convert its color."""
10    image = cv2.imread(path, cv2.IMREAD_UNCHANGED)
11    if color_conversion:
12        image = cv2.cvtColor(image, color_conversion)
13    return image
14
15def extract_alpha_channel(image):
16    """Extract the alpha channel and the RGB channels from an image."""
17    alpha_channel = image[:,:,3]
18    rgb_channels = image[:,:,0:3]
19    return alpha_channel, rgb_channels
20
21def apply_blur_to_alpha(alpha, blur_amount):
22    """Apply blur to the alpha channel."""
23    return cv2.blur(alpha, (blur_amount, blur_amount))
24
25def expand_and_normalize_alpha(alpha):
26    """Expand alpha dimensions and normalize its values to the range [0,1]."""
27    expanded_alpha = np.expand_dims(alpha, axis=2)
28    repeated_alpha = np.repeat(expanded_alpha, 3, axis=2)
29    normalized_alpha = repeated_alpha / 255
30    return normalized_alpha
31
32def create_shadow_on_bg(bg, alpha_blur):
33    """Put shadow (based on blurred alpha) on top of the background."""
34    black_canvas = np.zeros(bg.shape, dtype=np.uint8)
35    shadowed_bg = (alpha_blur * black_canvas + (1 - alpha_blur) * bg).astype(np.uint8)
36    return shadowed_bg
37
38def composite_foreground_on_bg(fg, alpha, bg_with_shadow):
39    """Put the foreground image on top of the background with shadow."""
40    composited_image = (alpha * fg + (1 - alpha) * bg_with_shadow).astype(np.uint8)
41    return composited_image
42
43if __name__ == "__main__":
44    # Load images and convert their color if necessary
45    fg = load_image(FG_IMG_PATH, cv2.COLOR_BGRA2RGBA)
46    bg = load_image(BG_IMG_PATH, cv2.COLOR_RGB2BGR)
47
48    # Extract alpha and RGB channels from the foreground image
49    alpha, fg_rgb = extract_alpha_channel(fg)
50
51    # Blur the alpha channel to get the shadow
52    alpha_blur = apply_blur_to_alpha(alpha, BLUR_AMOUNT)
53
54    # Expand and normalize the blurred alpha for shadow calculation
55    alpha_blur_normalized = expand_and_normalize_alpha(alpha_blur)
56
57    # Create a version of the background with the shadow
58    bg_with_shadow = create_shadow_on_bg(bg, alpha_blur_normalized)
59
60    # Expand and normalize the original alpha for compositing foreground over background
61    alpha_normalized = expand_and_normalize_alpha(alpha)
62
63    # Composite the foreground on the shadowed background
64    final_image = composite_foreground_on_bg(fg_rgb, alpha_normalized, bg_with_shadow)
65
66    # Display the final image (optional)
67    cv2.imshow("Final Image", final_image)
68    cv2.waitKey(0)
69    cv2.destroyAllWindows()

Implementation with Pillow

Alternative implementation using the Pillow library:

Pillow Implementation

Python implementation using Pillow for adding drop shadows

PYTHON
1from PIL import ImageOps, Image, ImageFilter
2
3FG_IMG_PATH = "fg.png"
4BG_IMG_PATH = "bg.jpeg"
5
6def load_image(path):
7    """Load an image using PIL."""
8    return Image.open(path)
9
10def extract_alpha(image):
11    """Extract the alpha channel from an image."""
12    return image.split()[-1]
13
14def create_shadow_from_alpha(alpha, blur_radius):
15    """Create a shadow based on a blurred version of the alpha channel."""
16    alpha_blur = alpha.filter(ImageFilter.BoxBlur(blur_radius))
17    shadow = Image.new(mode="RGB", size=alpha_blur.size)
18    shadow.putalpha(alpha_blur)
19    return shadow
20
21def composite_images(bg, fg, shadow):
22    """Composite the shadow and foreground onto the background."""
23    bg.paste(shadow, (0, 0), shadow)
24    bg.paste(fg, (0, 0), fg)
25    return bg
26
27if __name__ == "__main__":
28    # Load the images
29    fg = load_image(FG_IMG_PATH)
30    bg = load_image(BG_IMG_PATH)
31
32    # Create the shadow based on the alpha channel of the foreground
33    alpha = extract_alpha(fg)
34    shadow = create_shadow_from_alpha(alpha, blur_radius=25)
35
36    # Composite the shadow and foreground onto the background
37    final_image = composite_images(bg, fg, shadow)
38
39    # Display the final image (optional)
40    final_image.show()

Implementation with HTML Canvas

For web applications, you can implement drop shadows using HTML Canvas:

HTML Canvas Implementation

JavaScript implementation using HTML Canvas for adding drop shadows

JAVASCRIPT
1// Get the canvas element and its context
2var canvas = document.getElementById("dropShadowCanvas");
3var ctx = canvas.getContext("2d");
4
5// Load the background image
6var bgImage = new Image();
7bgImage.onload = function () {
8  // Set canvas size to background size
9  canvas.width = bgImage.width;
10  canvas.height = bgImage.height;
11
12  // Draw the background
13  ctx.drawImage(bgImage, 0, 0);
14
15  // Load and draw the PNG image with shadow
16  var pngImage = new Image();
17  pngImage.onload = function () {
18    // Calculate center position
19    var x = (canvas.width - pngImage.width) / 2;
20    var y = (canvas.height - pngImage.height) / 2;
21
22    // Set shadow properties
23    ctx.shadowColor = "rgba(0, 0, 0, 1)";
24    ctx.shadowBlur = 30;
25    ctx.shadowOffsetX = 0;
26    ctx.shadowOffsetY = 0;
27
28    // Draw the PNG image
29    ctx.drawImage(pngImage, x, y);
30  };
31  pngImage.src =
32    "https://withoutbg.com/images/resources/adding-drop-shadow/fg.png";
33};
34bgImage.src =
35  "https://withoutbg.com/images/resources/adding-drop-shadow/bg.jpeg";

Result

This is the final result of adding a drop shadow to an image:

Drop shadow process visualization