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

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

1.4.3 形态学:对象计数

形态学(或数学形态学)度量和分析基本形状的图像处理方法的基本框架与集合

形态学通常用于处理二值图像,但是也能够用于灰度图像。

二值图像是指图像的每个像素只能取两个值,通常是 0 和 1。

二值图像通常是,在计算物体的数目,或者度量其大小时,对一幅图像进行阈值化后的结果。

你可以从 http://en.wikipedia.org/wiki/Mathematical_morphology 大体了解形态学及其处理图像的方式。

scipy.ndimage 中 的 morphology 模 块 可 以 实 现 形 态 学 操 作。

你 可 以 使 用 scipy.ndimage 中的 measurements 模块来实现二值图像的计数和度量功能。

下面通过一个简单的例子介绍如何使用它们。

考虑在图 1-12a1 里的二值图像,计算该图像中的对象个数可以通过下面的脚本实现:

from scipy.ndimage import measurements,morphology
# 载入图像,然后使用阈值化操作,以保证处理的图像为二值图像
im = array(Image.open('houses.png').convert('L'))
im = 1*(im<128)
labels, nbr_objects = measurements.label(im)
print "Number of objects:", nbr_objects

上面的脚本首先载入该图像,通过阈值化方式来确保该图像是二值图像。通过和 1相乘,脚本将布尔数组转换成二进制表示。然后,我们使用 label() 函数寻找单个的物体,并且按照它们属于哪个对象将整数标签给像素赋值。

图 1-12b 是 labels 数组的图像。

图像的灰度值表示对象的标签。可以看到,在一些对象之间有一些小的连接。进行二进制开(binary open)操作,我们可以将其移除:

# 形态学开操作更好地分离各个对象
im_open = morphology.binary_opening(im,ones((9,5)),iterations=2)
labels_open, nbr_objects_open = measurements.label(im_open)
print("Number of objects:", nbr_objects_open)

binary_opening() 函数的第二个参数指定一个数组结构元素。该数组表示以一个像素为中心时,使用哪些相邻像素。在这种情况下,我们在 y 方向上使用 9 个像素(上面 4 个像素、像素本身、下面 4 个像素),在 x 方向上使用 5 个像素。你可以指定任意数组为结构元素,数组中的非零元素决定使用哪些相邻像素。参数iterations 决定执行该操作的次数。你可以尝试使用不同的迭代次数 iterations 值,看一下对象的数目如何变化。你可以在图 1-12c 与图 1-12d 中查看经过开操作后的图像,以及相应的标签图像。正如你想象的一样,binary_closing() 函数实现相反的操作。我们将该函数和在 morphology 和 measurements 模块中的其他函数的用法留作练习。

你可以从 scipy.ndimage 模块文档http://docs.scipy.org/doc/scipy/reference/ndimage.html 中了解关于这些函数的更多知识。

图 1-12:形态学示例。使用二值开操作将对象分开,然后计算物体的数目:(a)为原始二值图像;(b)为对应原始图像的标签图像,其中灰度值表示物体的标签;(c)为使用开操作后的二值图像;(d)为开操作后图像的标签图像

1.4.4 一些有用的SciPy模块

SciPy 中包含一些用于输入和输出的实用模块。下面介绍其中两个模块:

  • io
  • misc

1. 读写.mat文件

如果你有一些数据,或者在网上下载到一些有趣的数据集,这些数据以 Matlab的 .mat 文件格式存储,那么可以使用 scipy.io 模块进行读取。

data = scipy.io.loadmat('test.mat')

上面代码中,data 对象包含一个字典,字典中的键对应于保存在原始 .mat 文件中的变量名。

由于这些变量是数组格式的,因此可以很方便地保存到 .mat 文件中。你仅需创建一个字典(其中要包含你想要保存的所有变量),然后使用 savemat() 函数:

data = {}
data['x'] = x
scipy.io.savemat('test.mat',data)

因为上面的脚本保存的是数组 x,所以当读入到 Matlab 中时,变量的名字仍为 x。

