当前位置:首页 >> IT/计算机 >>

数字图像处理源程序及详解之灰度直方图


图像的点运算

图像的点运算是图像处理中相对简单的技术, 它主要用于改变一 幅图像的灰度分布范围。点运算通过一个变换函数将图 像的像素一 一转换,最终构成一幅新的图像。由于操作对象是图像的一个个像 素,故得名为“点运算”。点运算的最大特点是输出像素值只与当 前输入像素值有关。 点运算的图像处理过程可以用以下公式表示: g(x, y)=T[f(x, y)] 其中 f(x, y)表示输入图像,g(x, y)表示处理后的图像。函数 T 是对 f 的一种变换操作,在这里它表示灰度变换公式。可以看到, 对于点运算而言,最重要的是确定灰度变换公式。变换公式一旦确 定,点运算对于图像的处理效果就确定了。 本章研究的主要内容包括灰度直方图、线性变换、非线性变换、 阈值变换、灰度拉伸及灰度均衡等。若无特别说明,本 章的点运算 函数所针对的待处理对象即为 8 位灰度图。 ■ 本章学习地图 ◆ 学会利用灰度直方图查看图像信息 ◆ 了解各种灰度变换公式 ◆ 掌握各种灰度变换的实现方法 ◆ 进一步了解各种点运算对于图像效果的影响

9.1 灰度直方图
本节介绍灰度直方图的相关概念和实现原理,它是提取图像信息的重要工具之 一。

9.1.1 灰度直方图

任何一幅图像都包含着丰富的图像信息,对于图像处理而言,如何提取这些信息 并找出其中的特征就显得十分关键。 灰度直方图直观地显示了图像灰度分布的 情况,这些信息在图像灰度变换等处理过程中显得十分重要。在本章随后的内容 中,也会经常通过直方图来分析变换后的图 像效果。 图 9-1 显示一幅灰度图及它所对应的灰度直方图。 可以看到,灰度直方图是一 个二维图。从数学上来说,它描绘了图像各灰度值的统计特性,显示了各个灰度 级出现的次数或概率。从图形上来说,其横坐标表示图像 的灰度值,取值范围 是 0 到 255。其纵坐标则通过高度来表示出现次数的多少或者概率的高低。在本 章中,纵坐标表示像素出现的次数,最大值为该图像在 0 至 255 阶灰度上分布 像素出现次数的最大值。

图 9-1

利用灰度直方图显示图像灰度分布

需要说明的是,如果没有特别指出,在本章后续内容 中的所有变换都是基于图 9-1 中左侧的图像进行的。 接下来分析直方图的作用。图 9-2 所示的 4 幅图像 都是根据同一幅图像进行基 本灰度特征处理后获得的,分别是高亮度、低亮度、高对比度、低对比度 4 种类 型的图像。每幅图像右侧都显示了对应的直方图。仔细观 察可以发现如下特征。

图 9-2

4 种基本图像及对应的直方图

高亮度图像的直方图组成集中在灰度高的一侧。8 位 灰度图能表示 256 种灰度, 也就是灰度取值范围为 0 至 255。其中 0 表示黑色,255 表示白色。对于高亮度 图像,整个画面的颜色偏亮,故灰度直方图偏向灰 度高的一侧。相反,低亮度 图像的直方图则偏向灰度较低的一侧。 在高对比度的图像中,直方图的覆盖范围很广。图像 在任意一段灰度范围中都 有一定的像素数量。同时,高对比图像的灰度分布相比其他图像而言较为均匀, 整个直方图显得比较平滑。而低对比度图像的灰度则主要分 布在中间狭窄的区 域中,图像就像被冲淡了一样。

9.1.2 基本原理

