欢迎关注 “小白玩转Python”,发现更多 “有趣”
图像增强对于任何图像处理都是一个至关重要的步骤。我们在日常工作中使用的大多数图像很可能不是在理想的环境中拍摄的。过度曝光、曝光不足和彩色阴影等问题在现实图像中很常见。因此了解如何处理这些问题是很有用的。在本文中,我们将讨论如何处理带有彩色阴影的图像。
让我们从导入需要的库开始!
#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分量。
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)
从纯视觉的角度,我们可以看到图像有明显的深红色。
当然,这只是我们的目测。为了更精确地了解图像的实际属性,我们必须以计算机看到图像的方式对其进行检测。下面的函数将生成每个颜色通道的相关统计信息。
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)
在上表中,我们看到图像上确实有一个红色的阴影。现在我们如何平衡呢?
尽管这可能没什么作用,我们先通过每个通道的平均值、中位数以及最大值来调整图像试试。
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_purple*1.0 /
np.mean(image_overcast )).clip(0, 1)))
ax[1].set_title('Mean Adjusted', fontsize = f_size)
ax[2].imshow(img_as_ubyte((image_purple*1.0 /
np.mean(image_overcast)).clip(0, 1)))
ax[2].set_title('Median Adjusted', fontsize = f_size);
我们可以看出,问题并没有得到解决。尽管可以说这张图像明显要亮得多,但红色的阴影仍然非常明显。解决这个问题的一个可能方法是重点关注红色通道。下面的代码将使用其最大值调整红色通道,而所有其他通道则使用其平均值和中值进行调整。
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);
看来我们的方法是正确的。这张照片明显地消除了红色的阴影,整体上更加明亮。然而我们现在看到了更明显的绿色和蓝色的阴影。这意味着我们可能对红色调得太多,或者对绿色和蓝色调得不够。现在我们需要微调参数。
下面的代码将根据每个通道的特定百分比等级处理图像。
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)
图像增强的结果是相当明显的。不仅是红色的阴影大大减少,而且其他通道的过度曝光也保持在最低限度。
从视觉上看,最好的结果是当其他通道在90-95%之间调整时。
然而,图像增强也是非常棘手的。对某些图像特别有效的方法对其他图像可能没有那么有效。让我们看下面这个例子。
image_overcast_beach = imread('tinted_sky.png')
imshow(image_overcast_beach);
这副图像明显的与第一幅图像有相似的问题。让我们试着用同样的方法,看看结果是否一样好。
percentile_adjustment(image_overcast_beach)
我们看到红色阴影大大减少了。然而可以看到我们的方法在图片的上半部分和下半部分的有效性之间有明显的差异。在超过95%时,图像的天空部分曝光过度。但是水只有在低于95%才没有曝光过度。我们该如何解决这个问题?
记住,计算机实际上是用矩阵来读取图像的。所以我们可以很容易地将其分割,并对每个区域应用不同的参数。在这种情况下,让我们把图像分为天空和水。
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的连接函数将这两个部分组合起来。
imshow(np.concatenate((beach_correction_top, beach_correction_bottom), axis=0));
正如我们所见,图像增强是非常棘手的。对一个图像有效的方法可能对另一个图像效果不好。但只要我们记住图像不过是三维矩阵,总能找到一种方法来适当地操纵它以满足我们的需要。因此多做练习增加经验,对我们的图像增强将会非常有帮助。让我们开始动手实践吧!
· END ·
HAPPY LIFE
觉得有趣就点亮在看吧