【数字图像处理】三.MFC实现图像灰度、采样和量化功能详解
【摘要】 本文主要讲述基于VC++6.0 MFC图像处理的应用知识,主要结合自己大三所学课程《数字图像处理》及课件进行讲解,主要通过MFC单文档视图实现显示BMP格式图片,并通过Bitmap进行灰度处理、图片采样和量化功能,希望对您有所帮助!
本文主要讲述基于VC++6.0 MFC图像处理的应用知识,主要结合自己大三所学课程《数字图像处理》及课件进行讲解,主要通过MFC单文档视图实现显示BMP格式图片,并通过Bitmap进行灰度处理、图片采样和量化功能。
个人认为对初学者VC++6.0可能还是很值得学习的工具,所以采用它来讲解,而不是VS或C#。同时文章比较详细基础,希望该篇文章对你有所帮助~
一. 单文档显示BMP图片
第一步:新建项目"MFC AppWizard(exe)",项目名为ImageProcessing,在应用程序类型中选择"单个文档",点击"确定"。在左栏的"资源视图"中,点击"Menu->IDR_MAINFRAM"可以查看并修改菜单视图。
第二步:向CImageProcessingView类添加成员变量和成员函数。在右栏的"类视图"右键ImageProcessingView添加函数或直接在ImageProcessingView.h中直接添加public成员变量和成员函数。添加代码如下:
第三步:编辑ImageProcessingView.cpp中ShowBitmap()函数。通过它显示BMP图片,其中代码及详细注释如下:
PS:这是非常著名的一张图片莱娜图(Lenna),全图是一张花花公子封面的裸图,后成为数字图像处理的标志图片。哈哈~至于BMP图片格式参照第一篇文章
二. 读取BMP图片和保存图片
BMP图片格式如下图所示:(参考
第四步:在XXXView.cpp中OnDraw()函数中调用读取图片函数。
if( EntName.Compare(_T("bmp")) == 0 ) //bmp格式
{
ReadBmp();
ShowBitmap(pDC,BmpName); //显示图片
}
运行程序,打开图片点击保存即可实现。重点是ReadBmp获取一些重要参数。
第三步:添加保存menu控件和函数。点击”查看-建立类向导“,在ID列表中找到ID_FILE_SAVE,点击COMMAND(Message列表),双击添加默认成员函数OnFileSave,同时在Member Functions(成员函数)中双击该函数进入函数并编辑。添加如下代码:
)
在很多处理中,都需要获取BMP图像的一些数据,如图像宽度、高度、像素大小等,后面的处理与之相关,主要的是ReadBmp函数。
第一步:在XXXView.h中添加BMP格式图像相关的成员变量和成员函数,其中成员函数通过类视图右键添加,成员变量可以在XXXView.h中直接复制。
第二步:在ImageProcessingView.cpp中实现ReadBmp函数和SaveBmp函数。
其中SaveBmp()函数代码如下:
第一步:在XXXView.h中添加BMP格式图像相关的成员变量和成员函数,其中成员函数通过类视图右键添加,成员变量可以在XXXView.h中直接复制。
第二步:在ImageProcessingView.cpp中实现ReadBmp函数和SaveBmp函数。
其中SaveBmp()函数代码如下:
第四步:在XXXView.cpp中OnDraw()函数中调用读取图片函数。
if( EntName.Compare(_T("bmp")) == 0 ) //bmp格式
{
ReadBmp();
ShowBitmap(pDC,BmpName); //显示图片
}
运行程序,打开图片点击保存即可实现。重点是ReadBmp获取一些重要参数。
三. 图像灰度处理
(参考我的百度文库:
)1.灰度图像概念
什么叫灰度图?任何颜色都有红、绿、蓝三原色组成,假如原来某点的颜色为RGB(R,G,B),那么我们可以通过下面几种方法,将其转换为灰度:
浮点算法:Gray=R*0.3+G*0.59+B*0.11
整数方法:Gray=(R*30+G*59+B*11)/100
移位方法:Gray=(R*28+G*151+B*77)>>8;
平均值法:Gray=(R+G+B)/3;(此程序采用算法)
仅取绿色:Gray=G;
通过上述任一种方法求得Gray后,将原来的RGB(R,G,B)中的R,G,B统一用Gray替换,形成新的颜色RGB(Gray,Gray,Gray),用它替换原来的RGB(R,G,B)就是灰度图了。
改变象素矩阵的RGB值,来达到彩色图转变为灰度图
加权平均值算法:根据光的亮度特性,其实正确的灰度公式应当是:
R=G=B=R*0.299+G*0.587+B0.144
为了提高速度我们做一个完全可以接受的近似,公式变形如下:R=G=B=(R*3+G*6+B)/10
真正的24位真彩图与8位的灰度图的区别就在于,真彩图文件中没有调色板,灰度图有调色板,真彩图中的象素矩阵是RGB值,灰度图中的象素矩阵是调色板索引值。源代码只简单的改变象素矩阵的RGB值,来达到彩色图转为灰度图,并没有添加调色板;该程序未实现添加了调色板。
2.灰度处理源码
第一步:在前面的代码基础上继续,先在ImageProcessingView.h中添加成员变量m_bitmaplin和BmpNameLin,因为后面处理操作是处理备份文件与原图进行比较。
第二步:在ImageProcessingView.cpp中ShowBitmap()函数前添加变量numPicture和level。
第三步:修改ImageProcessingView.cpp中OnFileOpen()函数,添加临时变量名和显示一张图片标志变量。代码如下:
第四步:将视图切换到ResourceView界面,选中Menu->在IDR_MAINFRAME中添加菜单”显示“,双击它在菜单属性中选择”弹出“。在”显示“的子菜单中添加:
双图显示--ID_SHOW_TWO(ID)--默认属性
灰度图片--ID_SHOW_HD(ID)--默认属性
第五步:点击"查看"->"建立类向导"(Ctrl+W),选择CImageProcessing类,然后ID_SHOW_TWO,双击COMMAND(Message),生成默认成员函数。
在XXXView.cpp中实现OnShowTwo()函数,代码如下:
第六步:同上面相同的方法,"查看"->”建立类向导“->ID_SHOW_HD(ID)->COMMAND(Message),默认成员函数名。在XXXView.cpp添加代码如下:
第七步:修改ShowBitmap()函数中双显部分,添加如下代码:
双显和灰度运行效果如下图所示:
四. 图片量化处理
(参考我的文库:
)1.量化基本概念
图像数字化包括量化和取样两个过程,其中:
量化:幅值f(x,y)的离散化,f(x,y)表示静止灰度图像的空间坐标
取样:对空间连续坐标(x,y)的离散化
一幅行数为M、列数为N的图像大小为M×N的矩阵形式为:(其中矩阵中每个元素代表一个像素)
该工程所有的处理都基于24位的bmp格式图片的处理,24为表示biBitCount=24,1个像素占3个字节(red、green、blue)。
如图量化级不同产生的灰度也不同,量化是使连续信号的幅度用有限级的数码表示的过程。
量化等级=2:使用2种灰度级(0~255)表示图片,小于128的取0,大于等于128的取128。把位图数据块所有数据在临时图片中取值,在显示即可。
量化等级=4:使用4种灰度级显示图片,就会发现图片分层为4种颜色。同时,0-64区间取0,64-128区间取64,128-192区间取128,192-255区间取192。
量化的取值各不相同,我采用的是最简单的取值。其它方法可自己去查阅资料。
2.量化处理源码
第一步:设置菜单栏。将试图切换到ResourceView界面--选中Menu--在IDR_MAINFRAME中添加菜单“量化”--双击它在菜单属性中选择“弹出”。在“显示”的子菜单中添加:属性为默认属性。
量化 Level 2--ID_LH_2 量化 Level 4--ID_LH_4
量化 Level 8--ID_LH_8 量化 Level 16--ID_LH_16
量化 Level 32--ID_LH_32 量化 Level 64--ID_LH_64
第二步:建立类向导。查看->建立类导向(Ctrl+W)->CXXXView(类名)->ID_LH_2->COMMAND(Messages)->默认成员函数名。相同方法分别为量化等级2、4、8、16、32、64建立类导向。
第三步:在ImageProcessingView.cpp中编辑灰度函数。代码如下:
核心流程是打开两张图片原图(BmpName)和临时图片(BmpNameLin),然后读取原图信息头赋值给临时处理图片,在读取原图m_nImage整个像素矩阵,量化处理每个像素(即分等级量化),最后文件写量化后的像素矩阵给BmpNameLin,在赋值全局变量level\numPicture和调用Invalidate()重绘图像即可。
第四步:修改ShowBitmap()函数,显示量化处理。添加如下代码:
运行效果如下图,当量化Level=2时很明显的两种灰度颜色,Level=4有4种颜色。
核心流程是打开两张图片原图(BmpName)和临时图片(BmpNameLin),然后读取原图信息头赋值给临时处理图片,在读取原图m_nImage整个像素矩阵,量化处理每个像素(即分等级量化),最后文件写量化后的像素矩阵给BmpNameLin,在赋值全局变量level\numPicture和调用Invalidate()重绘图像即可。
第四步:修改ShowBitmap()函数,显示量化处理。添加如下代码:
运行效果如下图,当量化Level=2时很明显的两种灰度颜色,Level=4有4种颜色。
五. 图像采样功能
(参考我的文库:
)1.图像采样概念
该工程所有的处理都基于24位的bmp格式图片的处理,24为表示biBitCount=24,1个像素占3个字节(red、green、blue)。如图一张512*512的原图,保持灰度级256不变后的各种采样。输入采样坐标:如16*16,它的含义是原图512*512像素,现在组成一个新的图片为16*16像素,(512/16=32,512/16=32)则每32*32组成一个新的区域。共有这种区域16*16个,采样的方法有2种:
a.把这个32*32区域全部赋值成左上角那个像素,这样图片的大小不变,困难在于赋值要4层循环。(项目中采用的就是这种方法)
b.把这个32*32区域的左上角取出来,组成一个新的图片,共有16*16个像素,这张图片的大小要变小,只有16*16个像素。但难点在于同时要把bmp文件头中的图片大小、信息头中的长宽像素改变、偏移量等信息更新。
又如下图所示:
原图8*8的矩阵要处理成3*3的矩阵,则循环先处理第一二行,①②④⑤为3*3处理,去左上角的RGB,③⑥为2*3的处理;重点是原图读取一维数组需要转成二维数组赋值处理;最后再处理最后一行数据。采样中公式为:
//获取填充颜色 相当于一次读取一个像素的RGB值再乘3跳3个字节
red=m_pImage[(X+Y*m_nWidth)*3];
green=m_pImage[(X+Y*m_nWidth)*3+1];
blue=m_pImage[(X+Y*m_nWidth)*3+2];
//填出图像循环 小区域中的长宽循环
//(X+Y*m_nWidth)*3跳到该小区域 再赋值3*3小区域的RGB 同一区域RGB相同
m_pImage[(X+Y*m_nWidth)*3+m+n*m_nWidth*3]=red; m++;
m_pImage[(X+Y*m_nWidth)*3+m+n*m_nWidth*3]=green; m++;
m_pImage[(X+Y*m_nWidth)*3+m+n*m_nWidth*3]=blue; m++;
原图8*8的矩阵要处理成3*3的矩阵,则循环先处理第一二行,①②④⑤为3*3处理,去左上角的RGB,③⑥为2*3的处理;重点是原图读取一维数组需要转成二维数组赋值处理;最后再处理最后一行数据。采样中公式为:
//获取填充颜色 相当于一次读取一个像素的RGB值再乘3跳3个字节
red=m_pImage[(X+Y*m_nWidth)*3];
green=m_pImage[(X+Y*m_nWidth)*3+1];
blue=m_pImage[(X+Y*m_nWidth)*3+2];
//填出图像循环 小区域中的长宽循环
//(X+Y*m_nWidth)*3跳到该小区域 再赋值3*3小区域的RGB 同一区域RGB相同
m_pImage[(X+Y*m_nWidth)*3+m+n*m_nWidth*3]=red; m++;
m_pImage[(X+Y*m_nWidth)*3+m+n*m_nWidth*3]=green; m++;
m_pImage[(X+Y*m_nWidth)*3+m+n*m_nWidth*3]=blue; m++;
PS:难点是还未处理剩余部分的采样。
2.图像采样代码
第一步:设置菜单栏
a.将视图切换到ResourceView界面--选中Menu--在IDR_MAINFRAME中添加菜单“采样”--双击它在菜单属性中选择“弹出”;
b.在“采样”的子菜单中添加:属性为默认属性。ID_CY--图片采样。
c.建立类导向:查看--建立类导向(Ctrl+W)--CImageProcessingView(类名)--ID_CY--COMMAND(Messages)--默认成员函数名。生成void CImageProcessingView::OnCy()采样函数。
第二步:设置采样对话框
a.将试图切换到ResourceView界面--选中Dialog,右键鼠标新建一个Dialog,并新建一个名为IDD_DIALOG_CY。编辑框(X)IDC_EDIT_CYX 和 (Y)IDC_EDIT_CYY,确定为默认按钮。设置成下图对话框:
a.将视图切换到ResourceView界面--选中Menu--在IDR_MAINFRAME中添加菜单“采样”--双击它在菜单属性中选择“弹出”;
b.在“采样”的子菜单中添加:属性为默认属性。ID_CY--图片采样。
c.建立类导向:查看--建立类导向(Ctrl+W)--CImageProcessingView(类名)--ID_CY--COMMAND(Messages)--默认成员函数名。生成void CImageProcessingView::OnCy()采样函数。
第二步:设置采样对话框
a.将试图切换到ResourceView界面--选中Dialog,右键鼠标新建一个Dialog,并新建一个名为IDD_DIALOG_CY。编辑框(X)IDC_EDIT_CYX 和 (Y)IDC_EDIT_CYY,确定为默认按钮。设置成下图对话框:
b.在对话框资源模板空白区域双击鼠标—Create a new class创建一个新类--命名为CImageCYDlg。会自动生成它的.h和.cpp文件。类向导Ctrl W--类名:CImageCYDlg--CImageCYDlg(IDs)—WM_INITDLAOG建立这个函数可以用于初始化。
c.打开类向导Ctrl+W--选择MemberVariables页面,类名:CImageCYDlg--Add Variables--设置成:
IDC_EDIT_CYX--int--m_xPlace
IDC_EDIT_CYY--int--m_yPlace
d.在View.cpp中添加采样的头文件#include "ImageCYDlg.h"
IDC_EDIT_CYY--int--m_yPlace
d.在View.cpp中添加采样的头文件#include "ImageCYDlg.h"
第三步:在ImageProcessingView.cpp中添加代码
第四步:修改ShowBitmap(CDC* pDC,CString BmpName)中的代码:
else if(level==3) //图片采样
{
m_hBitmapChange = (HBITMAP) LoadImage(NULL,BmpNameLin,IMAGE_BITMAP,0,0,
LR_LOADFROMFILE|LR_DEFAULTSIZE|LR_CREATEDIBSECTION);
}
运行效果如下图所示,其中彩色图片应该先灰度处理再进行其他操作。
运行效果如下图所示,其中彩色图片应该先灰度处理再进行其他操作。
总结:后悔当初还没有写博客,通过回忆几年前的代码,很多当时的体会和思想都已不复存在了!可能你在百度文库中看到类似的文章,因为那些都是我在2012年上传的,最初是通过它进行分享编程知识的,后来发现了更好的CSDN而取代之。这篇文章感觉太详细,有时候一直怀疑是不是失去了算法的本质,不应该写这么详细的文章,而更加精简一点,但可能和从小记笔记有关,很难改过来了,慢慢改吧!
最后还是希望文章对你有所帮助,如果文章有不足或错误之处,请海涵~
(By:Eastmount 2023-09-20 夜于贵阳)
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
作者其他文章
评论(0)