灰度直方图的基本思想是统计。对于拥有 256 种灰度的图像,灰度值为 k 的像素 个数由一个离散函 数确定: 其中 nk 表 示当前图像灰度值为 k 的像素个数,则对应的出现概率可以使用如下 公式表示: ,显然有成 立。 其中 n 表示图像像素个数的总和,可 以用图像宽度与高度的乘积来表示。 本章灰度直方图的坐标系为。 横坐标表示输入灰度值 k,而纵坐标表示对应灰 度值的统计个数 nk。可见,绘制直方图最重要的是确定灰 度值为 k 的像素个数。 直方图在绘制时采用相对高度,即纵坐标的最大值为 ymax=max(n0,n1,n2……,n254,n255), 如果 ymax 的绘制高度为 1,灰度值 k 的绘制高度 为。

9.1.3 编程实现
1.效果预览 本节通过对话框实现灰度直方图的显示,其效果见图 9-3 所示。

图 9-3

灰度直方图对话框

灰度直方图对话框实现了如下功 能。 l l 灰度直方图的显示。 允许用户修改显示灰度的上限和下限。对于一些图像而言,可 能某个像素值的计数远远大于其他像素的个数,这样往往看不清其他灰 度的分布。 例如白色背景的图 片, 拥有灰度值 255 的像素个数相对较多。 该对话框提供了一个灰度区间显示的功能,如图 9-4 所示。

图 9-4

灰度直方图的区间显示

可以看到,改变显示灰度的范围后,用户能够更加清 楚地了解某一区域的灰度 分布特征。 l l 允许用户通过鼠标操作来改变显示灰度的上限和下限。方法很 简单,只需要拖动对话框上两侧的蓝色虚线即可。 显示当前鼠标所在位置的灰度值及出现的几率。

2.构建 MagicHouse MagicHouse 是贯穿本书第 3 篇的重点实 例。它是一款以图像处理为主,综合图 像浏览等多种功能的实用软件。MagicHouse 支持多种常见图片格式,并实现了 以下功能。 l l l l l l 图像的浏览 图像的特效显示 图像的点运算 图像的几何变换 图像的增强 图像的滤镜效果

本章将完成 MagicHouse 框架的搭建工作并 在其基础上添加点运算的功能, 随后 的 3 章内容都是在已有的 MagicHouse 框架上进行功能的完善和补充的。 MagicHouse 框架是在第 8 章实例 GraphShower 的基础上改进的,它继承了 GraphShower 的框架结构和图像浏览、特效显示等功能,并新增了如下功能。 1)运行模式

添加了“运行模式”菜单,提供两种运行模式:图片 浏览模式,该模式功能与 GraphShower 完全相同,提供图像的浏览功能;编辑模式,这个模式允许对图像 进行各种处理,本章及后面 3 章的功能都是在该 模式下实现的。编辑模式下的 图像会被锁定,不能进行图像的浏览。 2)JPEG 解码器 MagicHouse 使用了第 4 章编写的 JPEG 解码器完成 JPEG 文件的解码工作。用户 只需要将第 4 章的对应文件添加到 MagicHouse 的工程下即可。 3)Dib 封装类 MagicHouse 使用第 5 章介绍的 MyDib 类获取 Bmp 文件的像素信息,MagicHouse 支持的图像为 8 位灰度图与 24 位、32 位彩色图像。 4)预览对话框 预览对话框用于显示图像处理后的效果图。它与第 5 章中介绍的 CPreViewDlg 类很类似,不同的是这里采用 GDI+显示图像。 5)统一的图像处理接口 MagicHouse 的核心是 CMagicHouseView 类,它不但提供了图像的显示和处理功 能,还提供了图像的处理接口。下面是 CMagicHouseView 类中关于图像处理 十 分重要的成员变量。 public: BYTE* BYTE* UINT UINT UINT UINT m_pImageBuffer; m_pImageTempBuffer; m_nPicWidth; m_nPicHeight; m_nTempWidth; m_nTempHeight; // 编辑图像原始像素数组 // 处理后的像素数组 // 当前编辑图像宽度 // 当前编辑图像高度 // 处理后图像的宽度 // 处理后图像的高度

在编辑模式下,所有图像都会保存在一个线性数组 中,且图像的每一个像素都 是以 32 位的形式保存的。 图像处理函数可以通过 m_pImageBuffer 指针获取原始 图像信息,然后将处理后的图像保存在另 一个线性数组中。处理后的图像信息 可以通过 m_pImageTempBuffer 指针访问。最后通过预览对话框显示处理后的图 像效果。后续的图像处理函数 会经常使用这 6 个变量。

