简单的物体识别
===已解决===
感谢大家的建议和评论。通过研究《Python可视化入门》这本书中的洪水填充算法(第9章 - 图像处理),我实现了我想要的功能。我可以数出图像中的物体,获取每个物体的外接矩形(因此可以得到高度和宽度),最后可以为每个物体构建NumPy数组或矩阵。
虽然这个方法不是最优的,但它能满足我的需求。我使用的源代码(lab2.py)和png文件(lab2-particles.png)已经放在http://code.google.com/p/ccnworks/source/browse/#svn/trunk/AtSc450上。
你需要安装NumPy和PIL,以及matplotlib来查看直方图。代码的核心在于objfind函数,这里是主要的递归对象搜索操作。
还有一个更新:
SciPy的ndimage.label()也能完美实现我想要的功能。
感谢David-Warde Farley和Zachary Pincus,他们在NumPy和SciPy的邮件列表中给了我很好的建议 :)
=============
你好,
我有一张图片,里面包含了通过粒子光谱仪测量的冰粒子的阴影。我想识别每个物体,以便后续进行分类和进一步计算。
简单来说,我想实现一个模糊选择工具,这样我就可以轻松选择每个实体。
我该如何简单地解决这个问题?(最好使用Python)
谢谢。
注意:在我的问题中,我将每个特定的连通像素称为物体或实体。我打算提取它们并创建NumPy数组表示,如下所示。(这里我使用的是左上角的物体;如果像素存在则用1表示,不存在则用0表示。这个物体的形状是3乘3,对应于3个像素的高度和3个像素的宽度。这些是实际冰粒子在二维平面上的投影,假设它们是球形的,等效半径为(高度+宽度)/2,后面会进行一些从像素到实际尺寸和体积计算的缩放。)
import numpy as np
np.array([[1,1,1], [1,1,1], [0,0,1]])
array([[1, 1, 1],
[1, 1, 1],
[0, 0, 1]])
这是我将要使用的图像的一部分。
5 个回答
我以前在显微照片上做这种分析,后来把需要的功能都整合到一个用C语言写的图像处理和分析软件包里,通过Tcl来操作。这个软件包只处理512 x 512的图像,这也解释了为什么512这个数字出现得这么频繁。虽然有些图像的像素大小不同,但大部分工作都是用8位像素完成的,这就是为什么会提到0xff和图像上最大有效像素值为254的原因。
简单来说,Tcl命令开头的'zz'会把后面的内容发送给软件包的解析器,然后调用相应的C语言程序来处理这些参数。在'zz'后面是一个参数,表示命令的输入和输出。(可以有多个输入,但只能有一个输出。)'r'表示一个512 x 512 x 8位的图像。第三个词是要调用的命令名;'graphs'是用来标记图像的,具体内容在下面的文字中有说明。所以,'zz rr graphs'的意思是'调用ZZ解析器;输入一个r图像到graphs命令,并返回一个r图像。' Tcl命令行的其余部分则指定了要使用的预分配图像。('g'图像是一个感兴趣区域图像;几乎所有ZZ操作都是在ROI控制下进行的。)因此,'r1 r1 g8'的意思是'使用r1作为输入,使用r1作为输出(也就是说,标记输入图像本身),并在图像g8(即r8,作为ROI使用)对应的像素值大于0的地方进行操作。
我觉得这个软件包在网上找不到,但如果你想查看源代码或者甚至编译整个程序,我很乐意把它发给你。这是手册中的一段摘录(不过我觉得现在看手册时发现了一些错误,真是尴尬……):
示例6. 计数特征。
问题
计数是一个常见的任务。被计数的项目称为“特征”,通常需要仔细准备图像,以确保特征与要计数的真实物体一一对应。不过在这里,我们不考虑图像准备,而是关注计数的过程。第一个计数练习是找出目录./cells中的图像有多少个特征。
方法
首先,我们定义一下“特征”。特征是指可以通过从一个已设置的像素沿着上下左右的路径(北、南、东、西)到达的所有“设置”(非零)像素的最大组合。检测和标记图像上这些特征的zz命令是“zz rr graphs R:src R:dest G:ROI”,之所以这样称呼,是因为这种特征的数学术语是“图”。如果图像上的所有像素都被设置,那么图像上就只有一个图,但它包含262144个像素(512 * 512)。如果像素以棋盘格的方式设置和清除(等于零),那么将会有131072个图,但每个图只包含一个像素。简单来说,“zz rr graphs”从图像的左上角开始,逐行扫描,直到找到一个已设置的像素,然后找到所有通过上下左右相连的已设置像素(“4连通”)。它会把这个图中的所有像素设置为1(0x01)。在找到并标记图1后,它会从首次发现图1的像素之后开始扫描,这次会忽略已经属于某个图的像素。它找到的前254个图会被唯一标记;但之后找到的所有图都会被标记为255(0xff),因此无法区分。准确计数任何数量图的关键是分阶段处理每个图像,也就是说,先找出图像上的图的数量,如果数量超过254,就删除刚找到的254个图,重复这个过程,直到找到254个或更少的图。Tcl语言提供了设置控制这个操作的手段。
让我们开始构建需要的命令,先把ZZ图像文件读入R图像,并检测和标记图。处理循环之前,我们声明并初始化一个变量,用来保存图像系列中的特征总数。在处理循环中,我们首先读取图像文件到R图像,并检测和标记图。
zz ur to $inDir/$img r1
zz rr graphs r1 r1 g8
接下来,我们初始化一些变量来跟踪计数,然后使用“ra max”命令来检查是否检测到超过254个图。
set nGraphs [ zz ra max r1 a1 g1 ]
如果nGraphs等于255,那么应该将254个准确计数的图加到总数中,1到254的图应该被删除,并重复计数,直到图的数量减少到255以下。
while {$nGraphs == 255} {
incr sumGraphs 254
zz rbr lt r1 155 r1 g1 0 255
set sumGraphs 0
zz rr graphs r1 r1 g8
set nGraphs [ zz ra max r1 a1 g8 ]
}
当“while”循环结束时,变量nGraphs必须保存一个小于255的数字,也就是准确计数的图的数量;这个数量会加到图像系列的特征总数中。
incr sumGraphs $nGraphs
在处理循环之后,打印出在系列中找到的特征总数。
puts “Total number of features in $inDir \
images $beginImg through $endImg is $sumGraphs.”
在处理循环之后,打印出在系列中找到的特征总数。
根据你提供的图片,接下来你只需要使用一个简单的 区域生长算法。
如果我是用MATLAB的话,我会用 bwlabel 或者 bwboundaries 这两个函数。我相信在 Numpy 中也有类似的函数,或者可以使用 OpenCV 和Python的封装,正如@kwatford所建议的那样。
从左上角开始,逐个检查每个方块,按从左到右、从上到下的顺序。
当你遇到一个蓝色方块时:
a. 记下这个方块的位置,作为一个新物体的标记。
b. 找出所有相连的蓝色方块(比如查看这个方块的邻居,再查看那些邻居的邻居,依此类推),并把它们标记为同一个物体的一部分。
继续扫描。
当你发现另一个蓝色方块时,先检查一下它是否已经属于一个已知的物体,然后再进行第二步;或者在第二步的b中,找到一个方块后就把它从未标记的方块中删除。