第
3
章
数字图像的运算
.. 3.1 点 运 算
点运算(PointOperation)是指对一幅图像中部分像素点的灰度值进行计
算。它将输入图像映射为输出图像,输出图像中每个像素点的灰度值仅由对应
的输入像素点的灰度值决定,运算结果不会改变图像内像素点之间的空间关
系。设输入图像在坐标(x,y)处的灰度值为f(x,y),输出图像在坐标(x,y)处
的灰度值为g(x,y),则点运算为
g(x,y)=T[f(x,y)] (3-1) 
其中,T 是对f(x,y)的一种数学运算,即点运算是一种像素的逐点运算, 
是灰度到灰度的映射过程,通常称T 为灰度变换函数。
点运算又称为“对比度增强”“对比度拉伸”“灰度变换”等。按灰度变换函
数T 的性质,可将点运算分为以下两类。
(1)灰度变换增强,又包括线性灰度变换(线性点运算)、分段线性灰度变换
(分段线性点运算)、非线性灰度变换(非线性点运算)。
(2)直方图增强。
灰度变换是图像增强的重要手段之一,用于改善图像显示效果,属于空间
域处理方法,它可以使图像的动态范围加大,图像的对比度扩展,图像更加清
晰,特征更加明显。灰度变换的实质是按一定的规则修改图像每个像素的灰
度,从而改变图像的灰度范围。
下面给出了灰度值增加的C++代码,点运算示例如图3-1所示,其中,显示
的示例图为每个像素增加给定的灰度值的效果。
C++示例代码3-1:灰度值增加 
/* 
将图像中的每个像素的蓝、绿、红3 个分量的灰度值分别加上blue、green、red,以增
强图像的亮度。注意:若灰度值增加后超过255,则直接取255。
*/ 
void CFgImage::Add(int blue,int green,int red){ 
for (int row = 0; row < m_height; row++){ 
for (int col = 0; col < m_width; col++){

第3章 数字图像的运算 65 
BYTE* p = m_pBmpBuf + (m_height-row-1)*m_widthStep + col * 3; 
BGR bgr; 
bgr.Blue = *p; 
bgr.Green = *(p + 1); 
bgr.Red = *(p + 2); 
if (bgr.Blue + blue>=255) 
*p = 255; 
else 
*p = (bgr.Blue + blue); 
if (bgr.Green + green >= 255) 
*(p + 1) = 255; 
else 
*(p + 1) = (bgr.Green + green); 
if (bgr.Red + red >= 255) 
*(p + 2) = 255; 
else 
*(p + 2) = (bgr.Red + red); 
} 
} 
} 
图3-1 点运算示例
3.1.1 线性灰度变换
线性灰度变换又称为线性点运算,主要有对比度增强等。假定原图像f(x,y)的灰
度范围为[a,b],若希望变换后的图像g(x,y)的灰度范围扩展为[c,d],则其函数表现形
式如式(3-2)所示。线性灰度变换如图3-2所示。

