如果需要处理的原图及代码,请移步小编的GitHub地址

  传送门:请点击我

  如果点击有误:https://github.com/LeBron-Jian/ComputerVisionPractice

  下面主要学习图像灰度化的知识,结合OpenCV调用 cv2.cvtColor()函数实现图像灰度化,使用像素处理方法对图像进行灰度化处理。

1.  图像灰度化

1.1  图像灰度化原理

  图像灰度化是将一幅彩色图像转换为灰度化图像的过程。彩色图像通常包括R、G、B三个分量,分别显示出红绿蓝等各种颜色,灰度化就是使彩色图像的R、G、B三个分量相等的过程。灰度图像中每个像素仅具有一种样本颜色,其灰度是位于黑色与白色之间的多级色彩深度,灰度值大的像素点比较亮,反之比较暗,像素值最大为255(表示白色),像素值最小为0(表示黑色)。

  假设某点的颜色由RGB(R,G,B)组成,常见灰度处理算法如下表所示(盗图:https://blog.csdn.net/Eastmount/article/details/88785768)

  上表中Gray表示灰度处理之后的颜色,然后将原始RGB(R,G,B)颜色均匀地替换成新颜色RGB(Gray,Gray,Gray),从而将彩色图片转化为灰度图像。

  一种常见的方法是加权平均灰度处理,这种效果是最好的。是将RGB三个分量求和再取平均值,但更为准确的方法是设置不同的权重,将RGB分量按不同的比例进行灰度划分。比如人类的眼睛感官蓝色的敏感度最低,敏感最高的是绿色,因此将RGB按照0.299、0.587、0.114比例加权平均能得到较合理的灰度图像,如公式所示:

  下面代码是调用cvtColor()函数将图像进行灰度化处理的代码:

#_*_coding:utf-8_*_
import cv2
import numpy as np
import matplotlib.pyplot as plt #读取原始图片
src1 = cv2.imread('irving.jpg')
src2 = cv2.cvtColor(src1, cv2.COLOR_BGR2RGB) #图像灰度化处理
grayImage = cv2.cvtColor(src1, cv2.COLOR_RGB2GRAY)
grayImage1 = cv2.imread('irving.jpg', 0)
# #显示图像
# cv2.imshow("src", src)
# cv2.imshow("result", grayImage) # #等待显示
# cv2.waitKey(0)
# cv2.destroyAllWindows() plt.subplot(2,2,1), plt.imshow(src1)
plt.xticks([]), plt.yticks([])
plt.title('origin image BGR')
plt.subplot(2,2,2), plt.imshow(src2)
plt.xticks([]), plt.yticks([])
plt.title('origin image RGB')
plt.subplot(2,2,3), plt.imshow(grayImage)
plt.xticks([]), plt.yticks([])
plt.title('BGR2gray image')
plt.subplot(2,2,4), plt.imshow(grayImage1)
plt.xticks([]), plt.yticks([])
plt.title('gray image')
plt.show()

  处理结果如下:

  注意:灰度化的图,在matplotlib里显示图像不是灰色的,这里因为通道转换问题,这里我们在OpenCV中显示则正常。

2,基于像素操作的图像灰度化处理

  基于像素操作的图像灰度化处理方法,主要是最大值灰度处理、平均灰度处理和加权平均灰度处理方法。其实之前也学习过,这里再整理一遍。

2.1  最大值灰度处理方法

  该方法的灰度值等于彩色图像R、G、B三个分量中的最大值,其公式如下:

  其方法灰度化处理后的灰度图亮度很高(即灰度偏亮)。

  代码如下:

#_*_coding:utf-8_*_
import cv2
import numpy as np
import matplotlib.pyplot as plt #读取原始图像
img = cv2.imread('irving.jpg')
src = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) #获取图像高度和宽度
height = img.shape[0]
width = img.shape[1] #创建一幅图像
grayimg = np.zeros((height, width, 3), np.uint8) #图像最大值灰度处理
for i in range(height):
for j in range(width):
#获取图像R G B最大值
gray = max(img[i,j][0], img[i,j][1], img[i,j][2])
#灰度图像素赋值 gray=max(R,G,B)
grayimg[i,j] = np.uint8(gray) # #显示图像
# cv2.imshow("src", img)
# cv2.imshow("gray", grayimg) # #等待显示
# cv2.waitKey(0)
# cv2.destroyAllWindows() plt.subplot(1,3,1), plt.imshow(img)
plt.xticks([]), plt.yticks([])
plt.title('origin image BGR')
plt.subplot(1,3,2), plt.imshow(src)
plt.xticks([]), plt.yticks([])
plt.title('origin image RGB')
plt.subplot(1,3,3), plt.imshow(grayimg)
plt.xticks([]), plt.yticks([])
plt.title('max gray image')
plt.show()

  图如下:

2.2   平均灰度处理方法

  该方法的灰度值等于彩色图像 R,G,B 是哪个分量灰度值的求和平均值,其计算公式如下所示:

  平均灰度处理方法实现代码如下所示:

#_*_coding:utf-8_*_
import cv2
import numpy as np
import matplotlib.pyplot as plt #读取原始图像
img = cv2.imread('irving.jpg') src = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) #获取图像高度和宽度
height = img.shape[0]
width = img.shape[1] #创建一幅图像
grayimg = np.zeros((height, width, 3), np.uint8) #图像平均灰度处理方法
for i in range(height):
for j in range(width):
#灰度值为RGB三个分量的平均值
gray = (int(img[i,j][0]) + int(img[i,j][1]) + int(img[i,j][2])) / 3
grayimg[i,j] = np.uint8(gray) # #显示图像
# cv2.imshow("src", img)
# cv2.imshow("gray", grayimg) # #等待显示
# cv2.waitKey(0)
# cv2.destroyAllWindows() plt.subplot(1,3,1), plt.imshow(img)
plt.xticks([]), plt.yticks([])
plt.title('origin image BGR')
plt.subplot(1,3,2), plt.imshow(src)
plt.xticks([]), plt.yticks([])
plt.title('origin image RGB')
plt.subplot(1,3,3), plt.imshow(grayimg)
plt.xticks([]), plt.yticks([])
plt.title('mean gray image')
plt.show()

 效果如下:

2.3   加权平均灰度处理方法

  该方法根据色彩重要性,将三个分量以不同的权值进行加权平均。由于人眼对绿色的敏感最高,对蓝色敏感最低,因此,按下式对RGB三分量进行加权平均能得到较合理的灰度图像。

  加权平均灰度处理方法实现代码如下:

#_*_coding:utf-8_*_
import cv2
import numpy as np
import matplotlib.pyplot as plt #读取原始图像
img = cv2.imread('irving.jpg') src = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) #获取图像高度和宽度
height = img.shape[0]
width = img.shape[1] #创建一幅图像
grayimg = np.zeros((height, width, 3), np.uint8) #加权平均灰度处理方法
for i in range(height):
for j in range(width):
#灰度加权平均法
gray = 0.30 * img[i,j][0] + 0.59 * img[i,j][1] + 0.11 * img[i,j][2]
grayimg[i,j] = np.uint8(gray) # #显示图像
# cv2.imshow("src", img)
# cv2.imshow("gray", grayimg) # #等待显示
# cv2.waitKey(0)
# cv2.destroyAllWindows() plt.subplot(1,3,1), plt.imshow(img)
plt.xticks([]), plt.yticks([])
plt.title('origin image BGR')
plt.subplot(1,3,2), plt.imshow(src)
plt.xticks([]), plt.yticks([])
plt.title('origin image RGB')
plt.subplot(1,3,3), plt.imshow(grayimg)
plt.xticks([]), plt.yticks([])
plt.title('weighted mean gray image')
plt.show()

  效果如下:

