Passing 3d arrays to a convolution function in C - c

I need to do a function that executes a 2D convolution and for that I need to pass to it a couple of 3d arrays. However I've been told my method is not an ideal way to do this.
First, I declare the variables:
typedef struct {
float img[224][224][3];
} input_224_t;
typedef struct {
float img[112][112][32];
} input_112_t;
typedef struct {
float img[3][3][32];
} weightsL1_t;
Then, the convolution looks like this:
void convolution(input_224_t* N, weightsL1_t* M, input_112_t* P, int size, int ksize, int channels, int filters, int stride)
{
// Effectively pads the image before convolution. Technically also works for pointwise, but it's inefficient.
// find center position of kernel (half of kernel size)
int kcenter = ksize / 2;
// Declare output indexes
int a = 0;
int b = -1;
for (int k = 0; k < filters; ++k) // filters
{
for (int i = 0; i < size; i = i + stride) // rows
{
for (int j = 0; j < size; j = j + stride) // columns
{
b++;
if (b == ksize) {b=0;a++;} // Increment output index
for (int m = 0; m < ksize; ++m) // kernel rows
{
for (int n = 0; n < ksize; ++n) // kernel columns
{
// Index of input signal, used for checking boundary
int ii = i + (m - kcenter);
int jj = j + (n - kcenter);
// Ignore input samples which are out of bound
if (ii >= 0 && ii < size && jj >= 0 && jj < size) {
for (int p = 0; p < channels; ++p) // channels
{
P.img[a][b][k] += N.img[ii][jj][p] * M.img[m][n][k]; // convolve
}
}
}
}
}
}
}
}
(This returns "field 'img' could not be resolved" at the "convolve" line)
I then import the values into the correct structs (which was a previous question of mine which has been answered: Write values to a 3D array inside a struct in C) and I call the function like this:
convolution(test_image, test_filter, test_result, 6, 3, 1, 1, 2);
I have been told in my previous question that this is not an ideal way to handle 3D arrays, and that it may use a lot more memory than I intend. This is a very memory-intensive process, and this will run in an embedded system, so optimizing memory allocation is paramount.
My objective, if possible, is to only allocate one of each of these 3D arrays at any point in time as to not use unnecessary memory, and do it in a way that this space can be freed at a later point.
Thank you in advance.

You could use Variable Length Arrays as function parameters.
void convolve(int isize, // width/height of input (224)
int osize, // width/height of output (112)
int ksize, // width/height of kernel (3)
int stride, // shift between input pixels, between consecutive outputs
int pad, // offset between (0,0) pixels between input and output
int idepth, int odepth, // number of input and output channels
float idata[isize][isize][idepth],
float odata[osize][osize][odepth],
float kdata[idepth][ksize][ksize][odepth])
{
// iterate over the output
for (int oy = 0; oy < osize; ++oy) {
for (int ox = 0; ox < osize; ++ox) {
for (int od = 0; od < odepth; ++od) {
odata[oy][ox][od] = 0;
for (int ky = 0; ky < ksize; ++ky) {
for (int kx = 0; kx < ksize; ++kx) {
// map position in output and kernel to the input
int iy = stride * oy + ky - pad;
int ix = stride * ox + kx - pad;
// use only valid inputs
if (iy >= 0 && iy < isize && ix >= 0 && ix < isize)
for (int id = 0; id < idepth; ++id)
odata[oy][ox][od] += kdata[id][ky][kx][od] * idata[iy][ix][id];
}}
}}}
}
Typical usage would be:
// allocate input
float (*idata)[224][3] = calloc(224, sizeof *idata);
// fill input using idata[y][x][d] syntax
// allocate kernel
float (*kdata)[3][3][32] = calloc(3, sizeof *kdata);
// fill kernel
// allocate output
float (*odata)[112][32] = calloc(112, sizeof *odata);
convolve(224, 112, 3, // input, output, kernel size
2, // stride
1, // pad input by one pixel what will center the kernel
3, 32, // number of input and output channels
idata, odata, kdata);
// free memory if it is no longer used
free(idata); free(odata); free(kdata);
The multidimentional arrays could be allocated with:
float (*arr)[10][20][30] = malloc(sizeof *arr);
however accessing elements is a bit cumbersome due to syntax (*arr)[i][j][j]. Therefore it is simple to use a pointer to the first element of array and allocate multiple subarrays at this pointer.
float (*arr)[20][30] = malloc(10 * sizeof *arr);
or with calloc() with automated zeroing and avoiding overflows.
float (*arr)[20][30] = calloc(10, sizeof *arr);
BTW. I suggest to reorder dimensions of the kernel to ODEPTH x KSIZE x KSIZE x IDEPTH. This would make iterating over the kernel more cache-friendly.

