IDN (由信息蒸馏网络实现的快速准确的超分辨率)
在《IDN-Caffe-master》和《IDN-tensorflow-master》都有训练好的模型。
里面的一张和其它几个运行时间和效果比较图表:
效果比MemNet,DRRN好一点,差不多在同一个数量级,但速度很快。
这里用C++实现其中的4倍重建:
流程图:
信息蒸馏块:
上面的4组是说核数量只有通道数的4分之1
按caffe,tensorflow模型的不同,有部分差别:
1。1(亮度)或 3(RGB)输入输出
2。反卷积 或 像素混组(放大部分)
------------------------------------------------------------------------
先实现caffe模型:
定义蒸馏块:
struct 蒸馏块
{
//增强单元-----------
//上组
层数据 *conv1_上; //64->48
层数据 *conv2_上; //48->32 (4组:12x4=48)
层数据 *conv3_上; //32->64
//64分割成(16+48) (其中16和输入64连接成80,48传入下组)
//下组
层数据 *conv1_下; //48->64
层数据 *conv2_下; //64->48 (4组:16x4=64)
层数据 *conv3_下; //48->80
//和连接相加
//压缩单元
层数据 *down;//80->64
};
模型定义和初始化:
struct IDN模型
{
//特征提取
层数据 * conv1;//1->64
层数据 * conv2;//64->64
//信息蒸馏块
int 蒸馏块数量;//4块
蒸馏块 * 块;
//重建(upsample)
层数据 * 放大4倍;//64->1 反卷积(步长:4,核:17)
//输入 和 输入双三次放大的 相加 即重建
//构造函数
IDN模型();
};
IDN模型::IDN模型()
{
int size;
层数据 * 层;
//用于一层(宏)
/*输入维度,输出维度,核宽*/
/*输入维度,输出维度,核宽*/
#define 初始化ONE层(IN,OUT,KW) \
\
层->输入维度=IN;\
层->输出维度=OUT;\
层->核宽=KW;\
层->权重长度=层->输出维度*层->输入维度*层->核宽*层->核宽;\
层->权重_数据=(float*)malloc(sizeof(float) * 层->权重长度);\
层->偏移长度=层->输出维度;\
层->偏移_数据=(float*)malloc(sizeof(float) * 层->偏移长度);\
#define 初始化层(ConvX,IN,OUT,KW) \
size = sizeof(层数据);\
\
层=ConvX =(层数据 *)malloc(size);\
初始化ONE层(IN,OUT,KW)\
//组中核数量只有普通卷积的4分之1-------------------!!
#define 初始化组卷积层(ConvX,IN,OUT,KW) \
\
初始化层(ConvX,IN,OUT,KW) \
层->输入维度*=4;\
//用于一组(宏)
/*组名称,输入维度,输出维度,核宽,组中层数*/
//特征提取: 2层
/*名称,输入维度,输出维度,核宽,组中层数*/
初始化层(conv1,1,64,3);
初始化层(conv2,64,64,3);
蒸馏块数量=4;
size = sizeof(蒸馏块)*蒸馏块数量;
块=(蒸馏块*)malloc(size);
蒸馏块 * 蒸馏块0=块;
for (int k = 0;k<蒸馏块数量;k++)
{
初始化层(蒸馏块0->conv1_上,64,48,3);
初始化组卷积层(蒸馏块0->conv2_上,12,32,3);
初始化层(蒸馏块0->conv3_上,32,64,3);
初始化层(蒸馏块0->conv1_下,48,64,3);
初始化组卷积层(蒸馏块0->conv2_下,16,48,3);
初始化层(蒸馏块0->conv3_下,48,80,3);
初始化层(蒸馏块0->down,80,64,1);
蒸馏块0++;
}
初始化层(放大4倍,64,1,17);
}
主函数:
void IDN(char * savefilename,IDN模型 & sr)
{
//
int wid=bmp.width;
int hei=bmp.height;
cout<<"输入图像宽度:"<<wid<<endl;
cout<<" 高度:"<<hei<<endl;
//
卷积层 Y(wid,hei,1);//亮度
Y.data=new float[wid * hei ];
卷积层 U(wid,hei,1);//
U.data=new float[wid * hei ];
卷积层 V(wid,hei,1);
V.data=new float[wid * hei ];
//jpg转换为Y,U,V卷积层
bmp2YUV(&Y,&U,&V);
卷积层 Y备份(wid,hei,1);//亮度
Y备份.data=new float[wid * hei ];
//备份
卷积层复制(&Y,&Y备份);
del卷积层(U);
del卷积层(V);
//---------------------------------------------->
层数据 * 层;
//两个卷积层 交替前传(源,目标)
//用这个传回
卷积层 * di=(卷积层 *)malloc(sizeof(卷积层));
di->width=1;
di->height=1;
di->depth=1;
di->data=new float[1 ];
卷积层 *源,*目标;
源 = &Y;
目标 = di;
int pad;
//定义一个宏
//用于各个层
#define 卷积前传(ConvX,步长)/* 上采样、下采样 */ \
\
层=ConvX;/* Conv2 层 */ \
if(层->输出维度 != 目标->depth || 目标->width != wid || 目标->height != hei)\
Resize卷积层(*目标,wid,hei,层->输出维度);\
pad=层->核宽/2;\
vl_nnconv(源,目标,层 ,步长,步长,pad,pad,pad,pad);\
vl_nnrelu(目标,0.05f);\
\
std::swap (源,目标);\
cout<<"输入层..."<<endl;
卷积前传(sr.conv1,1);
卷积前传(sr.conv2,1);
//----------------------------------------------<
//第二部分 4个蒸馏块
卷积层 convfea5(wid,hei,源->depth);
convfea5.data=new float[wid * hei * 源->depth ];
卷积层 *源备份=&convfea5;
if(源->depth != 目标->depth || 目标->width != wid || 目标->height != hei)
Resize卷积层(*目标,wid,hei,源->depth);
蒸馏块 * 蒸馏块0=sr.块;
cout<<sr.蒸馏块数量<<"个蒸馏块... 包括 2 组卷积"<<endl;
卷积层 割1(wid,hei,16);
割1.data=new float[wid * hei * 16 ];
卷积层 割2(wid,hei,48);
割2.data=new float[wid * hei * 48 ];
卷积层 加(wid,hei,80);
加.data=new float[wid * hei * 80 ];
for (int k = 0;k<sr.蒸馏块数量;k++)
{
cout<<"\r"<<k;
//备份 (split)
卷积层复制(源,源备份);
卷积前传(蒸馏块0->conv1_上,1);
//卷积前传(蒸馏块0->conv2_上,1);//4组 组卷积?
组卷积4分(*源,*目标,蒸馏块0->conv2_上);
std::swap (源,目标);
卷积前传(蒸馏块0->conv3_上,1);
//通道分割 (slice) 64->16,48
通道分割(*源,割1,割2);
Resize卷积层(*源,wid,hei,割2.depth);
卷积层复制(&割2,源);
卷积前传(蒸馏块0->conv1_下,1);
//卷积前传(蒸馏块0->conv2_下,1);
组卷积4分(*源,*目标,蒸馏块0->conv2_下);
std::swap (源,目标);
卷积前传(蒸馏块0->conv3_下,1);
//concat
通道连接(*源备份,割1,加);//16+64=80
//数据增强 (Eltwise sum)
卷积层相加(&加,源);
//数据压缩
卷积前传(蒸馏块0->down,1);
蒸馏块0++;//到下蒸馏块
}
cout<<endl;
del卷积层(*源备份);
//放大4倍
cout<<"放大4倍... "<<endl;
wid *= 4;hei *= 4;
层=sr.放大4倍;/* Conv2 层 */
if(层->输出维度 != 目标->depth || 目标->width != wid || 目标->height != hei)
Resize卷积层(*目标,wid,hei,层->输出维度);
pad=层->核宽/2;
//调整图像位置和双三次重合
int e=-4;//上下
int f=-4;//左右
vl_nnconvt(源,目标,层 ,4,4,pad+e,pad-e,pad+f,pad-f);
//vl_nnrelu(目标);
//cout<<层->偏移_数据[0]<<endl;
std::swap (源,目标);
//save_卷积层2jpg(源,"up");
//原图放大4倍
ResizeGrayscaleImage(4.0);
wid=bmp.width;
hei=bmp.height;
卷积层 uY(wid,hei,1);//亮度
uY.data=new float[wid * hei ];
卷积层 uU(wid,hei,1);//
uU.data=new float[wid * hei ];
卷积层 uV(wid,hei,1);
uV.data=new float[wid * hei ];
//jpg转换为RGB卷积层
bmp2YUV(&uY,&uU,&uV);
//bmp2YUV(&uU,&uV);
//卷积层双三次插值(Y备份,uY);
//输入加上残差
卷积层相加(源,&uY);
//save_卷积层2jpg(&uY,"uY");
YUV2bmp(&uY,&uU,&uV);
del卷积层(*源);
del卷积层(*目标);
cout<<"图像转换成jpg格式... "<<endl;
savejpg(savefilename);
cout<<"转换文件已经保存为: "<<savefilename<<endl;
}
效果图:
小图
IDN 4倍
EDSR 4倍
效果和EDSR可以说在一个数量级,运行时间只有EDSR的一半左右,并且占用内存小。
下载:
win32超分辩重建IDN实用程序
快速超分辨率重建IDN(4倍)的win32程序,由IDN-Caffe-master的模型改编而来.
https://download.csdn.net/download/juebai123/11123292