3,图像灰度线性变换原理

  图像的灰度线性变换是通过建立灰度映射来调整原始图像的灰度,从而改善图像的质量,凸显图像的细节,提高图像的对比度。灰度线性变换的计算公式如下所示:

  该公式中DB表示灰度线性变换后的灰度值,DA表示变换前输入图像的灰度值,α和b为线性变换方程f(D)的参数,分别表示斜率和截距。

  • 当α=1,b=0时,保持原始图像
  • 当α=1,b!=0时,图像所有的灰度值上移或下移
  • 当α=-1,b=255时,原始图像的灰度值反转
  • 当α>1时,输出图像的对比度增强
  • 当0<α<1时,输出图像的对比度减小
  • 当α<0时,原始图像暗区域变亮,亮区域变暗,图像求补

  如下图所示,显示了图像灰度线性变换对应的效果图:

  下面一一学习。

3.1 图像灰度上移变换

  该算法将实现图像灰度值的上移,从而提升图像的亮度,由于图像的灰度值位于0到255区间之内,所以需要对灰度值进行溢出判断。其实现代码如下所示:

import cv2
import numpy as np
import matplotlib.pyplot as plt # 读取原始图像
img = cv2.imread('irving.jpg') # 图像灰度转换
grayimage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 获取图像高度和宽度
height = grayimage.shape[0]
width = grayimage.shape[1] # 创建一幅图像
result = np.zeros((height, width), np.uint8) # 图像灰度上移变换 DB = DA + 50
for i in range(height):
for j in range(width):
if (int(grayimage[i, j] + 50) > 255):
gray = 255
else:
gray = int(grayimage[i, j] + 50)
result[i, j] = np.uint8(gray) # 显示图像
cv2.imshow("Gray Image", grayimage)
cv2.imshow("Result", result) # 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

  其输出结果如下所示:

  我们可以看到,图像的所有灰度值上移 50,图像变得更白了。(注意:纯黑色对应的灰度值为0,纯白色的对应的灰度值为255)

3.2  图像对比度增强变换

  该算法将增强图像的对比度。Python实现代码如下:

import cv2
import numpy as np
import matplotlib.pyplot as plt # 读取原始图像
img = cv2.imread('irving.jpg') # 图像灰度转换
grayimage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 获取图像高度和宽度
height = grayimage.shape[0]
width = grayimage.shape[1] # 创建一幅图像
result = np.zeros((height, width), np.uint8) # 图像对比度增强变换 DB = DA * 1.5
for i in range(height):
for j in range(width):
if (int(grayimage[i, j] * 1.5) > 255):
gray = 255
else:
gray = int(grayimage[i, j] * 1.5)
result[i, j] = np.uint8(gray) # 显示图像
cv2.imshow("Gray Image", grayimage)
cv2.imshow("Result", result) # 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

  结果如下:

  结果就是图像所有的灰度值增强 1.5倍,我们发现变亮了。

3.3  图像对比度减弱变换

  该算法将减弱图像的对比度,Python实现代码如下所示:

import cv2
import numpy as np
import matplotlib.pyplot as plt # 读取原始图像
img = cv2.imread('irving.jpg') # 图像灰度转换
grayimage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 获取图像高度和宽度
height = grayimage.shape[0]
width = grayimage.shape[1] # 创建一幅图像
result = np.zeros((height, width), np.uint8) # 图像对比度减弱变换 DB = DA * 0.8
for i in range(height):
for j in range(width):
if (int(grayimage[i, j] * 0.8) > 255):
gray = 255
else:
gray = int(grayimage[i, j] * 0.8)
result[i, j] = np.uint8(gray) # 显示图像
cv2.imshow("Gray Image", grayimage)
cv2.imshow("Result", result) # 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

  结果如下所示:

  我们从结果可以看出,图像的所有灰度值减弱,图像变得更暗。

3.4  图像灰度反色变换

  反色变换又称线性灰度求补变换,它是对原图像的像素值进行反转,即黑色变为白色,白色变为黑色的过程。其Python实现代码如下所示:

import cv2
import numpy as np
import matplotlib.pyplot as plt # 读取原始图像
img = cv2.imread('irving.jpg') # 图像灰度转换
grayimage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 获取图像高度和宽度
height = grayimage.shape[0]
width = grayimage.shape[1] # 创建一幅图像
result = np.zeros((height, width), np.uint8) # 图像灰度反色变换 DB = 255 - DA
for i in range(height):
for j in range(width):
gray = 255 - grayimage[i, j]
result[i, j] = np.uint8(gray) # 显示图像
cv2.imshow("Gray Image", grayimage)
cv2.imshow("Result", result) # 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

  其结果如下所示:

  我们发现,图像处理前后的灰度值是互补的。

  注意:图像灰度反色变换在医学图像处理中有一定的应用,如下图所示:

4,图像灰度非线性变换

  图像的灰度非线性变换主要包括对数变换,幂次变换,指数变换,分段函数变换,通过非线性关系对图像进行灰度处理,下面学习三种常见类型的灰度非线性变换。

4.1 图像灰度非线性变换:DB = DA * DA / 255

  下面我们将原始图像的灰度值按照DB = DA * DA / 255 的公式进行非线性变换,其代码如下:

import cv2
import numpy as np
import matplotlib.pyplot as plt # 读取原始图像
img = cv2.imread('irving.jpg') # 图像灰度转换
grayimage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 获取图像高度和宽度
height = grayimage.shape[0]
width = grayimage.shape[1] # 创建一幅图像
result = np.zeros((height, width), np.uint8) # 图像灰度非线性变换 DB = DA * DA / 255
for i in range(height):
for j in range(width):
gray = int(grayimage[i, j]) * int(grayimage[i, j]) / 255
result[i, j] = np.uint8(gray) # 显示图像
cv2.imshow("Gray Image", grayimage)
cv2.imshow("Result", result) # 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

  结果如下:

4.2 图像灰度对数变换

  图像灰度的对数变换一般表示如公式所示:

  其中 c 为尺度比较常数,DA为原始图像灰度值,DB为变换后的目标灰度值。如下图所示,它表示对数曲线下的灰度值变换情况。

  由于对数曲线在像素值较低的区域斜率大,在像素值高的区域斜率较小,所以图像经过对数变换后,较暗区域的对比度将有所提升。这种变换可用于增强图像的暗部细节,从而用来扩展被压缩的高值图像中的较暗像素。

  对数变换实现了扩展低灰度值而压缩高灰度值的效果,被广泛的应用于频谱图像的显示中。一个典型的应用是傅里叶频谱,其动态范围可能宽达 0 ~ 106 直接显示频谱时,图像显示设备的动态范围往往不能满足要求,从而丢失大量的暗部细节;而在使用对数变换之后,图像的动态范围被合理的非线性压缩,从而可以清晰地显示。在下图中,未经变换的频谱经过对数变换后,增加了低灰度区域的对比度,从而增强暗部的细节。(关于傅里叶变换,我后面会学习)

  下面代码实现了图像灰度的对数变换:

import cv2
import numpy as np
import matplotlib.pyplot as plt # 绘制曲线
def log_plot(c):
x = np.arange(0, 256, 0.01)
y = c * np.log(1 + x)
plt.plot(x, y, 'r', linewidth=1)
# 正常显示中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.title(u'对数变换函数')
plt.xlim(0, 255), plt.ylim(0, 255)
plt.show() # 对数变换
def log(c, img):
output = c * np.log(1.0 + img)
output = np.uint8(output + 0.5)
return output # 读取原始图像
img = cv2.imread('irving.jpg') # 绘制对数变换曲线
log_plot(42) # 图像灰度对数变换
result = log(42, img) # 显示图像
cv2.imshow("Image", img)
cv2.imshow("Result", result) # 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

  结果如下图:

  对应的对数函数曲线如图所示:

  对数变换对于整体对比度偏低并且灰度值偏低的图像增强效果较好。

