【数字图像处理】一.MFC具体解释显示BMP格式图片
【数字图像处理】二.MFC单文档切割窗体显示图片
【数字图像处理】三.MFC实现图像灰度、採样和量化功能具体解释
【数字图像处理】四.MFC对话框绘制灰度直方图
http://download.csdn.net/detail/eastmount/8764373
一. 点运算与初始操作
图像的点运算是图像处理中很基础的技术,它主要用于改变一篇图像的灰度分布范围,通过一定的变换函数将图像的像素进行转换,终于生成一幅新的图像。点运算的最大特点就是输出像素值仅仅与当前输入像素值相关。定义例如以下。
点运算(Point Operation)指对于一幅输入图像,将产生一幅输出图像,输出图像的每一个像素点的灰度值由输入像素点决定。
点运算由灰度变换函数(Grap Scale Transformation,GST)确定:B(x,y)=F[A(x,y)]
须要注意一下几点:
(1).与局部或邻域运算的区别。输入像素和输出像素是一一相应的;(2).与几何运算的区别,不改变图像的空间关系;(3).又称为对照增强。对照拉伸或灰度变换。
在前面第四篇博客的基础上添加点运算处理。
第一步:在资源视图中Menu中加入“图像点运算”菜单条例如以下所看到的:
线性变换 ID_DYS_XXYD(点运算 线性移动) ID_DYS_XXZQ( 点运算 线性增强)
ID_DYS_XXJX(点运算 线性减小) ID_DYS_XXQB(点运算 线性求补)
非线性变换 ID_DYS_FXXPF(点运算 非线性平方) ID_DYS_FXXHS(非线性函数)
阈值变换 ID_DYS_YZBH(点运算 阈值变换) 图像均衡化 ID_DYS_JHH
第二步:打开类向导(Ctrl+W),为点运算每一个ID菜单加入对应的功能处理函数,例如以下图所看到的:选择类CImageProcessingView,在选择IDs为ID_DYS_...(点运算)加入函数OnDysXxqb()线性求补。
二. 线性变换
图像线性变换是通过建立灰度映射来调整资源图像的灰度。从而达到图像增强的目的。当中GST函数f(D)为线性的。即:
若a=1,b!=0图像全部灰度值上移或下移
若a>1输出图像对照度增强
若0<a<1输出图像对照度减小
若a<0暗区域变亮,亮区域变暗。图像求补
首先是图像移动。代码例如以下:
/**********************************************************************/
/* 图像点运算 4种线性变化直方图:
/* ID_DYS_XXYD:表示线性灰度变化移动 D(B)=D(A)+50 灰度值上移下移
/* ID_DYS_XXZQ:表示线性灰度变化增强 D(B)=1.5*D(A) 图像对照度增强
/* ID_DYS_XXJX:表示线性灰度变化减小 D(B)=0.8*D(A) 图像对照度减小
/* ID_DYS_XXQB:表示线性灰度求补 D(B)=-1*D(A)+255 图像暗区变亮,亮区变暗
/**********************************************************************/// 1.点运算 线性灰度变化移动 D(B)=D(A)+50
void CImageProcessingView::OnDysXxyd()
{// TODO: Add your command handler code hereif(numPicture==0) {AfxMessageBox("加载图片后才干线性灰度运算!",MB_OK,0);return;}AfxMessageBox("线性灰度直方图-灰度变化移动 D(B)=D(A)+50!",MB_OK,0);int i;//打开暂时的图片FILE *fpo = fopen(BmpName,"rb");FILE *fpw = fopen(BmpNameLin,"wb+");//读取文件fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);//灰度图像unsigned char color;unsigned char red,green,blue;for( i=0; i<m_nImage/3; i++ ){fread(&red,sizeof(char),1,fpo);fread(&green,sizeof(char),1,fpo);fread(&blue,sizeof(char),1,fpo);if( (int)red+50 >255 )red=255;elsered=(int)red+50;if( (int)green+50>255 )green=255;elsegreen=(int)green+50; if( (int)blue+50>255 )blue=255;elseblue=(int)blue+50;fwrite(&red,sizeof(char),1,fpw);fwrite(&green,sizeof(char),1,fpw);fwrite(&blue,sizeof(char),1,fpw);}fclose(fpo);fclose(fpw);numPicture = 2;level=101; //赋值101在ShowBitmap中调用显示处理后的图片Invalidate();
}
同一时候改动void CImageProcessingView::ShowBitmap(CDC *pDC,
CString BmpName)函数中的代码:
else //图像点运算 线性变化
if(level=101)
{m_hBitmapChange = (HBITMAP) LoadImage(NULL,BmpNameLin,IMAGE_BITMAP,0,0,LR_LOADFROMFILE|LR_DEFAULTSIZE|LR_CREATEDIBSECTION);
}
执行效果例如以下图所看到的。同一时候我截取了直方图(RGB同样仅仅显示一种)。2.D(B)=1.5*D(A)
// 2.点运算 线性灰度变化增强 D(B)=1.5*D(A)
void CImageProcessingView::OnDysXxzq()
{if(numPicture==0) {AfxMessageBox("加载图片后才干线性灰度运算!",MB_OK,0);return;}AfxMessageBox("线性灰度直方图-灰度变化增强 D(B)=1.5*D(A)!",MB_OK,0);int i;//打开暂时的图片FILE *fpo = fopen(BmpName,"rb");FILE *fpw = fopen(BmpNameLin,"wb+");fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);//灰度图像unsigned char color;unsigned char red,green,blue;for( i=0; i<m_nImage/3; i++ ){fread(&red,sizeof(char),1,fpo);fread(&green,sizeof(char),1,fpo);fread(&blue,sizeof(char),1,fpo);if( (int)red*1.5 >255 )red=255;elsered=(int)red*1.5;if( (int)green*1.5>255 )green=255;elsegreen=(int)green*1.5; if( (int)blue*1.5>255 )blue=255;elseblue=(int)blue*1.5;fwrite(&red,sizeof(char),1,fpw);fwrite(&green,sizeof(char),1,fpw);fwrite(&blue,sizeof(char),1,fpw);}fclose(fpo);fclose(fpw);numPicture = 2;level=101; //线性变化 ShowBitmap中调用Invalidate();
}
执行效果例如以下图所看到的,图像对照度增强,平均灰度122*1.5=1813.D(B)=0.8*D(A)
// 3.点运算 线性灰度变化减小D(B)=0.8*D(A)
void CImageProcessingView::OnDysXxjx()
{if(numPicture==0) {AfxMessageBox("加载图片后才干线性灰度处理!",MB_OK,0);return;}AfxMessageBox("线性灰度直方图-灰度减小 D(B)=0.8*D(A)!",MB_OK,0);int i;//打开暂时的图片FILE *fpo = fopen(BmpName,"rb");FILE *fpw = fopen(BmpNameLin,"wb+");fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);//灰度图像unsigned char color;unsigned char red,green,blue;for( i=0; i<m_nImage/3; i++ ){fread(&red,sizeof(char),1,fpo);fread(&green,sizeof(char),1,fpo);fread(&blue,sizeof(char),1,fpo);red=(int)red*0.8;green=(int)green*0.8; blue=(int)blue*0.8;fwrite(&red,sizeof(char),1,fpw);fwrite(&green,sizeof(char),1,fpw);fwrite(&blue,sizeof(char),1,fpw);}fclose(fpo);fclose(fpw);numPicture = 2;level=101;Invalidate();
}
执行例如以下图所看到的,图像减弱。4.D(B)=-1*D(A)+255
// 4.点运算 线性灰度求补 D(B)=-1*D(A)+255
void CImageProcessingView::OnDysXxqb()
{if(numPicture==0) {AfxMessageBox("加载图片后才干线性灰度处理!",MB_OK,0);return;}AfxMessageBox("线性灰度直方图-灰度求补 D(B)=-1*D(A)+255!",MB_OK,0);int i;//打开暂时的图片FILE *fpo = fopen(BmpName,"rb");FILE *fpw = fopen(BmpNameLin,"wb+");fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);//灰度图像unsigned char color;unsigned char red,green,blue;for( i=0; i<m_nImage/3; i++ ){fread(&red,sizeof(char),1,fpo);fread(&green,sizeof(char),1,fpo);fread(&blue,sizeof(char),1,fpo);red=(int)red*(-1)+255;green=(int)green*(-1)+255; blue=(int)blue*(-1)+255;fwrite(&red,sizeof(char),1,fpw);fwrite(&green,sizeof(char),1,fpw);fwrite(&blue,sizeof(char),1,fpw);}fclose(fpo);fclose(fpw);numPicture = 2;level=101;Invalidate();
}
执行效果例如以下图所看到的,它是图像的求补。发现直方图是互补的。同一时候这些图片制作还挺麻烦的,仅仅是为了给你更好的呈现它们的变化,希望对你实用和尊重作者,不喜勿喷~
三. 非线性变换
灰度非线性变换主要包含对数变换、幂次变换、指数变换、分段函数变换,通过非线性关系对图像进行灰度处理。以下主要解说课件中的两个函数对其进行处理。当中对数变换实现了扩展低灰度值而压缩高灰度值的效果,图像灰度分布更符合而你的视觉特征。
/************************************************************************/
/* 2种非线性变化直方图:
/* ID_DYS_FXXPF:表示非线性平方灰度变化,D(B)=D(A)*D(A)/255
/* ID_DYS_FXXHS:表示非线性函数灰度变化,D(B)=D(A)+0.8*D(A)*(255-D(A))/255
/************************************************************************/// 非线性平方灰度变化 D(B)=D(A)*D(A)/252
void CImageProcessingView::OnDysFxxpf()
{if(numPicture==0){AfxMessageBox("加载图片后才干非线性灰度处理!",MB_OK,0);return;}AfxMessageBox("非线性灰度变化 D(B)=D(A)*D(A)/255!",MB_OK,0);int i;//打开暂时的图片FILE *fpo = fopen(BmpName,"rb");FILE *fpw = fopen(BmpNameLin,"wb+");//读取文件fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);//灰度图像unsigned char color;unsigned char red,green,blue;for( i=0; i<m_nImage/3; i++ ){fread(&red,sizeof(char),1,fpo);fread(&green,sizeof(char),1,fpo);fread(&blue,sizeof(char),1,fpo);red=(int)red*(int)red/255;green=(int)green*(int)green/255;blue=(int)blue*(int)blue/255;fwrite(&red,sizeof(char),1,fpw);fwrite(&green,sizeof(char),1,fpw);fwrite(&blue,sizeof(char),1,fpw);}fclose(fpo);fclose(fpw);numPicture = 2;level=101;Invalidate();
}
执行效果例如以下图所看到的:2.D(B)=D(A)+0.8*D(A)*(255-D(A))/255
// 非线性函数灰度变化 D(B)=D(A)+0.8*D(A)*(255-D(A))/255
void CImageProcessingView::OnDysFxxhs()
{if(numPicture==0){AfxMessageBox("加载图片后才干非线性灰度处理!",MB_OK,0);return;}AfxMessageBox("线性灰度直方图-灰度变化增强 D(B)=D(A)+0.8*D(A)*(255-D(A))/255!",MB_OK,0);int i;FILE *fpo = fopen(BmpName,"rb");FILE *fpw = fopen(BmpNameLin,"wb+");fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo); fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);unsigned char color;unsigned char red,green,blue;for( i=0; i<m_nImage/3; i++ ){fread(&red,sizeof(char),1,fpo);fread(&green,sizeof(char),1,fpo);fread(&blue,sizeof(char),1,fpo);if( ((int)red+0.8*(int)red*(255-(int)red)/255) > 255 )red=255;elsered=(int)red+0.8*(int)red*(255-(int)red)/255;if( ((int)green+0.8*(int)green*(255-(int)green)/255) > 255 )green=255;elsegreen=(int)green+0.8*(int)green*(255-(int)green)/255; if( ((int)blue+0.8*(int)blue*(255-(int)blue)/255) > 255 )blue=255;elseblue=(int)blue+0.8*(int)blue*(255-(int)blue)/255;fwrite(&red,sizeof(char),1,fpw);fwrite(&green,sizeof(char),1,fpw);fwrite(&blue,sizeof(char),1,fpw);}fclose(fpo);fclose(fpw);numPicture = 2;level=101;Invalidate();
}
执行效果例如以下图所看到的:写到此处你会发现图像灰度的线性变换和非线性变换是很easy的,主要是通过下面步骤完毕:
第一步:赋值处理后图像的BMP头信息
FILE *fpo = fopen(BmpName,"rb");
FILE *fpw = fopen(BmpNameLin,"wb+");
fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);
fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);
fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);
fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);
第二步:通过循环和线性变换或非线性便函函数处理每个像素
for( i=0; i<m_nImage/3; i++ )
{
fread(&red,sizeof(char),1,fpo);
处理像素RBG 如:red=(int)red*(int)red/255;
fwrite(&red,sizeof(char),1,fpw);
}
第三步:调用ShowBitmap自己定义函数并重画图像
numPicture = 2;
level=101;
Invalidate();
而它的主要应用包含:光度学标定,希望数字图像的灰度可以真实反映图像的物理特性。对照度增强和对照度扩展;显示标定和轮廓线确定(阈值化)。
四. 灰度阈值化
阈值又称为临界值,它的目的是确定出一个范围,然后这个范围内的部分使用同一种方法处理,而阈值之外的部分则使用还有一种处理方法或保持原样。经常使用的包含产生二值图:当x<T时y=0,当x>=T时y=255(当中T是阈值)。阈值变换在生物学上的应用比較广泛,经常使用语细胞图像切割等。
打开类向导(Ctrl+W)生成选择ImageProcessingView类,IDs选择ID_DYS_YZBH后加入对应的函数。代码例如以下:
/**************************************************************/
/* ID_DYS_YZBH:表示点运算阈值变换 也看做灰度拉伸
/* 此处的拉伸是:阈值化(thresholding)能够看作是削波的一个特例
/* 仅仅要令削波中的g1old=g2old就实现了阈值化。
/* 阈值就象个门槛,比它大就是白,比它小就是黑,二值
/**************************************************************/void CImageProcessingView::OnDysYzbh()
{if(numPicture==0){AfxMessageBox("加载图片后才干点运算阈值化处理!",MB_OK,0);return;}AfxMessageBox("图像点运算阈值化处理!",MB_OK,0);//读写文件FILE *fpo = fopen(BmpName,"rb");FILE *fpw = fopen(BmpNameLin,"wb+");fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);//处理unsigned char color;unsigned char red,green,blue;for(int i=0; i<m_nImage/3; i++ ){fread(&red,sizeof(char),1,fpo);fread(&green,sizeof(char),1,fpo);fread(&blue,sizeof(char),1,fpo);if( (int)red > 128 )red=255;elsered=0;if( (int)green > 128 )green=255;elsegreen=0; if( (int)blue > 128 )blue=255;elseblue=0;fwrite(&red,sizeof(char),1,fpw);fwrite(&green,sizeof(char),1,fpw);fwrite(&blue,sizeof(char),1,fpw);}fclose(fpo);fclose(fpw);numPicture = 2;level=101;Invalidate();
}
执行效果例如以下图所看到的。感觉还挺好看的,显然此时的直方图就是0和255两条直线。 五. 灰度均衡化
灰度均衡化的目的是使一输入图像转换为在每一灰度级上都有同样的像素点(即输出的直方图是平的),它能够产生一幅灰度级分布概率均衡的图像。
换句话说。经过均衡化后的图像在每一级灰度上像素点的数量相差不大。相应的灰度直方图的每一级高度也相差不大。
它是增强图像的有效手段之中的一个。
研究思路是通过直方图变换公式实现:
Pi:0.03, 0, 0.06, 0.10, 0.20, 0.11, 0, 0, 0, 0.03, 0, 0.06, 0.10, 0.20, 0.11, 0
步骤2:i从1開始,令s[i]=s[i]+s[i-1],得到的结果是s: 0.03, 0.03, 0.09, 0.19, 0.39, 0.50, 0.50, 0.50, 0.50, 0.53, 0.53, 0.59, 0.69, 0.89, 1.0, 1.0
这样就找到了原来的调色板索引值和新的调色板索引值之间的相应关系。即
0→0, 1→0, 2→1, 3→2, 4→4, 5→5, 6→5, 7→5, 8→5, 9→5, 10→5, 11→5, 12→6, 13→8, 14→9, 15→9。
比如。原来的索引值0,1都相应了新的索引值0。则灰度索引值为0的概率为P0+P1=0.03;新的索引值3和7找不到老的索引值与之相应,所以令Q3和Q7为0。最后得到的结果是Qi:0.03, 0.06, 0.10, 0, 0.20, 0.20, 0.10, 0, 0.20, 0.11
// ID_DYS_JHH:表示图像均衡化 相见算法
void CImageProcessingView::OnDysJhh()
{if(numPicture==0) {AfxMessageBox("加载图片后才干图像均衡化!",MB_OK,0);return;}AfxMessageBox("图像均衡化!",MB_OK,0);//第一步:获取图像的数据信息//此操作能够在打开图片时就进行 在直方图採样(ZFTCY)中也有该代码FILE *fpo = fopen(BmpName,"rb");fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);int i,j,k;for(j=0;j<256;j++) { //定义数组并清零Red[j]=0;Green[j]=0;Blue[j]=0;}//计算4个数据unsigned char red,green,blue;int IntRed,IntGreen,IntBlue; //强制转换double sumRedHD=0,sumGreenHD=0,sumBlueHD=0; //记录像素总的灰度值和for(i=0; i<m_nImage/3; i++ ){fread(&red,sizeof(char),1,fpo);IntRed=int(red);sumRedHD=sumRedHD+IntRed;if( IntRed>=0 && IntRed<256 ) Red[IntRed]++;fread(&green,sizeof(char),1,fpo);IntGreen=int(green);sumGreenHD=sumGreenHD+IntGreen;if( IntGreen>=0 && IntGreen<256 ) Green[IntGreen]++;fread(&blue,sizeof(char),1,fpo);IntBlue=int(blue);sumBlueHD=sumBlueHD+IntBlue;if( IntBlue>=0 && IntBlue<256 ) Blue[IntBlue]++;}fclose(fpo);/*****************************************************************//* 图像均衡化处理 /* 利用全局变量 Red[256] Blue[256] Green[256] /* 第一步:用3个数组Count..记录0-255灰度出现的概率,即 /* 概率=该灰度出现次数*3/总得像素 (由于分成3部分RGB) /* 第二步:i从1開始。令s[i]=s[i]+s[i-1] 记录新概率数 /* 第三步:用一个数组L记录新的调色板索引值,即 /* L[i]=s[i]×(256-1)结果四舍五入2.8即为3 /* 第四步:将老的索引值相应的概率合并,作为相应的新的索引值的概率 /* 1.原来的索引值0,1都相应了新的索引值0,则灰度索引值为0的概率 /* 为P0+P1=0.03 /* 2.新的索引值3和7找不到老的索引值与之相应。所以令Q3和Q7为0 /*****************************************************************///记录出现的概率,会加到1 用于相加到调色板float CountRed[256],CountGreen[256],CountBlue[256]; //记录原始数据,不会相加到1 用于计算新灰度概率float CountRedLin[256],CountGreenLin[256],CountBlueLin[256]; for( k=0 ; k<256 ; k++ ){CountRed[k]=(float)(Red[k])*3/m_nImage;CountRedLin[k]=CountRed[k];CountGreen[k]=(float)(Green[k])*3/m_nImage;CountGreenLin[k]=CountGreen[k];CountBlue[k]=(float)(Blue[k])*3/m_nImage;CountBlueLin[k]=CountBlue[k];}for( k=1 ; k<256 ; k++ ){ CountRed[k]=CountRed[k]+CountRed[k-1];CountGreen[k]=CountGreen[k]+CountGreen[k-1];CountBlue[k]=CountBlue[k]+CountBlue[k-1];}/****************************************************//* 此处百度到一个四舍五入浮点型的算法: /* float a=3.456; 保留到小数点后两位 /* float b=(int)((a * 100) + 0.5) / 100.0; /* output b=3.46 /****************************************************/int LRed[256],LGreen[256],LBlue[256]; //记录调色板for( k=0 ; k<256 ; k++ ){LRed[k]=(int)(CountRed[k]*(256-1)+0.5);LGreen[k]=(int)(CountGreen[k]*(256-1)+0.5);LBlue[k]=(int)(CountBlue[k]*(256-1)+0.5);}//第三步:处理均衡化图像写入 打开暂时的图片fpo = fopen(BmpName,"rb");fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);FILE *fpw = fopen(BmpNameLin,"wb+");fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);//m_nWidth*m_nHeight 读取图片最后一行不为m_nWidth时会报错 改为m_nImage/3for( i=0; i<m_nImage/3 ; i++ ){ fread(&red,sizeof(char),1,fpo);fread(&green,sizeof(char),1,fpo);fread(&blue,sizeof(char),1,fpo);red=LRed[int(red)];green=LGreen[int(green)];blue=LBlue[int(blue)];fwrite(&red,sizeof(char),1,fpw);fwrite(&green,sizeof(char),1,fpw);fwrite(&blue,sizeof(char),1,fpw);}fclose(fpw);numPicture = 2;level=101;Invalidate();
}
执行结果例如以下图所看到的。图像增强并且异常清晰:最后介绍下图像对照度拉伸,它就是把你感兴趣的灰度范围拉开。使得该范围内像素,亮的更亮,暗的更暗。从而达到增强对照度的目的。
例如以下图所看到的,a、b、c为三段直线的斜率。g1old和g2old表示途中要进行对照度扩展的范围,g1new和g2new表示相应的新值。当g1old=g2old就是二值图像阈值化处理。
最后还是希望文章对你有所帮助,假设文章有不足或错误之处。请海涵。近期挺忙的,写这些古老的文章有人说在浪费青春,但我还是准备把这个系列讲完。很高兴曾经的代码凝视和风格都不错,回顾起来挺好的。希望你也能养成好的代码和凝视风格~
(By:Eastmount 2015-06-02 下午4点 http://blog.csdn.net/eastmount/)