Related

how to correctly use a dynamically allocated array as a (bi)(nth)dimentional one?

If I allocate memory with malloc I get a contiguous chunk of memory:
typedef struct s_point
{
float x;
float y;
float z;
float w;
} t_point;
t_point *matrix = malloc(sizeof(t_point) * (i * j));
But then how can I do something like:
matrix[x][y] = data;
On it? If it it is just a pointer and not a pointer pointer?
If you allocated a one dimensional array that simulates a two-dimensional array like
t_point *matrix = malloc(sizeof(t_point) * (m * n));
where m is the number of rows and n is the number of columns.
Then for two indices i and j you can write for example
for ( size_t i = 0; i < m; i++ )
{
for ( size_t j = 0; j < n; j++ )
{
matrix[i * n + j] = data;
}
}
Actually it is the same if to write
for ( size_t i = 0; i < m * n; i++ )
{
matrix[i] = data;
}
In the both cases the variable data must have the type t_point. Otherwise you need to assign each data member of objects separately as for example
for ( size_t i = 0; i < m * n; i++ )
{
matrix[i].x = x;
matrix[i].y = y;
matrix[i].z = z;
matrix[i].w = w;
}
You can use a pointer to Variable Length Array:
t_point (*matrix)[n] = malloc(sizeof(t_point[m][n]));
It allocates a contiguous chunk of memory where individual elements are accessible via matrix[i][j]. Just remember to call free(matrix) when the memory is no longer needed.
Vlad's and tstanisl's answers are great.
Another way, that support matrix[x][y] syntax, doesn't use VLA and allocate just two continous chunks of memory:
t_point* buf = malloc(sizeof(t_point) * rows * cols);
t_point** matrix = malloc(sizeof(t_point*) * rows);
for(unsigned i = 0; i<rows; ++i) {
matrix[i] = buf + (i*cols);
}
// ...
free(buf);
free(matrix);
It also allows you to do tricks like swapping rows by just reassigning pointers (I don't know if that happens with matrices, but it is sometimes handy with something like argv). If you don't need that, I would probably go for Vlad's method

(C programming) Segfault when combining 2 one-dimensional array into 1 multi-dimensional array

I am writing a program where I take 2 one dimensional arrays and generate a matrix in its most simplified form Ax=b.
This part of the function takes in the arrays A and b. A is A[n*n] and b is b[n]. In this section I tried to combine the two arrays so it looks like an actual matrix.
This code works, however, if n were to be greater than 1023 it would cause a segmentation fault when calling the main function. I was wondering if there is a better way in doing this. When I tried to use the GDB debugger, it stoped at the line Y[i][j] = A[k]; so I think this is the problem that requires fixing
int linsolve ( int n, double A[], double b[], double x []) {
double Y[n][n+1]; //Creating multidimensional matrix
int k = 0;
// Turns the two one dimensional array into one multidimensional
for (int i=0; i < n; i++){ //iterating row
for (int j=0; j < n; j++){ // per column
Y[i][j] = A[k]; // adding from array A to Y
k++;
}
Y[i][n] = b[i]; // adding from Array b to Y
}
I assume you are using a Unix/Linux type system. First find out the stack size by typing
ulimit -s
This is the stack size in kilobytes. On my system it is 8Mb. If you have a 1200x1200 matrix, that will require
1200x1201x8 appx 10Mb
This is why the program segvs. You are creating 10Mb array on an 8Mb stack. The question is, does A or b live on the stack too? You may be getting a segv because the item you are passing through was created on the stack and is larger than the allocated stack.
To solve it, create the array on the heap as #shirish has suggested. An alternative to #shirish's technique would be
int linsolve ( int n, double A[], double b[], double x []) {
double **Y = new double *[n];
double *Ybody = new double[n * (n + 1)];
for (int i = 0; i < n; i++) {
Y[i] = &Ybody[i * (n + 1)];
}
// Turns the two one dimensional array into one multidimensional
int k = 0
for (int i=0; i < n; i++){
for (int j=0; j < n; j++){
Y[i][j] = A[k++];
}
Y[i][n] = b[i];
}
// Do something
// Free up Y before returning
delete [] Y;
delete [] Ybody;
}
// Assuming A has n * n elements
int linsolve ( int n, double A[], double b[], double x []) {
double **Y = new double *[n];
for (int i = 0; i < n; i++) {
Y[i] = new double[n + 1];
}
int k = 0;
// Turns the two one dimensional array into one multidimensional
for (int i=0; i < n; i++){ //iterating row
for (int j=0; j < n; j++){ // per column
Y[i][j] = A[k++]; // adding from array A to Y
}
Y[i][n] = b[i]; // adding from Array b to Y
}
// Do something
// Free up Y before returning
for(int i = 0; i < n; i++) {
delete [] Y[i];
}
delete [] Y;
//Return int here
}

Treating one dimensional array as two dimensional at run time

I have some data stored in a one dimensional array of size say 'M'. Now I need to treat it as a two dimensional array with dimension NxP, where the product of N and P is equal to M. I know the values of N and P only at runtime. How can I implement such a function in C?
int array[M]; /* one dimensional array where some data is stored*/
int** newArray; /* the dimension of newArray should be NxP such that we can access the data in 'array' as a two-dimensional array*/
Just cast it to the appropriate array pointer type:
int (*newArray)[N] = (int (*)[N])array;
After that, you can access the array with:
for(int y = 0; y < P; y++) {
for(int x = 0; x < N; x++) {
array[y][x] = 42;
}
}
This is equivalent to the following indexing:
for(int y = 0; y < P; y++) {
for(int x = 0; x < N; x++) {
newArray[y*N + x] = 42;
}
}
This works even if N is only known at run time since C99. Note that you do not need to set up an index array that way, as you would have to do if you used an int**.
You don't need to define a new array. You can use the existing one.
Assuming you know N and P, and N is the number of rows, you can access item (i,j) as:
array[i*N + j]
You could do it like this:
int ** newArray = malloc(sizeof(int*) * N);
for (i = 0; i < N; ++i) {
newArray[i] = array[i * J];
}
This will make an array that "looks" just like a dynamically allocated 2D array of N rows and J columns, but in fact points to the rows of the 1D array.
That way if you have functions to operate on 2D arrays already, you don't need to rewrite them to use the 1D syntax described in other answer.
The runtime makes this a little harder, but :-
newArray = malloc( sizeof( int*) * N ); /* create an array of pointers.
{
size_t i;
for( i = 0; i < N; i++ ) {
newArray[i] = &array[ i* P];
}
}
/* Now newArray[i][j] is usable */
You can just cast the 1d array as the 2d array you want. It's just a block of memory.
int _tmain(int argc, _TCHAR* argv[])
{
int oneDArray[12] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
int(*twoDArray)[3] = (int(*)[3])&oneDArray[0]; // This is the magic line!
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 3; j++)
{
printf("i: %d j: %d value: %d\r\n", i, j, twoDArray[i][j]);
}
}
_getch();
return 0;
}
Also see question Convert Array Question
There's some inherent unsafeness in doing this, but your question states that NxP=M, so if that's true it will work. People will frown at it though.

