数字图像处理课设报告_数字图像处理课程报告
数字图像处理课设报告由刀豆文库小编整理,希望给你工作、学习、生活带来方便,猜你可能喜欢“数字图像处理课程报告”。
数字图像处理课程设计报告
细胞识别
目录
第一部分
1、实验课题名称--------------------32、实验目的--------------------------3
第 1 页
3、实验内容概要--------------------3 第二部分
1、建立工程文件--------------------32、图像信息获取--------------------43、如何建立下拉菜单--------------64、标记Mark点----------------------65、二值化96、填洞---97、收缩---108、获取中心点------------------------119、细胞计数---------------------------1310、All-steps---------------------------1311、扩展功能-------------------------14 第三部分
12、各步骤结果和错误举例------16 第四部分
13、心得体会--------------------------22
第一部分
1、实验课题:细胞识别
2、实验目的:对血液细胞切片图片进行各种处理,最终得出细胞的数目、面积等信息。
3、实验内容概要:基于VC++6.0软件下的细胞识别,通过细胞的标记、二
第 2 页 值化、填洞、收缩、获取中心点、计数等过程完成实验目的。
第二部分——实验具体步骤
1、建立工程文件
① 新建MFC工程项目:--MFC AppWizard、工程名
② 拷贝cdib.h,cdib.cpp到工程文件夹,再向工程里添加
③ doc.h添加变量:m_lpDib 和头文件#include”cdib.h”
④ doc.cpp:变量(m_lpDib)的new、delete
第 3 页
⑤ doc.cpp: Serialize()
2、图像信息获取
① 读取图像参数View.cpp: OnDraw()m_pDib->Draw()如果图像不为空的话,那么就执行如下主要代码:
② 点击键,建立类向导,在meages中添加OnInitialUpdate()函数,添加代码实现对自动打开固定图片。
③ 通过鼠标右击,点击建立类向导,在meages中添加OnMouseMove()函数,添加代码实现获取所要信息,即实现鼠标在图像任一位置移动时可以直观的读取相对应位置的信息。可以在屏幕上显示鼠标所指点的坐标以及RGB、HSI和灰度值,通过HSI的可以选取合适的阈值来找到细胞以及边界。
第 4 页 ④ 为了RGB图像转化为人眼更容易识别的HSI模型,我们可以通过添加成员函数RgbtoHsi来实现这一功能。
HSI模型与RGB模型的转化关系
(添加函数时,可以右击类窗口中的view.h,选中add member function,之后选择函数的返回值类型和函数描述,其它默认不变)
确定后在里面添加实现函数功能的代码。
3、添加下拉菜单
在resourceview那栏的找到菜单按键设置
第 5 页 双击,后在里面添加所需按键
每个按键的ID号为注意在填写为IDR_加菜单大写。
之后右击按键,建立类向导添加按键所需函数
4、标记mark 分为四步
1.找出mark(red)点和maybemark(blue)点
2.将maybemark(blue)点变成mark(black red)点
3.将mark(black red)点变成edge(yellow(fullred&&fullgreen))点 4.edge点滤波
基本思想:Mark点指的是我们要寻找的细胞内的点。我们先获取每一个像素点的RGB分量,然后我们将其转化成HSI分量,将H分量进行归一化,因为S的范围是0到1,所以我们要进行尺度的一致,这样才具有可计算性。然后我们通过每个像素点的H分量和S分量的值与细胞内部的H分量和S分量计算欧几里得距离,设定一个Mark门限值(我们这里将MarkDoor设置为0.09,大家可以行设置合适的参数),小于这个门限值我们就当做是细胞的内部,然后对细胞进行标记(Red)。还需要设定一个Maybe Mark门限值(我们这里将Maybe MarkDoor设置为0.15,大家可以行设置合适的参数),我们大于Mark门限值小于Maybe Mark门限值时,我们暂时看成是细胞,我们进行Maybe Mark的标记(Blue)。否则的话,我们需要考虑,一些不是Mark和Maybe Mark点的*lpSrc==0我们区别一下赋值为1,*lpSrc==255
第 6 页 我们区别一下赋值为254,*(lpSrc+1)==255我们区别一下赋值为254.这样的话,我们在后面判断是否为Mark点的时候,我们只需要判断*lpSrc是否为0就可以了,判断Maybe Mark点时只需要判断*(lpSrc)是否为255就可以了。对于边缘的判断只需要判断*(lpSrc+1)是否为255就可以了。
将细胞标记为Mark用红色(255,0,0)标记出来,将可能是的细胞标记为MayBe Mark用蓝色(0,0,255)标记出来。将MayBeMark To Mark的区域用亮红(128,0,0)表示,将不可能是细胞的区域、细胞边界分别用绿色标记出来。操作过程:(1)根据H、S的欧几里得距离sqrt(s2+h2)来大致的确定哪些是细胞(Mark)和可能是细胞(Maybe Mark)的点。
(2)根据Maybe Mark点周围的情况,如果它的上下左右四个方向有Mark点,则将Maybe Mark点变成Mark点。
(3)用Sobel算子来做边缘的提取边界(0,255,255)(255,255,0),使用3*3的模板,使用欧几里得距离来判断是否为边缘。
两种Sobel算子如下:
第 7 页
主要代码如下:
doubletmp1=pixel[0]+2*pixel[1]+pixel[2]-pixel[6]-2*pixel[7]-pixel[8];doubletmp2=pixel[0]+2*pixel[3]+pixel[6]-pixel[2]-2*pixel[5]-pixel[8];double edge=sqrt(tmp1*tmp1+tmp2*tmp2);
if(edge>edgeDoor)*(lpDst+1)=255;//Sobel判断该点是否edge//edgeDoor=40(4)edge滤波
就是去除全边缘点(四周都是背景或边缘)(强度为5)
5、二值化
基本思想:将原有彩色图像变换为二值图像,其中细胞0X80(128)用Gray(灰色)标记出来,边缘0xF0(240)用Bright(亮色)标记出来,其他表示为0。主要代码:
第 8 页
6、填洞
将细胞中或者细胞相邻的地方的较小的背景填成细胞的背景,填完的细胞背景的灰度值是129,因为都被访问过了,然后将边缘去掉。
填洞的基本思想:首先将细胞或边缘内的黑点置为vistied = 0x01,以该黑点为中心,在其上下左右侧进行访问是否有未访问的黑点,若有则将上方黑点压栈,且上下左右侧的黑点置已访问。将堆栈顶端的数据弹出,作为新的种子进行扩散,即以该元素为基点,判断其周围是否存在未访问黑点,若有则继续压栈,重复操作。直到找到最后一点,此点四周均不存在未访问黑点,结束访问。若洞像素数小于100大于50,洞内像素数及其初进栈的点(56,(409,222))时,则进行填洞。填洞的过程就是将非mark点转化为mark点。
主要标记访问代码:
填洞函数主要代码分析:
填完洞后,进行下面操作:
如果图像中只有已访问黑点0x01则将其恢复成0;如果图像中只有Edge点0xf0则将 Edge置为黑点。这样图像中只有黑色的背景以及灰色的细胞mark(0x80)点。
主要代码:
第 9 页
7、收缩
收缩的目的是为了方便计数。通过扫描图像,对图像进行预先的3次腐蚀,判断所生成边界点,然后根据原理判定是否标注该点,存放所标志的中心点,便于统计细胞个数及计算细胞半径。
由Mark生成边界,我们有四邻域生成边界和八邻域生成边界。判断该点是否为Mark点,如果是Mark点的话,我们判断i、j是否是我们选取图片的边界,如果是的话,我们将该点变成边缘点,否则我们判断它的上下左右(周围八个点)是否有非Mark点,如有有,则将这边变成边缘点,反之,不变。
8邻域收缩操作代码(4邻域与8邻域思想相同):
第 10 页
8、获取中心点
根据前面所作工作统计获得的中心点个数,去掉一系列不符合要求的点得出最终的细胞个数、细胞的平均半径和平均面积,用对话框输出统计结果。
操作过程如下:
① 首先我们要去除访问标志,是我们先前一次在判断是否需要保存点的时候(MarkIt(int i, int j)),我们将边缘点都标记成访问过了,这时在处理下一次遍历图片发现中心点的时候,我们要进行判断点是否要保存就没有办法做了,所以在没进行一次图片的遍历之前我们都需要去除访问标志。主要代码:*lpSrc&=NO_VISITED;//0xfe// 清除visited标志
最后位 置0操作
② 需要判断是否是边界以外的点,这里我们只处理边界内部的点,对于边界外部的点不加以查找中心点。对于内部的点,我们先要判断是否是孤立的边缘点,即判断该边缘点的上下左右四个点都不是Mark点和边缘点我们认为是孤立的边缘点,但是我们在这里也要去除半径不大于2的孤立点,因为我们认为它的半径太小,是噪声。如果是半径大于2的孤立点,我们对他进行标记成中心点,对半径做一点补偿(pt.radius=k+pre_shrink_count+4,4为补偿)。然后在入队。
主要代码如下:
{
第 11 页 if(k
continue;
// 孤立的点
*lpSrc |=CENTERED;//0x2 对孤立点加上中心点标志0xf2//后面shrink时为0x02
// 保存一下CENTER_POINT信息(圆心,半径)
pt.x=i;pt.y=j;pt.radius=k + pre_shrink_count + 4;// +4放大补偿,k为把此圆收缩到一点所经历的收缩次数
points_temp.push_back(pt);continue;}
③ 需要判断是否需要保存该点,我们在判断它的上下左右是否有没有访问过的边缘点,这里我们运用递归函数来找相连通的边缘点,如果是全边缘点的时候我们就需要保存,即将m_bFullEdge=1。在保存的函数中我们将该点变成中心点,然后半径补偿然后我们来对该点入队(保存),然后将该点设置成没有访问过的点,因为下面在做该点的上下左右是否为访问过的,访问过才保存,因为这个点已经保存过了,所以要将保存过的点设置成没有访问过,我们必须将这点变成没有访问过。然后判断该点的周围八点是否是访问过的,如果访问过就保存该点,这里也是运用递归函数来实现的。
对中心点处理:
a)独立的中心点直接存储
b)相邻的中心点通过递归求质心作为圆心,最大半径作为新的半径,合并各中心为一点
主要代码如下:
pt.x=tot_x/tot_num;//质心 pt.y=tot_y/tot_num;pt.radius=max_radius;//取最大半径作为质点中心点半径 *(lpSrc-(pt.y-j)*lLineBytes+pt.x-i)|=CENTERED;//质点置为中心点 points.push_back(pt);c)相近但不相邻的点,求质心为圆心,最大半径为半径(直到无相近点)
主要代码如下:
if(abs(x0-x)+abs(y0-y)
{
/*pt=points.at(j);if(points.at(i).radius
pt=points.at(i);*/ points.at(i).x=(x+x0)/2;//取均值,保存最大半径
第 12 页
points.at(i).y=(y+y0)/2;points.at(i).radius=max(points.at(i).radius,points.at(j).radius)+4;pt=pt=points.at(i);d)在无相近点的情况下,若半径小于8,则删除。
主要代码如下:
if(bdelete)
{
} e)两圆相交,若其中一圆非相交部分面积小于50%,则删除
主要代码如下:
if(total
9、细胞计数
打开我们处理前的图片,根据前面保存中心点的队列,我们知道中心点的位置和细胞的半径,然后我们重新的导入细胞的图片,在上面画圆,标出细胞。然后我们获取细胞内部的HSI的最大值和最小值,计算出细胞的平均面积和个数。主要代码如下:
msg.Format(“共有%d个细胞,平均半径%d,平均面积%d : H(%3.1f,%3.1f)S(%3.2f,%3.2f)I(%3.2f,%3.2f)”, m_vCenterPoints.size(),(int)(totr/m_vCenterPoints.size()+.5),(int)(tota/m_vCenterPoints.size()+.5), 360.0*min[0]/255.0,360.0*max[0]/255.0, 1.0*min[1]/255.0,1.0*max[1]/255.0, 1.0*min[2]/255.0,1.0*max[2]/255.0);
10、All-steps
第 13 页 可以一次性实现细胞识别的所有操作步骤
设置控制按键的权限,点击update_command_ui ,键入控制条件
每步操作时给cellproce设置不同数值,表示那步进行过,只能进行规定的下步操作,从而在运行过程中放置按键误触导致程序崩溃。
11、扩展:
区域选择:
第 14 页
建立类向导:OnButtonDown 和OnButtonUp 键入代码:
在OnDraw中添加下列代码
注意:bool 变量m_bDrag要定义在view.cpp文件头部,定义在view.h会出现第一次区域选择时出现错误。
添加复位按键:
第 15 页
点击该键后会重新读取图像(和图像自动打开代码一样)
第三部分
12、各步骤结果和错误举例
① 各步骤结果图
a.Mark(Red)& MayBe Mark(Blue)
maybemark to mark(black red)第 16 页
edge information and edge filter
twovalue
fillholes 第 17 页
shrink
findcenter 第 18 页
count
出现的错误举例:
Mousemove 程序中出现问题: 1.错误:
没有加#include “MainFrm.h”头文件 2.错误
第 19 页
error C2248: 'm_wndStatusBar' : cannot acce protected member declared in cla 'CMainFrame'
需将protected: // control bar embedded members CStatusBar m_wndStatusBar;CToolBar
m_wndToolBar;
protected变为public 供用户操作使用
MouseMove函数中((CMainFrame*)AfxGetMainWnd())->m_wndStatusBar.SetPaneText(0,str);使str.Format中内容显示在标准窗口图像的左下方bar
3.MouseMove的坐标判决放在for循环外,鼠标移动到图像外,程序会崩溃。解决:改变坐标判决代码的位置
通见问题:
在ClaView中的类视图不见了,在FileView视图中该类的.h和.cpp文件仍然存在解决方案:先保存workspace,然后关闭工程,删除此工程目录中的.ncb文件,重新打开workspace 原因:claview显示混乱 在类中添加的成员变量和成员函数不能显示出来,即使显示出来了变量或函数,双击后不能跳至正确的位置。
第 20 页
Edge information中出现问题
正常
不正常
memcpy(lpNewDIBBits,lpSrc,lHeight *lLineBytes);代码应放在图像处理前,参考图像是初始状态的图像,把第一步的四个小步骤分开写在不同的函数内时,因为每一小步的操作都会改变图像的状态,如果把: memcpy(lpNewDIBBits,lpSrc,lHeight *lLineBytes);写在maybemark_to_mark之后那么参考图像就不是原始图像
发现:做到shrink时,看到收缩后的图像效果很差,和标准收缩图像相差较大,经调试后发现问题(没注意 ppt最后一页有,老师在qq群在中也提到过)。
Shrink操作后,关闭图像,出现问题 GenEdge4()函数中出现问题 for(int j=0;j
{
lpSrc =(unsigned char *)pDoc->m_lpDib->m_lpImage + lLineBytes*(lHeight-1-j)-1;
for(int i=0;i
{
lpSrc++;......和
for(int j=0;j
for(int i=0;i
{
第 21 页 lpSrc =(unsigned char *)pDoc->m_lpDib->m_lpImage + lLineBytes*(lHeight-1-j)-i;....for循环问题 有差异
后面起始点地址为dot1 前面起始点地址为 dot1+1,不处理四周边界
可以把第二个for循环语句中前面的i=0 改成i=1.观察图像,发现Shirnk后未保存填洞点,结果只有 0x00 0x80 0xf0 //
*lpSrc &=NO_EDGE_POINT;//至关重要
少写后,程序一直崩溃。
这段代码在GenEdge函数中,在shrink步骤中没影响,但在找中心点过程中用到这个函数时,这段代码就十分必要。否则程序会在运行findcenter时直接崩溃。
中心点标志 0x02
不是0xf2
Temp.at(n).y 等同于 temp[n].y
13、心得体会
通过对本次数字图像处理课程设计的学习,进一步加深了对数图知识的理解,同时也基本掌握了VC++软件的使用方法。从一开始的连图像都无法打开,到最后在老师上课的资料以及同学的帮助和学长的参考程序下终于完成了一幅细胞图像的整个识别过程。自己一向在编程方面有所欠缺,通过咨询老师和同学还有百度,自己也慢慢理解了所写程序代码的含义,间接地提高了自己写代码与识别代码的能力。
通过一个星期多的学习,我对细胞识别的基本思想有了深一步的理解,也让我对c语言相关的知识得到了回顾。此次课程设计给我们提供了一个既能学习又能锻炼的机会,使我们养成了查找资料(主要是在百度上查阅一些代码的含义)的习惯,将理论与实际相结合起来,锻炼了分析问题和实际解决问题的能力。提高了适应能力,为今后的学习和实践打下了基础。
第 22 页