66 数字图像处理系列教程———基础知识篇
g(x,y)=d -c 
b-a[f(x,y)-a]+c (3-2) 
图3-2 线性灰度变换 
下面给出了线性灰度变换的C++代码实现,图3-3为线性灰度变换示例。
C++示例代码3-2:线性灰度变换 
void CFgImage::GrayTrans1(double a, double b, double c, double d) { 
struct TranFunc{ 
double a, b, c, d; 
TranFunc(double a, double b, double c, double d) :a(a), b(b), c(c), d(d){ 
} 
BYTE func(int gray){ 
double val = (d - c) * (gray - a) / (b - a) + c; 
return saturate_cast(val); 
} 
BGR operator()(BYTE Blue, BYTE Green, BYTE Red){ 
BGR bgr; 
bgr.Blue = func(Blue); 
bgr.Green = func(Green); 
bgr.Red = func(Red); 
return bgr; 
} 
}; 
GrayTrans(TranFunc(a, b, c, d)); 
}
template <typename Func> void CFgImage::GrayTrans(Func& func) { 
for (int row = 0; row < m_height; row++){ 
for (int col = 0; col < m_width; col++){ 
BYTE* p = m_pBmpBuf + row * m_widthStep + col * 3;

第3章 数字图像的运算 67 
BGR bgr = func(*p, *(p + 1), *(p + 2)); 
*p = bgr.Blue; 
*(p + 1) = bgr.Green; 
*(p + 2) = bgr.Red; 
} 
} 
} 
图3-3 线性灰度变换示例
3.1.2 分段线性灰度变换
为了突出图像中用户感兴趣的目标或者灰度区间,相对抑制那些用户不感兴趣的灰
度区域而不牺牲其他灰度级上的细节,可以采用分段线性灰度变换,它可将需要的图像细
节灰度拉伸,增强对比度,而将不需要的细节灰度级压缩。
分段线性灰度变换是将输入图像f(x,y)的灰度级区间分成两段乃至多段,然后分
别对每一段做线性灰度变换,以获得增强图像g(x,y)。典型的3段线性灰度变换如
图3-4所示,其函数表达形式如下。
g(x,y)= 
c
af(x,y) 0<f(x,y)<a 
d -c 
b-a[f(x,y)-a]+c a ≤f(x,y)≤b 
Gmax -d 
Fmax -b[f(x,y)-b]+d b <f(x,y)≤Fmax 
ì
.
í
.... 
.... 
(3-3)

68 数字图像处理系列教程———基础知识篇 
其中,参数a、b、c、d 表示用于确定3条线段斜率的常数,取值可根据具体变换设定。
图3-4 三段线性灰度变换
此外,也存在一些情况,即用户可能仅对某个范围内的灰度感兴趣,这时只需对其进
行线性拉伸,以便清晰化。例如: 
(1)在式(3-3)中,当0<f(x,y)<a 和b<f(x,y)<Fmax时,g(x,y)=f(x,y),这
表示对区间(0,a)和(b,Fmax)范围内的像素不做变换,而将处于[a,b]的原图像的灰度线
性地变换成新图像的灰度。
(2)在式(3-3)中,当0<f(x,y)<a 时,g(x,y)=c;当b<f(x,y)<Fmax时,g(x, 
y)=d,这表示只将处于[a,b]的原图像的灰度线性地变换成新图像的灰度,而对于[a, 
b]以外的像素,则将原图像的灰度强行压缩为灰度c 和d。
下面给出了分段线性灰度变换的C++代码示例,图3-5为分段线性灰度变换示例。
C++示例代码3-3:分段线性灰度变换 
void CFgImage::GrayTrans2(double a, double b, double c, double d, int G_max, 
int F_max) { 
struct TranFunc{ 
double a, b, c, d; 
int G_max, F_max; 
TranFunc(double a, double b, double c, double d, int G_max, int F_max) : 
a(a), b(b), c(c), d(d), G_max(G_max), F_max(F_max){ 
} 
BYTE func(int gray){ 
double val = 0; 
if (gray < a) val = c * gray / a; 
else if (gray <= b) val = (d - c) * (gray - a) / (b - a) + c; 
else val = (G_max - d) * (gray - b) / (F_max - b) + d; 
return saturate_cast(val); 
} 
BGR operator()(BYTE Blue, BYTE Green, BYTE Red){ 
BGR bgr;

第3章 数字图像的运算 69 
bgr.Blue = func(Blue); 
bgr.Green = func(Green); 
bgr.Red = func(Red); 
return bgr; 
} 
}; 
GrayTrans(TranFunc(a, b, c, d, G_max, F_max)); 
} 
图3-5 分段线性灰度变换示例
3.1.3 非线性灰度变换
当用某些非线性变化函数作为灰度变换的变换函数时,可实现图像灰度的非线性变
换。对数变换、指数变换和幂次变换是常见的非线性变换。
1.对数变换
对数变换如图3-6所示,其函数形式如下所示。
g(x,y)=c·log[f(x,y)+1] (3-4) 
其中,c 是尺度比例常数,其取值可以结合输入图像的范围来定。f(x,y)的取值为
[f(x,y)+1]是为了避免对0求对数,确保log[f(x,y)+1]≥0。
当希望对图像的低灰度区做较大拉伸,对高灰度区做压缩时,可采用这种变换,它能
使图像的灰度分布与人的视觉特性相匹配。对数变换一般用于处理过暗图像。

