细胞识别课程设计Word格式.docx
- 文档编号:6647788
- 上传时间:2023-05-07
- 格式:DOCX
- 页数:12
- 大小:110.97KB
细胞识别课程设计Word格式.docx
《细胞识别课程设计Word格式.docx》由会员分享,可在线阅读,更多相关《细胞识别课程设计Word格式.docx(12页珍藏版)》请在冰点文库上搜索。
明白了以前学的C++原来是很重要的,还让我也对以往的知识有了个回顾和进一步的加深。
也让我对别的知识有了了解。
此次课程设计给我们提供了一个既能学习又能锻炼的机会,使我们养成了查找资料的习惯,将理论与实际相结合起来,锻炼了分析问题和实际解决问题的能力。
提高了适应能力,为今后的学习和实践打下了基础。
三、算法分析
1.打开图像
建立当文档工程
添加CDIB类
添加CImgcell203View的公共成员函数m_Cdib
添加Serialize()
添加显示代码
2.RgbtoHsi(&
rgb,&
Hsi)
RGB向HSI模型的转换是由一个基于笛卡尔直角坐标系的单位立方体向基于圆柱极坐标的双锥体的转换。
基本要求是将RGB中的亮度因素分离,将色度分解为色调和饱和度,并用角向量表示色调。
如果直接对R、G、B处理,其处理过程中很可能会引起三个量不同程度的变化,这样就会产生色差问题,甚至带来颜色上的失真。
HSI模型的出现,使得在保持色彩无失真的情况下实现图像处理成为可能。
HSI可以更好地区分细胞与非细胞。
3.OnMouseMove(UINTnFlags,CPointpoint)
当鼠标移动时调用此函数。
nFlags指示各种虚拟按键是否按下。
point:
鼠标的X,Y坐标:
该坐标为鼠标距离截获该消息的窗口左上角的位置是一个相对位置而不是在屏幕像素上的绝对位置。
在OnMouseMove函数里调用RgbtoHsi(&
Hsi)函数,可以在屏幕上显示鼠标所指点的坐标以及RGB、HSI和灰度值,通过HSI的可以选取合适的阈值来找到细胞以及边界。
4.OnMark()
Mark点指的是我们要寻找的细胞内的点。
通过计算色调的平均值设置一个门限值,将色调与平均值差距在门限范围内的点设置为Mark点,同理通过计算色调的平均值设置一个MaybeMark的门限,将色调与平均值差距在门限范围内的点设置为MaybeMark点。
if(dis<
MarkDoor){//Mark
*lpSrc=0;
*(lpSrc+1)=0;
*(lpSrc+2)=255;
//Red
}
elseif(dis<
MayBeMarkDoor){//maybeMark
*lpSrc=255;
*(lpSrc+2)=0;
//Blue
else{//notMark/maybeMark
if(*lpSrc==0)*lpSrc=1;
//Mark
elseif(*lpSrc==255)*lpSrc=254;
//maybemark
if(*(lpSrc+1)==255)*(lpSrc+1)=254;
//edge
5.OnmayMarktoMark()
这一步是把可能的Mark点变成Mark点,因为在拍摄图片时由于光线等原因是本来的Mark点变暗,在进行Mark处理时被标记为MaybeMark,所以需要把可能的Mark点还原Mark点。
for(i=0;
i<
lHeight;
i++)
{
for(j=0;
j<
lWidth;
j++)
{
lpSrc=pDoc->
m_pDib->
m_lpImage+
lLineBytes*(lHeight-1-i)+j*3;
if(*lpSrc==255){//maybeMark
boolbProc=false;
if(j>
0)if(*(lpSrc-3)==0)bProc=true;
if(j<
lWidth-1)if(*(lpSrc+3)==0)bProc=true;
if(i>
0)if(*(lpSrc+lLineBytes)==0)bProc=true;
if(i<
lHeight-1)if(*(lpSrc-lLineBytes)==0)bProc=true;
//maybeMarkhaveMarkPointtoMark
if(bProc){
*lpSrc=0;
MarkChg=true;
*(lpSrc+2)=128;
}
}
}
6、OnEdge()
OnEdge()是把边缘标记出来,方便后面的图像处理。
提取边缘的过程是先开辟一块内存用于存放数据,通过设置的门限值来找到边缘点,在提取边缘时用到重要的sobel算子,sobel算子主要用作边缘检测。
在技术上,它是一离散性差分算子,用来运算图像亮度函数的梯度之近似值。
在图像的任何一点使用此算子,将会产生对应的梯度矢量或是其法矢量。
该算子包含两组3x3的矩阵,分别为横向及纵向,将之与图像作平面卷积,即可分别得出横向及纵向的亮度差分近似值。
doublepixel[9];
lpDst=pDoc->
m_lpImage+lLineBytes*(lHeight-1-i)+j*3;
if(*(lpDst)==0||*(lpDst)==255){//Mark/MaybeMark
doublepixel[9];
lpSrc=lpNewDIBBits+lLineBytes*(lHeight-1-i)+j*3;
for(intm=-1;
m<
2;
m++)
for(intn=-1;
n<
n++){
unsignedchar*lpSrc1=lpSrc-lLineBytes*m+3*n;
pixel[(m+1)*3+n+1]=((int)*lpSrc1+*(lpSrc1+1)+*(lpSrc1+2))/3;
//Sobel
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];
doubleedge=sqrt(tmp1*tmp1+tmp2*tmp2);
if(edge>
edgeDoor)
*(lpDst+1)=255;
//edge
}
constintM=5;
boolbdelete;
//filter
for(inti=StartPoint.y+M;
i<
EndPoint.y-M;
i++){
for(intj=StartPoint.x+M;
j<
EndPoint.x-M;
j++){
pDst=pDoc->
if(*(lpDst+1)==255)//edge
{bdelete=true;
for(intm=-M;
=M;
for(intn=-M;
n++)
{if(m==-M||m==M||n==-M||n==M){在边缘上
if(*(lpDst+lLineBytes*m+n*3)||(*(lpDst+lLineBytes*m+n*3+1)==255))//noMark&
&
noEdge
{bdelete=false;
m=M+1;
n=M+1;
//out跳出循环
}}}
if(bdelete)
*(lpDst+1)=0;
//deleteedge
}}}
7.OnTwoVaule()
一幅图像包括目标物体、背景还有噪声,要想从多值的数字图像中直接提取出目标物体,最常用的方法就是设定一个阈值T,用T将图像的数据分成两部分:
大于T的像素群和小于T的像素群。
这是研究灰度变换的最特殊的方法,称为图像的二值化。
二值化后更加有利于做图像处理判别。
二值化时还要进行均值滤波,因为获得的图像有很多噪音。
这主要由于平时的工作和环境引起的,图像增强是减弱噪音,增强对比度。
想得到比较干净清晰的图像并不是容易的事情。
为这个目标而为处理图像所涉及的操作是设计一个适合、匹配的滤波器和恰当的阈值。
均值滤波的基本原理是用均值代替原图像中的各个像素值,即对待处理的当前像素点(x,y),选择一个模板,该模板由其近邻的若干像素组成,求模板中所有像素的均值,再把该均值赋予当前像素点(x,y),作为处理后图像在该点上的灰度个g(x,y),即个g(x,y)=1/m∑f(x,y)m为该模板中包含当前像素在内的像素总个数。
8.OnFillHoles()
孔洞填充的原理如下:
在阈值处理时,如果像素在阈值范围内,则像素将被标志。
孔洞填充将先统计所有连通的非标志区域面积,总会有一个或者几个面积特别大的区域,其它的都是面积相对较小的区域。
较小或者很小的往往就是系统所要填充的孔洞了。
从一个为访问过的非MARK点开始(该点可以看做一粒种子),从该点开始向四周扩散找未被访问过的非MARK点。
将找到的符合条件的点同时压如堆栈和队列中。
之所以要同时在堆栈和队列中同时保存相同的点是因为堆栈中的点并没有保存,每次循环都会弹出一个点作为新的种子进行扩散,当堆栈中的数据全部弹出之后,即表示搜索到了洞内的所有点,而队列中则保存了洞内的所有点。
因此通过判断队列的大小可以知道洞的大小从而判断是否进行填充。
填充就是经洞内的非MARK点变换为MARK点。
while(s.size())
//Addnewmemberstostack
//Abovecurrentpixel
lpSrc=(unsignedchar*)pDoc->
m_lpImage+lLineBytes*(lHeight-1-yt)+xt;
if(yt>
StartPoint.y){//循环结束条件是队列中的内容为空
//ifno-marked&
no-visited
if(!
(*(lpSrc+lLineBytes)&
MARK_VISITED))
{
s.push(CPoint(xt,yt-1));
v.push_back(CPoint(xt,yt-1));
*(lpSrc+lLineBytes)|=VISITED;
//将访问过的点加上VISITED标记
}elsebBorder=true;
……
//上下左右四个方向寻找未访问过的非MARK点。
并将找到的点压如堆栈和队列
xt=s.top().x;
yt=s.top().y;
s.pop();
//将堆栈顶端的数据弹出,作为新的种子进行扩散
9.shrink()
先去掉边缘点,然后将剩下的Mark点生成边缘点,再去掉在生成,直到去掉三次边缘,就这样收缩,如果收缩过程中有些Mark点收缩到很小,可以认为那不是细胞,那就可以直接去掉。
GenEdge();
for(intk=0;
k<
pre_shrink_count;
k++)
for(inti=0;
{
for(intj=0;
lpSrc=(unsignedchar*)pDoc->
m_lpImage+lLineBytes*(lHeight-1-i)+j;
if(*lpSrc==0xf0)//||*lpSrc==0xc0if(*lpSrc&
EDGEPOINT)
*lpSrc=0;
//(*lpSrc)&
=NO_MARK;
//marked=0;
if(k%2==0)
GenEdge4();
else
GenEdge();
MessageBox("
times"
);
Invalidate(true);
Invalidate(true);
10.findcenter()
此处我将收缩算法和中心点的获取放在一起写,是因为我觉得收缩算法是获取中心点的一部分,中心点的获取就是通过不断地进行收缩直到最后一次收缩后会导致中心点消失时停止。
收缩算法的思想就是在识别出细胞和其边界的基础上,首先进行一次遍历将所有已标记为边界的点变为非MARK点,然后再通过genEdge()、genEdge4()从八方向和四方向进行交替生成边界,至于为什么要八方向和四方向交替使用可能是因为这样效果比较好的缘故。
生成边界的思想很简单,就是通过判断MARK点上下左右四个方向或者八个方向是否有非Mark点,如果有的话即认为是边界,将该点加上边界标志。
而中心点的获取就是在每一次的收缩之前先进性一次判断,如果该点是孤立点或者是全边界点则将该点保存起来。
因为对于孤立点和全边界点如果不进行保存的话在进行一次收缩之后该点就会消失,因此为了保存该点,就要在其消失前将其保存。
最后每一个细胞收缩到最后可能剩下一个点到四个点。
因此第一次获取的中心点个数会是真正中心点个数的三倍左右。
细胞半径大小的获取是通过判断该中心点是通过多少次收缩之后获得的从而近似获得细胞的半径。
if(*lpSrc&
EDGEPOINT)
(*lpSrc)&
//收缩即将所有的边缘点变为
//非MARK点
if(k%2==0)//交替生成新的边界
GenEdge4();
//四方向生成边界只要该MARK点相邻有非MARN点
//则认为是边缘点
else
GenEdge();
//同样只要该点八个方向有非MARK点则认为是边缘点
m_bFullEdge=true;
if(*lpSrc&
EDGEPOINT&
!
(*lpSrc&
VISITED))//首先判断是否为未访问过的//边界点
{
if(!
(*(lpSrc-1)&
MARKED)&
!
(*(lpSrc+1)&
(*(lpSrc+lLineBytes)&
(*(lpSrc-lLineBytes)&
MARKED))//判断是否为孤立点
{
if(k==0)continue;
//如果进行第一次收缩即消失的点
//则认为该点是噪点,不进行保存直接进行下一次收缩
*lpSrc|=CENTERED;
//对孤立点加上中心点标志
pt.x=i;
pt.y=j;
pt.radius=k+pre_shrink_count+4;
//circleadjust
points_temp.push_back(pt);
//保存CENTER_POINT信息
continue;
}
elseMarkIt(i,j);
//判断是否需要保存,保存成立的条件是该点是//全边缘点,即与该点相连的只有非MARK点和边缘点,如果条件满足的话则保存于该点相//邻的边缘点
//没有访问过标志了并且是边缘邻域
//需要保存!
if(m_bFullEdge)SaveIt(i,j,k+pre_shrink_count+3);
//保存全边界
四、建议
刚开始老师上课讲程序时,可以说大概听懂了。
过了几天以后再去实验室实际操作,经过了半天的学习才学会了MFC的用法,但是同时发现原理忘的差不多了,建议先讲软件MFC用法,然后再讲原理,或者实验做到一定程度在上一次课讲讲程序,那样不至于忘得太快,效果也会好很多。
五、错误举例
1.指针飞掉,在没有定义指针边界时,指针会飞掉。
2.图像为读取时会出现很多连带错误'
pDoc'
:
undeclaredidentifier
errorleftof'
->
m_pDib'
mustpointtoclass/struct/union
m_lpBMIH'
biWidth'
mustpointtoclass/struct/union等等。
3,将memcpy(lpNewDIBBits,lpSrc,lLineBytes*lHeight);
放在MaybeMark前会出现MaybeMark和Mark点之间也会生成边界,收缩之后会出现不少错误,不少细胞收缩不见了。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 细胞 识别 课程设计