MTCNN(六)c代码网络结构的更改

背景:将MTCNN部署在FPGA上需要将其代码设计为C代码,c代码的网络结构需要与python代码保持一致。

目的:将MTCNN的c代码网络结构转为与python代码一致。

目录

一、相关代码与含义

1.1 相关知识

类对象

this指针

1.2 与网络结构相关的数据

1.3 相关的函数及含义

在network.cpp之中,

具体系列的函数

二、与网络结构定义相关的语句

2.1 mtcnn.h

mtcnn.cpp

2.2 Pnet::Pnet

2.3 Pnet::~Pnet

2.4 Pnet::run

2.5 Rnet::,Onet::系列相关

2.6 mtcnn.cpp中与网络结构无关的函数

三、Pnet结构的更改

3.1 python代码前后结构

3.2 c代码中变量及含义

3.3 相关函数参数

init系列

运算系列

3.4 参数更改顺序

四、卷积运算相关代码

4.1 pad运算

4.2 feature转为矩阵形式

4.3 convolution

五、结构更改顺序

5.1 mtcnn.h中的定义

5.2 mtcnn.cpp


一、相关代码与含义

1.1 相关知识

类对象

https://blog.csdn.net/qq_32583189/article/details/52412369

http://www.baike.com/wiki/%E7%B1%BB%E5%92%8C%E5%AF%B9%E8%B1%A1

实例化一个对象就是通过new运算符为对象分配空间(类属于复合数据类型,在声明对象时,系统并没有为对象分配空间,用户需要应用new完成分配空间的任务)。既可以在声明对象时实例化(创建)对象,也可以先声明对象,然后再创建。

this指针

https://baike.baidu.com/item/C++this%E6%8C%87%E9%92%88/637012

this作用域是在类内部,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。

1.2 与网络结构相关的数据

pBox.h程序之中,均为struct结构体

  • pBox 理解为三维的picture box,包含了数据指针,宽,高,channel
  • pRelu 相关prelu的斜率参数,包含了数据指针,宽度
  • Weight 权重参数,包含了两个数据指针:1指向卷积核的数据,2与卷积核的偏置,其他参数为lastChannel上一层的featureMap数量,selfChannel本层输出的featureMap数量,卷积核大小,步长,pad信息
  • Bbox,bounding box, 包含了得分score,四个顶点的坐标,area,exist,关键点的坐标ppoint[2*NumPoint],和regrecoord
  • orderScore,包含了指向结构的指针,score以及初始的顺序。

1.3 相关的函数及含义

在network.cpp之中,

  • 所有的init函数都是malloc相应的空间,然后数据指针指向的部分全部置0.
  • 所有的__toMatirx函数都是将相应的数据转为矩阵形式。
  • 具体名字的函数是具体的前馈运算

具体系列的函数

  • initConvAndFc函数,初始化卷积层与Fc层,把相应的权重参数传给Weight结构体,然后返回值该层权重的个数,指针指向的权重值先置为0。

Pnet::run

image2Matrix(image, this->rgb);
feature2Matrix(this->rgb, this->conv1_matrix, this->conv1_wb);
convolution(this->conv1_wb, this->rgb, this->conv1, this->conv1_matrix);
  • image2Matrix,将Mat格式读入的图像转为pBox格式的图像
  • featurePad, 将pBox格式的图像进行pad
  • feature2Matrix,将featureMap转为矩阵形式用于后面的矩阵乘
  • convolution,第一个参数为权重(权重自读取进来之后就始终没有变化,矩阵形式的权重与三维权重大小相同,而featureMap的大小在转换时就需要发生相应的变化),第二个参数的目的仅仅是传递网络结构给卷积函数。最后一个参数为矩阵形式的featureMap。

二、与网络结构定义相关的语句

2.1 mtcnn.h

定义了Pnet,Rnet,Onet的class,class中定义了网络的结构

mtcnn.cpp

2.2 Pnet::Pnet

定义Pnet::Pnet函数,在函数中实例化Pnet的class,将其中指针指向具体的struct 的pBox,weight

初始化conv与fc层

根据层参数初始化权重指针组,读取权重

2.3 Pnet::~Pnet

释放Pnet之前的指针指向的内存

2.4 Pnet::run

Init系函数,转化为matrix系列函数,卷积与池化init系列函数

然后是网络具体的前馈计算

2.5 Rnet::,Onet::系列相关

与上面这些一致

2.6 mtcnn.cpp中与网络结构无关的函数

Pnet::generateBbox,根据相应score生成备选框,无需更改

mtcnn::detectObject应该不涉及关于网络结构的更改相关的问题。

三、Pnet结构的更改

3.1 python代码前后结构

Pnet原始结构

Feature size

name

Kernel size

Stride

Padding

12*12*3

conv1

PReLU1

3*3*10

1

Valid

10*10*10

pool1

Maxpool 2*2

2

Same

5*5*10

conv2

PReLU2

3*3*16

1

Valid

3*3*16

conv3

PReLU3

3*3*32

1

Valid

1*1*32

 

 

 

 

Pnet  最终结构,只有3×3的卷积(为保证输出的得分图与输入的映射,需要same与valid)

Feature size

name

Kernel size

Stride

Padding

12*12*3

conv1

PReLU1

3*3*10

1

Valid

10*10*10

pool1_conv1

pool1_PReLU1

3*3*16

2

Same

5*5*16

conv2

PReLU2

3*3*32

2

Valid

3*3*32

conv3

PReLU3

3*3*32

1

Valid

1*1*32

 

 

 

 

3.2 c代码中变量及含义

原始结构:mtcnn.h与mtcnn.cpp

layer name input and output weight

conv1

PReLU1

