[笔记]Python计算机视觉编程《一》 基本的图像操作和处理(二)

简介: [笔记]Python计算机视觉编程《一》 基本的图像操作和处理(二)

1.2.2 图像轮廓和直方图

下面来看两个特别的绘图示例:图像的轮廓直方图

图像的轮廓

绘制图像的轮廓(或者其他二维函数的等轮廓线)在工作中非常有用。因为绘制轮廓需要对每个坐标 [x, y] 的

像素值施加同一个阈值,所以首先需要将图像灰度化:

from PIL import Image
from pylab import *
# 读取图像到数组中
im = array(Image.open('data/empire.jpg').convert('L'))
# 新建一个图像
figure()
# 不使用颜色信息
gray()
# 在原点的左上角显示轮廓图像
contour(im, origin='image')
axis('equal')
axis('off')
show()

像之前的例子一样,这里用 PIL 的 convert() 方法将图像转换成灰度图像。

直方图

图像的直方图用来表征该图像像素值的分布情况。用一定数目的小区间(bin)来指定表征像素值的范围,每个小区间会得到落入该小区间表示范围的像素数目。该(灰度)图像的直方图可以使用 hist() 函数绘制:

figure()
hist(im.flatten(),128)
show()

hist() 函数的第二个参数指定小区间的数目。需要注意的是,因为 hist() 只接受一维数组作为输入,所以我们在绘制图像直方图之前,必须先对图像进行压平处理。

flatten() 方法将任意数组按照行优先准则转换成一维数组。图 1-3 为等轮廓线和直方图图像。

图 1-3:用 Matplotlib 绘制图像等轮廓线和直方图

1.2.3【交互式标注】

有时用户需要和某些应用交互,例如在一幅图像中标记一些点,或者标注一些训练数据。PyLab 库中的 ginput() 函数就可以实现交互式标注。

下面是一个简短的例子:

from PIL import Image
from pylab import *
im = array(Image.open('data/empire.jpg'))
imshow(im)
print('Please click 3 points\n')
x = ginput(3)
x_str=[''.join(str(i)) for i in x]
print('\nyou clicked:'.join(x_str))
show()

上面的脚本首先绘制一幅图像,然后等待用户在绘图窗口的图像区域点击三次。程序将这些点击的坐标 [x, y] 自动保存在 x 列表里。

1.3 NumPy

