2

I'm trying to use Eigen to interface with unsigned char * types. I can convert to Eigen and then to float, but when I convert back to unsigned char , the result is wrong. I need the Eigen matrix to be in float type so I can perform arithmetic operations on it. Then I need to bring it back to unsigned char so I can save it.

#include <Eigen/Dense>
#include <iostream>
int main(){
unsigned char* data;
for (int i=0;i<9;++i){ 
    data[i]= i;
}
//Map data to Eigen
Map<Matrix<unsigned char,3,3> ,RowMajor> img(data,3,3);
//Convert to float
MatrixXf gray = img.cast<float>();
//Convert back to unsigned char*
float *gray_array = gray.data();
unsigned char *gray_UC = (unsigned char*)gray_array;

std::cout<<"Eigen matrix converted to unsigned char:"<<(float)*(gray_UC+1)<<std::endl;
std::cout<<"Eigen matrix as float: "<<*(gray_array+i)<<std::endl;
std::cout<<"Original data converted to float: "<<(float)*(data+1)<<std::endl;
}

Data being an unsigned char* type.

I expect gray_UC and data to be the same, but it is not the case. Even more, gray_array prints the right float value, so it might be the conversion to unsigned char that is wrong.

Bubble
  • 63
  • 1
  • 9
  • I am unable to copy-and-paste the code provided, and compile it. It is not self-contained. A [mcve] would be very helpful here. The problem may be pointer aliasing. The casts provided are rather suspicious. – Eljay Jun 20 '19 at 17:16
  • 1
    Casting the array will not cast the entries. See [this question](https://stackoverflow.com/questions/825344/casting-an-array-of-unsigned-chars-to-an-array-of-floats). Or simply use Eigen's `cast` method for the way back. But you probably want a custom conversion to include rounding. – Nico Schertler Jun 20 '19 at 17:19

2 Answers2

1

When you do:

(float)*(gray_UC+1)

You are taking the pointer gray_UC and adding one to it according to pointer arithmetic. If an unsigned char is one byte that means just one byte after gray_UC. Then you dereference that, getting that next byte as unsigned char, and then cast that value to float. The result, I presume, should be a float number between 0 and 255.

Here:

(float)*(data+1)

You take the pointer data and increase it by one, but in this case is a pointer to float which, being 4 bytes, would take the pointer four bytes forward. Next you dereference that and cast it to float (which shouldn't have any effect anyway), so you get the second float value in the floats buffer pointed by data.

In order to get the same from gray_UC, you would do something like:

*(((float*) gray_UC) + 1)
jdehesa
  • 58,456
  • 7
  • 77
  • 121
  • I don't understand why `(float)*(gray_UC+1)` gives the wrongs result. `gray_UC+1` is the adress of the next byte, which is then converted to float. It should give the right result, unless `gray_UC` was wrong from the beginning – Bubble Jul 03 '19 at 11:48
  • 1
    @Bubble A `float` value takes four bytes. If you take `gray_UC+1` and interpret that as a `float` pointer, you will be taking three bytes of one `float` value and one byte from the next one, which doesn't produce any meaningful result. – jdehesa Jul 03 '19 at 12:21
0

Here is a solution that works for me, inspired by @Nico Schertler 's comment. I do the cast within a for loop. However I had to use an array first and then give the adress of the first element to my unsigned char *.

In the future I should consider trying std::transformas is advised in the link from @Nico Schertler .

Note that I did not cast the Eigen array to unsigned char directly, because my real application is using a big array. When I tried it, I got an error linked to memory issues.

Here is my solution:

unsigned char gray_UC_tmp[size];
for (int i=0; i<9;++i){
      gray_UC_tmp[i] = static_cast<unsigned char> (gray_array[i]);
}
gray_UC = gray_UC_tmp;
Bubble
  • 63
  • 1
  • 9