这里实现《IDN-tensorflow-master》中的IDN,一些不同之处见上章。
先说明一下,由于IDN中要用到'组卷积',vl_nnconv在用到这里时会出错,所以前面矩阵乘法中要修改一下:
filters_getHeight= filters_getWidth= filters_biases->核宽;
//filters_getSize=filters_biases->输出维度;
filters_getSize=filters_biases->偏移长度;
//filters_getDepth=filters_biases->输入维度;
filters_getDepth=filters_biases->权重长度/(filters_getHeight*filters_getHeight*filters_getSize);//组卷积时 != 输入维度
有兴趣的同学可以看 《matconvnet》源代码原文,我这里只是凑合着用。
上章中的“组卷积4分”是按IDN-tensorflow-master 中的GroupConv2d函数改编来的(python 代码):
inputGroups = tf.split(axis=3, num_or_size_splits=n_group, value=x)
weightsGroups = tf.split(axis=3, num_or_size_splits=n_group, value=We)
convGroups = [groupConv(i, k) for i, k in zip(inputGroups, weightsGroups)]
outputs = tf.concat(axis=3, values=convGroups)
大意是:把当前层输入数据和权重权重数据都分成4份(按通道分割),然后组卷积(这里的权重还是只有数据量的1/4),最后组合在一起。
C++实现(重新命名为“_4分组卷积”):
void _4分组卷积( 卷积层 & si,卷积层 & di,层数据 * 层 )
{
int wid = si.width;
int hei = si.height;
int wh= wid * hei;
if(层->输出维度 != di.depth || di.width != si.width || di.height != si.height)
Resize卷积层(di,si.width ,si.height,层->输出维度);
int pad=层->核宽/2;
//4分
卷积层 源(si.width ,si.height,si.depth/4);
源.data=si.data;
卷积层 目标(di.width ,di.height,di.depth/4);
目标.data=di.data;
层数据 层4分;
层4分.偏移长度=层->偏移长度/4;
层4分.权重长度=层->权重长度/4;
层4分.输入维度 =层->输入维度 /4;
层4分.输出维度 =层->输出维度 /4;
层4分.偏移_数据 =层->偏移_数据;
层4分.权重_数据 =层->权重_数据;
层4分.核宽 =层->核宽;
for(int i=0;i<4;i++)
{
vl_nnconv(&源,&目标,&层4分 ,1,1,pad,pad,pad,pad);
//下一份
源.data+= wh*源.depth;
目标.data+=wh* (目标.depth);
层4分.偏移_数据+=层4分.偏移长度;
层4分.权重_数据+=层4分.权重长度;
}
vl_nnrelu(&di,0.05f);
}
正文开始,定义数据池:
struct IDN模型
{
//特征提取
层数据 * conv1;//3->64
层数据 * conv2;//64->64
//信息蒸馏块
int 蒸馏块数量;//4块
蒸馏块 * 块;
//重建(upsample)
层数据 * up1;//64->64 卷积
层数据 * up2;//64->48 卷积
//亚像素卷积 48->3
//输入 和 输入双三次放大的 相加 即重建
//构造函数
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)\
#define 初始化组卷积层(ConvX,IN,OUT,KW) \
\
初始化层(ConvX,IN,OUT,KW) \
层->输入维度*=4;\
//用于一组(宏)
/*组名称,输入维度,输出维度,核宽,组中层数*/
//Conv1: 1层
/*名称,输入维度,输出维度,核宽,组中层数*/
初始化层(conv1,3,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++;
}
初始化层(up1,64,64,3);
初始化层(up2,64,48,3);
}
主函数:
void IDN(char * savefilename,IDN模型 & sr)
{
//
int wid=bmp.width;
int hei=bmp.height;
cout<<"输入图像宽度:"<<wid<<endl;
cout<<" 高度:"<<hei<<endl;
//
卷积层 rgb(wid,hei,3);
rgb.data=new float[wid * hei * 3 ];
//jpg转换为RGB卷积层
bmp2RGB(rgb);
卷积层 rgb备份(wid,hei,3);
rgb备份.data=new float[wid * hei * 3 ];
//备份
卷积层复制(&rgb,&rgb备份);
//---------------------------------------------->
层数据 * 层;
//两个卷积层 交替前传(源,目标)
//用这个传回
卷积层 * di=(卷积层 *)malloc(sizeof(卷积层));
di->width=1;
di->height=1;
di->depth=1;
di->data=new float[1 ];
卷积层 *源,*目标;
源 = &rgb;
目标 = 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);
//----------------------------------------------<
//第二部分 16残差层
卷积层 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);
//通道分割(*源,割2,割1);
Resize卷积层(*源,wid,hei,割2.depth);
卷积层复制(&割2,源);
卷积前传(蒸馏块0->conv1_下,1);
//卷积前传(蒸馏块0->conv2_下,1);4组 组卷积?
_4分组卷积(*源,*目标,蒸馏块0->conv2_下);
std::swap (源,目标);
卷积前传(蒸馏块0->conv3_下,1);
//concat
通道连接(*源备份,割1,加);//16+64=80
//通道连接(割1,*源备份,加);//16+64=80
//数据增强 (Eltwise sum)
卷积层相加(&加,源);
//数据压缩
卷积前传(蒸馏块0->down,1);
蒸馏块0++;//到下蒸馏块
}
cout<<endl;
del卷积层(*源备份);
//load_mat2卷积层("me/up0.txt",源);
//放大4倍
cout<<"放大4倍... "<<endl;
层=sr.up1;/* Conv2 层 */
if(层->输出维度 != 目标->depth || 目标->width != wid || 目标->height != hei)
Resize卷积层(*目标,wid,hei,层->输出维度);
pad=层->核宽/2;
vl_nnconv(源,目标,层 ,1,1,pad,pad,pad,pad);
//vl_nnrelu(目标);
std::swap (源,目标);
层=sr.up2;/* Conv2 层 */
if(层->输出维度 != 目标->depth || 目标->width != wid || 目标->height != hei)
Resize卷积层(*目标,wid,hei,层->输出维度);
pad=层->核宽/2;
vl_nnconv(源,目标,层 ,1,1,pad,pad,pad,pad);
//vl_nnrelu(目标);
std::swap (源,目标);
//load_mat2卷积层("me/PS.txt",源);
//亚像素卷积
wid *= 4;hei *= 4;
Resize卷积层(*目标,wid,hei,3);
像素混组放大(源,目标);
std::swap (源,目标);
//save_卷积层2jpg(源,"up");
//load_mat2卷积层("me/upsample.txt",源);
cout<<"生成宽,高:"<<wid<<","<<hei<<endl;
卷积层 urgb(wid,hei,3);
urgb.data=new float[wid * hei * 3 ];
//1。原图放大4倍--------------------------
ResizeGrayscaleImage(4.0);
//jpg转换为RGB卷积层
bmp2RGB(urgb);
//load_mat2卷积层("me/b.txt",&urgb);
//2。卷积层放大---------------------------
//卷积层双三次插值(rgb备份,urgb);
//输入加上残差
卷积层相加(源,&urgb);
//save_卷积层2jpg(&urgb,"urgb");
RGB2bmp(urgb);
del卷积层(*源);
del卷积层(*目标);
cout<<"图像转换成jpg格式... "<<endl;
savejpg(savefilename);
cout<<"转换文件已经保存为: "<<savefilename<<endl;
}
效果图:
小图
听说亚像素卷积比反卷积效果更好,是否比上一个更好?我也没仔细比过,我想可能需要更少的内存吧。
下载:
win32超分辩重建IDN实用程序(2)
快速超分辨率重建IDN(4倍)的win32程序,由IDN-tensorflow-master中的模型改编而来
https://download.csdn.net/download/juebai123/11130567