去除图像阴影

原理应该是这样的:

首先为了方便处理,我们通常会对图片进行灰度转换(即将图片转换成只有一个图层的灰色图像)。

然后我们分析一下,在上面的图片中有三个主色调,分别是字体颜色(黑色)、纸张颜色(偏白)、阴影颜色(灰色)。知道这点后我们就好办了。我们只需要把灰色和白色部分都处理为白色就好了。

那要我怎么才知道白色和灰色区域呢?对于一个8位的灰度图,黑色部分的像素大致在0-30左右。白色和灰色应该在31-255左右(这个范围只是大致估计,实际情况需要看图片)。如图:

去除图像阴影_第1张图片

左边是原图,右边是处理后的图片。我们将灰色和接近白色的部分都处理成了白色。

代码

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


# 只适用于比较简单的图片,比如试卷这种黑白分明的,有一些灰色就可以很好去掉
# 删除阴影时,有两件事要注意。由于图像是灰度图像,如果图像背景较浅且对象较暗,则必须先执行最大滤波,然后再执行最小滤波。
# 如果图像背景较暗且物体较亮,我们可以先执行最小滤波,然后再进行最大滤波。

# 【最大滤波】:让我们假设我们有一定大小的图像I。我们编写的算法应该逐个遍历I的像素,并且对于每个像素(x,y),它必须找到该像素周围的邻域(大小为N*N
# 的窗口)中的最大灰度值,并进行写入A中相应像素位置(x,y)的最大灰度值。所得图像A称为输入图像I的最大滤波图像。
# 如果图像的背景较浅,执行最大过滤,这将为我们提供增强的背景

def max_filtering(N, I_temp):  # N=20   # I_temp就是个ndarray
    # 它最初在输入数组周围创建一个“墙”(带有-1的填充),当我们遍历边缘像素时会有所帮助。
    a = I_temp.shape[0]  # 图片的行数
    b = I_temp.shape[1]
    c = (N // 2) * 2  # c就是20,为什么是这个取值呢?为什么不直接取跟下面的nn一样的10呢?因为这堵墙厚度10,所以行增加20,列也是增加20。并不是上下都增加20
    hang = a + c
    lie = b + c
    wall = np.full((hang, lie), -1)  # 创建一个比原图大的-1的一堵墙(wall也是ndarray)。-1比0小,肯定不会被amax返回

    ws0 = wall.shape[0]  # 就是上面的hang=a+c=a+20
    ws1 = wall.shape[1]
    nn = (N // 2)  # N=20时,nn=10
    wall[nn:ws0 - nn, nn:ws1 - nn] = I_temp.copy()  # 这估计就是往这面大墙里面塞图片的像素,让这堵墙变成围墙。wall和图的大小一样,这里容易搞错,其实是一样的

    # 然后,我们创建一个“ temp”变量,将计算出的最大值复制到其中。
    temp = np.full((a + c, b + c), -1)  # 就跟原来那堵墙一样
    for y in range(0, ws0):
        for x in range(0, ws1):
            if wall[y, x] != -1:  # 如果这个像素点不属于墙
                window = wall[y - nn:y + nn + 1, x - nn:x + nn + 1]  # 遍历该数组并围绕大小为 NxN 的当前像素创建一个窗口。
                num = np.amax(window)  # 使用“ amax()”函数在该ndarray中的最大值
                temp[y, x] = num  # 一个像素点一个像素点地替换,num是170、148……这样子的一个个整数
    A = temp[nn:ws0 - nn, nn:ws1 - nn].copy()  # nn:ws0 - nn, nn:ws0 - nn 就是真实图片大小,在这里已经被滤波处理了。用copy是为了深拷贝,让两个不指向同一块内存
    # nn:ws0 - nn,意思是从nn行到ws0-nn行
    return A  # A是输入I的最大滤波图像


# 此算法与最大滤波完全相同,但是我们没有找到附近的最大灰度值,而是在该像素周围的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


def background_subtraction(I, B):  # 归一化将白色背景修改贴近原图
    O = I - B
    norm_img = cv2.normalize(O, None, 0, 255, norm_type=cv2.NORM_MINMAX)
    return norm_img


def min_max_filtering(M, N, I):  # N=20
    if M == 0:
        A = max_filtering(N, I)  # I就是个ndarray图片
        B = min_filtering(N, A)  # 将最大过滤后的图像传递给最小过滤功能,该功能将负责实际的内容增强。
        print(B)
        # subtraction
        normalised_img = background_subtraction(I, B)  # 因此,执行最小-最大滤波后,我们获得的值不在0-255的范围内。为啥?
        # 因此,我们必须归一化使用背景减法获得的最终阵列,该方法是将原始图像减去最小-最大滤波图像,以获得去除阴影的最终图像。
        print(normalised_img)
    elif M == 1:
        A = min_filtering(N, I)
        B = max_filtering(N, A)
        # subtraction
        normalised_img = background_subtraction(I, B)
    return normalised_img


P = cv2.imread('Test_image.jpg', 0)  # P是个ndarray数组,图片变数组
plt.imshow(P, cmap='gray')  # 变成灰度图
plt.title("original image")
plt.show()

# We can edit the N and M values here for P and C images
O_P = min_max_filtering(M=0, N=20, I=P)  # 如果图像的背景较浅,我们要先执行最大过滤
# 变量N(用于过滤的窗口大小)将根据图像中粒子或内容的大小进行更改。对于测试图像,选择大小N = 20。

# Display final output
plt.imshow(O_P, cmap='gray')
plt.title("Final output")
plt.show()

归一化 

去除图像阴影_第2张图片

你可能感兴趣的:(图像处理,opencv,人工智能,计算机视觉)