新型除雾算法的HLS硬件化实现IP核
1、新型除雾算法介绍
新型除雾算法在暗通道的基础上做了改进,效果总体上看与暗通道去雾差不多,当然也要依图片具体情况而定,解释一下算法:
无雾图像的恢复一般采用以下公式,J为去雾后图像、I为待去雾图像,A是全球大气光成分,t(x)是透射率。
所以我们要求解的变量就是t和A。
求A一般的做法是,选取暗通道前0.1%像素的指标,然后利用这些指标对A进行估计。但是如果场景对象大于patch的尺寸,就会导致对A的高估,而且,如果A的估计精度很大程度上依赖于patch的尺寸,那么patch的尺寸越大,估计的精度就越高。但是,如果我们增加patch的尺寸,那么计算成本也会增加。此外,补丁的大小必须足够大,以覆盖场景中明亮的对象。由于明亮物体的大小会随着图像的变化而变化,所以patch的大小无法最合适地决定。
求A的另一种做法是韩国人的一篇优化对比度增强算法,方法是基于四叉树细分对A进行估计。在这种迭代方法中,首先将图像分成四个区域,然后选择得分最大的区域进行进一步分解,直到所选区域的大小小于阈值。其中,每个区域的得分由矩形内像素值的标准差减去平均像素值计算,但是出现某些亮物体时(见论文Dehazing for images with large sky region)这种方法也会失败。
根据A Novel Image Dehazing and Assessment Method这篇论文,提出了一种利用雾霾等级代替空气光色来估计透射图的方法,为什么可以替代下文会讲到。
我们将上述公式的A用K替代,并求三通道中的最小值,有如下方程:
对每个颜色矢量进行最小操作也会导致数值较低,t也比较小,所以公式第一项忽略,变成:
解得:
由于该式子忽略较小项,会造成粗糙,于是用一个更小的值对它进行归一化
其中,
这样一来,t可以顺利得到了,只剩下了变量K。根据论文里实验结果,K值随着雾霾变化而变化,同质雾霾场景的K是相同的,因此说K的值代表了雾霾的程度。公式表示为
根据论文的方法,K被确定如下
其中C即
α用于避免颜色失真,值为三通道均值减去三通道最小值的均值
得到的C需要用均值滤波器进行平滑。
最后处理一下防止数值太低:
我们知道,使用HLS设计算法加速器一般是用HLS生成IP核,再设计Soc系统,调用生成的IP核。由于现在的场景是对单张图像进行处理而不是视频流,我们没有采用hls的视频流处理库函数,而是简单得把接口定义为数组,使用BRAM资源,这样一来对zedboard来说资源有限很可能会超标,暂时也想不到怎么处理。算法大致步骤如下:
- RGB=RGB除以255
- 求R,G,B的最小值分量dc(即暗通道),再求RGB平均值分量Im
- 将向量A设为A=Im + (Im的均值 –dc的均值 )
- 对A做卷积操作
- 求灰度图gray,将gray与A做导向滤波,返回结果存在A中
- 对A值做过滤:A = min(A,0.8)
- 将RGB向量分别除以A后,求结果向量中的三通道最小值dc
- 计算向量t = (1 -0.85*(dc))/(1-beta),其中向量beta是RBG三通道的均值减去最小值,并对t值做过滤t =max(t,0.1)
- 计算结果J,J = (I-A)/(t) + (A)
HLS代码如下(由于资源限制去掉了导向滤波):
``
#include <hls_video.h>
#include <ap_axi_sdata.h>
#define W 240
#define H 320
float temp[W+4][H+4];
void darkchannel(unsigned char img_bgr[3*H*W],float A[W*H],float Im[W*H],float out_bgr[3*H*W]){
int len=H*W;
int r=2*H*W;
float dc=0,temp_i=0;
for(int i=0;i<len;i++){
float I1=img_bgr[i]/255.0;
float I2=img_bgr[len+i]/255.0;
float I3=img_bgr[r+i]/255.0;
float min; //get min of rgb
min=I1<I2?I1:I2;
min=min<I3?min:I3;
dc+=min;
Im[i]=(I1+I2+I3)/3.0;
temp_i+=Im[i];
}
float d_mean=temp_i/(len*1.0)-dc/(len*1.0);
for(int j=0;j<len;j++){
Im[j]=Im[j]+d_mean;//dark[j]=Im[j]+d_mean;
}
int x=W,y=H;
for(int i=1+3;i<=x+3;i++){
for(int j=1+3;j<=y+3;j++){
temp[i][j]=Im[(i-4)*y+j-4];
}
}
//fill up using board_default way
for(int j=0;j<4;j++){
for(int i=4;i<=y+3;i++){
temp[j][i]=temp[8-j][i];
temp[x+4+j][i]=temp[x+2-j][i];
}
}
for(int j=0;j<4;j++){
for(int i=4;i<=x+3;i++){
temp[i][j]=temp[i][8-j];
temp[i][y+4+j]=temp[i][y+2-j];
}
}
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
temp[i][j]=temp[8-i][j];
temp[x+4+i][j]=temp[x+2-i][j];
temp[i][y+4+j]=temp[8-i][y+4+j];
temp[x+4+i][y+4+j]=temp[x+2-i][y+4+j];
}
}
for(int pos_i=4;pos_i<4+x;pos_i++){
for(int pos_j=4;pos_j<4+y;pos_j++){
int start_x=pos_i-4;
int start_y=pos_j-4;
float re=0;
for(int i=0;i<9;i++){
for(int j=0;j<9;j++){
re+=temp[start_x+i][start_y+j]/81.0;
}
}
A[start_x*y+start_y]=re;
}
}
for(int i=0;i<len;i++){
float ele1=(img_bgr[i]/255.0)/A[i];
float ele2=(img_bgr[len+i]/255.0)/A[i];
float ele3=(img_bgr[r+i]/255.0)/A[i];
float min;
min=ele1<ele2?ele1:ele2;
min=min<ele3?min:ele3;
float min_bgr; //get min of rgb
float I1=img_bgr[i]/255.0;
float I2=img_bgr[len+i]/255.0;
float I3=img_bgr[r+i]/255.0;
min_bgr=(I1)<(I2)?I1:I2;
min_bgr=min_bgr<I3?min_bgr:I3;
float t=(1-0.95*min)/(1-Im[i]+min_bgr);
if(t<0.1) t=0.1;
img_bgr[i]=(img_bgr[i]/255.0-A[i])/(t)+A[i];
img_bgr[len+i]=(img_bgr[len+i]/255.0-A[i])/(t)+A[i];
img_bgr[r+i]=(img_bgr[r+i]/255.0-A[i])/(t)+A[i];
}
}