1.算法原理
分段线性拉伸算法是图像灰度变换中常用的算法,在商业图像编辑软件Photoshop中也有相应的功能。分段线性拉伸主要是利用图像对比度,突显图像细节。设输入图像为F(x),输出图像为G(x),分段区间为[start , end]映射区间为[sout, eout]。分段线性拉伸示意图如下:
图为分段线性拉伸示意图,从图中可以明显得到,分段线性拉伸算法需要明确4个参数Start、End、Sout以及Eout。当这四个参数均已知时,根据两点确定直线法,计算出直线L1、L2和L3的参数,分别为(K1、C1=0)、(K2、C2)和(K2、C2)。那么分段线性拉伸算法的公式如下:
对于参数分段区间[start, end]以及映射区间[sout, eout], 有人工设定、基于直方图设定等办法,下面实现核心算法。
2.算法实现
由于分段线性拉伸也是图像灰度变换的一种,代码如下所示:
//分段线性拉伸
//fStart :分段区间起点
//fEnd : 分段区间终点
//fSout :映射区间起点
//fEout :映射区间终点
void dividedLinearStrength(BYTE *SrcImage, BYTE *DstImage, int nWidth, int nHeight, float fStart, float fEnd, float fSout, float fEout)
{
//计算直线L1参数
float fk1 = fSout / fStart;
//L2
float fk2 = (fEout - fSout)/(fEnd - fStart);
float fc2 = fSout - fk2 * fStart;
//L3
float fk3 = (255.0f - fEout)/(255.0f - fEnd);
float fc3 = 255.0f - fk3*255.0f;
//建立查询表
vector<unsigned char> loolupTabel(256);
for (int m=0;m<256;m++)
{
if (m<fStart)
{
loolupTabel[m]=(m*fk1);
}
else if (m>fEnd)
{
loolupTabel[m] = (m*fk3 + fc3);
}
else
{
loolupTabel[m] = (m*fk2+fc2);
}
}
//灰度映射
for (int i=0;i<nWidth*nHeight;i++)
{
DstImage[i]=loolupTabel[SrcImage[i]];
}
}
结果如下:
3.基于直方图的分段线性拉伸
基于直方图的分段线性拉伸算法主要改进在于,通过从直方图自动地计算出分段区间和映射区间四个参数。本质上,直方图是对图像像素进行排序的一个过程。根据分段线性拉伸的原理,对图像像素进行升序排序后,去高灰度等级NH个点对应的最小灰度作为fEnd,取低灰度等级NL个点对应的最大灰度等级作为fStart,fSout=fStart * Sigma, fEout=fEnd *(1+sigma),sigma<1。具体代码实现步骤为:
(1).统计直方图;
(2).计算分段区间和映射区间。为使参数与图像尺寸无关,使用比例的方法限定NH和NL。设图像尺寸MXN,高灰度等级个数比例为fH、低灰度等级个数比例为fL。
代码如下所示:
//基于直方图的分段线性拉伸
//fH:高灰度等级比例
//fL:低灰度等级比例
//fSigma:拉伸系数
void dlsBaseHistogram(BYTE *SrcImage, int bit,BYTE *DstImage, int nWidth, int nHeight, float fH, float fL, float fSigma)
{
//统计直方图
vector<int> histogram(256);
if (bit=8)
{
LONG i;
LONG j;
LONG lLineBytes = (nWidth*bit/8+3)/4*4;
int m_imgSize = nWidth*nHeight;
//初始化直方图矩阵
for (int k=0;k<256;k++)
{
histogram[k]=0;
}
//统计灰度
for (i=0;i<nWidth;i++)
{
for (j=0;j<lLineBytes;j++)
{
histogram[SrcImage[lLineBytes*i+j]]++;
}
}
}
//计算分段区间
int nNH = nWidth*nHeight*fH;
int nNL = nWidth*nHeight*fL;
int nAcc=0;
float fStart =0 ,fEnd = 0;
for (int m=255;m>=0;m--)
{
nAcc += histogram[m];
if (nAcc>nNH)
{
fEnd = m;
break;
}
}
nAcc=0;
for (int m=0;m<histogram.size();m++)
{
nAcc += histogram[m];
if (nAcc>nNL)
{
fStart = m;
break;
}
}
//计算映射区间
float fSout = fStart*fSigma;
float fEout = fEnd * (fSigma + 1.0f);
fEout = fEout >255.0f ? 254 : fEout;
//分段线性拉伸
dividedLinearStrength(SrcImage,DstImage,nWidth,nHeight,fStart,fEnd,fSout,fEout);
}
结果如下:
在本例中参数fH=0.2、fL=0.5、fSigma=0.5。