NumPy(http://www.scipy.org/NumPy/)是非常有名的 Python 科学计算工具包,其中包含了大量有用的思想,比如数组对象(用来表示向量、矩阵、图像等)以及线性代数函数。NumPy 中的数组对象几乎贯穿用于本书的所有例子中 1 数组对象可以帮助你实现数组中重要的操作,比如矩阵乘积、转置、解方程系统、向量乘积和归一化,这为图像变形、对变化进行建模、图像分类、图像聚类等提供了基础。

NumPy 可以从 http://www.scipy.org/Download 免费下载,在线说明文档(http://docs.scipy.org/doc/numpy/)包含了你可能遇到的大多数问题的答案。关于 NumPy 的更多

内容,请参考开源书籍 [24]。

1.3.1 图像数组表示

在先前的例子中,当载入图像时,我们通过调用 array() 方法将图像转换成 NumPy 的数组对象,但当时并没有进行详细介绍。NumPy 中的数组对象是多维的,可以用来表示向量、矩阵和图像。一个数组对象很像一个列表(或者是列表的列表),但是数组中所有的元素必须具有相同的数据类型。除非创建数组对象时指定数据类型,否则数据类型会按照数据的类型自动确定。

对于图像数据,下面的例子阐述了这一点:

from PIL import Image
from pylab import *
im = array(Image.open('data/empire.jpg'))
print(str(im.shape)+","+str(im.dtype))
imshow(im)
im = array(Image.open('data/empire.jpg').convert('L'),'f')
print(str(im.shape)+","+str(im.dtype))
imshow(im)
show()

控制台输出结果如下所示:

(800, 569, 3) uint8
(800, 569) float32

每行的第一个元组表示图像数组的大小(行、列、颜色通道),紧接着的字符串表示数组元素的数据类型。

因为图像通常被编码成无符号八位整数(uint8),所以在第一种情况下,载入图像并将其转换到数组中,数组的数据类型为“uint8”。

第二种情况下,对图像进行灰度化处理,并且在创建数组时使用额外的参数“f”;该参数将数据类型转换为浮点型。关于更多数据类型选项,可以参考图书 [24]。

注意,由于灰度图像没有颜色信息,所以在形状元组中,它只有两个数值。

数组中的元素可以使用下标访问。位于坐标 i、j,以及颜色通道 k 的像素值可以像下面这样访问:

value = im[i,j,k]

注 1: PyLab 实际上包含 NumPy 的一些内容,如数组类型。这也是我们能够在 1.2 节使用数组类型的原因。

多个数组元素可以使用数组切片方式访问。切片方式返回的是以指定间隔下标访问该数组的元素值。下面是有关灰度图像的一些例子:

im[i,:] = im[j,:] # 将第 j 行的数值赋值给第 i 行
im[:,i] = 100 # 将第 i 列的所有数值设为 100
im[:100,:50].sum() # 计算前 100 行、前 50 列所有数值的和
im[50:100,50:100] # 50~100 行,50~100 列(不包括第 100 行和第 100 列)
im[i].mean() # 第 i 行所有数值的平均值
im[:,-1] # 最后一列
im[-2,:] (or im[-2]) # 倒数第二行

注意,示例仅仅使用一个下标访问数组。如果仅使用一个下标,则该下标为行下标。

注意,在最后几个例子中,负数切片表示从最后一个元素逆向计数。我们将会频繁地使用切片技术访问像素值,这也是一个很重要的思想。我们有很多操作和方法来处理数组对象。本书将在使用到的地方逐一介绍。你可以查阅在线文档或者开源图书 [24] 获取更多信息。

1.3.2 灰度变换

将图像读入 NumPy 数组对象后,我们可以对它们执行任意数学操作。一个简单的例子就是图像的灰度变换。考虑任意函数 f,它将 0…255 区间(或者 0…1 区间)映射到自身(意思是说,输出区间的范围和输入区间的范围相同)。

下面是关于灰度变换的一些例子:

from PIL import Image
from numpy import *
im = array(Image.open('empire.jpg').convert('L'))
im2 = 255 - im # 对图像进行反相处理
im3 = (100.0/255) * im + 100 # 将图像像素值变换到 100...200 区间
im4 = 255.0 * (im/255.0)**2 # 对图像像素值求平方后得到的图像

第一个例子将灰度图像进行反相处理;第二个例子将图像的像素值变换到 100…200

区间;第三个例子对图像使用二次函数变换,使较暗的像素值变得更小。

图 1-4 为所使用的变换函数图像。

图 1-5 是输出的图像结果。你可以使用下面的命令查看图像中的最小和最大像素值:

print int(im.min()), int(im.max())

完整代码:

from PIL import Image
from numpy import *
from pylab import *
im = array(Image.open('data/empire.jpg').convert('L'))
print(int(im.min()), int(im.max()))
im2 = 255 - im  # invert image
print(int(im2.min()), int(im2.max()))
im3 = (100.0/255) * im + 100  # clamp to interval 100...200
print(int(im3.min()), int(im3.max()))
im4 = 255.0 * (im/255.0)**2  # squared
print(int(im4.min()), int(im4.max()))
figure()
gray()
subplot(1, 3, 1)
imshow(im2)
axis('off')
title(r'$f(x)=255-x$')
subplot(1, 3, 2)
imshow(im3)
axis('off')
title(r'$f(x)=\frac{100}{255}x+100$')
subplot(1, 3, 3)
imshow(im4)
axis('off')
title(r'$f(x)=255(\frac{x}{255})^2$')
show()

1-5:灰度变换。对图像应用图1-4中的函数:f (x)=255-x 对图像进行反相处理(左);f (x)=(100/255)

x+100 对图像进行变换(中);f (x)=255(x/255)2 对图像做二次变换(右)

如果试着对上面例子查看最小值和最大值,可以得到下面的输出结果:

2 255
0 253
100 200
0 255

array() 变换的相反操作可以使用 PIL 的 fromarray() 函数完成:

pil_im = Image.fromarray(im)

如果你通过一些操作将“uint8”数据类型转换为其他数据类型,比如之前例子中的im3 或者 im4,那么在创建 PIL 图像之前,需要将数据类型转换回来:

pil_im = Image.fromarray(uint8(im))

如果你并不十分确定输入数据的类型,安全起见,应该先转换回来。

注意,NumPy总是将数组数据类型转换成能够表示数据的“最低”数据类型。对浮点数做乘积或除法操作会使整数类型的数组变成浮点类型。

1.3.3 图像缩放

NumPy 的数组对象是我们处理图像和数据的主要工具。想要对图像进行缩放处理没有现成简单的方法。我们可以使用之前 PIL 对图像对象转换的操作,写一个简单的用于图像缩放的函数。

把下面的函数添加到 imtool.py 文件里:

def imresize(im,sz):
 """ 使用 PIL 对象重新定义图像数组的大小 """
 pil_im = Image.fromarray(uint8(im))
 return array(pil_im.resize(sz))

我们将会在接下来的内容中使用这个函数。

1.3.4 直方图均衡化

图像灰度变换中一个非常有用的例子就是直方图均衡化。

直方图均衡化是指将一幅图像的灰度直方图变平,使变换后的图像中每个灰度值的分布概率都相同。在对图像做进一步处理之前,直方图均衡化通常是对图像灰度值进行归一化的一个非常好的方法,并且可以增强图像的对比度。

在这种情况下,直方图均衡化的变换函数是图像中像素值的累积分布函数(cumulative distribution function,简写为 cdf,将像素值的范围映射到目标范围的归一化操作)。

下面的函数是直方图均衡化的具体实现。

将这个函数添加到 imtool.py 里:

def histeq(im,nbr_bins=256):
 """ 对一幅灰度图像进行直方图均衡化 """
 # 计算图像的直方图
 imhist,bins = histogram(im.flatten(),nbr_bins,normed=True)
 cdf = imhist.cumsum() # cumulative distribution function
 cdf = 255 * cdf / cdf[-1] # 归一化
 # 使用累积分布函数的线性插值,计算新的像素值
 im2 = interp(im.flatten(),bins[:-1],cdf)
 return im2.reshape(im.shape), cdf

该函数有两个输入参数,一个是灰度图像,一个是直方图中使用小区间的数目

函数返回直方图均衡化后的图像,以及用来做像素值映射的累积分布函数。注意,函数中使用到累积分布函数的最后一个元素(下标为 -1),目的是将其归一化到 0…1范围。

你可以像下面这样使用该函数:

#imtools.py
from PIL import Image
from numpy import *
def histeq(im,nbr_bins=256):
    """ 对一幅灰度图像进行直方图均衡化 """
    # 计算图像的直方图
    imhist,bins = histogram(im.flatten(),nbr_bins,normed=True)
    cdf = imhist.cumsum() # cumulative distribution function
    cdf = 255 * cdf / cdf[-1] # 归一化
    # 使用累积分布函数的线性插值,计算新的像素值
    im2 = interp(im.flatten(),bins[:-1],cdf)
    return im2.reshape(im.shape), cdf
def imresize(im,sz):
 """ 使用 PIL 对象重新定义图像数组的大小 """
 pil_im = Image.fromarray(uint8(im))
 return array(pil_im.resize(sz))
#test01_03.py
from PIL import Image
from numpy import *
from pylab import *
import imtools
# im = array(Image.open('data/AquaTermi_lowcontrast.jpg').convert('L'))
im = array(Image.open('data/empire.jpg').convert('L'))
im2,cdf = imtools.histeq(im)
imshow(im2)
gray()
show()

图 1-6 和图 1-7 为上面直方图均衡化例子的结果。

上面一行显示的分别是直方图均衡化之前和之后的灰度直方图,以及累积概率分布函数映射图像。

可以看到,直方图均衡化后图像的对比度增强了,原先图像灰色区域的细节变得清晰。

图 1-7:直方图均衡化示例。左侧为原始图像和直方图,中间图为灰度变换函数,右侧为直

方图均衡化后的图像和相应直方图

相关文章
|
6天前
|
存储 设计模式 算法
|
2天前
|
机器学习/深度学习 数据挖掘 API
pymc,一个灵活的的 Python 概率编程库!
pymc,一个灵活的的 Python 概率编程库!
5 1
|
2天前
|
人工智能 算法 调度
uvloop,一个强大的 Python 异步IO编程库!
uvloop,一个强大的 Python 异步IO编程库!
11 2
|
3天前
|
机器学习/深度学习 人工智能 数据可视化
Python:探索编程之美
Python:探索编程之美
9 0
|
3天前
|
机器学习/深度学习 人工智能 数据处理
Python编程的魅力与实践
Python编程的魅力与实践
|
4天前
|
SQL 关系型数据库 MySQL
第十三章 Python数据库编程
第十三章 Python数据库编程
|
4天前
|
存储 网络协议 关系型数据库
Python从入门到精通:2.3.2数据库操作与网络编程——学习socket编程,实现简单的TCP/UDP通信
Python从入门到精通:2.3.2数据库操作与网络编程——学习socket编程,实现简单的TCP/UDP通信
|
4天前
|
JSON 数据格式 索引
python 又一个点运算符操作的字典库:Munch
python 又一个点运算符操作的字典库:Munch
21 0
|
6天前
|
存储 索引 Python
|
8天前
|
机器学习/深度学习 TensorFlow 算法框架/工具
使用Python构建简单的图像识别应用
本文将介绍如何利用Python语言及其相关库来构建一个简单但功能强大的图像识别应用。通过结合OpenCV和深度学习模型,我们将展示如何实现图像的特征提取和分类,从而实现对图像中物体的自动识别和分类。无需复杂的算法知识,只需一些基本的Python编程技巧,你也可以轻松地创建自己的图像识别应用。