C/C++图像处理1 灰度图修正

编程入门 行业动态 更新时间:2024-10-17 04:55:54

C/C++图像处理1 <a href=https://www.elefans.com/category/jswz/34/1769417.html style=灰度图修正"/>

C/C++图像处理1 灰度图修正

目录

  • 写在前面
  • 内容
  • bmp图片文件的读写显示
  • 处理程序框架
    • 界面
    • 打开原图按钮
    • 编辑框控件的使用
    • picture控件的使用
    • 灰度修正按钮
    • 组合框框控件选择灰度直方图
    • 直方图显示
  • 处理核心代码
    • 变换a
    • 变换b
    • 变换c
    • 变换d

写在前面

非专业做图像处理的,如有错漏,敬请指正。
这是课程作业,对我这种之前没用过C语言图像处理的人来说很费劲。在此分享一是做学习记录,二是希望为遇到此类问题的朋友提供一种思路和方法;但如果有同样作业的同学看到此文,不建议直接下载源码抄作业,没意义,建议看懂原理之后自己敲处理核心部分代码、改改界面框架。所以这个没有百度云免费分享,而且我这个做得也很low,如果真的有学习需要的,希望能帮到:
gray-level_correction_noel202007.zip
需要用到teechart控件,没有的朋友看这里:VS2013 简单MFC应用以及teechart使用方法
若出现以下错误(没出现就不管)

那么点击项目属性,更改平台工具集

内容

搭建一个图像处理程序框架,实现将图片像素读取至二维数组,对图片的打开、显示、保存等基本功能,并做灰度修正处理。图像处理的部分是c写的,主要是将图片读入二维数组,然后对每个像素的灰度值进行操作,c++主要是用于搭建处理程序框架;
处理内容是利用分段折线变换关系对灰度图像进行修正,显示、保存变换结果和直方图;
图片显示使用picture控件,直方图用的teechart绘制。
参考书目:share_noel/图像处理/数字图像处理-夏良正.pdf

灰度变换原理(教材P140):



灰度变换结果:前面两张待处理原图在此:share_noel/图像处理/lena.bmp(lena1.bmp) 提取码:0ooc
第一行第一张是lena.bmp原图,后面4张是lena.bmp的分段折线变换图,第二行第一张是lena1原图,后面4张是lena1.bmp的分段折线变换图。左下角是直方图,可通过下拉框选择显示。

bmp图片文件的读写显示

这里涉及bmp图片格式及灰度图片转换,参考这几篇博客:
c语言数字图像处理(一):bmp图片格式及灰度图片转换
BMP图片结构解析(文中是用UltraEdit软件打开的BMP文件,UltraEdit的安装推荐微信公众号“软件安装管家”)
BMP文件结构
BMP图片文件读取及灰度图转化代码参考了一个cnblogs的博主,推荐阅读,这是他的主页/

处理程序框架

界面

用vs2013新建一个基于对话框的MFC应用程序
添加多个picture控件、编辑框控件、按钮,一个下拉框控件、一个teechart控件

打开原图按钮

(按钮添加使用见此处:VS2013 简单MFC应用以及teechart使用方法)
整个函数目的是获取当前选择的图片完整路径,然后准备好新文件的完整路径
在按钮的处理函数里面添加代码:

void Cgraylevel_correction_LXDlg::OnBnClickedButton1()
{// TODO:  在此添加控件通知处理程序代码int a = 10,i,j;char *cc;CString str;CString filter;filter = "所有文件(*.bmp,*.jpg,*.gif)|*.bmp;*.jpg| BMP(*.bmp)|*.bmp| JPG(*.jpg)|*.jpg||";CFileDialog dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY, filter, NULL);//直方图数组清零for (i = 0; i < h1; i++){gramf1[i] = 0;}for (i = 0; i < h2; i++){gramf2[i] = 0;}//弹框提示MessageBox(_TEXT("请选择图片1"));if (dlg.DoModal() == IDOK){bmpfilepath1 = dlg.GetPathName();     //获取文件路径名   如D:\pic\abc.bmp  }if (pFileName1a!=NULL)//先检查是否是第二次选择文件,是的话先重新初始化变量,防止文件名追加{file1fa = _TEXT("file1fa.bmp");	file1fb = _TEXT("file1fb.bmp");//处理后图像保存的文件名file1fc = _TEXT("file1fc.bmp"); file1fd = _TEXT("file1fd.bmp");file2fa = _TEXT("file2fa.bmp");	file2fb = _TEXT("file2fb.bmp");file2fc = _TEXT("file2fc.bmp"); file2fd = _TEXT("file2fd.bmp");free(pFileName1a); free(pFileName1b);free(pFileName1c); free(pFileName1d);free(pFileName2a); free(pFileName2b);free(pFileName2c); free(pFileName2d);}//CString转charCStringtochar(bmpfilepath1, &pFileName1);//记得要free(pFileName)cc = strstr(pFileName1, ".bmp");//判断是否为bmp文件if (cc == NULL){str.Format(_T("%s不是bmp文件"), pFileName1);MessageBox(str);return;}/*文件路径保存pFileName1是通过选择原图获取的路径如“D:\\csdn\\gray-level_correction_noel202007\\test_picture\\lena.bmp”newbmppath函数的作用是将pFileName1的文件名去掉变成路径D:\\csdn\\gray-level_correction_noel202007\\test_picture\\然后再加上比如"file1fa.bmp"变成新文件的完整路径D:\\csdn\\gray-level_correction_noel202007\\test_picture\\file1fa.bmp最后将新文件名放入两个变量file1fa与pFileName1afile1fa是CString类型的,用于在showbmp函数里面加载图片pFileName1a是char类型的,用于c语言对文件的读写*/newbmppath(pFileName1, &file1fa, &pFileName1a);newbmppath(pFileName1, &file1fb, &pFileName1b);newbmppath(pFileName1, &file1fc, &pFileName1c);newbmppath(pFileName1, &file1fd, &pFileName1d);MessageBox(_TEXT("请选择图片2"));if (dlg.DoModal() == IDOK){bmpfilepath2 = dlg.GetPathName();     //获取文件路径名   如D:\pic\abc.bmp  }CStringtochar(bmpfilepath2, &pFileName2);//记得要free(pFileName)cc = strstr(pFileName2, ".bmp");if (cc == NULL){str.Format(_T("%s不是bmp文件"), pFileName2);MessageBox(str);return;}newbmppath(pFileName2, &file2fa, &pFileName2a);newbmppath(pFileName2, &file2fb, &pFileName2b);newbmppath(pFileName2, &file2fc, &pFileName2c);newbmppath(pFileName2, &file2fd, &pFileName2d);Cgraylevel_correction_LXDlg::showbmp(bmpfilepath1, 1);//显示原图1Cgraylevel_correction_LXDlg::showbmp(bmpfilepath2, 2);//显示原图2get_image_size(pFileName1, &h1, &w1);//获取图像尺寸in_array1 = allocate_image_array(h1, w1);//根据图像尺寸分配内存read_bmp_image(pFileName1, in_array1);//读像素进数组in_array1get_image_size(pFileName2, &h2, &w2);in_array2 = allocate_image_array(h2, w2);read_bmp_image(pFileName2, in_array2);for (i = 0; i < h1; i++){for (j = 0; j < w1; j++){gramf1[in_array1[i][j]]++;//直方图数组}}for (i = 0; i < h2; i++){for (j = 0; j < w2; j++){gramf2[in_array2[i][j]]++;}}
}

编辑框控件的使用

编辑框控件用于输入阈值更新至变量以做灰度变换
添加一个编辑框控件,方便自己记忆和编程改下ID


添加编辑框控件后,右键添加变量

类别选value,类型选int,变量名自己起一个,我这里已经添加好了,所以是灰色。其他编辑框同理

添加好以后可以看到xxxDlg.h里面已经为我们定义好了int型的变量

在xxxDlg.cpp的DoDataExchange函数里面为我们做了变量m_p1dvalueA与控件ID IDC_P1AVALUEA的绑定

所以在编辑框里面输入数字,就能通过

