1

I want to read an 3-channel RGB image in opencv and convert it to a linear C++ float array. Sounds simple, but still I'm struggling. I tried it with this code, but for some reason it's giving me a segmentation fault

  cv::Mat rgb_image;
  cv::imread(filename.c_str(), cv::IMREAD_COLOR).convertTo(rgb_image,  CV_32FC3, (1./255.));

  float *rgb_data = new float(rgb_image.rows * rgb_image.cols * rgb_image.channels());
  int counter = 0;
  for (int z = 0; z < rgb_image.channels(); z++)
    for (int i = 0; i < rgb_image.rows; i++)
      for (int j = 0; j < rgb_image.cols; j++){
        rgb_data[counter] = rgb_image.at<cv::Vec3f>(i,j)[z] ;
        counter++;
      }

Does anyone see what's going wrong?
Is there an easier way of doing it?
I know there are stackoverflow-examples for converting a cv::Mat into an std::vector, would something similar possible for a C++ array? Output dimensions should be [Rows x Cols x Channel]

mcExchange
  • 6,154
  • 12
  • 57
  • 103
  • 1
    `float *rgb_data = new float(rgb_image.rows * rgb_image.cols * rgb_image.channels());` you created one object, not array, use [] instead (). – rafix07 Jan 23 '18 at 12:49
  • Damn'it such a stupid error :P. My C++ skills have really gone soft :-/. Thanks a lot! – mcExchange Jan 23 '18 at 12:54

1 Answers1

2

You have two problems:

  1. Pointed by rafix07: use [] instead of ()

  2. rgb_image.at(i,j)[z] gives 3 floats not 1

I would suggest to use std::memcpy

float *rgb_data = new float(rgb_image.rows * rgb_image.cols * rgb_image.channels()); // Dst
float* dataPointer = reinterpret_cast<float*>(rgb_image.data); // Src
// pointer from, pointer two, size in bytes
std::memcpy(rgb_data, dataPointer, rgb_image.rows * rgb_image.cols * rgb_image.channels() * sizeof(float));

it is optimized and probably better than the loops :)

Kinght 金
  • 17,681
  • 4
  • 60
  • 74
api55
  • 11,070
  • 4
  • 41
  • 57
  • Thanks. Unfortunately this seems to have a different ordering of the data. I assume it is [Channels x Rows x Cols], I'll have to check that. Else this is a good way to avoid the loop – mcExchange Jan 23 '18 at 13:29
  • @mcExchange thanks for the typo correction :) I am used to OpenCV functions that are src,dest. The order is RGB RGB RGB ... in row major... so it copies the 3 values of each pixel, if you want it in another order... then the loops may be better – api55 Jan 23 '18 at 13:29
  • @mcExchange I think, but I have not tried it yet, is to use the [split](https://docs.opencv.org/2.4/modules/core/doc/operations_on_arrays.html#split) function. this divides the channels in individual cv::mat so the order will be image in blue, image in green then image in red (assuming BGR). What I have not tried is to pass the array of float instead of a vector of cv mat, but it may work. Something in the lines of `cv::split(rgb_image, rgb_data)` – api55 Jan 23 '18 at 13:36
  • cv::split doesn't seem to work with arrays as destination. Anyway, I'll stick to the loop for now. Still thanks for your help! – mcExchange Jan 23 '18 at 13:58