关 于 scipy.io 模 块 的 更 多 内 容, 请 参 见 在 线 文 档 http://docs.scipy.org/doc/scipy/reference/io.html

2. 以图像形式保存数组

因为我们需要对图像进行操作,并且需要使用数组对象来做运算,所以将数组直接保存为图像文件 1 非常有用。本书中的很多图像都是这样的创建的。

imsave() 函数 可以从 scipy.misc 模块中载入。

要将数组 im 保存到文件中,可以使用下面的命令:

from scipy.misc import imsave
imsave('test.jpg',im)

scipy.misc 模块同样包含了著名的 Lena 测试图像:

lena = scipy.misc.lena()

该脚本返回一个 512×512 的灰度图像数组。

1.5 高级示例:图像去噪

我们通过一个非常实用的例子——图像的去噪——来结束本章。图像去噪是在去除图像噪声的同时,尽可能地保留图像细节和结构的处理技术。我们这里使用 ROF(Rudin-Osher-Fatemi)去噪模型。该模型最早出现在文献 [28] 中。图像去噪对于很多应用来说都非常重要;这些应用范围很广,小到让你的假期照片看起来更漂亮,大到提高卫星图像的质量。ROF 模型具有很好的性质:使处理后的图像更平滑,同时保持图像边缘和结构信息。

注 1:所有 Pylab 图均可保存为多种图像格式,方法是点击图像窗口中的“保存”按钮。

ROF 模型的数学基础和处理技巧非常高深,不在本书讲述范围之内。在讲述如何基于 Chambolle 提出的算法 [5] 实现 ROF 求解器之前,本书首先简要介绍一下 ROF模型。

一幅(灰度)图像 I 的全变差(Total Variation,TV)定义为梯度范数之和。

image.png

其中范数 || I - U || 是去噪后图像 U 和原始图像 I 差异的度量。

也就是说,本质上该模型使去噪后的图像像素值“平坦”变化,但是在图像区域的边缘上,允许去噪后的图像像素值“跳跃”变化。

按照论文 [5] 中的算法,我们可以按照下面的代码实现 ROF 模型去噪:

from numpy import *
def denoise(im,U_init,tolerance=0.1,tau=0.125,tv_weight=100):
 """ 使用 A. Chambolle(2005)在公式(11)中的计算步骤实现 Rudin-Osher-Fatemi(ROF)去噪模型
 输入:含有噪声的输入图像(灰度图像)、U 的初始值、TV 正则项权值、步长、停业条件
 输出:去噪和去除纹理后的图像、纹理残留 """
m,n = im.shape # 噪声图像的大小
# 初始化
U = U_init
Px = im # 对偶域的x 分量
Py = im # 对偶域的y 分量
error = 1
while (error > tolerance):
 Uold = U
# 原始变量的梯度
 GradUx = roll(U,-1,axis=1)-U # 变量 U 梯度的x 分量
 GradUy = roll(U,-1,axis=0)-U # 变量 U 梯度的y 分量
 # 更新对偶变量
 PxNew = Px + (tau/tv_weight)*GradUx
 PyNew = Py + (tau/tv_weight)*GradUy
 NormNew = maximum(1,sqrt(PxNew**2+PyNew**2))
 Px = PxNew/NormNew # 更新x 分量(对偶)
 Py = PyNew/NormNew # 更新y 分量(对偶)
 # 更新原始变量
 RxPx = roll(Px,1,axis=1) # 对x 分量进行向右x 轴平移
 RyPy = roll(Py,1,axis=0) # 对y 分量进行向右y 轴平移
 DivP = (Px-RxPx)+(Py-RyPy) # 对偶域的散度
 U = im + tv_weight*DivP # 更新原始变量
 # 更新误差
 error = linalg.norm(U-Uold)/sqrt(n*m);
return U,im-U # 去噪后的图像和纹理残余

