简体   繁体   中英

C++ data packing from uchar to int (both positive and negative numbers)

I have a OpenCV Mat of type CV_32S with contains integer values of >= -1. I am trying to access the underlying data pointer which is a flat 1D array of type uchar .

I suppose that since int is 4 byte [32 bits] and uchar is 1 byte [8 bits] I need to unpack the data into int type which is initially provided in a OpenCV Mat structure.

cv::Mat opencv_data;  //! data of type CV_32 with both negative and positive values.

I pass the uchar *data pointer of opencv_data to cuda kernel. To unpack the four uchars into single int I doing the following.

int value =   (uchar)opencv_data[index] |
            (((uchar)opencv_data[index + 1]) << 8) |
            (((uchar)opencv_data[index + 2]) << 16);

When the opencv_data has only positive values I get the correct unpack value in value . However if I put a single negative number in opencv_data the above unpacking produces value = -1 .

I fail to understand the reason behind this problem and I need help with this.

EDIT: based on suggestion to use reinterpret_cast . The update code is below but for negative numbers the result is still not correct.

//! for test only    
cv::Mat test = cv::Mat(40, 60, CV_32S);

for (int j = 0; j < test.rows; j++) {
    for (int i = 0; i < test.cols; i++) {
        test.at<int>(j, i) = -2;
    }
}

int INDICES_BYTE = test.step * test.rows;
uchar *data = reinterpret_cast<uchar*>(test.data);
for (int i = 0; i < INDICES_BYTE; i += 4) {
    int index = reinterpret_cast<uchar>(data[i]) |
                (reinterpret_cast<uchar>(data[i + 1]) << 8) |
                (reinterpret_cast<uchar>(data[i + 2]) << 16) |
                (reinterpret_cast<uchar>(data[i + 3]) << 32);
    std::cout << index  << "\n";
}

The edited code produces correct result for positive number but not for negative numbers in test .

Eg: for -2 the result is 16777215

It would appear that there are a few concepts you have misunderstood here.

The openCV mat stores the address of the memory allocation which holds the matrix data in a uchar * . This doesn't mean that the data is in any way transformed. If you want to access the data associated with the matrix directly, you simply cast the pointer to the correct type and use that cast pointer. Like this:

#include <opencv2/core/core.hpp>
#include <iostream>

int main()
{
    cv::Mat test = cv::Mat(4, 6, CV_32S);
    for (int j = 0; j < test.rows; j++) {
       for (int i = 0; i < test.cols; i++) {
          test.at<int>(j, i) = -2*i;
       }
    }

    int *p = reinterpret_cast<int*>(test.data);
    for(int j=0; j<test.rows; ++j) {
        for(int i=0; i<test.cols; ++i) {
            std::cout << j << "," << i << " = " << p[i] << std::endl;
        }
        p += test.cols;
    }

    return 0;
}

You mistyped the last shift, it should be:

(reinterpret_cast<uchar>(data[i + 3]) << 24);

Note that this method invokes undefined behavior if the last shift overflows, but for most current implementations, it should not cause a problem. Btw, you do not need the reinterpret_cast<uchar> since data is an array of uchar . Here is a safer method:

for (int i = 0; i < INDICES_BYTE; i += 4) {
    unsigned u32 = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) |
                   ((unsigned)data[i + 3] << 24);
    int index = (u32 <= INT_MAX) ? u32 : ~(int)~u32;
    std::cout << index << "\n";
}

Note still that this method makes assumptions about the byte ordering of integer types. A much simpler approach is to consider test.data as pointing to a simple array of int with int *p = reinterpret_cast<int*>(test.data); as posted in talomies ' answer.

Finally, all these methods assume that the 2D array is packed, ie: test.step == test.cols * sizeof(int) , which may not be guaranteed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM