
一句话介绍: 这套 MATLAB 程序读取一张血细胞灰度图,自动完成阈值分割、去噪、二值逻辑优化、孔洞填充和形态学开运算,再利用 8 邻域连通域统计细胞,并对面积异常大的粘连区域进行补偿计数。项目配套实验结果为 28 个细胞。
显微图像中的细胞并不是天然就能直接“数”。
原图中同时存在:
灰度背景;细胞内部亮暗变化;小颗粒噪声;边缘处不完整细胞;
两个细胞互相接触或粘连的情况。
如果直接对原图做目标计数,背景和噪声很容易被误认为细胞。
这套程序先把图像逐步整理成适合计算机计数的二值目标:
原始细胞图 → Otsu 自动二值化 → 中值滤波 → 二值逻辑优化 → 取反 → 孔洞填充 → 圆盘开运算 → 8 邻域连通域 → 粘连区域补偿 → 细胞总数
最终目标很直接:把“看起来像细胞”的灰度区域,转换成一个个可以编号和统计的二值连通区域。
1. 当前程序处理的是什么图像?
主程序为 main.m,灰度血细胞图像。图中可以看到多个近圆形细胞,同时存在少量小黑点和多个相互接触的细胞,因此很适合演示“图像分割 + 形态学 + 连通域计数”的完整流程。
工程中还附带 统计细胞的数目.docx,记录了该程序的处理思路和实验结果。
2. 第一步:为什么先做自动二值化?
计算机要统计细胞,最方便的形式不是灰度图,而是:
背景 = 0目标 = 1这样的二值图像。
程序使用 graythresh,也就是经典的 Otsu 最大类间方差法自动寻找阈值。
它的核心思想可以写成:
σ_B²(t) = ω₀(t) · ω₁(t) · [μ₀(t) - μ₁(t)]²
其中:
t:候选灰度阈值;
ω₀、ω₁:阈值两侧两类像素所占比例;
μ₀、μ₁:两类像素的平均灰度。
程序选择让 σ_B²(t) 最大的阈值,使前景与背景尽量分开。
对当前 blood1.BMP 核对,Otsu 阈值约为:
90 / 255 ≈ 0.353
因此不需要用户手工反复试阈值。
3. 为什么二值化以后还不能直接计数?
初次二值化后仍然会存在:
小黑点;局部边缘不连续;细胞内部空洞;细胞粘在一起。
如果此时直接使用连通域统计,一个噪点可能被当成一个目标,一个环形细胞也可能因为内部结构影响后续形态处理。
所以程序继续做二值图像清理。
4. 中值滤波在这里做什么?
程序对二值图像使用:中值滤波
中值滤波会观察一个局部邻域,用邻域中的中间值替换当前像素。
对于二值图来说,可以简单理解为:如果某个小黑点周围绝大多数都是白色背景,它很容易被邻域多数像素“淹没”,从而被去除。
因此它特别适合当前图像中的孤立小颗粒噪声。
程序随后没有直接只保留中值滤波结果,而是把:
原始二值图 OR 中值滤波图
进行逻辑“或”运算,用两次分割结果共同优化二值区域。
5. 为什么还要把图像取反?
经过前面的二值化后,程序需要继续对“细胞目标”做:
填孔;开运算;连通域标记。
MATLAB 的这些二值形态学操作通常把逻辑 1 看成前景目标。
所以程序执行取反:把细胞转换成白色前景 1,把背景转换成黑色 0。
这样后续每个细胞就可以直接作为一个二值目标处理。
6. 为什么要把细胞内部的孔洞填满?
原始细胞呈现出明显的环形或中心亮暗结构。
如果只保留轮廓,一个细胞可能像一个“空心圆”。
程序使用孔洞填充,把被前景包围的内部背景区域填成前景。
这样处理后:一个细胞更接近一块完整的实心区域。
这对后面的面积统计和连通域计数非常重要,因为程序还要根据每个连通区域所占像素数判断是否存在细胞粘连。
7. 圆盘开运算为什么适合细胞图像?
程序建立半径为:4 像素的圆盘结构元素,再进行形态学开运算。
开运算可以写成:
A ○ B = (A ⊖ B) ⊕ B
也就是:先腐蚀 → 再膨胀
它通常能够:去除小型前景噪点;平滑目标边缘;去掉细小突出;断开一部分较窄连接。
细胞本身接近圆形,因此使用圆盘结构元素也比较符合当前目标的几何特征。
8. 程序怎样真正“数细胞”?
预处理完成后,程序调用 8 邻域连通域标记。
所谓 8 邻域,就是一个像素周围:
上、下、左、右 + 4 个对角方向
只要前景像素在这些方向上连续相接,就归入同一个区域。
程序会给每个区域一个标签:1、2、3、……然后统计标签总数。
按当前图像和处理流程重新计算,开运算后首先得到:26 个连通区域
如果所有细胞都完全分开,那么这一步就可以直接把 26 当成细胞数。
但当前图中还有粘连细胞,所以程序继续做一次面积判断。
9. 两个细胞粘在一起时,为什么连通域会少算?
如果两个细胞相互接触,并且二值图中已经连成一块,那么 bwlabel 只会看到:一个连通区域
即使肉眼可以判断里面实际上有两个细胞。
当前程序没有使用分水岭去进一步切开粘连区域,而是采用一个更简单的面积补偿规则:如果某个连通区域面积超过 1500 像素,就把它视为两个细胞相连,因此额外加 1。
可以概括成:
N_final = N_cc + N_large
其中:
N_cc:普通连通域数量;
N_large:面积大于 1500 像素的区域数量。
10. 为什么这种方法比直接数黑色 区域更合理?
如果只做一次阈值分割然后立即 bwlabel:
噪声可能增加假目标;细胞中的孔洞会影响区域形态;小连接可能让两个细胞粘成一个;不同大小区域无法进一步区分。
当前程序相当于先做:
分割 → 清理 → 填充 → 形态优化
再进行:
连通域计数 + 大区域补偿
因此计数对象比初始二值图更加规则。
这也是图像计数问题中很典型的思路:
先把图像整理成“适合数”的目标,再进行统计。
11. 这套方法理论上为什么适合当前细胞图?
原图灰度变化较明显,Otsu 根据整幅图的灰度分布自动选择前景/背景分界,比完全靠人工输入一个灰度值更方便。
它利用局部邻域的多数信息去除小黑点,又不像普通均值滤波那样直接把所有像素做平均。
填孔让环状细胞变成完整区域,圆盘开运算进一步清除小结构和整理边界,为连通域统计创造更干净的输入。
完成标记以后,每一个细胞候选区域都有自己的标签和面积。
程序因此可以从像素级处理进一步进入:目标级统计
这正是自动细胞计数真正需要的一步。
12. 程序最终能得到什么?
对于当前样本,程序最终能够得到:
自动二值化后的细胞区域;去噪和形态学优化后的二值目标;
每个连通区域的独立标签;每个区域的像素面积;
粘连大区域数量;最终细胞总数。
13. 它适合用在哪里?
非常适合理解从灰度图到目标数量的完整计算机视觉流程。
工程集中使用了中值滤波、逻辑运算、取反、填孔、开运算和连通域标记,可以直观看到每一步怎样改变目标区域。
只要目标与背景灰度差异较明显,并且粘连程度不是特别复杂,这套流程也可以作为颗粒、液滴等目标计数的基础模板。
14. 怎么运行?
主程序是:main.m
程序会自动完成:
读取图像 → Otsu 阈值 → 中值滤波 → 逻辑优化 → 取反 → 填孔 → 开运算 → 连通域计数
要复现项目实验文档中的 28 个细胞,当前源码有一处计数循环需要按原设计遍历全部连通区域:将 for i=length(Sum) 改成 for i=1:length(Sum)。否则当前文件只检查最后一个标签,不能完成文档描述的“统计所有大面积粘连区域”。
最值得调整的只有 中值滤波窗口、半径 4 的圆盘结构元素和 1500 像素粘连判断阈值,它们分别影响去噪强度、形态清理程度和大区域补偿计数。
15. 一句话看懂这个项目
这是一个 MATLAB 显微细胞自动计数程序:使用 Otsu 自动阈值进行二值化,再通过 13×13 中值滤波、逻辑“或”、图像取反、孔洞填充和半径 4 的圆盘开运算整理细胞区域。