MagicHouse 拥有良好的可扩展性,它对底 层的解析过程进行了良好的封装,并 采用统一的接口对图像进行处理,它不仅是一款数字图像处理工具,更是一款理 想的数字图像算法试验平台,读者可以在其基础 上扩展更多的图像处理功能。 由于篇幅有限,这里不再给出 MagicHouse 框架的代码,读者可到指定网站下载。 3.概要设计 下面开始完成灰度直方图对话框的设计。 启动 MagicHouse 项目文件,打开“资源视 图”,添加 ID 为 IDD_HISTOGRAM 的 对话框资源并按照图 9-5 完成设计。 然后为其创建对话框类 CHistogramDlg 并按 照表 9-1 关联对 应变量。

图 9-5 表 9-1 编 1 2 3 4 5

灰度直方图对话框的设计 灰度直方图对话框资源与变量的关系 号 资 源 类 型 图片控件 文本框 文本框 静态文本 静态文本 资源 ID IDC_HISTOGRAM IDC_LIMIT_LOWER IDC_LIMIT_UP IDC_STATIC_GRAY IDC_STATIC_PER 关联变量类 型 CStatic int int int float 关联变量名称 m_stiHistogram m_nLimitLow m_nLimitUp M_nGray M_dPer

4.代码分析 下面结合灰度直方图的基本原理一步一步剖析对话框 的实现过程。 1)各级灰度数量的统计 统计数据是绘制灰度直方图的依据。该对话框在初始 化时,也就是在 OnInitDialog 函数中完成统计工作。正如前面介绍的一样,MagicHouse 将图像 保存在一个线性数组中,图像的每个像素都统 一采用 32 位形式存储,故 8 位灰 度图的一个像素也会占用 32 位。为方便统计,对话框默认当前处理图像为灰度 图,即 R=G=B,故每次只需要统计其中一种颜 色的数量。

2)灰度直方图重绘过程 绘制工作主要由 Refresh 函数完成,它完成了 以下功能。 l 双缓存的创建

以双缓存模式绘制直方图,防止闪烁。 l l 绘制直方图的坐标系和刻度 查找所有灰度中最多的出现次数

该次数会显示在 Y 轴的旁边,拥有该 次数灰度值的直方图最高。 l 以相对高度绘制直方图

其余灰度值的高度由其出现次数与最大出现次数的比 值决定。 l 利用鼠标操作改变显示灰度的上下限

灰度上下限在对话框中以两条蓝色的虚线表示,用户 可以通过鼠标操作改变两 条虚线的位置来改变显示灰度的上下限。 用户在单击鼠标左键时程序会根据当前鼠标的位置, 判断鼠标是否在上下限虚 线的范围内。如果在上限虚线的范围中,则将 m_nIsDraging 变量赋值为 DT_UP, 如果在下限虚线的范围中,则赋值为 DT_LOW,否则赋值为 DT_NULL。 用户在移动鼠标时如果 m_nIsDraging 不 为 DT_NULL,则表示用户此时正在拖动 上限或者下限的虚线,此时就需要更改上下限的值,并将其限定在合法的取值范 围中。 5.实例代码清单 下面列出 HistogramDlg.h 的代码清 单: #pragma once #include "afxwin.h" // CHistogramDlg 对话框 class CHistogramDlg : public CDialog { DECLARE_MESSAGE_MAP() DECLARE_DYNAMIC(CHistogramDlg)

public: CStatic int int long long CPoint int int float m_stiHistogram; m_nLimitLow; m_nLimitUp; m_lCounts[256]; m_nPixelCount; m_psMove; m_nIsDraging; m_nGray; m_dPer; // 直方图显示区域 // 显示灰度的下限 // 显示灰度的上限 // 各级灰度出现的个数 // 图像像素总数 // 记录拖动时的鼠标位置 // 鼠标是否正在拖动 // 当前鼠标位置的灰度级数 // 出现概率 // 标准构造函数

