文章目录

  • 一些说明
  • 关于示例代码
  • 关于依赖环境
  • 关于教材
  • 灰度图、亮度图(Gray Image)
  • 彩色图转灰度图
  • 一般亮度转换(luminosity method)
  • 亮度优先转换(luminosity priority)
  • 平均值转换(average method)
  • 最小亮度转换(minimum luminosity)
  • 最大亮度转换(maximum luminosity)
  • 效果对比
  • 二值化


一些说明

关于示例代码

我的图像算法的全部示例程序,你都可以在 https://github.com/seagochen/AlgorithmsLearning 找到, 目前只上传了一部分,随着这个系列的完成, 我会同步更新还没有上传的部分。

你可以关注我的博客,又或者把相关文章收藏起来,在你比如面试图像方面的岗位时,又或者学习《数字图像处理》课程时,可以把代码都跑一遍。

需要说明一下, 所有示例代码都是Python完成的,避免使用C/C++或者其他语言,是为了最大程度减少语言特性导致的阅读障碍。

关于OpenCV,它的主要的函数C++ 版本和Python是一致的。所以在理解了程序逻辑后,你也可以很容易的用C/C++复现一遍。

至于程序中用到的素材,主要部分来源于冈萨雷斯的《数字图像处理》,其次是一些在Google上搜索到的公开图片,以及我自己制作的一部分,大部分素材你可以从网上很容易找到,由于这些数据体积很大,所以我没有放在仓库中,而且这不影响你使用。

如果出现了侵权,请你联系我,我会在第一时间内删除。

关于依赖环境

运行Python代码, 目前需要一些依赖包:

  • numpy
  • matplotlib
  • opencv-python

Python版本可以使用3.7的版本,至于怎么安装Python,怎么配置IDE,请你自行在网上搜索相关文章,我这里不做累述。

关于教材

推荐买一本冈萨雷斯的《数字图像处理》,这是从事数字图像领域专业的必备教科书,书中写的很详细,我所写的内容对于这位老教授的书来说,最多只能算是补充和摘要。

你也可以配合一些网上已有的视频,我们IT行业的从业人员大多很热衷于技术分享,所以也有不少教授的公开课的录像、个人UP主制作了这些很棒的内容,这些都是非常不错的借鉴和学习资料。

灰度图、亮度图(Gray Image)

在数字图像中,一张图片是不同的像素的组合。如果用矩阵来表示,就是一个二维的数组,或者说矩阵。每一个元素可以是一个包含3个元素的向量 {R,G,B}向量矩阵,或者仅一个元素的 {Gray} 的元素矩阵。

opencv 涂黑 opencv彩色转黑白_opencv 涂黑


为了更好的研究各类算法,我们常用灰度图作为研究对象。上图中使用的是林肯的照片,如果研究这张照片的原始数据,会发现它其实是这样一个 opencv 涂黑 opencv彩色转黑白_opencv_02 的元素矩阵,每个元素都是一个范围在 opencv 涂黑 opencv彩色转黑白_opencv 涂黑_03的整数。

用数学矩阵进行表示的话,大概就是这个样子:

opencv 涂黑 opencv彩色转黑白_opencv_04

当前主流的图像是8比特位的,所以也解释了为什么大多数PNG、JPG、BMP的格式的图片,图像数据范围都是在 opencv 涂黑 opencv彩色转黑白_opencv 涂黑_03

彩色图转灰度图

将彩色图片转换为灰度图片(亮度图片),经常是数字图像处理中常用到重要一部,转换方法有多种多样,比如在之前文章里提到的HSV等颜色空间中的转换方法,不过在这里我仅提一些在RGB色域空间中常用到的方法。

尽管以下方法,已经有前人实现了相关函数,并广泛存在于各种图形图像函数库中,但我依然希望你能明白理解这些是怎么计算的。

一般亮度转换(luminosity method)

opencv 涂黑 opencv彩色转黑白_彩色转黑白_06

这是一种权重转换,对于每个像素,分别对应其RGB色道上的数据,乘以对应的权重,例如opencv 涂黑 opencv彩色转黑白_灰度图_07,这一类权重,通常在G色道上保留较大的权重,而对于R、B色道上保留较小权重。保留较多的G色道,主要因为在G色道上能保有较多的数据,且人眼对于黄、绿、青的光更为敏感。

在人眼可感知的可见光波的 400nm - 700nm这样一个范围里,绿色、黄色、青色处于可见光光带中央,具备最大的颜色覆盖度,也就是最大的亮度覆盖范围,所以无论哪一种权重转换,都需要尽最大可能保留绿色、黄色、青色的占比权重。

因此对于RGB三原色来说,保留最多的绿色,和适量的红色和蓝色,可以近似的模拟出我们对于光通量的感受情况。如果有需要知道更多细节,可以看看这篇文章《为你更新色彩观: 直观理解光谱和颜色》

opencv 涂黑 opencv彩色转黑白_权重_08


原图:

