点击上方“小白学视觉”,选择加"星标"或“置顶”
重磅干货,第一时间送达
图像增强是对于任何图像处理的一个重要步骤,我们将在日常工作中使用的大多数图像很可能不是在特别理想的环境中拍摄的。过度曝光、曝光不足和彩色阴影等问题在现实生活数据中很常见。因此,了解如何处理此类问题会很有用。
在这篇文章中,我们将讨论如何处理彩色阴影图像。
导入需要的库:
#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)
RGB 分割
从纯粹的视觉角度来看,我们可以看到图像具有明显的深红色,当然,这只是我们目测。为了更好地了解图像的实际属性,我们必须以计算机看到的方式来检查它。下面的函数将生成每个颜色通道的相关统计信息。
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)
频道属性
在生成的表格中,我们看到图像上确实存在红色阴天。现在我们如何平衡它?
虽然这可能行不通,但出于演示原因,让我们通过每个通道的平均值、中值、最大值来调整图像。
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)
通过平均值、中值和最大值进行调整
我们可以看到问题并没有通过这种方法得到解决。虽然可以说图像明显更亮了,但红色阴霾仍然非常明显。解决此问题的一种可能方法是关注红色通道。下面的代码将使用其最大值调整红色通道,而使用其平均值和中值调整所有其他通道。
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 函数,这使我们能够向其中输入任何图像。
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。
阴天调整后的天空
然而,图像增强有时会非常棘手。对某些图像非常有效的方法对于其他图像可能并不那么有效,让我们一起来看看下面这个例子。
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));
连接调整后的图像
正如我们所见,图像增强可能非常棘手。对一个图像有效的方法可能对另一个图像无效。但是只要我们记住图像只不过是 3 维矩阵,我们就应该能够找到一种方法来充分处理它以满足我们的需求。
下载1:OpenCV-Contrib扩展模块中文版教程
在「小白学视觉」公众号后台回复:扩展模块中文教程,即可下载全网第一份OpenCV扩展模块教程中文版,涵盖扩展模块安装、SFM算法、立体视觉、目标跟踪、生物视觉、超分辨率处理等二十多章内容。
下载2:Python视觉实战项目52讲
在「小白学视觉」公众号后台回复:Python视觉实战项目,即可下载包括图像分割、口罩检测、车道线检测、车辆计数、添加眼线、车牌识别、字符识别、情绪检测、文本内容提取、面部识别等31个视觉实战项目,助力快速学校计算机视觉。
下载3:OpenCV实战项目20讲
在「小白学视觉」公众号后台回复:OpenCV实战项目20讲,即可下载含有20个基于OpenCV实现20个实战项目,实现OpenCV学习进阶。
交流群
欢迎加入公众号读者群一起和同行交流,目前有SLAM、三维视觉、传感器、自动驾驶、计算摄影、检测、分割、识别、医学影像、GAN、算法竞赛等微信群(以后会逐渐细分),请扫描下面微信号加群,备注:”昵称+学校/公司+研究方向“,例如:”张三 + 上海交大 + 视觉SLAM“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,否则会请出群,谢谢理解~