70 数字图像处理系列教程———基础知识篇
图3-6 对数变换
下面给出了对数变换的C++代码实现,图3-7为对数变换示例。
C++示例代码3-4:对数变换 
void CFgImage::GrayTrans3(double c) { 
struct TranFunc{ 
double c; 
TranFunc(double c) :c(c){ 
} 
BYTE func(int gray){ 
double val = c * std::log(gray + 1); 
return saturate_cast(val); 
} 
BGR operator()(BYTE Blue, BYTE Green, BYTE Red){ 
BGR bgr; 
bgr.Blue = func(Blue); 
bgr.Green = func(Green); 
bgr.Red = func(Red); 
return bgr; 
} 
}; 
GrayTrans(TranFunc(c)); 
} 
2.指数变换
指数变换如图3-8所示,函数形式如下所示。
g(x,y)=bc[f(x,y)-a]-1 (3-5) 
其中,a 用于决定指数变换函数曲线的初始位置;当f(x,y)=a 时,g(x,y)=0,曲
线与x 轴相交;b 是底数;c 用于决定指数变换曲线的陡度。

第3章 数字图像的运算 71 
图3-7 对数变换示例
图3-8 指数变换
当希望对图像的低灰度区做压缩,对高灰度区做较大拉伸时,可采用这种变换。指数
变换一般适用于处理过亮图像。对数变换常用来扩展低值灰度,压缩高值灰度,这样可使
低值灰度的图像细节更清晰。
下面给出了指数变换的C++代码实现,图3-9为指数变换示例。
C++示例代码3-5:指数变换 
void CFgImage::GrayTrans4(double a, double b, double c) { 
struct TranFunc{ 
double a,b,c; 
TranFunc(double a, double b, double c) :a(a), b(b), c(c){

72 数字图像处理系列教程———基础知识篇 
} 
BYTE func(int gray){ 
double val = std::pow(b, c * (gray - a)) - 1; 
return saturate_cast(val); 
} 
BGR operator()(BYTE Blue, BYTE Green, BYTE Red){ 
BGR bgr; 
bgr.Blue = func(Blue); 
bgr.Green = func(Green); 
bgr.Red = func(Red); 
return bgr; 
} 
}; 
GrayTrans(TranFunc(a, b, c)); 
} 
图3-9 指数变换示例
3.幂次变换
幂次变换的函数形式如下所示。
g(x,y)=c[f(x,y)]γ (3-6) 
其中,c 和γ 为正的常数,当c 取1、γ 取不同值时,可得到一簇变换曲线,如图3-10 
所示。

第3章 数字图像的运算 73 
图3-10 幂次变换
与对数变换的情况类似,幂次变换可以将部分灰度区域映射到更宽的区域中,从而增
强图像的对比度。当γ=1时,幂次变换变成线性正比变换;当0<γ<1时,幂次变换可
以扩展原始图像的中、低灰度级,压缩高灰度级,从而使得图像变亮,增强原始图像中暗区
的细节;当γ>1时,幂次变换可以扩展原始图像的中、高灰度级,压缩低灰度级,从而使
得图像变暗,增强原始图像中亮区的细节。
幂次变换常用于图像获取、打印和显示的各种转换设备的伽马校正中,这些设备的光
电转换特性都是非线性的,是根据幂次规律产生响应。幂次变换的指数即伽马值,因此幂
次变换也称为伽马变换。
下面给出了幂次变换的C++代码实现,图3-11为幂次变换示例。
C++示例代码3-6:幂次变换 
void CFgImage::GrayTrans5(double c, double gamma) { 
struct TranFunc{ 
double c, gamma; 
TranFunc(double c, double gamma) :c(c), gamma(gamma){ 
} 
BYTE func(int gray){ 
double val = c * std::pow(gray, gamma); 
return saturate_cast(val); 
} 
BGR operator()(BYTE Blue, BYTE Green, BYTE Red){ 
BGR bgr; 
bgr.Blue = func(Blue); 
bgr.Green = func(Green); 
bgr.Red = func(Red); 
return bgr; 
} 
};