Skip to content

Commit dd5abd1

Browse files
committed
added conv3d, pad3d, crop3d, upsample3d
1 parent 625f189 commit dd5abd1

File tree

6 files changed

+555
-6
lines changed

6 files changed

+555
-6
lines changed

README.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ It is designed to be as simple as possible for real time applications.
1414
Supported Layers
1515
****************
1616
- **Core Layers**: Dense, Activation, Dropout, Flatten, Input, Reshape, Permute, RepeatVector, ActivityRegularization, SpatialDropout1D, SpatialDropout2D, SpatialDropout3D
17-
- **Convolution Layers**: Conv1D, Conv2D, Cropping1D, Cropping2D, UpSampling1D, UpSampling2D, ZeroPadding1D, ZeroPadding2D
17+
- **Convolution Layers**: Conv1D, Conv2D, Conv3D, Cropping1D, Cropping2D, Cropping3D, UpSampling1D, UpSampling2D, UpSampling3D, ZeroPadding1D, ZeroPadding2D, ZeroPadding3D
1818
- **Pooling Layers**: MaxPooling1D, MaxPooling2D, AveragePooling1D, AveragePooling2D, GlobalMaxPooling1D, GlobalAveragePooling1D, GlobalMaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling3D,GlobalAveragePooling3D
1919
- **Recurrent Layers**: SimpleRNN, GRU, LSTM, SimpleRNNCell, GRUCell, LSTMCell
2020
- **Embedding Layers**: Embedding
@@ -26,7 +26,7 @@ Supported Layers
2626
ToDo
2727
****
2828
- **Core Layers**: Lambda, Masking
29-
- **Convolution Layers**: SeparableConv1D, SeparableConv2D, DepthwiseConv2D, Conv2DTranspose, Conv3D, Conv3DTranspose, Cropping3D, UpSampling3D, ZeroPadding3D
29+
- **Convolution Layers**: SeparableConv1D, SeparableConv2D, DepthwiseConv2D, Conv2DTranspose, Conv3DTranspose
3030
- **Pooling Layers**: MaxPooling3D, AveragePooling3D
3131
- **Locally Connected Layers**: LocallyConnected1D, LocallyConnected2D
3232
- **Recurrent Layers**: ConvLSTM2D, ConvLSTM2DCell

include/k2c_convolution_layers.h

Lines changed: 129 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,53 @@ void k2c_pad2d(k2c_tensor* output, k2c_tensor* input, float fill, size_t pad[])
5151
output->array[i] = fill;}
5252
}
5353
// memcpy the old array in the middle
54-
size_t offset = in_channels*(pad_left+pad_right+in_width)*pad_top + in_channels*pad_left;
54+
size_t offset = in_channels*(pad_left+pad_right+in_width)*pad_top +
55+
in_channels*pad_left;
5556
size_t num = in_channels*in_width;
5657
for (size_t i=0; i<in_height; i++) {
57-
memcpy(&output->array[offset],&input->array[i*num],num*sizeof(input->array[0]));
58+
memcpy(&output->array[offset],
59+
&input->array[i*num],
60+
num*sizeof(input->array[0]));
5861
offset += num+in_channels*(pad_left+pad_right);
5962
}
6063
}
6164

