纯C++超分辨率重建SRCNN --改编--卷积(前奏一)

从这章开始,准备把哪个超分辨率重建SRCNN (Matlab程序) 改编为 纯C++程序。

主要是每步实现一下能熟悉SRCNN重建运行机理,也方便在其它电脑上使用(不用安装Matlab什么的)。


图像显示部分用一个EasyX 库(EasyX 库采用静态链接方式,不会为您的程序增加任何额外的 DLL 依赖)

只有几个文件(easyx.h ,easyx.lib ,easyxw.lib)还有一个不错的中文帮助文件(EasyX_Help.chm)。

由于不涉及训练,核心只是一个卷积。

流程:

1。在Matlab导出卷积核(即网络权重和偏移)

2。输入图像

3。双三次放大

4。取单色转为单精度矩阵

5。三层卷积

6。转回单色图并显示和保存图像

这里先实现核心卷积:

搜索到一个《c++实现卷积过程》博文,就以这个为模板吧

c++实现卷积过程

#include<iostream>
#include<vector>
using namespace std;

int main()
{
    //定义被卷积的矩阵(其实是一个数组,数组元素的个数8*8)
    int const map = 8;
    float  A[map*map] =
    {
        1, 2, 3, 4, 5, 6, 7, 8,
        1, 2, 3, 4, 5, 6, 7, 8,
        1, 2, 3, 4, 5, 6, 7, 8,
        1, 2, 3, 4, 5, 6, 7, 8,
        1, 2, 3, 4, 5, 6, 7, 8,
        1, 2, 3, 4, 5, 6, 7, 8,
        1, 2, 3, 4, 5, 6, 7, 8,
        1, 2, 3, 4, 5, 6, 7, 8
    };

    //定义卷积核矩阵(其实也是一个数组,数组元素的个数3*3)
    int const kernel = 3;
    float B[kernel*kernel] =
    {
      1, 1, 1,
      1, 1, 1,
      1, 1, 1
    };

    //计算卷积输出矩阵的维数(其实是输出数组元素个数的开根号)
    int const outm = map - kernel + 1;   //被卷积矩阵的维数-卷积核的维数+1  即8-3+1=6

    //计算卷积过程中的被卷积矩阵的宽和高(就是把宽拉成和卷积核的高一样,这样才好对应相乘)
    int const convAw = kernel*kernel;//3*3=9
    int const convAh = map*map;//8*8=64

    float A_convert[convAh*convAw] = { 0 };//定义一个卷积过程中的矩阵(也就是被拉长过后的矩阵)

    for (int i = 0; i < outm; i++)
    {
        for (int j = 0; j < outm; j++)
        {
            int wh = i * outm * convAw + j * convAw; 

            int col1 = i * map + j;
            A_convert[wh] = A[col1];        //第一次循环时把A[0] 的值赋给 A_convert[0]
            A_convert[wh + 1] = A[col1 + 1];//第一次循环时把A[1] 的值赋给 A_convert[1]
            A_convert[wh + 2] = A[col1 + 2];//第一次循环时把A[2] 的值赋给 A_convert[2]

            int col2 = (i + 1) * map + j;
            A_convert[wh + 3] = A[col2];    //第一次循环时把A[8] 的值赋给 A_convert[3]
            A_convert[wh + 4] = A[col2 + 1];//第一次循环时把A[9] 的值赋给 A_convert[4]
            A_convert[wh + 5] = A[col2 + 2];//第一次循环时把A[10] 的值赋给 A_convert[5]

            int col3 = (i + 2) * map + j;
            A_convert[wh + 6] = A[col3];     //第一次循环时把A[16] 的值赋给 A_convert[6]
            A_convert[wh + 7] = A[col3 + 1]; //第一次循环时把A[17] 的值赋给 A_convert[7]
            A_convert[wh + 8] = A[col3 + 2]; //第一次循环时把A[18] 的值赋给 A_convert[8]
        }

    }
    vector<int> C;
    for (int i = 0; i < outm; i++)
    {

        for (int j = 0; j < outm; j++)
        {
            int a = 0;
            int wh = i * outm * convAw + j * convAw;
            for (int m =0; m < convAw; m++)
            {
                a += A_convert[wh + m] * B[m] ;

            }
            C.push_back(a); //在C中添加数据a

        }

    }


    //输出被卷积矩阵
    cout << "被卷积矩阵 :" << endl;
    for (int i = 0; i < map; i++)
    {
        for (int j = 0; j < map; j++)
        {
            cout << A[i*map + j] << " ";
        }
        cout << endl;
    }
    cout << endl;


    //输出卷积核矩阵
    cout << "卷积核矩阵:" << endl;
    for (int i = 0; i < kernel; i++)
    {
        for (int j = 0; j < kernel; j++)
        {
            cout << B[i*kernel + j] << " ";
        }
        cout << endl;
    }
    cout << endl;


    //输出卷积后输出矩阵
    cout << "卷积后输出矩阵:" << endl;
    for (int i = 0; i < outm; i++)
    {
        for (int j = 0; j < outm; j++)
        {
            cout << C[i*outm + j] << " ";
        }
        cout << endl;
    }

    system("pause");
    return 0;
}

抄了这一段只是便于后面对比理解

由于不算复杂,直接看注解吧,主要是easyx相关的

实现:

//c++实现卷积
#define USE_EGE 0 //选择ege (1)或 easyx(0)的开关
#if USE_EGE

	#include <ege.h> //使用ege库 和 easyx 差不多,这里没有实现
	using namespace ege;
#else
	#include <easyx.h>//使用easyx库
	#include<conio.h> 

#endif


#include<iostream>
#include<vector>
using namespace std;

#define SCREEN_WIDTH 1100 //窗口大小
#define SCREEN_HEIGHT 600

#define byte unsigned char

struct 卷积矩阵
{
	
	int		width;    //宽
	int     height;   //高

	//数据
	byte *	data;

	//构造函数
	卷积矩阵(int iwidth,int iheight);
};

IMAGE jpg;//一张原图

//构成一个卷积过程中的矩阵
卷积矩阵::卷积矩阵(int iwidth,int iheight): width(iwidth),
                                            height(iheight)
											
{
	int size=sizeof(byte)*width*iheight;
    data=(byte*)malloc(size);

	memset(data, 0,sizeof(size));
}


void error(char *s)
{ 
printf("%s\n",s);
getch();
exit(1);
} 


void loadjpg(char * jpgname)
{
	loadimage(&jpg,jpgname);//载入图像

}

bool 卷积(byte  *A,int map,float *B,int kernel,vector<int> & C)
{
    //计算卷积输出矩阵的维数(其实是输出数组元素个数的开根号)
    int  outm = map - kernel + 1;   //被卷积矩阵的维数-卷积核的维数+1  即8-3+1=6

    //计算卷积过程中的被卷积矩阵的宽和高(就是把宽拉成和卷积核的高一样,这样才好对应相乘)
    int const convAw = kernel*kernel;//3*3=9
    int  convAh = map*map;//8*8=64

	卷积矩阵 isA_convert(convAh,convAw);
    byte *A_convert=isA_convert.data;//(byte*)malloc(sizeof(byte)*convAh*convAw);//[convAh*convAw] = { 0 };//定义一个卷积过程中的矩阵(也就是被拉长过后的矩阵) //用float 内存不够用


    for (int i = 0; i < outm; i++)
    {
        for (int j = 0; j < outm; j++)
        {
            int wh = i * outm * convAw + j * convAw; 

            int col1 = i * map + j;
            A_convert[wh] = A[col1];        //第一次循环时把A[0] 的值赋给 A_convert[0]
            A_convert[wh + 1] = A[col1 + 1];//第一次循环时把A[1] 的值赋给 A_convert[1]
            A_convert[wh + 2] = A[col1 + 2];//第一次循环时把A[2] 的值赋给 A_convert[2]

            int col2 = (i + 1) * map + j;
            A_convert[wh + 3] = A[col2];    //第一次循环时把A[8] 的值赋给 A_convert[3]
            A_convert[wh + 4] = A[col2 + 1];//第一次循环时把A[9] 的值赋给 A_convert[4]
            A_convert[wh + 5] = A[col2 + 2];//第一次循环时把A[10] 的值赋给 A_convert[5]

            int col3 = (i + 2) * map + j;
            A_convert[wh + 6] = A[col3];     //第一次循环时把A[16] 的值赋给 A_convert[6]
            A_convert[wh + 7] = A[col3 + 1]; //第一次循环时把A[17] 的值赋给 A_convert[7]
            A_convert[wh + 8] = A[col3 + 2]; //第一次循环时把A[18] 的值赋给 A_convert[8]
        }

    }

	//卷积后输出矩阵
    //vector<int> C;
	int imin=0,imax=255;
    for (int i = 0; i < outm; i++)
    {

        for (int j = 0; j < outm; j++)
        {
            int a = 0;
            int wh = i * outm * convAw + j * convAw;
            for (int m =0; m < convAw; m++)
            {
                a += A_convert[wh + m] * B[m] ;

            }
            C.push_back(a); //在C中添加数据a


//-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=归一化  开始
			//找最小值
			if(a<imin) imin=a;

			//找最大值
			if(a>imax)imax=a;

        }

    }
	//注意该滤波器没有归一化(和不是1.0),故滤出来的值可能不在[0,255]之内。通过减去最小值、除以最大/最小值之差、再乘以255并取整,
	//把结果值归一到[0,255]之内,使之成为一幅灰度图像。
#if 1 //'sobel' 需要这个,否则得不到哪个效果
	float s;
    for (int i = 0; i < C.size (); i++)
    {
		s=C[i];
		s=(s-imin)/(imax-imin)*255;
		C[i]=(int)s;
		
	}
#endif
//-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=归一化  结束
	return true;
}

