| 105 | |
| 106 | template <typename T> |
| 107 | void im2col_nhwc(const SimpleTensor<T> &src, |
| 108 | SimpleTensor<T> &dst, |
| 109 | const Size2D &kernel_dims, |
| 110 | const PadStrideInfo &conv_info, |
| 111 | bool has_bias, |
| 112 | unsigned int channel_pad_right) |
| 113 | { |
| 114 | // Zero initialize the dst tensor for test cases involving padding |
| 115 | if (is_data_type_quantized(src.data_type())) |
| 116 | { |
| 117 | memset(static_cast<void *>(dst.data()), src.quantization_info().uniform().offset, dst.size()); |
| 118 | } |
| 119 | else |
| 120 | { |
| 121 | memset(static_cast<void *>(dst.data()), 0, dst.size()); |
| 122 | } |
| 123 | |
| 124 | ARM_COMPUTE_ERROR_ON(src.data_layout() != DataLayout::NHWC); |
| 125 | const int stride_x = conv_info.stride().first; |
| 126 | const int stride_y = conv_info.stride().second; |
| 127 | const int kernel_width = kernel_dims.width; |
| 128 | const int kernel_height = kernel_dims.height; |
| 129 | const int pad_x = conv_info.pad().first; |
| 130 | const int pad_y = conv_info.pad().second; |
| 131 | const int src_width = src.shape().y(); |
| 132 | const int src_height = src.shape().z(); |
| 133 | const int src_channels = src.shape().x(); |
| 134 | const int batches = src.shape().total_size_upper(3); |
| 135 | const int dst_width = has_bias ? dst.shape().x() - 1 : dst.shape().x(); |
| 136 | const int dst_height = dst.shape().y(); |
| 137 | const int pad_val = |
| 138 | is_data_type_quantized_asymmetric(src.data_type()) ? src.quantization_info().uniform().offset : 0; |
| 139 | |
| 140 | // Compute width and height of the convolved tensors |
| 141 | std::pair<unsigned int, unsigned int> convolved_dims = |
| 142 | scaled_dimensions(src_width, src_height, kernel_dims.width, kernel_dims.height, conv_info); |
| 143 | #if defined(_OPENMP) |
| 144 | #pragma omp parallel for schedule(dynamic, 1) collapse(2) |
| 145 | #endif /* _OPENMP */ |
| 146 | for (int b = 0; b < batches; ++b) |
| 147 | { |
| 148 | for (int yo = 0; yo < dst_height; ++yo) |
| 149 | { |
| 150 | // Compute input spatial coordinates |
| 151 | const int xi = (yo % convolved_dims.first) * stride_x; |
| 152 | const int yi = (yo / convolved_dims.first) * stride_y; |
| 153 | |
| 154 | for (int ci = 0; ci < src_channels; ++ci) |
| 155 | { |
| 156 | for (int yk = 0; yk < kernel_height; ++yk) |
| 157 | { |
| 158 | for (int xk = 0; xk < kernel_width; ++xk) |
| 159 | { |
| 160 | dst[ci + (xk + yk * kernel_width) * (src_channels + channel_pad_right) + yo * dst.shape().x() + |
| 161 | b * dst.shape().x() * dst.shape().y()] = |
| 162 | tensor_elem_at(src, Coordinates(ci, xi + xk - pad_x, yi + yk - pad_y, b), |
| 163 | BorderMode::CONSTANT, static_cast<T>(pad_val)); |
| 164 | } |
no test coverage detected