|
8 | 8 |
|
9 | 9 | #include <executorch/backends/cadence/generic/operators/op_im2row.h> |
10 | 10 |
|
11 | | -#include <algorithm> |
12 | | - |
13 | 11 | #include <executorch/runtime/core/exec_aten/util/scalar_type_util.h> |
14 | 12 |
|
15 | 13 | #ifndef DISABLE_ALWAYS_INLINE |
@@ -59,34 +57,32 @@ ALWAYS_INLINE void im2row_( |
59 | 57 | // array of size (out_height * out_width) x channels_col |
60 | 58 | const int32_t channels_col = channels * kernel_h * kernel_w; |
61 | 59 |
|
62 | | - // If the layout is NHWC, we can copy 'channels' worth of contiguous data |
63 | | - // points when performing im2row. |
| 60 | + // If the layout is NHWC, the input data is contiguous per-pixel (H, W, C). |
| 61 | + // The output layout must match torch.nn.functional.unfold, which is [c][kp]: |
| 62 | + // output[c * num_kp + kp] for each output position. |
64 | 63 | if (channels_last) { |
| 64 | + const int32_t num_kp = kernel_h * kernel_w; |
65 | 65 | // Iterate over the output domain |
66 | 66 | for (int _h = 0; _h < out_height; ++_h) { |
67 | 67 | for (int _w = 0; _w < out_width; ++_w) { |
68 | 68 | int32_t i_col = _h * out_width + _w; |
69 | | - // Each point in the output domain is the result of applying a filter of |
70 | | - // size kernel_h x kernel_w x channels on the input. But since channels |
71 | | - // is contiguous, we will not explicitly have a loop for it. |
72 | 69 | for (int _kh = 0; _kh < kernel_h; ++_kh) { |
73 | 70 | int32_t h_im = _h * stride_h - pad_h + _kh * dilation_h; |
74 | 71 | for (int _kw = 0; _kw < kernel_w; ++_kw) { |
75 | 72 | int32_t w_im = _w * stride_w - pad_w + _kw * dilation_w; |
| 73 | + int32_t kp = _kh * kernel_w + _kw; |
76 | 74 |
|
77 | | - // h_im and w_im are the actual height and width coordinates of the |
78 | | - // input tensor from where we need to copy 'channels' points. |
79 | | - const T* __restrict__ slice_im = |
80 | | - data_im + (h_im * width + w_im) * channels; |
81 | | - T* __restrict__ slice_col = data_col + i_col * channels_col + |
82 | | - (_kh * kernel_w + _kw) * channels; |
83 | | - // If the coordinates were within the input domain, we copy |
84 | | - // 'channels' contiguous values. Otherwise we will fill the output |
85 | | - // with 0's. |
86 | 75 | if (h_im >= 0 && w_im >= 0 && h_im < height && w_im < width) { |
87 | | - memcpy(slice_col, slice_im, channels * sizeof(T)); |
| 76 | + const T* __restrict__ pixel = |
| 77 | + data_im + (h_im * width + w_im) * channels; |
| 78 | + for (int _c = 0; _c < channels; ++_c) { |
| 79 | + data_col[i_col * channels_col + _c * num_kp + kp] = pixel[_c]; |
| 80 | + } |
88 | 81 | } else { |
89 | | - std::fill_n(slice_col, channels, T(in_zero_point)); |
| 82 | + for (int _c = 0; _c < channels; ++_c) { |
| 83 | + data_col[i_col * channels_col + _c * num_kp + kp] = |
| 84 | + static_cast<T>(in_zero_point); |
| 85 | + } |
90 | 86 | } |
91 | 87 | } |
92 | 88 | } |
|
0 commit comments