Ask Your Question

how could I change memory layout from hwc to chw ?

asked 2020-02-27 19:35:20 -0600

coincheung gravatar image

updated 2020-02-27 19:36:00 -0600


I noticed that the default memory order is HWC which means that the offset is computed as offset = h * im.rows * im.elemSize() + w * im.elemSize() + c. However, I need to change the layout to CHW mode, which means that the offset of (h, w, c) should be offset = c * im.rows * im.cols + h * im.cols + w.

Is there any built-in method to do this ? If not, is there any c++11/14 built-in method to do this? Or what is the fastest way to do this ?

edit retag flag offensive close merge delete



You can reshape (H, W, C) to (HW, C) and then perform a transpose. Transpose would give (C, HW) which can then be reshaped to (C, H, W).

Yashas gravatar imageYashas ( 2020-02-29 11:41:52 -0600 )edit

2 answers

Sort by ยป oldest newest most voted

answered 2020-06-11 14:46:00 -0600

updated 2020-06-11 15:36:21 -0600

The following has worked well for me, with no unnecessary data allocation:

void hwc_to_chw(cv::InputArray src, cv::OutputArray dst) {
  const int src_h = src.rows();
  const int src_w = src.cols();
  const int src_c = src.channels();

  cv::Mat hw_c = src.getMat().reshape(1, src_h * src_w);

  const std::array<int,3> dims = {src_c, src_h, src_w};                         
  dst.create(3, &dims[0], CV_MAKETYPE(src.depth(), 1));                         
  cv::Mat dst_1d = dst.getMat().reshape(1, {src_c, src_h, src_w});              

  cv::transpose(hw_c, dst_1d);                                                  

void chw_to_hwc(cv::InputArray src, cv::OutputArray dst) {                      
  const auto& src_size = src.getMat().size;                                     
  const int src_c = src_size[0];                                                
  const int src_h = src_size[1];                                                
  const int src_w = src_size[2];                                                

  auto c_hw = src.getMat().reshape(0, {src_c, src_h * src_w});                  

  dst.create(src_h, src_w, CV_MAKETYPE(src.depth(), src_c));                    
  cv::Mat dst_1d = dst.getMat().reshape(src_c, {src_h, src_w});                 

  cv::transpose(c_hw, dst_1d);                                                  
edit flag offensive delete link more

answered 2020-02-28 07:08:24 -0600

berak gravatar image

updated 2020-02-28 11:28:57 -0600

you will need to copy memory for this, it cannot be done simply by tweaking offsets

Mat hwc = .... // src
int sz[] = {c,h,w};
Mat chw(3, sz, hwc.depth()); // dst
// here's the trick: split it into existing, preallocated planes:
vector<Mat> planes(c);
for (size_t i=0; i<c; i++) {
     planes[i] = Mat(h, w, chw.depth(), chw.ptr<float>(i)); // warn: hardcoded type !
split(hwc, planes);
edit flag offensive delete link more

Question Tools

1 follower


Asked: 2020-02-27 19:35:20 -0600

Seen: 9,063 times

Last updated: Jun 11 '20