65+
void k2c_pad3d(k2c_tensor* output, k2c_tensor* input, float fill, size_t pad[]) {
66+
67+
size_t dim1 = input->shape[0];
68+
size_t dim2 = input->shape[1];
69+
size_t dim3 = input->shape[2];
70+
size_t outdim1 = dim1 + pad[0] + pad[1];
71+
size_t outdim2 = dim2 + pad[2] + pad[3];
72+
size_t outdim3 = dim3 + pad[4] + pad[5];
73+
size_t in_channels = input->shape[3];
74+
75+
// set output array to fill value
76+
if (fabs(fill) < 1e-6) {
77+
// fill is ~zero, use memset
78+
memset(output->array,0,output->numel*sizeof(output->array[0]));
79+
}
80+
else {
81+
for(size_t i=0; i<output->numel; i++) {
82+
output->array[i] = fill;}
83+
}
84+
// memcpy the old array in the middle
85+
size_t offset1 = in_channels*(outdim2*outdim3)*pad[0] + in_channels*outdim3*pad[2] + in_channels*pad[4];
86+
size_t num = in_channels*dim3;
87+
size_t outstep2 = num+in_channels*(pad[4]+pad[5]);
88+
size_t outstep1 = outdim2*outdim3*in_channels;
89+
size_t instep1 = dim2*dim3*in_channels;
90+
size_t instep2 = dim3*in_channels;
91+
92+
for (size_t i=0; i<dim1; i++) {
93+
for (size_t j=0; j<dim2; j++) {
94+
memcpy(&output->array[offset1+i*outstep1 + j*outstep2],
95+
&input->array[i*instep1+j*instep2],
96+
num*sizeof(input->array[0]));
97+
}
98+
}
99+
}
100+
62101
void k2c_conv1d(k2c_tensor* output, k2c_tensor* input, k2c_tensor* kernel,
63102
k2c_tensor* bias, size_t stride, size_t dilation,
64103
void (*activation) (float[], size_t)) {
@@ -108,7 +147,8 @@ void k2c_conv2d(k2c_tensor* output, k2c_tensor* input, k2c_tensor* kernel,
108147
for (size_t q=0; q < in_channels; q++) {
109148
size_t outsub[K2C_MAX_NDIM] = {x0,x1,k};
110149
size_t inpsub[K2C_MAX_NDIM] = {x0*stride[0] + dilation[0]*z0,
111-
x1*stride[1] + dilation[1]*z1,q};
150+
x1*stride[1] + dilation[1]*z1,
151+
q};
112152
size_t kersub[K2C_MAX_NDIM] = {z0,z1,q,k};
113153
output->array[k2c_sub2idx(outsub,output->shape,output->ndim)] +=
114154
kernel->array[k2c_sub2idx(kersub,kernel->shape,kernel->ndim)]*
@@ -123,6 +163,47 @@ void k2c_conv2d(k2c_tensor* output, k2c_tensor* input, k2c_tensor* kernel,
123163
activation(output->array,output->numel);
124164
}
125165

166+
void k2c_conv3d(k2c_tensor* output, k2c_tensor* input, k2c_tensor* kernel,
167+
k2c_tensor* bias, size_t stride[], size_t dilation[],
168+
void (*activation) (float[], size_t)) {
169+
/* 3D (spatial) convolution. Assumes a "channels last" structure
170+
*/
171+
memset(output->array,0,output->numel*sizeof(output->array[0]));
172+
size_t dim1 = output->shape[0];
173+
size_t dim2 = output->shape[1];
174+
size_t dim3 = output->shape[2];
175+
size_t out_channels = output->shape[3];
176+
size_t in_channels = input->shape[3];
177+
178+
for (size_t x0=0; x0 < dim1; x0++){
179+
for (size_t x1=0; x1 < dim2; x1++) {
180+
for (size_t x2=0; x2<dim3; x2++) {
181+
for (size_t k=0; k < out_channels; k++) {
182+
for (size_t z0=0; z0 < kernel->shape[0]; z0++) {
183+
for (size_t z1=0; z1 < kernel->shape[1]; z1++) {
184+
for (size_t z2=0; z2 < kernel->shape[2]; z2++) {
185+
for (size_t q=0; q < in_channels; q++) {
186+
size_t outsub[K2C_MAX_NDIM] = {x0,x1,x2,k};
187+
size_t inpsub[K2C_MAX_NDIM] = {x0*stride[0] + dilation[0]*z0,
188+
x1*stride[1] + dilation[1]*z1,
189+
x2*stride[2] + dilation[2]*z2,
190+
q};
191+
size_t kersub[K2C_MAX_NDIM] = {z0,z1,z2,q,k};
192+
output->array[k2c_sub2idx(outsub,output->shape,output->ndim)] +=
193+
kernel->array[k2c_sub2idx(kersub,kernel->shape,kernel->ndim)]*
194+
input->array[k2c_sub2idx(inpsub,input->shape,input->ndim)];
195+
}
196+
}
197+
}
198+
}
199+
}
200+
}
201+
}
202+
}
203+
k2c_bias_add(output,bias);
204+
activation(output->array,output->numel);
205+
}
206+
126207
void k2c_crop1d(k2c_tensor* output, k2c_tensor* input, size_t crop[]) {
127208

128209
size_t offset = crop[0]*input->shape[1];
@@ -147,6 +228,33 @@ void k2c_crop2d(k2c_tensor* output, k2c_tensor* input, size_t crop[]) {
147228
}
148229
}
149230

231+
void k2c_crop3d(k2c_tensor* output, k2c_tensor* input, size_t crop[]) {
232+
233+
size_t dim1 = input->shape[0];
234+
size_t dim2 = input->shape[1];
235+
size_t dim3 = input->shape[2];
236+
size_t outdim1 = dim1 - crop[0] - crop[1];
237+
size_t outdim2 = dim2 - crop[2] - crop[3];
238+
size_t outdim3 = dim3 - crop[4] - crop[5];
239+
size_t in_channels = input->shape[3];
240+
241+
size_t offset1 = in_channels*(dim2*dim3)*crop[0] +
242+
in_channels*dim3*crop[2] + in_channels*crop[4];
243+
size_t num = in_channels*outdim3;
244+
size_t instep2 = num+in_channels*(crop[4]+crop[5]);
245+
size_t instep1 = dim2*dim3*in_channels;
246+
size_t outstep1 = outdim2*outdim3*in_channels;
247+
size_t outstep2 = outdim3*in_channels;
248+
249+
for (size_t i=0; i<outdim1; i++) {
250+
for (size_t j=0; j<outdim2; j++) {
251+
memcpy(&output->array[i*outstep1 + j*outstep2],
252+
&input->array[offset1+i*instep1+j*instep2],
253+
num*sizeof(input->array[0]));
254+
}
255+
}
256+
}
257+
150258
void k2c_upsampling1d(k2c_tensor* output, k2c_tensor* input, size_t size) {
151259

152260
size_t in_height = input->shape[0];
@@ -178,6 +286,24 @@ void k2c_upsampling2d(k2c_tensor* output, k2c_tensor* input, size_t size[]) {
178286
}
179287
}
180288

289+
void k2c_upsampling3d(k2c_tensor* output, k2c_tensor* input, size_t size[]) {
181290

291+
size_t dim1 = output->shape[0];
292+
size_t dim2 = output->shape[1];
293+
size_t dim3 = output->shape[2];
294+
size_t channels = input->shape[3];
295+
296+
for (size_t i=0; i<dim1; i++) {
297+
for (size_t j=0; j<dim2; j++) {
298+
for (size_t k=0; k<dim3; k++) {
299+
size_t insub[K2C_MAX_NDIM] = {i/size[0],j/size[1],k/size[2],0};
300+
size_t outsub[K2C_MAX_NDIM] = {i,j,k,0};
301+
memcpy(&output->array[k2c_sub2idx(outsub,output->shape,output->ndim)],
302+
&input->array[k2c_sub2idx(insub,input->shape,input->ndim)],
303+
channels*sizeof(input->array[0]));
304+
}
305+
}
306+
}
307+
}
182308

183309
#endif /* KERAS2C_CONVOLUTION_LAYERS_H */

keras2c/layer2c.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ def write_layer_Conv(self, layer, inputs, outputs, i):
106106
fname = 'k2c_conv1d('
107107
elif layer_type(layer)[-2:] == '2D':
108108
fname = 'k2c_conv2d('
109+
elif layer_type(layer)[-2:] == '3D':
110+
fname = 'k2c_conv3d('
109111
if layer.get_config()['padding'] == 'valid':
110112
self.layers += fname + outputs + ',' + inputs + ',' + \
111113
pnm + '_kernel, \n\t' + pnm + '_bias,' + nm + \
@@ -124,6 +126,9 @@ def write_layer_Conv1D(self, layer, inputs, outputs, i):
124126
def write_layer_Conv2D(self, layer, inputs, outputs, i):
125127
self.write_layer_Conv(layer, inputs, outputs, i)
126128

129+
def write_layer_Conv3D(self, layer, inputs, outputs, i):
130+
self.write_layer_Conv(layer, inputs, outputs, i)
131+
127132
def write_layer_MaxPooling1D(self, layer, inputs, outputs, i):
128133
self.write_layer_Pooling(layer, inputs, outputs, i)
129134

@@ -388,6 +393,9 @@ def write_layer_UpSampling1D(self, layer, inputs, outputs, i):
388393
def write_layer_UpSampling2D(self, layer, inputs, outputs, i):
389394
self.write_layer_UpSampling(layer, inputs, outputs, i)
390395

396+
def write_layer_UpSampling3D(self, layer, inputs, outputs, i):
397+
self.write_layer_UpSampling(layer, inputs, outputs, i)
398+
391399
def write_layer_UpSampling(self, layer, inputs, outputs, i):
392400
nm, _, inputs, outputs = self.format_io_names(
393401
layer, inputs, outputs)
@@ -405,6 +413,9 @@ def write_layer_Cropping1D(self, layer, inputs, outputs, i):
405413
def write_layer_Cropping2D(self, layer, inputs, outputs, i):
406414
self.write_layer_Cropping(layer, inputs, outputs, i)
407415

416+
def write_layer_Cropping3D(self, layer, inputs, outputs, i):
417+
self.write_layer_Cropping(layer, inputs, outputs, i)
418+
408419
def write_layer_Cropping(self, layer, inputs, outputs, i):
409420
nm, _, inputs, outputs = self.format_io_names(
410421
layer, inputs, outputs)
@@ -422,6 +433,9 @@ def write_layer_ZeroPadding1D(self, layer, inputs, outputs, i):
422433
def write_layer_ZeroPadding2D(self, layer, inputs, outputs, i):
423434
self.write_layer_ZeroPad(layer, inputs, outputs, i)
424435

436+
def write_layer_ZeroPadding3D(self, layer, inputs, outputs, i):
437+
self.write_layer_ZeroPad(layer, inputs, outputs, i)
438+
425439
def write_layer_ZeroPad(self, layer, inputs, outputs, i):
426440
if 'Zero' in layer_type(layer):
427441
nm, _, inputs, outputs = self.format_io_names(

keras2c/weights2c.py

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# imports
77
import numpy as np
88
from keras2c.io_parsing import layer_type, get_layer_io_names, get_model_io_names
9-
maxndim = 4
9+
maxndim = 5
1010

1111

1212
__author__ = "Rory Conlin"
@@ -322,6 +322,50 @@ def write_weights_Conv2D(self, layer):
322322
self.write_weights_array2c(bias, layer.name + '_bias')
323323
self.stack_vars += '\n \n'
324324

325+
def write_weights_Conv3D(self, layer):
326+
padding = layer.get_config()['padding']
327+
stride = layer.get_config()['strides']
328+
dilation = layer.get_config()['dilation_rate']
329+
kernel_size = layer.get_config()['kernel_size']
330+
self.stack_vars += 'size_t ' + layer.name + \
331+
'_stride[3] = {' + ','.join([str(i) for i in stride]) + '}; \n'
332+
self.stack_vars += 'size_t ' + layer.name + \
333+
'_dilation[3] = {' + ','.join([str(i)
334+
for i in dilation]) + '}; \n'
335+
self.write_outputs(layer)
336+
if padding == 'same':
337+
inshp = layer.get_input_at(0).shape[1:]
338+
pad_along_height = dilation[0]*(kernel_size[0]-1)
339+
pad_top = int(pad_along_height // 2)
340+
pad_bottom = int(pad_along_height - pad_top)
341+
pad_along_width = dilation[1]*(kernel_size[1]-1)
342+
pad_left = pad_along_width//2
343+
pad_right = pad_along_width - pad_left
344+
pad_along_depth = dilation[1]*(kernel_size[1]-1)
345+
pad_front = pad_along_depth//2
346+
pad_back = pad_along_depth - pad_front
347+
padshp = (inshp[0]+pad_along_height,
348+
inshp[1]+pad_along_width,
349+
inshp[2]+pad_along_depth,
350+
inshp[3])
351+
pad = [pad_top, pad_bottom, pad_left,
352+
pad_right, pad_front, pad_back]
353+
self.write_weights_array2c(np.zeros(padshp), layer.name +
354+
'_padded_input')
355+
self.stack_vars += 'size_t ' + layer.name + \
356+
'_pad[6] = {' + ','.join([str(i) for i in pad]) + '}; \n'
357+
self.stack_vars += 'float ' + layer.name + '_fill = 0.0f; \n'
358+
359+
weights = layer.get_weights()
360+
kernel = weights[0]
361+
if layer.get_config()['use_bias']:
362+
bias = weights[1]
363+
else:
364+
bias = np.zeros(kernel.shape[3])
365+
self.write_weights_array2c(kernel, layer.name + '_kernel')
366+
self.write_weights_array2c(bias, layer.name + '_bias')
367+
self.stack_vars += '\n \n'
368+
325369
def write_weights_MaxPooling1D(self, layer):
326370
return self.write_weights_Pooling1D(layer)
327371

@@ -556,6 +600,14 @@ def write_weights_UpSampling2D(self, layer):
556600
',' + str(size[1]) + '}; \n'
557601
self.stack_vars += '\n\n'
558602

603+
def write_weights_UpSampling3D(self, layer):
604+
nm = layer.name
605+
self.write_outputs(layer)
606+
size = layer.get_config()['size']
607+
self.stack_vars += 'size_t ' + nm + '_size[3] = {' + str(size[0]) + \
608+
',' + str(size[1]) + ',' + str(size[2]) + '}; \n'
609+
self.stack_vars += '\n\n'
610+
559611
def write_weights_Cropping1D(self, layer):
560612
nm = layer.name
561613
self.write_outputs(layer)
@@ -577,6 +629,20 @@ def write_weights_Cropping2D(self, layer):
577629
',' + str(crop_right) + '}; \n'
578630
self.stack_vars += '\n\n'
579631

632+
def write_weights_Cropping3D(self, layer):
633+
nm = layer.name
634+
self.write_outputs(layer)
635+
crop0 = layer.get_config()['cropping'][0][0]
636+
crop1 = layer.get_config()['cropping'][0][1]
637+
crop2 = layer.get_config()['cropping'][1][0]
638+
crop3 = layer.get_config()['cropping'][1][1]
639+
crop4 = layer.get_config()['cropping'][2][0]
640+
crop5 = layer.get_config()['cropping'][2][1]
641+
self.stack_vars += 'size_t ' + nm + '_crop[6] = {' + str(crop0) + ','\
642+
+ str(crop1) + ',' + str(crop2) + ',' + str(crop3) + \
643+
',' + str(crop4) + ',' + str(crop5) + '}; \n'
644+
self.stack_vars += '\n\n'
645+
580646
def write_weights_ZeroPadding1D(self, layer):
581647
nm = layer.name
582648
self.write_outputs(layer)
@@ -600,6 +666,21 @@ def write_weights_ZeroPadding2D(self, layer):
600666
self.stack_vars += 'float ' + nm + '_fill = 0.0f; \n'
601667
self.stack_vars += '\n\n'
602668

669+
def write_weights_ZeroPadding3D(self, layer):
670+
nm = layer.name
671+
self.write_outputs(layer)
672+
pad0 = layer.get_config()['padding'][0][0]
673+
pad1 = layer.get_config()['padding'][0][1]
674+
pad2 = layer.get_config()['padding'][1][0]
675+
pad3 = layer.get_config()['padding'][1][1]
676+
pad4 = layer.get_config()['padding'][2][0]
677+
pad5 = layer.get_config()['padding'][2][1]
678+
self.stack_vars += 'size_t ' + nm + '_pad[6] = {' + str(pad0) + ','\
679+
+ str(pad1) + ',' + str(pad2) + ',' + str(pad3) + \
680+
',' + str(pad4) + ',' + str(pad5) + '}; \n'
681+
self.stack_vars += 'float ' + nm + '_fill = 0.0f; \n'
682+
self.stack_vars += '\n\n'
683+
603684
def write_weights_ActivityRegularization(self, layer):
604685
# no weights needed
605686
pass

0 commit comments

Comments
 (0)