4.3  图像灰度伽马变换

  gamma矫正通常用于电汇和监视器系统中重现摄像机拍摄的画面,在图像处理中也可用于调节图像的对比度,减少图像的光照不均和局部阴影。

  gamma变换又称为指数变换或幂次变换,是另外一种常用的灰度非线性变换。图像灰度的伽马变换一般表示如下:

  gamma矫正示意图:

  结合上图看gamma矫正的作用:

  • 1,当 gamma<1时,如虚线所示,在低灰度值区域内,动态范围变大,进而图像对比度增强(当 x € [0, 0.2] 时,y的范围从 [0, 0.218] 扩大到 [0, 0.5]);在高灰度值区域内,动态范围变小,图像对比度降低(当 x € [ 0.8, 1]时,y的范围从 [0.8, 1] 缩小到 [0.9, 1],同时,图像整体的灰度值变大)。简单来说:会拉伸图像中灰度级较低的区域,压缩灰度级较高的部分
  • 2,当 gamma >1时,如实线所示,低灰度值区域的动态范围变小,高灰度值区域在动态范围内变大,降低了低灰度值区域图像对比度。提高了高灰度值区域图像对比度。同时,图像整体的灰度值变小。简单来说:会拉伸体现中灰度级较高的区域,压缩灰度级较低的部分
  • 3,当 gamma=1时,该灰度变换是线性的,此时通过线性方式改变原图像。

  当一张图片的像素在颜色空间的值都比较大(曝光过度)或者比较小(曝光不足)的情况下,选用合理的R值能减少或者增加其颜色亮度,并使颜色分布更为均匀和丰富,图片效果得到明显的改善。但是这种方法并非对所有在曝光上有缺陷的图片都适用,这是在使用这个方法的时候必须要注意的。

  当一张曝光过度的图片中存在颜色较暗的区域(比如背光面,阴影,颜色较深的物体),由 gamma函数图像可以看出,当选取较小的R值,过度曝光得不到充分的改善;而当选取较大的R值,该区域将会变成黑乎乎的一片;同样,当一张曝光不足的图片存在颜色较亮的区域(比如天空,白色背景,反光物等),如果选取R值较大,则曝光不足得不到改善;而选取较小的R值,这个区域变得更亮,从图片上看就觉得很“扎眼”。

  因此,虽然这种方法对图片的曝光率具有较好的调整效果,但是如果素材敏感差异较大,在调整较暗或较亮区域时,要注意减少对较暗或较亮区域的影响。事实上可以根据相同的原理,利用插值的方法构造相对应的处理函数,以得到更加精致的处理效果。

  使用Python实现,可以通过除以像素最大值,先将图像像素值调整到 0~1之间,然后进行不同的 gamma值的gamma矫正,python代码如下:

import cv2
import numpy as np
import matplotlib.pyplot as plt # 绘制曲线
def gamma_plot(c, gamma):
x = np.arange(0, 256, 0.01)
# y = c * x ** gamma
y = c * np.power(x, gamma)
plt.plot(x, y, 'r', linewidth=1)
plt.rcParams['font.sans-serif'] = ['SimHei'] # 正常显示中文标签
plt.title(u'伽马变换函数')
plt.xlim([0, 255]), plt.ylim([0, 255])
plt.show() # 伽玛变换
def gamma(img, c, gamma):
# 映射表必须为0~255(改成其他会报错)
gamma_table = c * [np.power(x/255.0, gamma) * 255.0 for x in range(256)]
# Numpy数组默认数据类型为 int32,需要将数据类型转换为opencv图像适合使用的无符号八位整形
# round() 方法返回浮点数x的四舍五入值。
gamma_table = np.round(np.array(gamma_table)).astype(np.uint8)
output_img = cv2.LUT(img, gamma_table)
return output_img def gamma_1(img, c, gamma):
# 映射表必须为0~255(改成其他会报错)
output_img = c * np.power(img / float(np.max(img)), gamma) * 255.0
output_img = np.uint8(output_img)
return output_img # 读取原始图像
img = cv2.imread('irving.jpg') # 将图像转换为灰度,我们需要使用灰度图做gamma矫正
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 绘制伽玛变换曲线
gamma_plot(1, 4.0) # 图像灰度伽玛变换
result = gamma(gray, 1, 0.4)
result1 = gamma_1(gray, 1, 0.4) # 显示图像
cv2.imshow("Image", img)
cv2.imshow("Result", result)
cv2.imshow("Result 1", result1) # 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()

  除以最大值的目的是归一化,将像素值调整到0~1之间。

  我们看结果:

