Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert linear Array to 2D Matrix

Tags:

c++

opencv

I got an float pointer (array), which represents an image. It's elements count and index has width*height. The image is not like a matrix, which has it's origin at the upper left. Instead it has the origin in the lower left, like in the carthesian coordinate system. After reaching the max-width, it starts it's next row at the left side.

So I want to efficiently convert this array to a 2D matrix (optional: opencv).

How do I do that in a good and effective manner? And how do I convert it back?

Thanks in advance.

like image 217
mchlfchr Avatar asked Nov 12 '12 09:11

mchlfchr


People also ask

How do I turn an array into a 2d array?

Use reshape() Function to Transform 1d Array to 2d Array The number of components within every dimension defines the form of the array. We may add or delete parameters or adjust the number of items within every dimension by using reshaping.

Is 2d array a linear array?

A two-dimensional array could be considered to have “rows” and “columns”. The declaration of a two- dimensional array is extension of the declaration for a 1-D (linear) array. The first dimension is the “row” and the second is the “column”. However, in memory, the array is not stored in a 2-D fashion.

How do you convert a 1d array to a 2d array in Matlab?

Direct link to this answerx1 = mod(32 + 19* (1:i*i), i); x1 = reshape(x1, 8, 8); y1 = mod(16 + 13* (1:j*j), j); y1 = reshape(y1, 8, 8);


2 Answers

I'll throw a rock in the lake and watch the ripples. Note: I have no idea what the caller expects to do with the xformed data, mostly due to my fledgling knowledge of OpenCV. However the core question of transformation seemed pretty straight forward. If I'm way off-base kindly leave a comment and I'll drop the answer. I propose two approaches, one for data inversion in-place, and one for simple accessor wrapping using a C++ class.

In-place Inversion: If the caller needs to invert the rows to accommodate usage for passing to an API, it can be done in place. Just be sure to do it again once you're done using the inverted data. An example purely byte-oriented is:

// in-place inversion of the linear matrix to re-origin.
void mat_invert(float *data, size_t height, size_t width)
{
    // must be at least 2 rows high for this to mean anything.
    if (height < 2)
        return;

    // setup a pair of pointers to walk the rows in byte-form
    unsigned char* top = (unsigned char*)data;
    unsigned char *bottom = (unsigned char *)(data + (height-1)*width);
    size_t row_width = sizeof(data[0]) * width;
    while (top < bottom)
    {
        for (size_t i=0; i<row_width; i++)
        {
            *top ^= *bottom;
            *bottom ^= *top;
            *top++ ^= *bottom++;
        }
        bottom -= 2*row_width;
    }
}

A sample usage:

int main(int argc, char *argv[])
{
    const size_t w = 10;
    const size_t h = 5;

    float ar[h*w];
    memset(ar, 0, sizeof(ar));

    ar[0]       = 0.1;
    ar[1*w + 1] = 1.1;
    ar[2*w + 2] = 2.1;
    ar[3*w + 3] = 3.1;
    ar[4*w + 4] = 4.1;

    // dump original
    for (size_t i=0; i<h; i++)
    {
        for (size_t j=0; j<w; j++)
            cout << ar[i*w+j] << ' ';
        cout << endl;
    }
    cout << endl;

    // invert original
    mat_invert(ar, h, w);
    for (size_t i=0; i<h; i++)
    {
        for (size_t j=0; j<w; j++)
            cout << ar[i*w+j] << ' ';
        cout << endl;
    }
    cout << endl;

    // invert again
    mat_invert(ar, h, w);
    for (size_t i=0; i<h; i++)
    {
        for (size_t j=0; j<w; j++)
            cout << ar[i*w+j] << ' ';
        cout << endl;
    }
    cout << endl;

    return EXIT_SUCCESS;
}

Results:

