欢迎关注 “小白玩转Python”,发现更多 “有趣”
在本文中,我们将学习如何使用 NumPy 对图像进行二值化,当然,我们将使用 OpenCV 来读取灰度和 RGB 格式的图像。
要理解二进制是什么ーー二进制是由两种东西组成的东西。在计算机术语中,二进制只是0和1。如果我们要把同样的事情在图像中联系起来,那么就是说黑白图像中:
0 表示黑色
1 表示白色
在学习图像处理的初始阶段,我们通常认为灰度图像是一个二值图像。虽然不是。但是慢慢地,当我们开始谈论这个话题时,我们意识到我们错得有多离谱。因此,接下来,我们将学习如何使用库和不使用库(NumPy 用于矩阵操作,只是为了避免使用规则 for 循环时程序速度缓慢)将图像进行二进制化。除此之外,我们还将利用 Matplotlib 来绘制结果。
RGB 和灰度概述
对于灰度图像来说,二值化运算的效果非常好。彩色(RGB)图像的问题在于,每个像素都是一个矢量,代表3个唯一的值,一个是红色,一个是绿色,一个是蓝色。
一个典型的灰度图像的矩阵看起来像:
array([[162, 162, 162, ..., 170, 155, 128],
[162, 162, 162, ..., 170, 155, 128],
[162, 162, 162, ..., 170, 155, 128],
...,
[ 43, 43, 50, ..., 104, 100, 98],
[ 44, 44, 55, ..., 104, 105, 108],
[ 44, 44, 55, ..., 104, 105, 108]], dtype=uint8)
一个典型的 RGB 图像的矩阵看起来像:
array([[[226, 137, 125], ..., [200, 99, 90]],
[[226, 137, 125], ..., [200, 99, 90]],
[[226, 137, 125], ..., [200, 99, 90]],
...,
[[ 84, 18, 60], ..., [177, 62, 79]],
[[ 82, 22, 57], ..., [185, 74, 81]],
[[ 82, 22, 57], ..., [185, 74, 81]]], dtype=uint8)
如果我们将 R,G 和 B 三个通道的像素从上面的矩阵中分离出来,我们得到。
R 矩阵
array([[226, 226, 223, ..., 230, 221, 200],
[226, 226, 223, ..., 230, 221, 200],
[226, 226, 223, ..., 230, 221, 200],
...,
[ 84, 84, 92, ..., 173, 172, 177],
[ 82, 82, 96, ..., 179, 181, 185],
[ 82, 82, 96, ..., 179, 181, 185]], dtype=uint8)
G 矩阵
array([[137, 137, 137, ..., 148, 130, 99],
[137, 137, 137, ..., 148, 130, 99],
[137, 137, 137, ..., 148, 130, 99],
...,
[ 18, 18, 27, ..., 73, 68, 62],
[ 22, 22, 32, ..., 70, 71, 74],
[ 22, 22, 32, ..., 70, 71, 74]], dtype=uint8)
B 矩阵
array([[125, 125, 133, ..., 122, 110, 90],
[125, 125, 133, ..., 122, 110, 90],
[125, 125, 133, ..., 122, 110, 90],
...,
[ 60, 60, 58, ..., 84, 76, 79],
[ 57, 57, 62, ..., 79, 81, 81],
[ 57, 57, 62, ..., 79, 81, 81]], dtype=uint8)
无论我们对灰度图像进行什么操作,我们都需要对 RGB 图像进行相同的计算,但需要将 R,G 和 B 通道分离3次,最后将它们合并为一个正确的 RGB 图像。
编程时间
我们主要使用的软件库是:
NumPy
Matplotlib
OpenCV
导入软件库
import numpy as np
import cv2
import json
from matplotlib import pyplot as plt
读取图片
def read_this(image_file, gray_scale=False):
image_src = cv2.imread(image_file)
if gray_scale:
image_src = cv2.cvtColor(image_src, cv2.COLOR_BGR2GRAY)
else:
image_src = cv2.cvtColor(image_src, cv2.COLOR_BGR2RGB)
return image_src
上面的函数以灰度或者 RGB 的形式读取图像并返回图像矩阵。
实现代码
为了将图像转换为二值图像,我们可以简单地利用 cv2库中提供的threshold()方法。这种方法,不管图像是什么(灰度或 RGB)转换成二进制。使用时需要4个参数。
src:基本上就是图像矩阵
thresh:阈值,基于这个阈值像素被赋予一个新的值。如果像素小于这个值,我们将把这些像素重新赋值为255;否则,像素将重新定值为0
maxval:图像可以包含的最大像素值(255)
type:一种给定的阈值类型,并基于该类型计算操作。
在此之后,我们将在下面的函数绘制结果以查看变化。
def binarize_lib(image_file, thresh_val=127, with_plot=False, gray_scale=False):
image_src = read_this(image_file=image_file, gray_scale=gray_scale)
th, image_b = cv2.threshold(src=image_src, thresh=thresh_val, maxval=255, type=cv2.THRESH_BINARY)
if with_plot:
cmap_val = None if not gray_scale else 'gray'
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(10, 20))
ax1.axis("off")
ax1.title.set_text('Original')
ax2.axis("off")
ax2.title.set_text("Binarized")
ax1.imshow(image_src, cmap=cmap_val)
ax2.imshow(image_b, cmap=cmap_val)
return True
return image_b
让我们测试一下上面的函数:
binarize_lib(image_file='lena_original.png', with_plot=True)
binarize_lib(image_file='lena_original.png', with_plot=True, gray_scale=True)
现在我们已经看到了原始图像和二进制图像的结果,很明显,使用库中提供的函数编写的代码对这两者都适用。是时候让我们从头开始编写如何使用 NumPy 对图像进行二值化。
从零开始的代码实现
首先,我们将编写一个函数,将小于指定阈值的像素值重新赋值为255。
通过这样做,我们可以看到下面这样的东西:
def convert_binary(image_matrix, thresh_val):
white = 255
black = 0
initial_conv = np.where((image_matrix <= thresh_val), image_matrix, white)
final_conv = np.where((initial_conv > thresh_val), initial_conv, black)
return final_conv
我们将上面的函数通过分离 r、 g 和 b 值进行三次调用,最后将它们合并得到二值化图像。一旦这样做,我们就可以像以前那样绘制结果。
def binarize_this(image_file, thresh_val=127, with_plot=False, gray_scale=False):
image_src = read_this(image_file=image_file, gray_scale=gray_scale)
if not gray_scale:
cmap_val = None
r_img, g_img, b_img = image_src[:, :, 0], image_src[:, :, 1], image_src[:, :, 2]
r_b = convert_binary(image_matrix=r_img, thresh_val=thresh_val)
g_b = convert_binary(image_matrix=g_img, thresh_val=thresh_val)
b_b = convert_binary(image_matrix=b_img, thresh_val=thresh_val)
image_b = np.dstack(tup=(r_b, g_b, b_b))
else:
cmap_val = 'gray'
image_b = convert_binary(image_matrix=image_src, thresh_val=thresh_val)
if with_plot:
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(10, 20))
ax1.axis("off")
ax1.title.set_text('Original')
ax2.axis("off")
ax2.title.set_text("Binarized")
ax1.imshow(image_src, cmap=cmap_val)
ax2.imshow(image_b, cmap=cmap_val)
return True
return image_b
我们只是使用 NumPy 创建了我们的二进制化代码:
binarize_this(image_file='lena_original.png', with_plot=True)
binarize_this(image_file='lena_original.png', with_plot=True, gray_scale=True)
至此,我们发现使用 NumPy 从零编写的结果与我们调用库函数编写的代码得到的结果非常相似。
· END ·
HAPPY LIFE