opencv 涂黑 opencv彩色转黑白_权重_09

这样的实现代码其实很简单:

def rgb_2_gray_normal(rgb_file: str):
    image = cv2.imread(rgb_file)
    row, col, channel = image.shape
    image_gray = np.zeros((row, col))
    for r in range(row):
        for l in range(col):
            # convert rgb image to gray image
            # gray = 0.299⋅R + 0.587⋅G + 0.114⋅B
            image_gray[r, l] =  0.114 * image[r, l, 0] + \
                                0.587 * image[r, l, 1] + \
                                0.299 * image[r, l, 2]

    print_matrix(image_gray)
    return image_gray

最终输出效果

opencv 涂黑 opencv彩色转黑白_opencv 涂黑_10

亮度优先转换(luminosity priority)

opencv 涂黑 opencv彩色转黑白_彩色转黑白_11

实现代码:

def rgb_2_gray_brightness(rgb_file: str):
    image = cv2.imread(rgb_file)
    row, col, channel = image.shape
    image_gray = np.zeros((row, col))

    for r in range(row):
        for l in range(col):
            image_gray[r, l] = 0.5 * max(image[r, l, 0], image[r, l, 1], image[r, l, 2]) +\
                               0.5 * min(image[r, l, 0], image[r, l, 1], image[r, l, 2])

    print_matrix(image_gray)
    return image_gray

输出效果

opencv 涂黑 opencv彩色转黑白_彩色转黑白_12

平均值转换(average method)

亮度转换

opencv 涂黑 opencv彩色转黑白_灰度图_13

输出效果:

opencv 涂黑 opencv彩色转黑白_灰度图_14

最小亮度转换(minimum luminosity)

opencv 涂黑 opencv彩色转黑白_opencv 涂黑_15

def rgb_2_gray_min(rgb_file: str):
    image = cv2.imread(rgb_file)
    row, col, channel = image.shape
    image_gray = np.zeros((row, col))

    for r in range(row):
        for l in range(col):
            image_gray[r, l] = min(image[r, l, 0], image[r, l, 1], image[r, l, 2])

    print_matrix(image_gray)
    return image_gray

输出效果

opencv 涂黑 opencv彩色转黑白_权重_16

最大亮度转换(maximum luminosity)

opencv 涂黑 opencv彩色转黑白_opencv 涂黑_17

def rgb_2_gray_max(rgb_file: str):
    image = cv2.imread(rgb_file)
    row, col, channel = image.shape
    image_gray = np.zeros((row, col))

    for r in range(row):
        for l in range(col):
            image_gray[r, l] = max(image[r, l, 0], image[r, l, 1], image[r, l, 2])

    print_matrix(image_gray)
    return image_gray

输出效果

opencv 涂黑 opencv彩色转黑白_彩色转黑白_18

效果对比

我们把输出效果进行对比:

opencv 涂黑 opencv彩色转黑白_权重_19


相对的来说,采用权重法表现要更柔和一些,当然使用哪一种方法,纯粹依据具体情况来定。例如对于原图本身亮度欠佳的,采用最大亮度的方式会更合适,但对于本来已经光污染的图片来说,也许用最小亮度会更合适一些。

除了这些线性的方式,也有一些采用二次采样曲线的转换方法,以及转换颜色空间到HSV,以纯粹亮度进行输出的方法,这里就不做更多的赘述了。有兴趣的朋友可以自行查找相关资料,做做实验看一看。

二值化

除了灰度图以外,还有另外一类只有黑白的所谓二值化的颜色转换方法。“二值化”,也被称为黑白图像,它的颜色状态只有黑和白两种。也是一种比较常见的图像。它之所以能够存在,其一是压缩后的数据体量更小,便于网络传输,其二是在一些传统的新闻出版行业,可以节省印刷成本。当然,缺点也是有的,就是图片经过二值化后,会丢失很多细节。

比如这样的一个函数,可以当作二值化的处理函数:

opencv 涂黑 opencv彩色转黑白_opencv 涂黑_20

这里的X表示的是阈值,你可以简单的给定一个常数,比如100,也可以配合灰度直方图,动态的进行调整,以达到最大化保存图片细节的目的。

当然我们也可以使用比如图像抖动的方法,为二值化图像增加足够多的细节,不过这些内容会在我后面的文章里提到,这里就不做过多的补充了。

简单的代码实现方法:

def rgb_2_black(rgb_file: str):
    image = cv2.imread(rgb_file)
    row, col, channel = image.shape
    image_gray = np.zeros((row, col))

    for r in range(row):
        for l in range(col):
            # first of all, convert rgb image to gray
            X = 0.114 * image[r, l, 0] + 0.587 * image[r, l, 1] + 0.299 * image[r, l, 2]

            if X > 90:
                image_gray[r, l] = 255
            else:
                image_gray[r, l] = 0

    print_matrix(image_gray)
    return image_gray

输出效果

opencv 涂黑 opencv彩色转黑白_彩色转黑白_21