某天跑完步以后拍出了这样一张照片,由于晚上开闪光不太好,所以想自己处理一下(为了更好的看出处理效果,所以采用尺寸较大的原图像,没有进行尺寸缩小):
在光线较暗的情况下拍摄的图像整体的灰度值偏低,可以从直方图中很直接的看出,图像的像素值都分布在50以内(直方图就是统计图像中0-255这256个级别对应的像素值个数,比如图像有1000个像素点,像素值为0的有10个,值为1的有5个。。。。值为255的有3个)像素值很多都接近于0并且少部分分布在100-255之间。
这里并没有对RGB三个通道分别进行统计,而是将RGB转为灰度图再进行统计(我会在另外的文章中单独的介绍直方图和直方图均衡算法)。
import imageio
import matplotlib.pyplot as plt
import numpy as np
def rgb2gray(rgb):
gray = rgb[:, :, 0] * 0.299 + rgb[:, :, 1] * 0.587 + rgb[:, :, 2] * 0.114
gray = gray.astype(np.uint8)
return gray
def get_histgram(image, nums=256):
"""
calculate histgram of a gray image
Args:
image:gray image
nums:pixel grade, defualt is 256
Return:
histgram
"""
if len(image.shape) != 2:
raise ValueError("Input image is not a gray image")
h = image.shape[0]
w = image.shape[1]
hist = {}
for i in range(0, nums):
hist[i] = 0
for i in range(0, h):
for j in range(0, w):
hist[image[i][j]] += 1
return hist
image_rgb = np.array(imageio.imread("dark.jpg"))
image = rgb2gray(image_rgb)
hist = get_histgram(image, nums=256)
x = hist.keys()
y = hist.values()
plt.bar(x, y)
plt.savefig("hist")
所以,要增加图像的亮度,既然直方图显示整体像素值偏低,那么就可以直接的把全体的像素值直接加上一个数值:
其中c是一个可以自由选择的常数,通过线性加法获得了下面的照片:
代码如下(由于大于195的像素值加60以后大于255,所以要限制最大像素值的方式):
# y = pixel*k + b
# y = pixel*k + b
import imageio
import numpy as np
import matplotlib.pyplot as plt
plt.figure(figsize=(10,10))
dark_image = imageio.imread("dark.jpg")
plt.subplot(1,2,1)
plt.imshow(dark_image/255)
plt.subplot(1,2,2)
invert_image = dark_image + 60
invert_image = invert_image.astype(np.uint8)
plt.imshow(invert_image/255)
plt.show()
然后统计一下线性变换以后的直方图:
现在,像素值不再是集中在0-50之间,而是向50-100靠拢,可是照片中窗口颜色明显失真,看来这种增强方式不可行。
所以,尝试使用了分段线性变换的方式(由于计算速度太慢,所以resize了),分段线性就是针对不同的灰度区间采取不同变换函数,选择a=(x1,y1),b=(x2,y2)两个分段点,对0-a之间和a-b之间与b-255之间,这三个不同灰度区间采用不同的变换:
# 灰度分层
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
dark_image = np.array(Image.open("dark.jpg").resize((300,450),Image.ANTIALIAS))
# dark_image = np.array(Image.open("dark.jpg"))
def seg_linear(dark_image,x1,y1,x2,y2):
if (x1>x2 or y1>y2):
raise ValueError("must make sure x1<x2 and y1<y2")
seg_image = np.zeros(shape=dark_image.shape, dtype=np.float32)
for i in range(dark_image.shape[2]):
for j in range(dark_image.shape[1]):
for k in range(dark_image.shape[0]):
if dark_image[k][j][i] < x1:
seg_image[k][j][i] = (y1/x1)*dark_image[k][j][i]
elif dark_image[k][j][i] < x2:
seg_image[k][j][i] = ((y2-y1)/(x2-x1))*(dark_image[k][j][i]-x1) + y1
else:
seg_image[k][j][i] = ((255-y2)/(255-x2))*(dark_image[k][j][i]-x2) + y2
return seg_image
seg_image = seg_linear(dark_image,40,150,70,220)
plt.figure(figsize=(10,10))
plt.subplot(1,2,1)
plt.imshow(dark_image/255)
plt.subplot(1,2,2)
plt.imshow(seg_image/255)
plt.show()
获得了下面这种结果:
效果感觉还行,就是图像对比度不满意,但是这两个分段点太难选了。
统计一下直方图,发现像素值的分布在不同区间均匀了很多,这样图像的整体像素并没有全部升高,但是亮度还是增加 了不少:
最后尝试了一下采用伽马矫正,这个名字我也不是很理解,感觉就是幂函数:
代码如下:
import imageio
import numpy as np
import matplotlib.pyplot as plt
plt.figure(figsize=(10,10))
dark_image = imageio.imread("dark.jpg")
gama_image = (dark_image.astype(np.float32)/255)**0.25
plt.figure(figsize=(10,10))
plt.subplot(1,2,1)
plt.imshow(dark_image/255)
plt.subplot(1,2,2)
plt.imshow(gama_image)
imageio.imsave("gama.jpg",gama_image*255)
可以调节不同的gama值,就能得到不同程度的亮度增加,直方图如下,像素值整体迁移到了更高的区间,图像的 亮度很自然的增加了:
总结一下:想增强图像的亮度有很多种方式,但是核心就是增大像素值。