4.3  opencv中  cv2.LUT() 函数

  此函数主要用来其到突出的有用信息,增强图像的光对比度的作用。通过对input的灰度像素的改变,可以通过映射的关系得到需要输出的灰度像素矩阵 output。

  或者这样讲:使用查找表中的值填充输出数组。我们看源码解析:

def LUT(src, lut, dst=None): # real signature unknown; restored from __doc__
"""
LUT(src, lut[, dst]) -> dst
. @brief Performs a look-up table transform of an array.
对数组执行查找表转换。
.
. The function LUT fills the output array with values from the look-up table. Indices of the entries
. are taken from the input array. That is, the function processes each element of src as follows:
函数LUT使用查找表中的值填充输出数组。 条目的索引取自输入数组。
也就是说,该函数按以下方式处理src的每个元素:
. \f[\texttt{dst} (I) \leftarrow \texttt{lut(src(I) + d)}\f]
. where
. \f[d = \fork{0}{if \(\texttt{src}\) has depth \(\texttt{CV_8U}\)}{128}{if \(\texttt{src}\) has depth \(\texttt{CV_8S}\)}\f]
. @param src input array of 8-bit elements. 8位元素的输入数组。 . @param lut look-up table of 256 elements; in case of multi-channel input array, the table should
. either have a single channel (in this case the same table is used for all channels) or the same
. number of channels as in the input array.
256个元素的查询表;
如果是多通道输入数组,则该表应具有单个通道
(在这种情况下,所有通道都使用相同的表)或与输入阵列中的通道数相同。 . @param dst output array of the same size and number of channels as src, and the same depth as lut.
输出数组,其大小和通道数与src相同,深度与lut相同。
. @sa convertScaleAbs, Mat::convertTo
"""
pass

参考文献:

https://blog.csdn.net/Rothwale/article/details/79189032

https://blog.csdn.net/Eastmount/article/details/88785768

https://blog.csdn.net/akadiao/article/details/79679306

https://blog.csdn.net/Eastmount/article/details/88858696

https://blog.csdn.net/Eastmount/article/details/88929290

最新文章

  1. [翻译]利用顶点位移的VR畸变校正
  2. 高效 Java Web 应用开发框架 JessMA v3.2.2 正式发布
  3. Javascript高级程序设计——基本概念(二)
  4. 经典的nav导航
  5. hdu 1047 (big integer sum, fgets or scanf, make you func return useful infos) 分类: hdoj 2015-06-18 08:21 39人阅读 评论(0) 收藏
  6. 通过NuGet获取sqlite对应的.net的dll
  7. java Object类学习
  8. ecshop数据库操作类
  9. 检查浏览器url改变,处理ajax前进和后退键
  10. Hadoop hdfs完全分布式搭建教程
  11. Nodejs mongodb 管理组件adminmongodb
  12. VMware Workstation 常见问题解决
  13. 数据拆分之 垂直拆分 and 水平拆分
  14. python的日志配置
  15. Docker 日志都在哪里?怎么收集?
  16. Enum扩展特性,代替中文属性
  17. matplotlib-区域填充
  18. motor helper
  19. 《Gradle权威指南》--Groovy基础
  20. rpm 软件包管理

热门文章

  1. RTS寻路算法
  2. 1DadaFrame和Series创建
  3. 20190923-02Linux文件目录类 000 010
  4. mac如何安装YaPi
  5. 不定方程(Exgcd)
  6. python面向对象单继承,多继承和super()调用
  7. 云计算openstack核心组件——neutron网络服务(8)
  8. 项目初始化CSS公共样式
  9. spring中配事务的工具配置
  10. 刷题[GXYCTF2019]BabySQli