基于 OpenCV 的图像阴影去除,你会吗?

本文介绍了一种使用Numpy和OpenCV从图像中去除阴影的方法。通过最大和最小滤波增强背景和内容,最后通过背景减法得到清晰无阴影的图像。

作者 | 努比  责编 | 张文

头图 | 优快云 下载自东方 IC

来源 | 小白学视觉(ID:NoobCV)

我们经常需要通过扫描将纸上的全部内容转换为图像。有很多在线工具可以提高图像的亮度,或者消除图像中的阴影。那我们可以手动删除阴影吗?

答案是当然可以。我们只需要将图像加载到相应的代码中,无需任何应用程序即可在几秒钟内获得输出。这个代码可以通过 Numpy 和 OpenCV 基本函数来实现。

为了说明该过程,使用了以下图像进行操作。

Test_image

图像中有一个非常明显的阴影需要删除。

导入软件包

首先当然是将必要的软件包导入环境。代码如下:

import cv2
import numpy as np
import matplotlib.pyplot as plt

注意事项

删除阴影时,有两件事要注意。由于图像是灰度图像,如果图像背景较浅且对象较暗,则必须先执行最大滤波,然后再执行最小滤波。如果图像背景较暗且物体较亮,我们可以先执行最小滤波,然后再进行最大滤波。

那么,最大过滤和最小过滤到底是什么?

最大滤波

让我们假设我们有一定大小的图像 I。我们编写的算法应该逐个遍历 I 的像素,并且对于每个像素(x,y),它必须找到该像素周围的邻域(大小为 N x N 的窗口)中的最大灰度值,并进行写入 A 中相应像素位置(x,y)的最大灰度值。所得图像 A 称为输入图像I的最大滤波图像。现在让我们通过代码来实现这个概念。

  • max_filtering()函数接受输入图像和窗口大小 N;

  • 它最初在输入数组周围创建一个“墙”(带有-1的填充),当我们遍历边缘像素时会有所帮助;

  • 然后,我们创建一个“ temp”变量,将计算出的最大值复制到其中;

  • 然后,我们遍历该数组并围绕大小为 N x N 的当前像素创建一个窗口;

  • 然后,我们使用“ amax()”函数在该窗口中计算最大值,并将该值写入 temp 数组;

  • 我们将该临时数组复制到主数组 A 中,并将其作为输出返回。

  • A 是输入 I 的最大滤波图像。

