Tengo un OpenCV Mat de tipo CV_32S que contiene valores enteros de> = -1. Estoy intentando acceder al puntero de datos subyacente que es una matriz plana 1D de tipo uchar.

Supongo que como int es de 4 bytes [32 bits] y uchar es de 1 byte [8 bits], necesito descomprimir los datos en el tipo int que se proporciona inicialmente en una estructura OpenCV Mat.

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

Paso el puntero uchar *data de opencv_data al kernel de cuda. Para descomprimir los cuatro uchars en un solo int, hago lo siguiente.

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

Cuando opencv_data solo tiene valores positivos, obtengo el valor de descompresión correcto en value. Sin embargo, si pongo un solo número negativo en opencv_data, el desembalaje anterior produce value = -1.

No entiendo la razón detrás de este problema y necesito ayuda con esto.

EDITAR: según la sugerencia de uso reinterpret_cast. El código de actualización se encuentra a continuación, pero para los números negativos, el resultado aún no es correcto.

//! 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";
}

El código editado produce un resultado correcto para números positivos pero no para números negativos en test.

Por ejemplo: para -2 el resultado es 16777215

1
kcc__ 28 ago. 2016 a las 13:51

2 respuestas

La mejor respuesta

Parece que hay algunos conceptos que ha entendido mal aquí.

El openCV mat almacena la dirección de la asignación de memoria que contiene los datos de la matriz en un uchar *. Esto no significa que los datos se transformen de ninguna manera. Si desea acceder a los datos asociados con la matriz directamente, simplemente envíe el puntero al tipo correcto y use ese puntero de conversión. Me gusta esto:

#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;
}
2
talonmies 28 ago. 2016 a las 14:00

Escribió mal el último turno, debería ser:

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

Tenga en cuenta que este método invoca un comportamiento indefinido si el último turno se desborda, pero para la mayoría de las implementaciones actuales, no debería causar ningún problema. Por cierto, no necesita reinterpret_cast<uchar> ya que data es una matriz de uchar. Este es un método más seguro:

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";
}

Aún así, tenga en cuenta que este método hace suposiciones sobre el orden de bytes de los tipos enteros. Un enfoque mucho más simple es considerar que test.data apunta a una matriz simple de int con int *p = reinterpret_cast<int*>(test.data); como se publicó en la respuesta de talomies .

Finalmente, todos estos métodos asumen que la matriz 2D está empaquetada, es decir: test.step == test.cols * sizeof(int), lo que puede no estar garantizado.

1
chqrlie 28 ago. 2016 a las 22:43