rgb:矩阵格式的rgb图像

conv1_matrixI_in

conv1

conv1_wb

prelu_gmma1

pool1

maxPooling1

 

conv2

PReLU2

maxPooling_matrix

conv2

conv2_wb

prelu_gmma2

conv3

PReLU3

conv3_matrix

conv3

conv3_wb

prelu_gmma3

post conv

layers

   

3.3 相关函数参数

init系列

image2MatrixInit(输入mat格式图片,输出rgb格式图片)

feature2MatrixInit(该层输入,该层需转化为的matrix格式的输入,该层权重)

convolutionInit(该层权重,该层输入,该层输出,该层matrix格式的输入)

maxPoolingInit(该层输入,该层输出,kernelSize,Stride)

运算系列

feature2matrix(该层输入,该层需转化的matrix格式的输入,该层权重)

convolution(该层权重,该层输入,该层输出,该层marix格式输入)

prelu(该层输入输出,上层卷积的偏置,权重斜率)

maxpooling(该层输入,该层输出,kernelSize,Stride)

3.4 参数更改顺序

mtcnn.h中pnet中的private成员定义

mtcnn.cpp中

Pnet函数中this指针指向的量

dataNumber中数值的值

pointTeam中数值指针值

readData的读入的值

~Pnet中的free的指针值

init系列开辟内存空间的值

run内运行相应的运算的值

四、卷积运算相关代码

原版的代码之中只有形式为valid的卷积,我们需要加入padding以及stride的卷积。代码之中关于卷积以及stride和padding的代码在network.cpp之中,我们需要搞懂此代码。

4.1 pad运算

原版的pad是左右两边都加相同的pad,并且嵌套在convolution之中,顺序并不对。

原始的pad代码是两边都pad相同的尺寸,我们需要更改尺寸,我们加入leftPad与rightPad。(此处留有隐患就是左右顺序是否是正确的,但我们可以大致看出,feature的排列顺序是先行后列后channel)

//network.cpp  in  featurePad
for (int row = 0; row < outpBox->channel*outpBox->height;row++){
	if ((row%outpBox->height) height >(outpBox->height-rightPad-1))){
		p += outpBox->width;
		continue;
	}
	p += leftPad;
	memcpy(p, pIn, pbox->width*sizeof(mydataFmt));
	p += pbox->width + rightPad;
	pIn += pbox->width;
}

据此可以大致看出循环是for channel,for row,for col

4.2 feature转为矩阵形式

卷积的矩阵乘法运用的是二维矩阵乘,所以需要将feature转换为二维形式与权重对应。以下为图像转为矩阵乘的参考。

//image2Matrix  network.cpp
for (int rowI = 0; rowI < image.rows; rowI++){
	for (int colK = 0; colK < image.cols; colK++){
		*p = (image.at(rowI, colK)[0] - 127.5)*0.0078125;//opencvµÄͨµÀÅÅÐòÊÇRGB
		*(p + image.rows*image.cols) = (image.at(rowI, colK)[1] - 127.5)*0.0078125;
		*(p + 2*image.rows*image.cols) = (image.at(rowI, colK)[2] - 127.5)*0.0078125;
		p++;
	}
}

读取图像时的形式,将二维图像读取为线性的存储。

//network.cpp  feature2matrix
for (int row = 0; row< h_out; row ++){
	for (int col = 0; col < w_out; col++){
		pIn = pboxIn->pdata + row*stride*pboxIn->width + col*stride;

		for (int channel = 0; channel < pboxIn->channel; channel++){
			ptemp = pIn + channel*pboxIn->height*pboxIn->width;
			for (int kernelRow = 0; kernelRow < kernelSize; kernelRow++){
				memcpy(p, ptemp, kernelSize*sizeof(mydataFmt));//from ptemp to p
				p += kernelSize;
				ptemp += pboxIn->width;
			}
		}
	}
}

将feature转为矩阵形式,

4.3 convolution

// -------------convulution in 2D matrix format-------------------
// input kernel matrix  *  input feature matrix(Trans) = output feature matrix
// height (outChannels)    height (3D_KernelSize)        height (outChannels)
// width  (3D_KernelSize)  width  (outFeatureSize)       width  (outFeatureSize)
	//C=αAB + βC :   outpBox=weightIn*matrixIn(T)      
	//          RowMajor(c code)   A no trans    B trans
	cblas_sgemm(CblasRowMajor,     CblasNoTrans, CblasTrans, \
	//A row C row         B col C col      A col B row     alpha
	weightIn->out_ChannelNum,matrixIn->height,matrixIn->width, 1,\
	//A*            A'col           B*              B'col          beta        C*             C'col
	weightIn->pdata,matrixIn->width,matrixIn->pdata,matrixIn->width,0,outpBox->pdata,matrixIn->height);

cblas_segmm的参数

https://blog.csdn.net/u012235274/article/details/52769682

cblas_sgemm(order, transA, transB, M, N, K, ALPHA, A, LDA, B, LDB, BETA, C, LDA);

第一个参数的函数是存储的有限性,有行优先和列优先(c语言是行优先)
第二个参数和第三个参数是是否转置
A矩阵经过transA之后的维度是M×K
B矩阵经过transB之后的维度是K×N
C矩阵的维度是M×N
LDA和LDB是对应矩阵还没变换之前,在主维度方向的维度。(如果是行优先就是列数)。

五、结构更改顺序

5.1 mtcnn.h中的定义

5.2 mtcnn.cpp

  • 中的变量创建,pnet
  • 网络结构init函数
  • 读取的指针
  • free,run函数等

 

 

 

 

你可能感兴趣的:(机器学习,c/c++,目标检测,MTCNN)