0.1 0 0 0 0 0 0 0 0 0 
0 1.1 0 0 0 0 0 0 0 0 
0 0 2.1 0 0 0 0 0 0 0 
0 0 0 3.1 0 0 0 0 0 0 
0 0 0 0 4.1 0 0 0 0 0 

0 0 0 0 4.1 0 0 0 0 0 
0 0 0 3.1 0 0 0 0 0 0 
0 0 2.1 0 0 0 0 0 0 0 
0 1.1 0 0 0 0 0 0 0 0 
0.1 0 0 0 0 0 0 0 0 0 

0.1 0 0 0 0 0 0 0 0 0 
0 1.1 0 0 0 0 0 0 0 0 
0 0 2.1 0 0 0 0 0 0 0 
0 0 0 3.1 0 0 0 0 0 0 
0 0 0 0 4.1 0 0 0 0 0 

Implicit Access Class: If all you need is virtualized row/height math done for you, the following will suffice to do just that:

#include <iostream>
#include <exception>
#include <stdexcept>
using namespace std;

class matrix_xform
{
private:
    size_t width, height;
    float *data;

public:
    matrix_xform(float *data, size_t height, size_t width)
        : data(data), width(width), height(height)
    {
    }

    float * operator[](size_t x)
    {
        if (x > (height-1))
            throw std::out_of_range("matrix_xform[x]");
        return data + (width * (height - 1 - x));
    }

    const float * operator[](size_t x) const
    {
        if (x > (height-1))
            throw std::out_of_range("matrix_xform[x]");
        return data + (width * (height - 1 - x));
    }
};

A sample usage:

int main(int argc, char *argv[])
{
    const size_t w = 10;
    const size_t h = 5;

    float ar[h*w];
    memset(ar, 0, sizeof(ar));

    matrix_xform mat(ar, h, w);
    mat[0][0] = 1.0;
    mat[1][1] = 1.0;
    mat[2][2] = 1.0;
    mat[3][3] = 1.0;
    mat[4][4] = 1.0;

    // dump original
    for (size_t i=0; i<h; i++)
    {
        for (size_t j=0; j<w; j++)
            cout << ar[i*w+j] << ' ';
        cout << endl;
    }
    cout << endl;

    // dump using accessor
    for (size_t i=0; i<h; i++)
    {
        for (size_t j=0; j<w; j++)
            cout << mat[i][j] << ' ';
        cout << endl;
    }

    return EXIT_SUCCESS;
}

Results:

0 0 0 0 1 0 0 0 0 0 
0 0 0 1 0 0 0 0 0 0 
0 0 1 0 0 0 0 0 0 0 
0 1 0 0 0 0 0 0 0 0 
1 0 0 0 0 0 0 0 0 0 

1 0 0 0 0 0 0 0 0 0 
0 1 0 0 0 0 0 0 0 0 
0 0 1 0 0 0 0 0 0 0 
0 0 0 1 0 0 0 0 0 0 
0 0 0 0 1 0 0 0 0 0 

I hope that covers every base the OP is looking for.

like image 134
WhozCraig Avatar answered Sep 18 '22 17:09

WhozCraig


Planning your image processing API as

 void my_func (int *src, int *dst, int x_stride, int y_stride, int N);

makes it easy to iterate in continuous memory while flipping the scanning direction between left<->right, but also between up<->down.

If the API is designed for different input & output strides, one can also change the number of bytes per image element (change eg. color mode from RGBA to RGB or from 24-bit RGB to 16-bit R5G6B5, from int to float etc.) but also image width (and height also...).

The point is that math should be the same regardless of location of each row of the image.

One of these functions can be:

 copy_row(int *src, int* dst, int N, int x_stride);
 copy_2D_mem(int *src_base, int* dst_base, int N, int M, int y_stride, int x_stride);

Then again, it's quite possible that many of the existing opencv algorithms do not care about the orientation of the image. And writing one's own, the same approach could be utilized.

like image 44
Aki Suihkonen Avatar answered Sep 22 '22 17:09

Aki Suihkonen