UpdateData(true);

来更新输入值到变量使用了。
另外,可以在OnInitDialog()函数里面设定默认值

picture控件的使用

添加控件,改ID


在showbmp函数里面将图片显示至对应控件ID,传入的形参CString filepath是文件要显示的图片路径(这时候已经生成了对应的灰度变换图了并保存至文件中了),int ID形参ID是对应是10个picture控件,自己给他们编的号。不要和前面讲的控件ID名字混了

void Cgraylevel_correction_LXDlg::showbmp(CString filepath, int ID)
{CWnd* m_pWnd=NULL;switch (ID){case 1:m_pWnd = this->GetDlgItem(IDC_PICTURE_LX1); break;//  IDC_PICTURE_LX1为Picture控件IDcase 2:m_pWnd = this->GetDlgItem(IDC_PICTURE_LX2); break;case 3:m_pWnd = this->GetDlgItem(IDC_PIC1_F1); break;case 4:m_pWnd = this->GetDlgItem(IDC_PIC1_F2); break;case 5:m_pWnd = this->GetDlgItem(IDC_PIC1_F3); break;case 6:m_pWnd = this->GetDlgItem(IDC_PIC1_F4); break;case 7:m_pWnd = this->GetDlgItem(IDC_PIC2_F1); break;case 8:m_pWnd = this->GetDlgItem(IDC_PIC2_F2); break;case 9:m_pWnd = this->GetDlgItem(IDC_PIC2_F3); break;case 10:m_pWnd = this->GetDlgItem(IDC_PIC2_F4); break;default:	; break;}CRect rect;CImage image;image.Load(filepath);m_pWnd->GetWindowRect(&rect);//将客户区选中到控件表示的矩形区域内  CWnd *pWnd1 = NULL;pWnd1 = m_pWnd;//获取控件句柄  pWnd1->GetClientRect(&rect);//获取句柄指向控件区域的大小  CDC *pDc = NULL;pDc = pWnd1->GetDC();//获取picture的DC  pDc->SetStretchBltMode(STRETCH_HALFTONE);image.Draw(pDc->m_hDC, rect);//ReleaseDC(pDc);
}

灰度修正按钮

按钮的使用看另一篇博客:VS2013 简单MFC应用以及teechart使用方法
以“灰度修正1(a)”为例,目的是把加载的原图1按下图进行灰度变换。就是将A,B区间里面的灰度值扩展到Z1和Zk,其余区间灰度值不变。A、B、Z1、Z2对应变量m_p1avalueA、m_p1avalueB、m_p1avalueZ1、m_p1avalueZ2

void Cgraylevel_correction_LXDlg::OnBnClickedCorrection1a()
{// TODO:  在此添加控件通知处理程序代码if (bmpfilepath1 == "" || bmpfilepath2 == ""){MessageBox(_T("请先加载原图"));return;}int i, j, a=0,b=0,c=0;UpdateData(true);//更新编辑框值到变量if (m_p1avalueA == m_p1avalueB){MessageBox(_T("A、B不能相等"));return;}//直方图数组清零for (i = 0; i < h1; i++){gramf1a[i] = 0;}((CSeries)m_Chart.Series(0)).Clear();((CSeries)m_Chart.Series(1)).Clear();out_array = allocate_image_array(h1, w1);bmheader.height = h1;bmheader.width = w1;create_allocate_bmp_file(pFileName1a, &bmp_file_header, &bmheader);//灰度变换核心代码//for循环逐像素读取并进行灰度修正// in_array1是原图的像素值数组,out_array是灰度修正后存放像素值的数组for (i = 0; i < h1; i++){//h1*w1是图片尺寸for (j = 0; j < w1; j++){			if ((in_array1[i][j] >= m_p1avalueA) && (in_array1[i][j] <= m_p1avalueB)){//如果灰度值在(A,B)区间,则将区间扩展至(Z1,Z2)out_array[i][j] = (in_array1[i][j] - m_p1avalueA)*(m_p1avalueZ2 - m_p1avalueZ1) / (m_p1avalueB - m_p1avalueA) + m_p1avalueZ1;}//如果不在,则不变if ((0 <= in_array1[i][j] && in_array1[i][j] < m_p1avalueA) || (m_p1avalueB < in_array1[i][j] && in_array1[i][j] < 255)){out_array[i][j] = in_array1[i][j];//out_array[i][j] = 0;}}}for (i = 0; i < h1; i++){for (j = 0; j < w1; j++){gramf1a[out_array[i][j]]++;//灰度值统计,用于灰度直方图}}write_bmp_image(pFileName1a, out_array);free_image_array(out_array, h1);Cgraylevel_correction_LXDlg::showbmp(file1fa, 3);//显示灰度变换图1//显示灰度变换折线((CSeries)m_Chart.Series(0)).AddXY(m_p1avalueA, m_p1avalueZ1, NULL, 0);//A为横坐标 Z1为纵坐标画连线((CSeries)m_Chart.Series(0)).AddXY(m_p1avalueB, m_p1avalueZ2, NULL, 0);//B为横坐标 Z2为纵坐标画连线
}

变换前直方图

变换关系(左下)

变换后直方图(左下)

由原图直方图可知,图像大部分灰度集中在50-215,所以将此范围的灰度扩展到了20-255增加了图像的对比度。结果的灰度直方图可以看到灰度已经扩展到了255,符合预期。

组合框框控件选择灰度直方图

添加控件

右键添加变量

在在初始化函数OnInitDialog()里面初始化下拉列表

在控件属性的控件事件里面添加处理函数

处理代码

void Cgraylevel_correction_LXDlg::OnCbnSelchangeCombo1()
{// TODO:  在此添加控件通知处理程序代码((CSeries)m_Chart.Series(0)).Clear();((CSeries)m_Chart.Series(1)).Clear();switch (m_comboxdraw.GetCurSel()+1){//调用直方图显示showhistogram函数case 1:Cgraylevel_correction_LXDlg::showhistogram(gramf1); break;case 2:Cgraylevel_correction_LXDlg::showhistogram(gramf1a); break;case 3:Cgraylevel_correction_LXDlg::showhistogram(gramf1b); break;case 4:Cgraylevel_correction_LXDlg::showhistogram(gramf1c); break;case 5:Cgraylevel_correction_LXDlg::showhistogram(gramf1d); break;case 6:Cgraylevel_correction_LXDlg::showhistogram(gramf2); break;case 7:Cgraylevel_correction_LXDlg::showhistogram(gramf2a); break;case 8:Cgraylevel_correction_LXDlg::showhistogram(gramf2b); break;case 9:Cgraylevel_correction_LXDlg::showhistogram(gramf2c); break;case 10:Cgraylevel_correction_LXDlg::showhistogram(gramf2d); break;default:; break;}}

直方图显示

直方图的数组已经在前面处理部分准备好了
teechart的使用见VS2013 简单MFC应用以及teechart使用方法

void Cgraylevel_correction_LXDlg::showhistogram(int *histogram)//输入直方图数组
{int i = 0, j = 0;for (i = 0; i < 255; i++){//teechart绘直方图((CSeries)m_Chart.Series(1)).AddXY(i, histogram[i], NULL, 0);//A为横坐标 Z1为纵坐标画连线}
}

处理核心代码

变换a

	//for循环逐像素读取并进行灰度修正// in_array1是原图的像素值数组,out_array是灰度修正后存放像素值的数组for (i = 0; i < h1; i++){//h1*w1是图片尺寸for (j = 0; j < w1; j++){			if ((in_array1[i][j] >= m_p1avalueA) && (in_array1[i][j] <= m_p1avalueB)){//如果灰度值在(A,B)区间,则将区间扩展至(Z1,Z2)out_array[i][j] = (in_array1[i][j] - m_p1avalueA)*(m_p1avalueZ2 - m_p1avalueZ1) / (m_p1avalueB - m_p1avalueA) + m_p1avalueZ1;if (out_array[i][j] ==255){a++;}}//如果不在,则不变if ((0 <= in_array1[i][j] && in_array1[i][j] < m_p1avalueA) || (m_p1avalueB < in_array1[i][j] && in_array1[i][j] < 255)){out_array[i][j] = in_array1[i][j];//out_array[i][j] = 0;}}}

变换b

	//for循环逐像素读取并进行灰度修正// in_array1是原图的像素值数组,out_array是灰度修正后存放像素值的数组for (i = 0; i < h1; i++){for (j = 0; j < w1; j++){//如果灰度值在(A,B)区间,则将区间扩展至(Z1,Z2)if ((in_array1[i][j] >= m_p1bvalueA) && (in_array1[i][j] <= m_p1bvalueB)){out_array[i][j] = (in_array1[i][j] - m_p1bvalueA)*(m_p1bvalueZ2 - m_p1bvalueZ1) / (m_p1bvalueB - m_p1bvalueA) + m_p1bvalueZ1;}//(0, A)区间的灰度置为Z1if (in_array1[i][j] < m_p1bvalueA){out_array[i][j] = m_p1bvalueZ1;//out_array[i][j] = 0;}//(B, 255)区间的灰度置为Z2if ((in_array1[i][j]>m_p1bvalueB ) && (in_array1[i][j] <= 255)){out_array[i][j] = m_p1bvalueZ2;//out_array[i][j] = 0;}			}}

变换c

	//for循环逐像素读取并进行灰度修正// in_array1是原图的像素值数组,out_array是灰度修正后存放像素值的数组for (i = 0; i < h1; i++){for (j = 0; j < w1; j++){//如果灰度值在(A,B)区间,则将区间扩展至(Z1,Z2)if ((in_array1[i][j] >= m_p1cvalueA) && (in_array1[i][j] <= m_p1cvalueB)){out_array[i][j] = (in_array1[i][j] - m_p1cvalueA)*(m_p1cvalueZ2 - m_p1cvalueZ1) / (m_p1cvalueB - m_p1cvalueA) + m_p1cvalueZ1;if (out_array[i][j] == 255){a++;}}//(0,A)区间的灰度压缩至(0,Z1)if ((in_array1[i][j] < m_p1cvalueA) && (in_array1[i][j] >= 0)){out_array[i][j] = (m_p1cvalueZ1 / m_p1cvalueA)*in_array1[i][j];}//(B,255)区间的灰度压缩至(Z2,255)if ((in_array1[i][j] > m_p1cvalueB) && (in_array1[i][j] <= 255)){out_array[i][j] = ((255 - m_p1cvalueZ2) / (255 - m_p1cvalueB))*(in_array1[i][j] - m_p1cvalueB) + m_p1cvalueZ2;}}}

变换d

	//for循环逐像素读取并进行灰度修正// in_array1是原图的像素值数组,out_array是灰度修正后存放像素值的数组for (i = 0; i < h1; i++){for (j = 0; j < w1; j++){//将灰度区间在(0, A)上的灰度值扩展到(0, 255)区间if ((in_array1[i][j] >= 0) && (in_array1[i][j] < m_p1dvalueA)){out_array[i][j] = (255 / m_p1dvalueA)*in_array1[i][j];//out_array[i][j] = 0;}//(A,B)区间的灰度扩展至(0,255)if ((in_array1[i][j] >= m_p1dvalueA) && (in_array1[i][j] < m_p1dvalueB)){out_array[i][j] = (255 / (m_p1dvalueB - m_p1dvalueA))*(in_array1[i][j] - m_p1dvalueA);//out_array[i][j] = in_array1[i][j];}//(B, 255)区间的灰度扩展至(0, 255)if ((in_array1[i][j] >= m_p1dvalueB) && (in_array1[i][j] <= 255)){out_array[i][j] = (255 / (255 - m_p1dvalueB))*(in_array1[i][j] - m_p1dvalueB);//out_array[i][j] = 0;}}}

学习笔记,如有错漏,敬请指正
--------------------------------------------------------------------------------------------诺有缸的高飞鸟202007

更多推荐

C/C++图像处理1 灰度图修正

本文发布于:2024-02-07 09:57:25,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1755332.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:灰度   图像处理

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!