在这个例子中,我们使用了 roll() 函数。顾名思义,在一个坐标轴上,它循环“滚动”数组中的元素值。该函数可以非常方便地计算邻域元素的差异,比如这里的导数。我们还使用了 linalg.norm() 函数,该函数可以衡量两个数组间(这个例子中是指图像矩阵 U 和 Uold)的差异。我们将这个 denoise() 函数保存到 rof.py 文件中。

下面使用一个合成的噪声图像示例来说明如何使用该函数:

from numpy import *
from numpy import random
from scipy.ndimage import filters
import rof
# 使用噪声创建合成图像
im = zeros((500,500))
im[100:400,100:400] = 128
im[200:300,200:300] = 255
im = im + 30*random.standard_normal((500,500))
U,T = rof.denoise(im,im)
G = filters.gaussian_filter(im,10)
# 保存生成结果
from scipy.misc import imsave
imsave('synth_rof.pdf',U)
imsave('synth_gaussian.pdf',G)

原始图像和图像的去噪结果如图 1-13 所示。正如你所看到的,ROF 算法去噪后的图像很好地保留了图像的边缘信息。

图 1-13:使用 ROF 模型对合成图像去噪:(a)为原始噪声图像;(b)为经过高斯模糊的图像

(σ=10);(c)为经过 ROF 模型去噪后的图像

下面看一下在实际图像中使用 ROF 模型去噪的效果:

from PIL import Image
from pylab import *
import rof
im = array(Image.open('empire.jpg').convert('L'))
U,T = rof.denoise(im,im)
figure()
gray()
imshow(U)
axis('equal')
axis('off')
show()

经过 ROF 去噪后的图像如图 1-14c 所示。为了方便比较,该图中同样显示了模糊后的图像。可以看到,ROF 去噪后的图像保留了边缘和图像的结构信息,同时模糊了“噪声”。

图 1-14:使用 ROF 模型对灰度图像去噪:(a)为原始噪声图像;(b)为经过高斯模糊的图

像(σ=5);(c)为经过 ROF 模型去噪后的图像

练习

(1) 如图 1-9 所示,将一幅图像进行高斯模糊处理。随着 σ 的增加,绘制出图像轮廓。在你绘制出的图中,图像的轮廓有何变化?你能解释为什么会发生这些变化吗?

