图像的点运算是图像处理中非常基础的技术,它主要用于改变一篇图像的灰度分布范围,通过一定的变换函数将图像的像素进行转换,最终生成一幅新的图像。点运算的最大特点就是输出像素值只与当前输入像素值相关。定义如下。
点运算(Point Operation)指对于一幅输入图像,将产生一幅输出图像,输出图像的每个像素点的灰度值由输入像素点决定。
点运算由灰度变换函数(Grap Scale Transformation,GST)确定:B(x,y)=F[A(x,y)]
需要注意一下几点:
(1).与局部或邻域运算的差别,输入像素和输出像素是一一对应的;(2).与几何运算的差别,不改变图像的空间关系;(3).又称为对比增强,对比拉伸或灰度变换。
在前面第四篇博客的基础上增加点运算处理。
第一步:在资源视图中Menu中添加“图像点运算”菜单栏如下所示:
图像线性变换是通过建立灰度映射来调整资源图像的灰度,从而达到图像增强的目的。其中GST函数f(D)为线性的,即:
/**********************************************************************/
/* 图像点运算 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 here
if(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; i255 )
red=255;
else
red=(int)red+50;
if( (int)green+50>255 )
green=255;
else
green=(int)green+50;
if( (int)blue+50>255 )
blue=255;
else
blue=(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)
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; i255 )
red=255;
else
red=(int)red*1.5;
if( (int)green*1.5>255 )
green=255;
else
green=(int)green*1.5;
if( (int)blue*1.5>255 )
blue=255;
else
blue=(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=181
// 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
运行如下图所示,图像减弱。// 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
运行效果如下图所示,它是图像的求补,发现直方图是互补的。
灰度非线性变换主要包括对数变换、幂次变换、指数变换、分段函数变换,通过非线性关系对图像进行灰度处理,下面主要讲解课件中的两个函数对其进行处理。其中对数变换实现了扩展低灰度值而压缩高灰度值的效果,图像灰度分布更符合而你的视觉特征。
/************************************************************************/
/* 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
运行效果如下图所示:
// 非线性函数灰度变化 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 255 )
red=255;
else
red=(int)red+0.8*(int)red*(255-(int)red)/255;
if( ((int)green+0.8*(int)green*(255-(int)green)/255) > 255 )
green=255;
else
green=(int)green+0.8*(int)green*(255-(int)green)/255;
if( ((int)blue+0.8*(int)blue*(255-(int)blue)/255) > 255 )
blue=255;
else
blue=(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();
}
运行效果如下图所示:
阈值又称为临界值,它的目的是确定出一个范围,然后这个范围内的部分使用同一种方法处理,而阈值之外的部分则使用另一种处理方法或保持原样。常用的包括产生二值图:当x
打开类向导(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 128 )
red=255;
else
red=0;
if( (int)green > 128 )
green=255;
else
green=0;
if( (int)blue > 128 )
blue=255;
else
blue=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两条直线。
灰度均衡化的目的是使一输入图像转换为在每一灰度级上都有相同的像素点(即输出的直方图是平的),它可以产生一幅灰度级分布概率均衡的图像。
换句话说,经过均衡化后的图像在每一级灰度上像素点的数量相差不大,对应的灰度直方图的每一级高度也相差不大。它是增强图像的有效手段之一。
研究思路是通过直方图变换公式实现:
// 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=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/3
for( i=0; i
运行结果如下图所示,图像增强而且异常清晰:
最后还是希望文章对你有所帮助,如果文章有不足或错误之处,请海涵。最近挺忙的,写这些古老的文章有人说在浪费青春,但我还是准备把这个系列讲完,非常高兴以前的代码注释和风格都不错,回忆起来挺好的,希望你也能养成好的代码和注释风格~
(By:Eastmount 2015-06-02 下午4点 http://blog.csdn.net/eastmount/)