| ### what is packing and why | |
| packing is the form of storing multiple short-sized values as one long-sized value. | |
| element packing is well mapped with the underlying simd register, which usually use one very wide register to store different types of values. | |
| |C|elemsize|elempack| | |
| |---|---|---| | |
| |double|8|1| | |
| |float|4|1| | |
| |int|4|1| | |
| |short|2|1| | |
| |signed char|1|1| | |
| |arm neon|elemsize|elempack| | |
| |---|---|---| | |
| |float64x2_t|16|2| | |
| |float32x4_t|16|4| | |
| |int32x4_t|16|4| | |
| |float16x4_t|8|4| | |
| |int8x8_t|8|8| | |
| Though the real count of values doubles when elempack is two, the wide-sized value is still treated as one value in the view of Mat structure. For example, we want to store 40 float values in Mat object, if elempack 1 is used, Mat width is then 40, while 10 if elempack 4 is used. | |
| |dims|w|h|c|cstep|elemsize|elempack| | |
| |---|---|---|---|---|---|---| | |
| |1|40|1|1|40|4|1| | |
| |1|10|1|1|10|16|4| | |
| ### packing style convention | |
| In practice, elempack 1, 4, 8 are the most common cases. It is possible to use any other packing style in theory. | |
| The following table show the packing axis used in ncnn for different dimension. | |
| |dims|packing axis|shape before packing|shape after packing| | |
| |---|---|---|---| | |
| |1|w|w|w/elempack| | |
| |2|h|w, h|w, h/elempack| | |
| |3|c|w, h, c|w, h, c/elempack| | |
| If the packing axis dim is not evenly divisible by elempack, zero padding may be used. | |
| ``` | |
| outw = (w + elempack - 1) / elempack; | |
| ``` | |
| The following snippet shows the memory layout after elempack=4 on 3-dim Mat | |
| ``` | |
| // w=2 h=3 c=4 elempack=1 | |
| 0 1 | |
| 2 3 | |
| 4 5 | |
| 6 7 | |
| 8 9 | |
| 10 11 | |
| 12 13 | |
| 14 15 | |
| 16 17 | |
| 18 19 | |
| 20 21 | |
| 22 23 | |
| // w=2 h=3 c=1 elempack=4 | |
| (0,6,12,18) (1,7,13,19) | |
| (2,8,14,20) (3,9,15,21) | |
| (4,10,16,22) (5,11,17,23) | |
| ``` | |
| ### how to convert elempack | |
| There is a convenient wrapper function provided | |
| ``` | |
| // convert to elempack 4 if packing axis dim is evenly divisible by elempack | |
| // return the identity Mat otherwise | |
| ncnn::Mat a; | |
| ncnn::Mat a_packed; | |
| ncnn::convert_packing(a, a_packed, 4); | |
| if (a_packed.elempack == 4) | |
| { | |
| // check if packing is successful | |
| } | |
| // convert to packing 1, aka unpacking, shall be always successful | |
| ncnn::Mat b; | |
| ncnn::Mat b_unpacked; | |
| ncnn::convert_packing(b, b_unpacked, 1); | |
| ``` | |
| ### handle general interleaved data | |
| Here is an example of using convert packing to convert RGB interleaved data to planar | |
| **NOTE:** The following code is just presented to explain what packing is and the conversion process. Do not use it in production due to its poor performance. Do use ncnn::Mat::from_pixels() | |
| ```cpp | |
| // rgb_interleaved_u8 is RGB RGB RGB ... | |
| // rgb_interleaved_u8.w = w; | |
| // rgb_interleaved_u8.h = h; | |
| // rgb_interleaved_u8.c = 1; | |
| // rgb_interleaved_u8.elemsize = 3; | |
| // rgb_interleaved_u8.elempack = 3; | |
| ncnn::Mat rgb_interleaved_u8(w, h, 1, 3, 3); | |
| ncnn::Mat rgb_planar_u8; | |
| ncnn::convert_packing(rgb_interleaved_u8, rgb_planar_u8, 1); | |
| // rgb_planar_u8 is now RRR ... GGG ... BBB ... | |
| // rgb_planar_u8.w = w; | |
| // rgb_planar_u8.h = h; | |
| // rgb_planar_u8.c = 3; | |
| // rgb_planar_u8.elemsize = 1; | |
| // rgb_planar_u8.elempack = 1; | |
| ``` | |