static */
| 100 | } |
| 101 | |
| 102 | /* static */ std::unique_ptr<Array4D<float>> |
| 103 | ReferenceUtil::SeparableConvArray4D(const Array4D<float>& input, |
| 104 | const Array4D<float>& depthwise_weights, |
| 105 | const Array4D<float>& pointwise_weights, |
| 106 | std::pair<int64, int64> kernel_stride, |
| 107 | Padding padding) { |
| 108 | const int64 depth_multiplier = depthwise_weights.planes(); |
| 109 | CHECK_EQ(pointwise_weights.depth(), input.depth() * depth_multiplier); |
| 110 | |
| 111 | // Combine the two weights by reducing the depth_multiplier, so that we can |
| 112 | // apply a single convolution on the combined weights. |
| 113 | Array4D<float> weights(pointwise_weights.planes(), input.depth(), |
| 114 | depthwise_weights.height(), depthwise_weights.width()); |
| 115 | for (int64 kx = 0; kx < depthwise_weights.width(); ++kx) { |
| 116 | for (int64 ky = 0; ky < depthwise_weights.height(); ++ky) { |
| 117 | for (int64 kz = 0; kz < input.depth(); ++kz) { |
| 118 | for (int64 out = 0; out < pointwise_weights.planes(); ++out) { |
| 119 | float weight = 0.0; |
| 120 | for (int64 depth = 0; depth < depth_multiplier; ++depth) { |
| 121 | weight += |
| 122 | depthwise_weights(depth, kz, ky, kx) * |
| 123 | pointwise_weights(out, depth + kz * depth_multiplier, 0, 0); |
| 124 | } |
| 125 | weights(out, kz, ky, kx) = weight; |
| 126 | } |
| 127 | } |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | return ConvArray4D(input, weights, kernel_stride, padding); |
| 132 | } |
| 133 | |
| 134 | /* static */ int64 ReferenceUtil::WindowCount(int64 unpadded_width, |
| 135 | int64 window_len, int64 stride, |