int** vs int[const][const] differences

I was writing a code the other day and I found it rather strange, that int** and int[][] does not behave the same way. Can anyone point out the differences between them? Below is my sample code, which fails with a segmentation fault, if I pass constant size 2d array, while it does work fine when I pass a dinamically allocated 2d array.
I am confused mainly because ant int[] array works the same as int*.
#include<stdio.h>
#include<stdlib.h>
void sort_by_first_row(int **t, int n, int m)
{
int i, j;
for(i = m-1 ; i > 0 ; --i)
{
for(j = 0 ; j < i; ++j)
{
if(t[0][j] < t[0][j+1])
{
int k;
for(k = 0 ; k < n ;++k)
{
int swap;
swap = t[k][j];
t[k][j] = t[k][j+1];
t[k][j+1] = swap;
}
}
}
}
}
int main(void) {
int i, j;
/* Working version */
/*int **t;
t =(int**) malloc(3*sizeof(int*));
for(i = 0; i < 3; ++i)
{
t[i] = (int*) malloc(6*sizeof(int));
}*/
/*WRONG*/
int t[3][6];
t[0][0] = 121;
t[0][1] = 85;
t[0][2] = 54;
t[0][3] = 89;
t[0][4] = 879;
t[0][5] = 11;
for( i = 0; i < 6; ++i )
t[1][i] = i+1;
t[2][0] = 2;
t[2][1] = 4;
t[2][2] = 5;
t[2][3] = 3;
t[2][4] = 1;
t[2][5] = 6;
sort_by_first_row(t, 3, 6);
for(i = 0; i < 3; ++i)
{
for(j = 0; j < 6; ++j)
printf("%d ", t[i][j]);
printf("\n");
}
return 0;
}
So based on the below answers I realize, that a multidimensional array is stored continuously in a row major order. After some modification, the below code works:
#include<stdio.h>
#include<stdlib.h>
void sort_by_first_row(int *t, int n, int m)
{
int i, j;
for(i = m-1 ; i > 0 ; --i)
{
for(j = 0 ; j < i; ++j)
{
if(t[j] < t[j+1])
{
int k;
for(k = 0 ; k < n ;++k)
{
int swap;
swap = t[k*m + j];
t[k*m + j] = t[k*m + j+1];
t[k*m + j+1] = swap;
}
}
}
}
}
int main(void) {
int i, j;
/* Working version */
/*int **t;
t =(int**) malloc(3*sizeof(int*));
for(i = 0; i < 3; ++i)
{
t[i] = (int*) malloc(6*sizeof(int));
}*/
/*WRONG*/
int t[3][6];
t[0][0] = 121;
t[0][1] = 85;
t[0][2] = 54;
t[0][3] = 89;
t[0][4] = 879;
t[0][5] = 11;
for( i = 0; i < 6; ++i )
t[1][i] = i+1;
t[2][0] = 2;
t[2][1] = 4;
t[2][2] = 5;
t[2][3] = 3;
t[2][4] = 1;
t[2][5] = 6;
sort_by_first_row(t, 3, 6);
for(i = 0; i < 3; ++i)
{
for(j = 0; j < 6; ++j)
printf("%d ", t[i][j]);
printf("\n");
}
return 0;
}
My new question is this: How to modify the code, so that the procedure works with int[][] and int** also?
Realize that int **t makes t a pointer to a pointer, while int t[3][6] makes t an array of an array. In most cases, when an array is used in an expression, it will become the value of the address of its first member. So, for int t[3][6], when t is passed to a function, the function will actually be getting the value of &t[0], which has type pointer to an array (in this case, int (*)[6]).
The type of what is being pointed at is important for how the pointer behaves when indexed. When a pointer to an object is incremented by 5, it points to the 5th object following the current object. Thus, for int **t, t + 5 will point to the 5th pointer, while for int (*t)[M], t + 5 will point to the 5th array. That is, the result of t + 5 is the same as the result of &t[5].
In your case, you have implemented void sort_by_first_row(int **t, int n, int m), but you are passing it an incompatible pointer. That is, the type of &t[0] (which is what t will become in main) is not the same as what the function wants, a int **t. Thus, when the sorting function starts to use that address, it will think its indexing into pointers, when the underlying structure is an array of arrays.
int** is quite different from int[][]. int** is simply a pointer to a pointer and would appear like the following:
in reality, you can access the entire multidimensional array with simply int* pointing to the first element, and doing simple math from that.
Here is the result of the separate allocations (in your commented code):
However when you allocate a multidimensional array, all of the memory is contiguous, and therefore easy to do simple math to reach the desired element.
int t[3][6];
int *t = (int*) malloc((3 * 6) * sizeof(int)); // <-- similarly
This will result in a contiguous chunk of memory for all elements.
You certainly can use the separate allocations, however you will need to walk the memory differently.
Hope this helps.
int t[3][6] is very nearly the same thing as int t[18]. A single contiguous block of 18 integers is allocated in both cases. The variable t provides the address of the start of this block of integers, just like the one-dimensional case.
Contrast this with the case you have marked as "working", where t gives you the address of a block of 3 pointers, each of which points to a block of memory with 6 integers. It's a totally different animal.
The difference between t[3][6] and t[18] is that the compiler remembers the size of each dimension of the array, and automatically converts 2D indices into 1D offsets. For example, the compiler automatically converts t[1][2] into *(t + 1*6 + 2) (equivalent to t[8] if it were declared as a one-dimensional array).
When you pass a multi-dimensional array to a function, there are two ways to handle it. The first is to declare the function argument as an array with known dimension sizes. The second is to treat your array like a 1D array.
If you are going to declare the size of your array, you would declare your function like this:
void sort_by_first_row(int t[][6], int n)
or this
void sort_by_first_row(int t[3][6])
You either have to declare all array dimension sizes, or you can leave out the first size. In both cases, you access elements of t using t[i][j]; you've given the compiler enough information to do the offset math that converts from 2D notation to a 1D index offset.
If you treat it as a 1D array, you have to pass the array dimensions and then do the offset math yourself.
Here's a full working example, where f and f2 both generate the same output:
void f(int* t, int m, int n)
{
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
std::cout << t[i * n + j] << " ";
std::cout << std::endl;
}
void f2(int t[][6], int m)
{
for (int i = 0; i < m; i++)
for (int j = 0; j < 6; j++)
std::cout << t[i][j] << " ";
std::cout << std::endl;
}
int main()
{
int t[3][6];
int val = 1;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 6; j++)
{
t[i][j] = val;
val++;
}
}
f(&(t[0][0]), 3, 6);
f2(t, 3);
return 0;
}
One thing to note is the hack-ish way I had to pass t to f. It's been a while since I wrote in C/C++, but I remember being able to pass t directly. Maybe somebody can fill me in on why my current compiler won't let me.
A int ** is a pointer to a pointer to an int, and can be a pointer to an array of pointers to arrays of ints. A int [][] is a 2-dimensional array of ints. A two-dimensional array is exactly the same as a one-dimensional array in C in one respect: It is fundamentally a pointer to the first object. The only difference is the accessing, a two-dimensional array is accessed with two different strides simultaneously.
Long story short, a int[][] is closer to an int* than an int**.