(2) 通过将图像模糊化,然后从原始图像中减去模糊图像,来实现反锐化图像掩模操作(http://en.wikipedia.org/wiki/Unsharp_masking)。反锐化图像掩模操作可以实现图像锐化效果。试在彩色和灰度图像上使用反锐化图像掩模操作,观察该操作的效果。

(3) 除了直方图均衡化,商图像是另一种图像归一化的方法。商图像可以通过除以模糊后的图像 I/(I * Gσ) 获得。尝试使用该方法,并使用一些样本图像进行验证。

(4) 使用图像梯度,编写一个在图像中获得简单物体(例如,白色背景中的正方形)轮廓的函数。

(5) 使用梯度方向和大小检测图像中的线段。估计线段的长度以及线段的参数,并在原始图像中重新绘制该线段。

(6) 使用 label() 函数处理二值化图像,并使用直方图和标签图像绘制图像中物体的大小分布。

(7) 使用形态学操作处理阈值化图像。在发现一些参数能够产生好的结果后,使用morphology 模块里面的 center_of_mass() 函数寻找每个物体的中心坐标,将其在图像中绘制出来。

代码示例约定

从第 2 章起,我们假定 PIL、NumPy 和 Matplotlib 都包括在你所创建的每个文件和每

个代码例子的开头:

from PIL import Image
from numpy import *
from pylab import *

这种约定使得示例代码更清晰,同时也便于读者理解。除此之外,我们使用 SciPy模块时,将会在代码示例中显式声明。

一些纯化论者会反对这种将全体模块导入的方式,坚持如下使用方式:

import numpy as np
import matplotlib.pyplot as plt

这种方式能够保持命名空间(知道每个函数从哪儿来)。因为我们不需要 PyLab 中的NumPy 部分,所以该例子只从 Matplotlib 中导入 pyplot 部分。纯化论者和经验丰富的程序员们知道这些区别,他们能够选择自己喜欢的方式。但是,为了使本书的内容和例子更容易被读者接受,我们不打算这样做。请读者注意。

总结


相关文章
|
6月前
|
数据采集 机器学习/深度学习 人工智能
Python:现代编程的首选语言
Python:现代编程的首选语言
778 102
|
6月前
|
数据采集 机器学习/深度学习 算法框架/工具
Python:现代编程的瑞士军刀
Python:现代编程的瑞士军刀
413 104
|
6月前
|
人工智能 自然语言处理 算法框架/工具
Python:现代编程的首选语言
Python:现代编程的首选语言
331 103
|
5月前
|
Python
Python编程:运算符详解
本文全面详解Python各类运算符,涵盖算术、比较、逻辑、赋值、位、身份、成员运算符及优先级规则,结合实例代码与运行结果,助你深入掌握Python运算符的使用方法与应用场景。
379 3
|
5月前
|
数据处理 Python
Python编程:类型转换与输入输出
本教程介绍Python中输入输出与类型转换的基础知识,涵盖input()和print()的使用,int()、float()等类型转换方法,并通过综合示例演示数据处理、错误处理及格式化输出,助你掌握核心编程技能。
605 3
|
5月前
|
并行计算 安全 计算机视觉
Python多进程编程:用multiprocessing突破GIL限制
Python中GIL限制多线程性能,尤其在CPU密集型任务中。`multiprocessing`模块通过创建独立进程,绕过GIL,实现真正的并行计算。它支持进程池、队列、管道、共享内存和同步机制,适用于科学计算、图像处理等场景。相比多线程,多进程更适合利用多核优势,虽有较高内存开销,但能显著提升性能。合理使用进程池与通信机制,可最大化效率。
415 3
|
5月前
|
Java 调度 数据库
Python threading模块:多线程编程的实战指南
本文深入讲解Python多线程编程,涵盖threading模块的核心用法:线程创建、生命周期、同步机制(锁、信号量、条件变量)、线程通信(队列)、守护线程与线程池应用。结合实战案例,如多线程下载器,帮助开发者提升程序并发性能,适用于I/O密集型任务处理。
504 0
|
机器学习/深度学习 计算机视觉
AIGC核心技术——计算机视觉(CV)预训练大模型
【1月更文挑战第13天】AIGC核心技术——计算机视觉(CV)预训练大模型
1226 3
AIGC核心技术——计算机视觉(CV)预训练大模型
|
人工智能 测试技术 API
AI计算机视觉笔记二十 九:yolov10竹签模型,自动数竹签
本文介绍了如何在AutoDL平台上搭建YOLOv10环境并进行竹签检测与计数。首先从官网下载YOLOv10源码并创建虚拟环境,安装依赖库。接着通过官方模型测试环境是否正常工作。然后下载自定义数据集并配置`mycoco128.yaml`文件,使用`yolo detect train`命令或Python代码进行训练。最后,通过命令行或API调用测试训练结果,并展示竹签计数功能。如需转载,请注明原文出处。
1286 4
|
存储 人工智能 自然语言处理
关于计算机视觉中的自回归模型,这篇综述一网打尽了
这篇综述文章全面介绍了自回归模型在计算机视觉领域的应用和发展。文章首先概述了视觉中的序列表示和建模基础知识,随后根据表示策略将视觉自回归模型分为基于像素、标记和尺度的三类框架,并探讨其与生成模型的关系。文章详细阐述了自回归模型在图像、视频、3D及多模态生成等多方面的应用,列举了约250篇参考文献,并讨论了其在新兴领域的潜力和面临的挑战。此外,文章还建立了一个GitHub存储库以整理相关论文,促进了学术合作与知识传播。论文链接:https://arxiv.org/abs/2411.05902
437 1

热门文章

最新文章

推荐镜像

更多