def max_filtering(N, I_temp):
    wall = np.full((I_temp.shape[0]+(N//2)*2, I_temp.shape[1]+(N//2)*2), -1)
    wall[(N//2):wall.shape[0]-(N//2), (N//2):wall.shape[1]-(N//2)] = I_temp.copy()
    temp = np.full((I_temp.shape[0]+(N//2)*2, I_temp.shape[1]+(N//2)*2), -1)
    for y in range(0,wall.shape[0]):
        for x in range(0,wall.shape[1]):
            if wall[y,x]!=-1:
                window = wall[y-(N//2):y+(N//2)+1,x-(N//2):x+(N//2)+1]
                num = np.amax(window)
                temp[y,x] = num
    A = temp[(N//2):wall.shape[0]-(N//2), (N//2):wall.shape[1]-(N//2)].copy()
    return A

最小滤波

此算法与最大滤波完全相同,但是我们没有找到附近的最大灰度值,而是在该像素周围的 N x N 邻域中找到了最小值,并将该最小灰度值写入 B 中的(x,y)。所得图像 B 称为图像 I 的经过最小滤波的图像,代码如下。

def min_filtering(N, A):
    wall_min = np.full((A.shape[0]+(N//2)*2, A.shape[1]+(N//2)*2), 300)
    wall_min[(N//2):wall_min.shape[0]-(N//2), (N//2):wall_min.shape[1]-(N//2)] = A.copy()
    temp_min = np.full((A.shape[0]+(N//2)*2, A.shape[1]+(N//2)*2), 300)
    for y in range(0,wall_min.shape[0]):
        for x in range(0,wall_min.shape[1]):
            if wall_min[y,x]!=300:
                window_min = wall_min[y-(N//2):y+(N//2)+1,x-(N//2):x+(N//2)+1]
                num_min = np.amin(window_min)
                temp_min[y,x] = num_min
    B = temp_min[(N//2):wall_min.shape[0]-(N//2), (N//2):wall_min.shape[1]-(N//2)].copy()
    return B

如果图像的背景较浅,我们要先执行最大过滤,这将为我们提供增强的背景,并将该最大过滤后的图像传递给最小过滤功能,该功能将负责实际的内容增强。

获得图像

执行最小-最大滤波后,我们获得的值不在 0-255 的范围内。因此,我们必须归一化使用背景减法获得的最终阵列,该方法是将原始图像减去最小-最大滤波图像,以获得去除阴影的最终图像。

#B is the filtered image and I is the original image
def background_subtraction(I, B):
    O = I - B
    norm_img = cv2.normalize(O, None, 0,255, norm_type=cv2.NORM_MINMAX)
    return norm_img

输出图像

变量 N(用于过滤的窗口大小)将根据图像中粒子或内容的大小进行更改。对于测试图像,选择大小 N = 20。增强后的最终输出图像如下所示:

Test_image_output

输出图像相较于原始图像已经没有任何的阴影啦。

代码链接:https://github.com/kavyamusty/Shading-removal-of-images


更多精彩推荐
☞红帽将停止支持 CentOS 8;2020 百度沸点揭晓年度知识热词;Qt 6.0 发布|极客头条
☞踢掉 Docker 后,Kubernetes 还能欢快地跑 GPU?
  ☞科技垄断正在朝着纵向发展

☞挑战TensorFlow、PyTorch,谁才是中国AI开源框架之星
  ☞被弃用的 Docker 会被 Podman 取代吗?
  ☞索要 2.3 亿元赎金!富士康遭遇黑客攻击

点分享点点赞点在看
### 使用OpenCV去除图像中的阴影 为了有效去除图像中的阴影,可以采用多种方法和技术组合来处理这个问题。一种常见的策略是通过颜色空间转换和形态学操作相结合的方式实现。 #### 方法一:基于HSV颜色空间的方法 由于RGB色彩模型容易受到光照条件的响,在这种情况下,通常建议先将输入图片从BGR(OpenCV默认读取的颜色模式)转到HSV或其他更合适的色彩空间中去[^1]: ```python import cv2 import numpy as np def remove_shadow_hsv(image_path): img = cv2.imread(image_path) # Convert BGR to HSV color space hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # Define lower and upper bounds of what we call "shadow" lower_bound = np.array([0, 0, 0]) upper_bound = np.array([180, 255, 70]) mask = cv2.inRange(hsv_img, lower_bound, upper_bound) result = cv2.bitwise_and(img, img, mask=~mask) return result ``` 此代码片段展示了如何定义一个函数`remove_shadow_hsv()`用于移除给定路径下图像文件内的阴影部分。这里选择了Hue-Saturation-Value (HSV) 颜色空间,并设定了所谓的“阴影”的上下限范围;接着创建了一个掩码并将其应用于原始图像上以获得最终的结果图。 #### 方法二:利用形态学闭运算消除噪声 对于某些类型的阴影,特别是那些与背景相融合的情况,还可以考虑应用形态学变换来进行优化。例如,使用闭合操作可以帮助填充对象内部的小孔洞以及连接邻近的对象,从而少不必要的细节干扰[^2]: ```python kernel_size = (5, 5) kernel = cv2.getStructuringElement(cv2.MORPH_RECT, kernel_size) closing = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) final_result = cv2.bitwise_and(img, img, mask=closing) ``` 上述代码段说明了怎样构建矩形结构元素并通过调用`cv2.morphologyEx()`执行闭合运算。这一步骤有助于改善之前生成的二值化遮罩的质量,使得后续的操作更加精确可靠。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值