查表法的应用
比较普通算法与查LUT表法计算伽马变换和图像灰度化的快慢程度
- 伽马变换:
void GryImageGamaCorrect_Slow(BYTE *pGryImg,double gama,int nSize)
{
for(int i=0;i<nSize;i++)
{
*(pGryImg+i) = min(255,(int)(pow(*(pGryImg+i),gama)));
}
return;
}
void GryImageGamaCorrect_Fast(BYTE *pGryImg,double gama,int nSize)
{
int LUT[256];
int i;
LUT[0]=0;
for(i=1;i<256;i++) LUT[i]=min(255,(int)(pow(i,gama)));
for(i=0;i<nSize;i++)
{
*(pGryImg+i) = LUT[*(pGryImg+i)];
}
return;
}
在release模式下,用查表法运算一次的时间大约为0.4ms,用普通算法运算一次的时间大约为33ms;
在debug模式下,用查表法运算一次的时间大约为1.5ms,用普通算法运算一次的时间大约为35ms;
2. 图像灰度化:
void RGBImg2GryImg0(BYTE *pRGBImg,int width,int height,BYTE *pGryImg)
{
BYTE *pRGB,*pGry,*pEnd=pRGBImg+3*width*height;
double gry;
for(pRGB=pRGBImg,pGry=pGryImg;pRGB<pEnd;)
{
gry = *(pRGB++)*0.114; //B
gry += *(pRGB++)*0.587; //G
gry += *(pRGB++)*0.299; //R
*(pGry++) = (int)(gry);
}
return;
}
void RGBImg2GryImg1(BYTE *pRGBImg,int width,int height,BYTE *pGryImg)//LUT
{
BYTE *pRGB,*pGry,*pEnd=pRGBImg+3*width*height;
double LUTR[256],LUTG[256],LUTB[256],gry;
int i;
for(i=0;i<256;i++)
{
LUTR[i]=0.299*i;
LUTG[i]=0.587*i;
LUTB[i]=0.114*i;
}
for(pRGB=pRGBImg,pGry=pGryImg;pRGB<pEnd;)
{
gry = LUTB[*(pRGB++)];
gry += LUTG[*(pRGB++)];
gry += LUTR[*(pRGB++)];
*(pGry++) = (int)(gry);
}
return;
}
void RGBImg2GryImg2(BYTE *pRGBImg,int width,int height,BYTE *pGryImg)
{
BYTE *pRGB,*pGry,*pEnd=pRGBImg+3*width*height;
int LUTR[256],LUTG[256],LUTB[256];
int i,gry;
for(i=0;i<256;i++)
{
LUTR[i]=(int)(0.299*i*1000);
LUTG[i]=(int)(0.587*i*1000);
LUTB[i]=(int)(0.114*i*1000);
}
for(pRGB=pRGBImg,pGry=pGryImg;pRGB<pEnd;)
{
gry = LUTB[*(pRGB++)];
gry += LUTG[*(pRGB++)];
gry += LUTR[*(pRGB++)];
*(pGry++) = gry/1000;
}
return;
}
void RGBImg2GryImg3(BYTE *pRGBImg,int width,int height,BYTE *pGryImg)
{
BYTE *pRGB,*pGry,*pEnd=pRGBImg+3*width*height;
int LUTR[256],LUTG[256],LUTB[256];
int i,gry;
for(i=0;i<256;i++)
{
LUTR[i]=(int)(0.299*i*1024);
LUTG[i]=(int)(0.587*i*1024);
LUTB[i]=(int)(0.114*i*1024);
}
for(pRGB=pRGBImg,pGry=pGryImg;pRGB<pEnd;)
{
gry = LUTB[*(pRGB++)];
gry += LUTG[*(pRGB++)];
gry += LUTR[*(pRGB++)];
*(pGry++) = gry>>10;
}
return;
}
void RGBImg2GryImg4(BYTE *pRGBImg, int width, int height, BYTE *pGryImg)
{
BYTE *pRGB, *pEnd;
BYTE *pGry;
int sum;
pEnd = pRGBImg+3*width*height;
for (pRGB = pRGBImg, pGry = pGryImg; pRGB<pEnd;)
{
sum = *(pRGB++)*7471; //B*0.114
sum += *(pRGB++)*38469; //G*0.587
sum += *(pRGB++)*19595; //R*0.299
*(pGry++) = sum>>16;
}
return;
}
在release模式下,用查表法与移位操作运算一次的时间大约为0.7ms,用普通算法运算一次的时间大约为8.1ms;
在debug模式下,用查表法与移位操作运算一次的时间大约为3.5ms,用普通算法运算一次的时间大约为6.3ms;
均值方差规定化
代码如下:
void SetAverVar(BYTE *pImg, int width, int height, int aver0, int var0)
{
unsigned long hist[256];
int aver1, var1;
CalAverVar(pImg, width, height, aver1, var1);
BYTE *pCur = pImg, *pEnd = pImg + width * height;
memset(hist, 0, sizeof(unsigned long) * 256);
for (;pCur<pEnd;)
{
hist[min(255, (max(0, *(pCur++) - aver1))*var0 / var1 + aver0)]++;
//G1=(g-u1)*(d0/d1)+u0;
}
for (int i = 0; i < width*height; i++)
{
*(pImg + i) = hist[*(pImg + i)];
}
return;
}
将所有的像素点存进表里后先减去原始均值,再改变方差,再加上规定的均值,但有个问题是如何确定输入的一对均值方差合法?比如输入均值255,那么输入的方差只能为0,输入其他的也会有输出,但实际上,代码先满足了方差的规定化,后满足均值的规定化,而在原像素上会产生溢出,限定最大值为255后,可能会出现均值也没有满足,而前一步规定好的方差也被改变的情况,目前我还没有找到解决办法。
计算图像的均值方差
代码如下:
void CalAverVar(BYTE *pImg,int width,int height,int &aver,int &var)
{
unsigned long hist[256];
BYTE *pCur = pImg, *pEnd = pImg + width * height;
memset(hist, 0, sizeof(unsigned long) * 256);
for (; pCur < pEnd;) {
hist[*(pCur++)]++;
}
int sum = 0, sum_var = 0;
for (int i = 0; i < 256; i++)
{
sum += i * hist[i];
}
if (sum) aver = sum / (height*width);
else aver = 0;
for (int i = 0; i < 256; i++)
{
sum_var += (i - aver) * (i - aver) * hist[i];
}
if (sum_var) var = (int)(sqrt(sum_var / (height * width)));
else var = 0;
return ;
}
图像均值=像素值总和/像素个数,可以建立一个直方图表,将各点像素值作为下标,记录有多少个这样的点,再分别将下标与个数相乘即为总和。
方差=(各点像素值-均值)^2之和/像素个数,计算方法与均值类似。
用查表法计算两图像相减的绝对值;
代码如下:
void abs1(BYTE *pImgA,BYTE *pImgB,int width,int height)
{
int LUT[511];
BYTE *pA, *pB;
BYTE *pEndA = pImgA + width * height, *pEndB = pImgB + width * height;
for (int i = 0; i < 256; i++)
{
LUT[i + 255] = LUT[255 - i] = i;
}
for (pA=pImgA ,pB=pImgB; pA < pEndA , pB<pEndB ; )
{
*(pA++) = LUT[*pA - (*pB++) + 255];
}
}
因为两像素相减值域为512,先建一个相当于把y=|x|图像右移255个单位的图,然后直接查表。
除法的快速计算
代码如下:
void ImgDiv_Fast(BYTE *pImgA, BYTE *pImgB, int width, int height)
{
int LUT[256], b;
for (int i = 1; i < 256; i++)
LUT[i] = (1 << 22) / i;
for (int i = 0; i < width*height; i++)
{
b = *(pImgB + i);
if (b) (*(pImgA + i))*LUT[b] >> 22;
else *(pImgA + i) = 255;
}
return;
}
Y=X/N 等价与Y=X* (1/N),其中1/N=((aM)/N)/(aM),即((a<<M)/N)>>M,预先将(a<<M)/N 存进LUT表,这样A/B时,只需要给A*对应表中的值即可。
普通算法:
void ImgDiv_Slow(BYTE *pImgA, BYTE *pImgB, int width, int height)
{
int b;
for (int i = 0; i < width*height;i++)
{
b = *(pImgB + i);
if (b) (*(pImgA + i)) /= b;
else *(pImgA + i) = 255;
}
return;
}