CHistogramDlg(CWnd* pParent = NULL); virtual ~CHistogramDlg(); afx_msg void OnEnChangeLimitLower(); 函数 afx_msg void OnEnChangeLimitUp(); afx_msg void OnPaint(); virtual BOOL OnInitDialog(); 各级灰度数量

// 灰度下限改变的响应

// 灰度上限改变的响应函数 // 绘制对话框 // 对话框初始化时计算

afx_msg void OnMouseMove(UINT nFlags, CPoint point); // 鼠标移动时响应拖动动作 afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message); // 拖动时改变鼠标光标 afx_msg void OnLButtonDown(UINT nFlags, CPoint point); // 鼠标按下时判断是否在灰度上 下限直线范围中

afx_msg void OnLButtonUp(UINT nFlags, CPoint point); // 释放鼠标的响应函数 // 对话框数据 enum { IDD = IDD_HISTOGRAM }; protected: virtual void DoDataExchange(CDataExchange* pDX); 持 private: void Refresh(void); // 拖动枚举 enum DragingType { DT_NULL, DT_LOW, DT_UP }; }; 下面列出 HistogramDlg.h 的核心代码清单: // HistogramDlg.cpp : 实现文件 #include "stdafx.h" #include "MagicHouse.h" #include "HistogramDlg.h" #include "Mainfrm.h" #include "MagicHouseDoc.h" // 无拖动 // 拖动下限 // 拖动上限 // 刷新直方图 // DDX/DDV 支

#include "MagicHouseView.h" // CHistogramDlg 消息处理程序 /******************************************************************** ******* * 作用: 对话框初始化时计算各级灰度数量