int main()
{
	initgraph(SCREEN_WIDTH, SCREEN_HEIGHT,SHOWCONSOLE);//初始化图形环境


	char jpgname[]="lena.jpg";
	//载入图片
	loadjpg(jpgname);


	SetWorkingImage(&jpg);//设置已经加载图片为要扫描的图片



	int height = jpg.getheight();
	int width = jpg.getwidth();

	if(height!=width)
		error("出错了!这里需要方形的图");

    // 1。定义被卷积的矩阵(其实是一个数组)
	// 这里用一张单色图代替
    int  map = width;
	卷积矩阵 isA(width,width);
	byte  *A=isA.data;//(byte*)malloc(sizeof(byte)*map*map);



	//图像转化单色并保存结果
	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{

			A[i*width+j]= GetRValue(RGBtoGRAY(getpixel(i , j)));//返回指定颜色 范围0-255

		}
	}
	SetWorkingImage();//还原为屏幕

	byte c;
	//显示原图
	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			
			c=A[i*width+j];//返回指定颜色 范围0-255
			putpixel(i, j, RGB(c,c,c));

		}
	}


	getch();
	


    // 2。定义卷积核矩阵(其实也是一个数组,数组元素的个数3*3)
    int const kernel = 3;
    float B[kernel*kernel] =
    {
#if 1
		//'sobel'     索贝尔水平边缘增强滤波器
		//-1, 0, 1,
		//-2, 0, 2,
		//-1, 0, 1

		1.2886529 , 0.04068733 , -1.3082279,
		1.43157125, 0.01173212 , -1.45389295,
		1.34158182, -0.07245208, -1.27504027

#else
		// 'unsharp'   反锐化对比度增强滤波器
		-0.1667,   -0.6667,-0.1667,
		-0.6667,    4.3333,-0.6667,
		-0.1667,   -0.6667,-0.1667
#endif
    };
   

    //计算卷积输出矩阵的维数(其实是输出数组元素个数的开根号)
    int  outm = map - kernel + 1;   //被卷积矩阵的维数-卷积核的维数+1  即8-3+1=6

	//3。卷积后输出矩阵
    vector<int> C;

	//实现卷积,输出矩阵大小为 outm
    卷积(A, map, B, kernel, C);




    //输出卷积核矩阵
    cout << "卷积核矩阵:" << endl;
    for (int i = 0; i < kernel; i++)
    {
        for (int j = 0; j < kernel; j++)
        {
            cout << B[i*kernel + j] << " ";
        }
        cout << endl;
    }
    cout << endl;


    //显示卷积图
    //cout << "卷积后输出矩阵:" << endl;
	
	
    for (int i = 0; i < outm; i++)
    {
        for (int j = 0; j < outm; j++)
        {
            c= C[i*outm + j];

			putpixel(i+width+5, j, RGB(c,c,c));
        }
    }



		getch();

	closegraph();
    system("pause");
    return 0;
}

效果图:


其中加了归一化,相当于网络权重的偏置

注意这里只能是方形图

vs2008+easyx +win8.1

猜你喜欢

转载自blog.csdn.net/juebai123/article/details/80554249