Assign Memory to 3D array using triple pointer

I have to assign memory to a 3D array using a triple pointer.
#include <stdio.h>
int main()
{
int m=10,n=20,p=30;
char ***z;
z = (char***) malloc(sizeof(char**)*m*n*p);
return 0;
}
Is this correct way of doing this?(I think what i am doing is incorrect.)
To completely allocate a 3D dynamic array you need to do something like the following:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int m=10,n=20,p=30;
char ***z;
z = malloc(m * sizeof(char **));
assert(z != NULL);
for (i = 0; i < m; ++i)
{
z[i] = malloc(n * sizeof(char *));
assert(z[i] != NULL);
for (j = 0; j < n; ++j)
{
z[i][j] = malloc(p);
assert(z[i][j] != NULL);
}
}
return 0;
}
Freeing the data is left as an exercise for the reader.
There's no need to cast the return value of malloc(), in C.
And if you expect to store m * n * p characters directly (and compute the address yourself), then you should of course not scale the allocation by the size of a char **.
You mean:
int m = 10, n = 20, p = 30;
char *z = malloc(m * n * p * sizeof *z);
This will allocate 10 * 20 * 30 = 6000 bytes. This can be viewed as forming a cube of height p, with each "slice" along the vertical axis being n * m bytes.
Since this is for manual addressing, you cannot use e.g. z[k][j][i] to index, instead you must use z[k * n * m + j * m + i].
If you don't need the memory to be allocated in a single, contiguous chunk (which IME is the usual case), you would do something like this:
char ***z;
z = malloc(sizeof *z * m); // allocate m elements of char **
if (z)
{
int i;
for (i = 0; i < m; i++)
{
z[i] = malloc(sizeof *z[i] * n); // for each z[i],
if (z[i]) // allocate n elements char *
{
int j;
for (j = 0; j < n;j++)
{
z[i][j] = malloc(sizeof *z[i][j] * p); // for each z[i][j],
if (z[i][j]) // allocate p elements of char
{
// initialize each of z[i][j][k]
}
}
}
}
}
Note that you will need to free this memory in reverse order:
for (i = 0; i < m; i++)
{
for (j = 0; j < n; j++)
free(z[i][j];
free(z[i]);
}
free(z);
If you really need the memory to be allocated in a contiguous chunk, you have a couple of choices. You could allocate a single block and compute your offsets manually:
char *z = malloc(sizeof *z * m * n * p); // note type of z!
...
z[i * m + j * n + k] = some_value();
When you're done, you just need to do a single free:
free(z);
If you have a C99 compiler or a C11 compiler that supports variable-length arrays, you could do something like this:
int m=..., n=..., p=...;
char (*z)[n][p] = malloc(sizeof *z * m);
This declares z as a pointer to an nxp array of char, and we allocate m such elements. The memory is allocated contiguously and you can use normal 3-d array indexing syntax (z[i][j][k]). Like the above method, you only need a single free call:
free(z);
If you don't have a C99 compiler or a C11 compiler that supports VLAs, you would need to make n, and p compile-time constants, such as
#define n 20
#define p 30
otherwise that last method won't work.
Edit
m doesn't need to be a compile-time constant in this case, just n and p.
You would need the following nested loop -
z = (char**)malloc(sizeof(char*) * m);
for (int i = 0; i < m; ++i)
{
*(z + i) = (char*)malloc(sizeof(char*) * n);
for (int j = 0; j < n; ++j)
{
*(*(z + i)) = (char)malloc(p);
}
}
May not be synactically accurate, but it should be something along these lines.
You want sizeof(char) not sizeof(char**) as the latter will give you the size of a pointer which on most modern systems will be 4 bytes instead of the 1 you're expecting.

Resources