基于OpenCV Python的AR项目:上色后还原原始阴影技术咨询
Hey there! Let's fix that shadow preservation issue for your AR room coloring project—this is such a common hurdle when doing region-based filling, so I’ve got a few practical OpenCV/Python approaches that should bring back that realistic look you’re aiming for.
1. First: Extract a Shadow Mask from the Original Image
The core idea is to isolate the shadow regions from your original room photo, so we can reapply them to the colored image. Here are two reliable ways to do this:
Option A: Grayscale Thresholding (Simple & Fast)
Shadows are darker, so we can use a threshold on the grayscale version of your original image to pick out those dark areas:
import cv2 import numpy as np # Load your images original = cv2.imread("original_room.jpg") colored = cv2.imread("colored_room.jpg") # Convert original to grayscale and create shadow mask gray_original = cv2.cvtColor(original, cv2.COLOR_BGR2GRAY) # Adjust the threshold value (80 here) based on your specific lighting _, shadow_mask = cv2.threshold(gray_original, 80, 255, cv2.THRESH_BINARY_INV) # Clean up the mask with morphological operations to remove noise kernel = np.ones((3, 3), np.uint8) shadow_mask = cv2.morphologyEx(shadow_mask, cv2.MORPH_CLOSE, kernel) shadow_mask = cv2.morphologyEx(shadow_mask, cv2.MORPH_OPEN, kernel)
Option B: LAB Color Space (Better for Uneven Lighting)
The LAB color space separates brightness (L channel) from color, which makes it easier to detect shadows without being thrown off by wall colors:
lab_original = cv2.cvtColor(original, cv2.COLOR_BGR2LAB) L_channel = lab_original[:, :, 0] # Adaptive threshold works great for rooms with mixed lighting shadow_mask = cv2.adaptiveThreshold( L_channel, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2 )
2. Merge the Shadow Mask with Your Colored Image
Once you have your shadow mask, you can blend it into the colored image in two ways—pick the one that matches your desired realism:
Method 1: Preserve Shadow Brightness (Subtle & Natural)
This method adjusts the brightness of the colored image only in shadow regions to match the original:
# Convert colored image to LAB lab_colored = cv2.cvtColor(colored, cv2.COLOR_BGR2LAB) L_colored = lab_colored[:, :, 0] # Blend the original shadow brightness with the colored image's brightness # Adjust the weights (0.3 and 0.7) to control shadow intensity L_colored[shadow_mask == 255] = cv2.addWeighted( L_colored[shadow_mask == 255], 0.3, L_channel[shadow_mask == 255], 0.7, 0 ) # Put the adjusted L channel back and convert to BGR lab_colored[:, :, 0] = L_colored final_image = cv2.cvtColor(lab_colored, cv2.COLOR_LAB2BGR)
Method 2: Overlay Original Shadow Region (Stronger Shadow Effect)
If you want more prominent shadows, you can directly overlay the original shadow areas onto the colored image with transparency:
# Extract the shadow pixels from the original image shadow_region = cv2.bitwise_and(original, original, mask=shadow_mask) # Blend the shadow region with the colored image (adjust alpha to control darkness) final_image = cv2.addWeighted(colored, 1.0, shadow_region, 0.4, 0)
3. Pro Tips for Even Better Results
- Smooth the Shadow Mask: Avoid harsh edges by refining the mask with contours and Gaussian blur:
contours, _ = cv2.findContours(shadow_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) smooth_mask = np.zeros_like(shadow_mask) for cnt in contours: cv2.drawContours(smooth_mask, [cnt], 0, 255, thickness=cv2.FILLED) smooth_mask = cv2.GaussianBlur(smooth_mask, (5, 5), 0) - Limit Shadows to Colored Regions: If you only want shadows on the areas you painted with floodfill, use your floodfill mask to crop the shadow mask:
# Assume you have a mask of your floodfilled colored area called color_mask final_shadow_mask = cv2.bitwise_and(shadow_mask, color_mask)
Play around with the threshold values, blending weights, and mask smoothing to get the perfect balance for your specific room image—small tweaks can make a huge difference in realism!
内容的提问来源于stack exchange,提问作者Gowtham

