纯C++超分辨率重建LapSRN --改编--(一)数据和框架
程序员文章站
2022-03-09 13:06:31
...
由于网络上找不到多通道多核的转置卷积,那么只能从matconvnet入手了,先用LapSRN 8倍的,它的层数小一点(放大一个2倍差不多8层 3x8=24,实际上是25层,只要8,9层就可以出现一个2倍放大图,其它2倍的20多层,4倍的40多层 )
先说一下流程
1。从matlab中导出LapSRN数据(网络权重(卷积核)和偏置,按用到的顺序导出,所以在我们的程序中不需要排列核位置)
2。从文本文件中导入结构中
3。外框架和 LapSRN 函数(几乎和matlab中一样)
4。然后就是三个函数vl_nnconv(正向卷积),vl_nnconvt(反向),vl_nnrelu(激励)
vl_nnrelu比较简单,其它的围绕vl_nnconv 和vl_nnconvt 展开(也就是对matconvnet中的代码涂涂改改)。
现在开始:
1。导出LapSRN 8倍网络数据到 LapSRN_x8.txt 文件中
%% 参数
scale = 8; % SR 采样比例 可以是 2、4 或 8 倍
%% 加载预训练模型
model_filename = fullfile('pretrained_models', sprintf('LapSRN_x%d.mat', scale));
fprintf('载入 %s\n', model_filename);
net = load(model_filename);
net=net.net;
l=length(net.layers);
%
%
j=0;
%
upsample_num=0;% 图像放大计数
residual_num=0;% 残差图计数
%保存模型数据
%偏移
%---------------------------------------
FID=fopen('LapSRN_x8.txt','w');
%所有层
for i=1:l %42 是上采样层
issave=0;
disp(['第 ',num2str(i),' 层']);
if strTong(net.layers(i).type,'dagnn.Conv')
j=j+1
disp('卷积层');
%得到权重(也就是卷积核)和偏移
disp('参数是:');
disp(net.layers(i).params);
fft=net.layers(i).params{1};
for k=1:length(net.params)
if strTong(net.params(k).name,fft)
weight=net.params(k).value;
break;
end
end
if net.layers(i).block.hasBias==false
bias=[]; % 无偏移
else
fft=net.layers(i).params{2};
for k=1:length(net.params)
if strTong(net.params(k).name,fft)
bias=net.params(k).value;
break;
end
end
end
wname=['卷积层' num2str(j) '\n'];
fprintf(FID,wname );
issave=1;
end
if strTong(net.layers(i).type,'dagnn.ConvTranspose')
disp('上采样层');%反向卷积,转置卷积
disp('输入是:');
disp(net.layers(i).inputs);
%得到权重(也就是卷积核)和偏移
disp('参数是:');
disp(net.layers(i).params);
fft=net.layers(i).params{1};
for k=1:length(net.params)
if strTong(net.params(k).name,fft)
weight=net.params(k).value;
break;
end
end
if net.layers(i).block.hasBias==false
bias=[]; % 无偏移
else
fft=net.layers(i).params{2};
for k=1:length(net.params)
if strTong(net.params(k).name,fft)
bias=net.params(k).value;
break;
end
end
end
disp('输出是:');
disp(net.layers(i).outputs);
upsample=net.layers(i).block.upsample;
crop=net.layers(i).block.crop;
numGroups=net.layers(i).block.numGroups;
opts=net.layers(i).block.opts{:}
% 字符串是否包含
if strBao(net.layers(i).outputs{:}, 'img_up') %残差
wname=['图像放大层\n'];
fprintf(FID,wname );
else
wname=['残差放大层\n'];
fprintf(FID,wname );
end
issave=1;
end
if issave==1;
[h,w,c,p]=size(weight);
l0=h*w*c*p;
fprintf(FID,'长度 %d\n',l0 );%有多少个
for i0=1:c
for j0=1:p
for t=1:h
for s=1:w
fprintf(FID, '%f ',weight(s,t,i0,j0));
end
fprintf(FID, '\n');
end
end
end
if ~isempty(bias)
bname=['有偏移\n'];
fprintf(FID,bname );
l0=length(bias);
fprintf(FID,'长度 %d\n',l0 );%有多少个
for i0=1:l0
fprintf(FID, '%f ',bias(i0));
end
fprintf(FID, '\n');
else
bname=['无偏移\n'];
fprintf(FID,bname );
end
end
end
fclose(FID);
%---------------------------------------
模型数据结构:
#define 总层数 25
struct 层数据
{
char 类型[255];
int 权重长度;
float * 权重_数据;
int 偏移长度;
float * 偏移_数据;
};
struct LapSRN_x8模型
{
int 层数; //25
层数据 * 所有层;
//构造函数
LapSRN_x8模型();
};
LapSRN_x8模型::LapSRN_x8模型()
{
层数=总层数; //
int size;
size = sizeof(层数据)*层数;//
所有层=(层数据 *)malloc(size);
}
载入模型数据:
bool loadModel(LapSRN_x8模型 *sr)
{
char name[]="LapSRN_x8.txt";
std::ifstream fin(name);
//检查文件是否存在
if (!fin)
{
return false;
}
cout<<"正在载入‘LapSRN_x8.txt’的数据"<<endl;
//从档案载入
char str[40];
int len;
层数据 * 层=sr->所有层;
for (int k = 0;k<总层数;k++)
{
//cout<<k<<"层"<<endl<<endl;
fin >> 层->类型;
//cout<<层->类型<<endl;
fin >> str;
//cout<<str<<endl;
fin >> len;//需要载入的个数
//cout<<len<<endl;
层->权重长度=len;
层->权重_数据=(float*)malloc(sizeof(float) * 层->权重长度);
float * w=层->权重_数据;
//float tmp;
for(int i=0;i<len;i++)
{
fin >> *w++;
}
fin >> str; //有无偏移
//cout<<str<<endl;
if(strcmp(str, "有偏移")==0)
{
fin >> str;//长度
//cout<<str<<endl;
fin >> len;//需要载入的个数
//cout<<len<<endl;
层->偏移长度=len;
层->偏移_数据=(float*)malloc(sizeof(float) * 层->偏移长度);
float * b=层->偏移_数据;
for(int i=0;i<len;i++)
{
fin >> *b++;
}
}else
{
层->偏移长度=0;
层->偏移_数据=NULL;
}
层++;//到下一层
}//end
fin.close ();
return true;
}
卷积层:
class 卷积层
{
public:
int width; //宽
int height; //高
int depth; //通道 深度
float * data;
//构造函数
卷积层(int iwidth,int iheight);
卷积层(int iwidth,int iheight,int idepth);
卷积层(int iwidth,int iheight,int c,float * data);
~卷积层();
};
卷积层::卷积层(int iwidth,int iheight): width(iwidth),
height(iheight)
{
depth=1;
data=NULL;
}
卷积层::卷积层(int iwidth,int iheight,int idepth): width(iwidth),
height(iheight),depth(idepth)
{
data=NULL;
}
卷积层::卷积层(int iwidth,int iheight,int idepth,float * fdata): width(iwidth),
height(iheight),depth(idepth), data(fdata)
{
}
卷积层::~卷积层()
{
}
内存分配在外部
主函数main ,这里不用图形界面:
int main(int argc, char *argv[])
{
char jpgname[256];
if( argc == 2 )
strcpy(jpgname, argv[1]);
else
strcpy(jpgname, "测试1.jpg");//lena.jpg //6c7a
//载入图片
loadjpg(jpgname);
// 放大倍率 2,4 或 8 倍
int up_scale = 8;
clock_t start_t, end_t;//计算时间
double total_t;
start_t = clock();
// LapSRN 高分辨率重建
LapSRN(up_scale);
end_t = clock();
total_t = (double)(end_t - start_t) / CLOCKS_PER_SEC;
cout<<"已经完成"<<endl;
cout<<"用时:"<<total_t<<" 秒"<<endl;
system("pause");
return 0;
}
核心流程函数LapSRN:
void LapSRN(int up_scale)
{
LapSRN_x8模型 sr;
// 加载 CNN 模型参数
loadModel(&sr);
//cout<<"loadModel成功"<<endl;
int wid=jpg.getwidth();
int hei=jpg.getheight();
cout<<"输入图像宽度:"<<wid<<endl;
cout<<" 高度:"<<hei<<endl;
卷积层 im_b(wid,hei);//即Y通道
im_b.data=new float[wid * hei * sizeof(float)];
卷积层 U(wid,hei),V(wid,hei);
U.data=new float[wid * hei * sizeof(float)];
V.data=new float[wid * hei * sizeof(float)];
//RGB转换为YUV
RGB2YUV(&jpg,&im_b,&U,&V);
//U,V暂时无用,先删除占用内存
delete []U.data; U.data=NULL;
delete []V.data; V.data=NULL;
//loadImlow(&im_b);//载入matlab导出的数据用于比较
//save_mat ("im_bis.txt",im_b.data,im_b.width,im_b.height,im_b.depth); //保存
//总层数
int l=总层数;//
cout<<"共有:"<<l<<"层"<<endl;
//中间过程
卷积层 convfea1(wid,hei,64);
convfea1.data=new float[wid * hei * 64 * sizeof(float)];
卷积层 convfea2(wid,hei,64);
convfea2.data=new float[wid * hei * 64 * sizeof(float)];
卷积层 *源=&im_b, *目标=&convfea1;
// 残差图--------------------------------------------------------
// 2倍残差图
卷积层 residual1(wid*2,hei*2);
residual1.data=new float[wid * hei * 4 * sizeof(float)];
卷积层 hR1(wid*2,hei*2);
hR1.data=new float[wid * hei * 4 * sizeof(float)];
// 4倍残差图
卷积层 residual2(wid*4,hei*4);
residual2.data=new float[wid * hei * 16 * sizeof(float)];
卷积层 hR2(wid*4,hei*4);
hR2.data=new float[wid * hei * 16 * sizeof(float)];
// 8倍残差图
卷积层 residual3(wid*8,hei*8);
residual3.data=new float[wid * hei * 64 * sizeof(float)];
卷积层 hR3(wid*8,hei*8);
hR3.data=new float[wid * hei * 64 * sizeof(float)];
if(residual3.data==NULL)
{
printf("图片太大!只能运行4倍放大\n");
up_scale=4;
}
if(residual2.data==NULL)
{
printf("图片太大!只能运行2倍放大\n");
up_scale=2;
}
int upsample_num=0;// 图像放大计数
int residual_num=0;// 残差图计数
float *weight, *bias;
//save_矩阵 ("im_b.txt",im_b.data,im_b.width,im_b.height,im_b.depth); //保存
层数据 * 层=sr.所有层;
//循环各层直至结束
for (int i=0;i<l;i++ )//8 25层
{
cout<<"第 "<<i<<" 层"<<endl;
if (strstr(层->类型, "卷积层"))//strTong(net.layers(i).type,'dagnn.Conv')
{
//返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。
//j=j+1
cout<<"卷积..."<<endl;
//得到权重(也就是卷积核)和偏移
weight=层->权重_数据;
bias=层->偏移_数据;
if (i==0) //strTong(net.layers(i).inputs{1},'LR') //第一层
{
//convfea = vl_nnconv(im_b,weight,bias,'Pad',1);//一图得出64特征图
vl_nnconv(&im_b,目标,层,1 ,1 , 1 ,1 ,1 ,1 );
vl_nnrelu(目标, 0.2);//'leak',
//save_卷积层2jpg(目标,"con0");
源=目标;
目标=&convfea2;
}
else //一以后
{
if (层->权重长度==576) //strBao(net.layers(i).outputs{:}, 'residual')
{
cout<<"合成残差..."<<endl;
//residual_num=residual_num+1;
卷积层 *tmp=目标;//暂存
if (residual_num==1)
//residual1= vl_nnconv(convfea,weight,bias,'Pad',1);// 2倍残差图
目标= & residual1;
//end
if (residual_num==2)
//residual2= vl_nnconv(convfea,weight,bias,'Pad',1);// 4倍残差图
目标= &residual2;
if (residual_num==3)
//residual3= vl_nnconv(convfea,weight,bias,'Pad',1);// 8倍残差图
目标= &residual3;
vl_nnconv(源,目标,层,1 ,1 ,1 ,1 ,1 ,1 );
vl_nnrelu(目标, 0.2);//'leak',
//char txt[255];
//sprintf(txt, "第%d残差", residual_num);
//save_卷积层2jpg(目标,txt);
//sprintf(txt, "第%d残差前", residual_num);
//save_卷积层2jpg(源,txt);
目标=tmp;//还原
}
else
{
//中间层 64特征图合成1图 再重新生成深一层64特征图
//convfea = vl_nnconv(convfea,weight,bias,'Pad',1);
vl_nnconv(源,目标,层,1 ,1 , 1 ,1 ,1 ,1 );
vl_nnrelu(目标, 0.2);//'leak',
//char txt[255];
//sprintf(txt, "con%d", i);
//save_卷积层2jpg(目标,txt);
std::swap(源,目标);
}
//end
}//end
}//end
if (strstr(层->类型, "残差放大层"))//strTong(net.layers(i).type,'dagnn.Conv')
{
cout<<"残差放大..."<<endl;
residual_num++;
weight=层->权重_数据;
bias=层->偏移_数据;
// 残差放大参数是 'upsample', 2 ,'crop', [0 1 0 1],'numGroups', 1
//convfea = vl_nnconvt(convfea, weight, bias,'upsample', upsample ,'crop', crop,'numGroups', 1) ;//
//扩大第一卷积层内存
delete []目标->data; 目标->data=NULL;
if (residual_num==1)
{
目标->width=wid*2;目标->height=hei * 2;目标->depth=64;
目标->data=new float[wid * hei * 4 * 64 * sizeof(float)];
}
if (residual_num==2)
{
目标->width=wid*4;目标->height=hei * 4;目标->depth=64;
目标->data=new float[wid * hei * 16 * 64 * sizeof(float)];
}
if (residual_num==3)
{
目标->width=wid*8;目标->height=hei * 8;目标->depth=64;
目标->data=new float[wid * hei * 64 * 64 * sizeof(float)];
}
vl_nnconvt(源,目标,层,2,2, 0 ,1,0,1) ;//残差放大
vl_nnrelu(目标, 0.2);//'leak',
//char txt[255];
//sprintf(txt, "残差放大%d", i);
//save_卷积层2jpg(目标,txt);
std::swap(源,目标);
//扩大第二卷积层内存
delete []目标->data; 目标->data=NULL;
if (residual_num==1)
{
目标->width=wid*2;目标->height=hei * 2;目标->depth=64;
目标->data=new float[wid * hei * 4 * 64 * sizeof(float)];
}
if (residual_num==2)
{
目标->width=wid*4;目标->height=hei * 4;目标->depth=64;
目标->data=new float[wid * hei * 16 * 64 * sizeof(float)];
}
if (residual_num==3)
{
目标->width=wid*8;目标->height=hei * 8;目标->depth=64;
目标->data=new float[wid * hei * 64 * 64 * sizeof(float)];
}
}
if (strstr(层->类型, "图像放大层"))//strTong(net.layers(i).type,'dagnn.Conv')
{
cout<<"图像放大..."<<endl;
//到这里可以删除
delete []目标->data; 目标->data=NULL;
delete []源 ->data; 源 ->data=NULL;
weight=层->权重_数据;
bias=层->偏移_数据;//无偏移
upsample_num=upsample_num+1;
// 图像放大参数是 'upsample', 2 ,'crop', [1 1 1 1],'numGroups', 1
if (upsample_num==1) // 2倍放大图
{
//img_up1 = vl_nnconvt(imlow, weight, bias,'upsample', upsample ,'crop', crop,'numGroups', 1) ;
vl_nnconvt(&im_b,&hR1,层,2,2, 1 ,1,1,1) ;//残差放大
//save_卷积层2jpg(&im_b,"2倍放大前");
//save_卷积层2jpg(&hR1,"2倍放大");
卷积层相加(&residual1,&hR1);
//hR1=img_up1+residual1; // 合成2倍重建图
//save_卷积层2jpg(&hR1,"2倍重建图");
delete []im_b.data; im_b.data=NULL;
delete []residual1.data; residual1.data=NULL;
//色彩放大(2倍)
ResizeGrayscaleImage(&jpg,2) ;
//卷积层 U(wid,hei),V(wid,hei);
U.height=V.height=hei*2;
U.width =V.width =wid*2;
U.data=new float[wid * hei *2*2* sizeof(float)];
V.data=new float[wid * hei *2*2* sizeof(float)];
//RGB转换为YUV
RGB2YUV(&jpg,&U,&V);
//合成色彩重建图
IMAGE im_h(U.width,U.height);
YUV2RGB(&hR1,&U,&V, &im_h);
//保存图像
saveimage(L"LapSRN 2倍 重建.jpg", &im_h);
//U,V暂时无用,先删除占用内存
delete []U.data; U.data=NULL;
}
else
if (upsample_num==2) // 4倍放大图
{
//img_up2 = vl_nnconvt(hR1, weight, bias,'upsample', upsample ,'crop', crop,'numGroups', 1) ;//
vl_nnconvt(&hR1,&hR2,层,2,2, 1 ,1,1,1) ;//残差放大
//save_卷积层2jpg(&hR2,"4倍放大");
卷积层相加(&residual2,&hR2);
//hR2=img_up2+residual2; // 4倍
//save_卷积层2jpg(&hR2,"4倍重建图");
delete []hR1.data; hR1.data=NULL;
delete []residual2.data; residual2.data=NULL;
//色彩放大(2倍)
ResizeGrayscaleImage(&jpg,2) ;//再2倍就是4倍
//卷积层 U(wid,hei),V(wid,hei);
U.height=V.height=hei*4;
U.width =V.width =wid*4;
U.data=new float[wid * hei *16* sizeof(float)];
V.data=new float[wid * hei *16* sizeof(float)];
//RGB转换为YUV
RGB2YUV(&jpg,&U,&V);
//合成色彩重建图
IMAGE im_h(U.width,U.height);
YUV2RGB(&hR2,&U,&V, &im_h);
//保存图像
saveimage(L"LapSRN 4倍 重建.jpg", &im_h);
//U,V暂时无用,先删除占用内存
delete []U.data; U.data=NULL;
}
else
if (upsample_num==3 )// 8倍放大图
{
//img_up3 = vl_nnconvt(hR2, weight, bias,'upsample', upsample ,'crop', crop,'numGroups', 1) ;//
vl_nnconvt(&hR2,&hR3,层,2,2, 1 ,1,1,1) ;//残差放大
//save_卷积层2jpg(&hR3,"8倍放大");
卷积层相加(&residual3,&hR3);
//hR3=img_up3+residual3; // 8倍
//save_卷积层2jpg(&hR3,"8倍重建图");
delete []hR2.data; hR2.data=NULL;
delete []residual3.data; residual3.data=NULL;
//色彩放大(2倍)
ResizeGrayscaleImage(&jpg,2) ;//再2倍就是8倍
//卷积层 U(wid,hei),V(wid,hei);
U.height=V.height=hei*8;
U.width =V.width =wid*8;
U.data=new float[wid * hei *64* sizeof(float)];
V.data=new float[wid * hei *64* sizeof(float)];
//RGB转换为YUV
RGB2YUV(&jpg,&U,&V);
//合成色彩重建图
IMAGE im_h(U.width,U.height);
YUV2RGB(&hR3,&U,&V, &im_h);
//保存图像
saveimage(L"LapSRN 8倍 重建.jpg", &im_h);
//U,V暂时无用,先删除占用内存
delete []U.data; U.data=NULL;
}//end
//end
}
// disp('激励层');
// convfea = vl_nnrelu(convfea, 0.2);//'leak',
层++;
}//end
}
这个可以参看前面的matlab的代码。
外框架完成。