图像处理

去掉照片上的阴影

返回案例列表

资源下载

打开解决方案文件之前,请先阅读 如何查看案例

问题描述

如何去除照片上的阴影,使图像亮度更加均匀?

原始问题图片

原图

图0

结果图

图1

解答思路

方案一:

这个图和问题完美命中了传统图像处理中的低帽算法,下面上图

图2

对低帽的结果取反操作得到白底的图,如下

图3

再使用颜色传递算法,用白底图中的灰度值替换原图中的亮度通道,即可得到结果图。

OpenCV代码

import cv2
import numpy as np

# 1. 读取输入图像(彩色)
img = cv2.imread('input.jpg')
if img is None:
    raise FileNotFoundError("无法加载图像,请检查文件路径是否正确。")

# 转换图像到 LUV 颜色空间(L: 亮度,U/V: 色度)
imgLUV = cv2.cvtColor(img, cv2.COLOR_BGR2LUV)

# 分离 LUV 三个通道
L_channel = imgLUV[:, :, 0]   # L 通道(亮度)
U_channel = imgLUV[:, :, 1]   # U 通道
V_channel = imgLUV[:, :, 2]   # V 通道

# 2. 定义形态学操作的结构元素(矩形核,大小为 11x11)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (11, 11))

# 对 L 通道执行“黑帽”变换(Black Hat):

# 黑帽 = 原图 - 闭运算结果,用于增强暗细节(比如文字和线条)
blackhat = cv2.morphologyEx(L_channel, cv2.MORPH_BLACKHAT, kernel)

# 3. 对黑帽结果进行反色(按位取反),突出原图中较亮区域的互补信息
inverted_blackhat = cv2.bitwise_not(blackhat)

# 4. 对反色后的图像减去一个偏移量(29),以调整亮度(注意防止溢出)

# 使用 np.clip 确保像素值保持在 [0, 255] 范围内(uint8 类型安全)
adjusted_L = np.clip(inverted_blackhat.astype(np.int16) - 29, 0, 255).astype(np.uint8)

# 将调整后的 L 通道重新赋值回 LUV 图像
imgLUV[:, :, 0] = adjusted_L

# 转换回 BGR 颜色空间用于显示或保存
enhanced_img = cv2.cvtColor(imgLUV, cv2.COLOR_LUV2BGR)

# 5. 显示结果(注意:img2 是单通道,需指定 cmap 或转为三通道才能正确显示)
cv2.imshow('Original Image (BGR)', img)
cv2.imshow('BlackHat on L Channel', blackhat)
cv2.imshow('Inverted + Adjusted L Channel', adjusted_L)
cv2.imshow('Enhanced Result (LUV -> BGR)', enhanced_img)

# 等待任意键按下后关闭所有窗口
cv2.waitKey(0)
cv2.destroyAllWindows()

方案二

先对这个图进行中值计算,再求差的绝对值,最后取反得到图片

使用中值的处理结果

图4

差值计算后的结果

图5

取反后的结果

图6

OpenCV代码

import cv2
import numpy as np

# 1. 读取图片并转换为灰度图
img = cv2.imread('input.jpg')

# 转换为灰度图
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 2. 对灰度图进行中值滤波,kernel size 51x51
img1 = cv2.medianBlur(img, 51)

# 3. 计算差值图: img2 = |img1 - img|
img2 = cv2.absdiff(img1, img)

# 3. 对 img1 进行反色处理(即取反)
img3 = cv2.bitwise_not(img2)

# 显示结果
cv2.imshow('Original Grayscale', img)
cv2.imshow('Median Filtered (51x51)', img1)
cv2.imshow('Difference |img1 - img|', img2)
cv2.imshow('After Invert (img2)', img3)

# 等待按键后关闭窗口
cv2.waitKey(0)
cv2.destroyAllWindows()

以上代码中,img3得到之后,再使用和方案一类似的颜色传递算法,也可以得到和结果图类似的结果。

其他说明:普通照片不适用上面这两个方法,仅适用于你想要的内容都比局部的背景黑的情况,如果内容比背景白的话,第一个方法的代码仅需要把低帽换成高帽算法即可,第二个方法不需要改。