********************************************************************* ******/ BOOL CHistogramDlg::OnInitDialog() { CDialog::OnInitDialog(); CMainFrame* pMain = (CMainFrame*)AfxGetMainWnd(); CMagicHouseView* pView = (CMagicHouseView*)pMain->GetActiveView(); for (UINT i = 0; i < pView->m_nPicWidth * pView->m_nPicHeight; i++) { int value = pView->m_pImageTempBuffer[i * 4]; m_lCounts[value]++; } // 计算像素总个数 m_nPixelCount = pView->m_nPicWidth * pView->m_nPicHeight; return TRUE; // return TRUE unless you set the focus to a control

// 异常:OCX 属性页应返回 FALSE } /******************************************************************** ******* * 作用: 刷新直方图

*

备注: 双缓存绘制方法

********************************************************************* ******/ void CHistogramDlg::Refresh() { CDC* pDC = m_stiHistogram.GetDC(); CRect rect; CDC memDC; CBitmap MemBitmap; // 获取绘图区域 m_stiHistogram.GetClientRect(rect); // 设备描述表初始化 memDC.CreateCompatibleDC(NULL); // 建立与屏幕显示兼容的内存显示设备 MemBitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height()); // 选取空白位图 memDC.SelectObject(MemBitmap); memDC.FillSolidRect(0, 0, rect.Width(), rect.Height(), RGB(255,255,255)); Graphics graph(memDC.GetSafeHdc()); // 使用白色背景 graph.FillRectangles(&SolidBrush(Color::White), &Rect(0, 0, rect.Width(), rect.Height()), 1); // 绘制 y 轴

graph.DrawLine(&Pen(Color::Black), 10, 10, 10, 280); graph.DrawLine(&Pen(Color::Black), 10, 10, 5, 15); graph.DrawLine(&Pen(Color::Black), 10, 10, 15, 15); // 绘制 x 轴 graph.DrawLine(&Pen(Color::Black), 10, 280, 290, 280); graph.DrawLine(&Pen(Color::Black), 290, 280, 285, 285); graph.DrawLine(&Pen(Color::Black), 290, 280, 285, 275); // 绘制坐标原点 CString strNum; Font font(L"宋体", 10); strNum = L"0"; graph.DrawString(strNum, -1, &font, PointF(8, 290), &SolidBrush(Color::Black)); for (int i = 0; i < 256; i += 5) { if (i % 50 == 0) graph.DrawLine(&Pen(Color::Black), 10 + i, 280, 10 + i, 286); else if (i % 10 == 0) graph.DrawLine(&Pen(Color::Black), 10 + i, 280, 10 + i, 283); } // 绘制 x 轴刻度 strNum = L"50"; graph.DrawString(strNum, -1, &font, PointF(53, 290), &SolidBrush(Color::Black));

strNum = L"100"; graph.DrawString(strNum, -1, &font, PointF(100, 290), &SolidBrush(Color::Black)); strNum = L"150"; graph.DrawString(strNum, -1, &font, PointF(150, 290), &SolidBrush(Color::Black)); strNum = L"200"; graph.DrawString(strNum, -1, &font, PointF(200, 290), &SolidBrush(Color::Black)); strNum = L"255"; graph.DrawString(strNum, -1, &font, PointF(255, 290), &SolidBrush(Color::Black)); // 绘制当前灰度区域 Pen pen(Color::Blue); pen.SetDashStyle(DashStyleDash); graph.DrawLine(&pen, 10 + m_nLimitLow, 280, 10 + m_nLimitLow, 10); graph.DrawLine(&pen, 10 + m_nLimitUp, 280, 10 + m_nLimitUp, 10); long lMax = 0; REAL dHeight = 0.0; // 查找最大值 for (int i = m_nLimitLow; i <= m_nLimitUp; i++) lMax = max(lMax, m_lCounts[i]); // y 轴刻度 strNum.Format(L"%d", lMax);

graph.DrawString(strNum, -1, &font, PointF(10, 25), &SolidBrush(Color::Black)); // 绘制柱状图 for (int i = m_nLimitLow; i <= m_nLimitUp; i++) { dHeight = (REAL)(m_lCounts[i]) / lMax * 250; graph.DrawLine(&Pen(Color::Gray), i + 10.0f, 280.0f, i + 10.0f, 280 - dHeight); } // 复制内存画布内容 pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY); m_stiHistogram.ReleaseDC(pDC); } /******************************************************************** ******* * 作用: 鼠标移动时响应拖动动作

********************************************************************* ******/ void CHistogramDlg::OnMouseMove(UINT nFlags, CPoint point) { CRect rect; m_stiHistogram.GetWindowRect(rect); if ( (nFlags & MK_LBUTTON) && m_nIsDraging ) {

// 拖动偏移量 int offset = point.x - m_psMove.x; // 如果拖动的是上限 if (m_nIsDraging == DT_UP) { // 如果没有超界 if ( (offset + m_nLimitUp) <= 255 ) { if ( (offset + m_nLimitUp) >= m_nLimitLow ) m_nLimitUp += offset; else m_nLimitUp = m_nLimitLow; } else m_nLimitUp = 255; } else { // 如果没有超界 if ( (offset + m_nLimitLow) >= 0 ) { if ( (offset + m_nLimitLow) <= m_nLimitUp ) m_nLimitLow += offset; else

m_nLimitLow = m_nLimitUp; } else m_nLimitLow = 0; } UpdateData(FALSE); Refresh(); m_psMove = point; } else m_nIsDraging = DT_NULL; ClientToScreen(&point); // 鼠标当前所在灰度位置, 如果不在 0~255 之间则表示鼠标不在指定区域 内 int x = point.x - rect.left - 10; if (abs(x - m_nLimitUp) > 3 && abs(x - m_nLimitLow) > 3) m_nIsDraging = DT_NULL; // 如果鼠标在直方图区域中 if (rect.PtInRect(point)) { if (x >= m_nLimitLow && x <= m_nLimitUp) { m_nGray = x; m_dPer = float(m_lCounts[x]) / m_nPixelCount * 100;

} UpdateData(FALSE); } } /******************************************************************** ******* * 作用: 鼠标按下时判断是否在灰度上下限直线范围中

********************************************************************* ******/ void CHistogramDlg::OnLButtonDown(UINT nFlags, CPoint point) { CRect rect; CPoint oldPoint = point; m_stiHistogram.GetWindowRect(rect); ClientToScreen(&point); int x = point.x - rect.left - 10; if (abs(x - m_nLimitUp) <= 3) { m_psMove = oldPoint; m_nIsDraging = DT_UP; } else if (abs(x - m_nLimitLow) <= 3) { m_psMove = oldPoint;

m_nIsDraging = DT_LOW; } } /******************************************************************** ******* * 作用: 释放鼠标的响应函数

********************************************************************* ******/ void CHistogramDlg::OnLButtonUp(UINT nFlags, CPoint point) { m_nIsDraging = DT_NULL; } 6.使用该对话框 打开“资源视图”中的菜单资源 IDR_MAINFRAME,添加一个新菜单栏“点运 算”,并在其下拉菜单中添加一个新栏,如图 9-6 所示。然后将其 Caption 属性 设为“灰度直方 图”,其 ID 为 ID_POINT_HISTOGRAM。

图 9-6

在菜单资源中添加新项

为其添加命令响应函数,具体内容如下: void CMagicHouseView::OnPointHistogram() { if (!m_bIsEditMode || m_nPos == -1)

{ MessageBox(L"请先打开图像文件,然后选择编辑模式!"); return; } CHistogramDlg dlg; ResetImage(); dlg.DoModal(); } // 该函数用于清空 m_pImageTempBuffer 数组


赞助商链接
相关文章:
数字图像处理实验及报告(含源码)
数字图像处理实验及报告(含源码)_数学_自然科学_...代码: s=imread('E:\ps\ps 素材\壁纸\mario....实验三图像的灰度变换和直方图变换 3.1 实验目的 1...
数字图像处理实验指导书(带源程序)
2.实验基本原理; 4.源程序; 6.实验结论; 3 实验二一. 实验目的 1. 熟悉...数字图像处理中,一种最简单和最有用的工具是直方图,它概括了一幅图像的灰度级...
数字图像处理代码大全
数字图像处理代码大全_信息与通信_工程科技_专业资料...('灰度直方图') xlabel('灰度值') ylabel('出现...2015国考申论押密试卷及答案 2015国考面试通关宝典 ...
2011数字图像处理实验
(4)利用 imfinfo 函数查询图像文件的有关信息。 五、实验结果及分析源程序:f=...三、实验原理在数字图像处理中,灰度直方图是最简单且最有用的工具,可以说,对...
“数字图像处理与识别”实验报告__实验1_直方图均衡化处理
数字图像处理与识别”实验报告__实验1_直方图均衡...图像直方图均衡化处理 理解图像灰度直方图的概念,掌握...附:程序源代码 clear; data = imread('直方图均衡...
数字图像处理
图像特征分析等做了详细的研究, 最终得到不同粒径集...在数字图像处理中采用视觉助手 软件,选择灰度直方图、...程序流程图 双击颜色平面抽取函数,进入函数配置界面,...
数字图像处理文档-直方图均衡&灰度线性变换&灰度拉伸
数字图像处理文档-直方图均衡&灰度线性变换&灰度拉伸_IT/计算机_专业资料。技术文档...数字图像处理源程序及详... 20页 2下载券 VC++数字图像处理源程序... 8页...
数字图像处理
数字图像处理 MATLAB 实习报告 1. 显示灰度图象 p1~p6 及直方图; 及直方图;【...【源程序】 p1=imread('D:\MATLAB7\work\p1.bmp ') [m,n]=size(p1);...
数字图像处理大作业
数字图像处理大作业_天文/地理_自然科学_专业资料。...的灰度直方图'); 从程序运行结果可以看出:原图像...6 附:Matlab源程序 iptsetpref('ImshowBorder', '...
MATLAB数字图像处理源代码
MATLAB数字图像处理源代码_IT/计算机_专业资料。MATLAB 实用源代码 (2010-03-31...('灰度直方图') xlabel('灰度值') ylabel(' 出现概率') %绘制直方图 %计算...
更多相关标签: