Python 图像处理简介——色彩阴影调整

2022-02-14 09:35:48 浏览数 (1)

图像增强是对于任何图像处理的一个重要步骤,我们将在日常工作中使用的大多数图像很可能不是在特别理想的环境中拍摄的。过度曝光、曝光不足和彩色阴影等问题在现实生活数据中很常见。因此,了解如何处理此类问题会很有用。 在这篇文章中,我们将讨论如何处理彩色阴影图像。

导入需要的库:

代码语言:javascript复制
#Import the required Python libraries
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from skimage.io import imshow, imread

天空阴天

正如我们所看到的,上面的图片有一个彩色的阴影,虽然从艺术的角度来看很美,但这样的问题可能会对试图处理数据的机器产生影响。

我们需要做的第一个处理是检查图像的 RGB 光谱。为此,我们必须将图像分解为其 RGB 分量。

代码语言:javascript复制
rgb_list = ['Reds','Greens','Blues']
fig, ax = plt.subplots(1, 3, figsize=(15,5), sharey = True)
for i in range(3):
   ax[i].imshow(image_overcast[:,:,i], cmap = rgb_list[i])
   ax[i].set_title(rgb_list[i], fontsize = 15)

RGB 分割

从纯粹的视觉角度来看,我们可以看到图像具有明显的深红色,当然,这只是我们目测。为了更好地了解图像的实际属性,我们必须以计算机看到的方式来检查它。下面的函数将生成每个颜色通道的相关统计信息。

代码语言:javascript复制
def channel_statistics(image):
    df_color = []
    for i in range(0, 3):
        max_color =np.max(image[:,:,i])
        mean_color = np.mean(image[:,:,i])
        median_color = np.median(image[:,:,i])
        perc_90 = np.percentile(image[:,:,i], 90, axis=(0,1))
        perc_95 = np.percentile(image[:,:,i], 95, axis=(0,1))
        perc_99 = np.percentile(image[:,:,i], 99, axis=(0,1))
        
        row = (max_color, mean_color, median_color, 
               perc_90, perc_95, perc_99)
        df_color.append(row)
        
    return pd.DataFrame(df_color, 
                        index = ['Red', ' Green', 'Blue'],
                        columns = ['Max', 'Mean', 'Median', 
                                   'P_90',' P_95', 'P_99'])
channel_statistics(image_overcast)

频道属性

在生成的表格中,我们看到图像上确实存在红色阴天。现在我们如何平衡它?

虽然这可能行不通,但出于演示原因,让我们通过每个通道的平均值、中值、最大值来调整图像。

代码语言:javascript复制
def mean_and_median_adjusted(image):
    fig, ax = plt.subplots(2, 2, figsize=(12,12), sharey = True)
    f_size = 20
    ax[0][0].imshow(image_overcast )
    ax[0][0].set_title('Original', fontsize = f_size)
    ax[0][1].imshow(img_as_ubyte((image / np.mean(image)).clip(0, 
                                  1)))
    ax[0][1].set_title('Mean Adjusted', fontsize = f_size)
    
    ax[1][0].imshow(img_as_ubyte((image/ np.median(image)).clip(0, 
                                  1)))
    ax[1][0].set_title('Median Adjusted', fontsize = f_size)
    ax[1][1].imshow(img_as_ubyte((image/ np.max(image)).clip(0, 1)))
    ax[1][1].set_title('Max Adjusted', fontsize = f_size);
    
    fig.tight_layout()
mean_and_median_adjusted(image)

通过平均值、中值和最大值进行调整

我们可以看到问题并没有通过这种方法得到解决。虽然可以说图像明显更亮了,但红色阴霾仍然非常明显。解决此问题的一种可能方法是关注红色通道。下面的代码将使用其最大值调整红色通道,而使用其平均值和中值调整所有其他通道。

代码语言:javascript复制
fig, ax = plt.subplots(1, 3, figsize=(15,7), sharey = True)
f_size = 15
ax[0].imshow(image_overcast)
ax[0].set_title('Original', fontsize = f_size)
ax[1].imshow(img_as_ubyte((image_overcast / 
                           [np.max(image_overcast[:,:,0]),
                            np.mean(image_overcast[:,:,1]),
                            np.mean(image_overcast[:,:,2]),
                            np.mean(image_overcast[:,:,3])])
                            .clip(0, 1)))
ax[1].set_title('Red : Max, Others : Mean', fontsize = f_size)
ax[2].imshow(img_as_ubyte((image_overcast / 
                           [np.max(image_overcast[:,:,0]),
                            np.median(image_overcast[:,:,1]),
                            np.median(image_overcast[:,:,2]),                                    
                            np.median(image_overcast[:,:,3])])
                            .clip(0, 1)))
ax[2].set_title('Red : Max, Others : Median', fontsize = f_size);

最大红色

看来我们的方法是正确的,图像显然已经去除了红色阴霾,整体更加明亮。然而,我们现在看到了更加明显的绿色和蓝色阴天。这意味着我们可能对红色进行了过多的调整,或者对绿色和蓝色的调整不足。现在我们需要微调我们的参数了。

下面的代码将根据每个通道的特定百分位等级过滤图像。在这篇文章中,我们将其设为 Python 函数,这使我们能够向其中输入任何图像。

代码语言:javascript复制
def percentile_adjustment(image):
    fig, ax = plt.subplots(2, 3, figsize=(15,10), sharey = True)
    f_size = 15
    red = 99.75  
    parameter_matrix = [[red]   [99]*3,
                        [red]   [95]*3,
                        [red]   [90]*3,
                        [red]   [85]*3,
                        [red]   [80]*3]
    
    ax[0][0].imshow(image)
    ax[0][0].set_title('Original', fontsize = f_size)
    ax[0][1].imshow(img_as_ubyte((image /   
                   [np.percentile(image[:,:,i], 
                    parameter_matrix[0][i], axis=(0, 1))   
                    for i in range(0, 4)]).clip(0,1)))
   
    ax[0][1].set_title(f'Red : {red}, Others : {parameter_matrix[0] 
                       [1]}', fontsize = f_size)
    ax[0][2].imshow(img_as_ubyte((image / 
                    [np.percentile(image[:,:,i], 
                     parameter_matrix[1][i], axis=(0, 1))   
                     for i in range(0, 4)]).clip(0,1)))
    
    ax[0][2].set_title(f'Red : {red}, Others : {parameter_matrix[1]
                       [1]}', fontsize = f_size);
    ax[1][0].imshow(img_as_ubyte((image / 
                    [np.percentile(image[:,:,i], 
                    parameter_matrix[2][i], axis=(0, 1)) 
                    for i in range(0, 4)]).clip(0,1)))
    ax[1][0].set_title(f'Red : {red}, Others : {parameter_matrix[2] 
                       [1]}', fontsize = f_size);
    ax[1][1].imshow(img_as_ubyte((image /
                    [np.percentile(image[:,:,i], 
                    parameter_matrix[3][i], axis=(0, 1)) 
                    for i in range(0, 4)]).clip(0,1)))
    ax[1][1].set_title(f'Red : {red}, Others : {parameter_matrix[3] 
                       [1]}', fontsize = f_size);
    ax[1][2].imshow(img_as_ubyte((image/ 
                    [np.percentile(image[:,:,i], 
                    parameter_matrix[4][i], axis=(0, 1)) 
                    for i in range(0, 4)]).clip(0,1)))
    
    ax[1][2].set_title(f'Red : {red}, Others : {parameter_matrix[4]
                   [1]}', fontsize = f_size);
    
percentile_adjustment(image_overcast)

99.75% 处的红色

图像增强的结果是相当明显的。不仅红色阴霾被大大减少,而且其他通道的过度曝光也保持在最低限度。

从视觉上看,最好的结果是当其他通道在 90-95% 之间调整时。在那之后,图像似乎呈现出蓝色调。

有人可能会说,将调整设置为 80 会更好,因为天空本身是蓝色的,因此蓝色阴天是可以接受的。但是,在这篇文章中,我们仍将使用 95。

阴天调整后的天空

然而,图像增强有时会非常棘手。对某些图像非常有效的方法对于其他图像可能并不那么有效,让我们一起来看看下面这个例子。

代码语言:javascript复制
image_overcast_beach = imread('tinted_sky.png')
imshow(image_overcast_beach);

阴天海滩

第二张图片清楚地展示了与第一张图片类似的问题。让我们尝试应用相同的过程,看看结果是否一样好。由于我们已经编写了一个函数,我们应该能够调用它并将其提供给我们的第二张图像。

代码语言:javascript复制
percentile_adjustment(image_overcast_beach)

我们看到阴天大大减少了。但是,我们可以看到,我们的方法在图像上半部分和下半部分的有效性之间存在显着差异。图像的天空部分显示超过 95% 的过度曝光。但是,水只有在低于 95%才没有曝光过度。我们如何解决这个问题?

请记住,计算机实际上是通过数字矩阵读取图像的。因此,我们可以很容易地将其拆分,并将不同的参数应用于每个区域。在这种情况下,让我们将图像分割为天空和水。

代码语言:javascript复制
f_size = 15
parameter_matrix = [[99.9]   [97.75]*3,
                    [99.75]   [92]*3]
fig, ax = plt.subplots(2, 2, figsize=(15,6), sharey = True)
ax[0][0].imshow(image_overcast_beach[0:320])
ax[0][0].set_title('Sky Original', fontsize = f_size)
ax[1][0].imshow(image_overcast_beach[320:])
ax[1][0].set_title('Water Original', fontsize = f_size)
ax[0][1].imshow(img_as_ubyte(((image_overcast_beach[0:320]/ [np.percentile(image_overcast_beach[:,:,i], parameter_matrix[0][i], axis=(0, 1)) for i in range(4)]).clip(0,1))))
ax[0][1].set_title('Sky Adjusted', fontsize = f_size)
ax[1][1].imshow(img_as_ubyte(((image_overcast_beach[320:]/ [np.percentile(image_overcast_beach[:,:,i], parameter_matrix[1][i], axis=(0, 1)) for i in range(4)]).clip(0,1))))
ax[1][1].set_title('Water Adjusted', fontsize = f_size);
fig.tight_layout()

拆分和调整

最后,我们可以使用NumPy的连接函数将这两部分结合起来。

代码语言:javascript复制
imshow(np.concatenate((beach_correction_top, beach_correction_bottom), axis=0));

连接调整后的图像

正如我们所见,图像增强可能非常棘手。对一个图像有效的方法可能对另一个图像无效。但是只要我们记住图像只不过是 3 维矩阵,我们就应该能够找到一种方法来充分处理它以满足我们的需求。

0 人点赞