Reputation: 8857
Currently I'm learning C++ and I've been trying to create a simple image processing library for learning purposes.
One of my features is eroding an image. As input it has two 2d arrays, and it returns another 2d array. All these arrays are of variable size. Let's give you an example of what I want to achieve.
int image[5][5] =
{
{'0', '0', '0', '0','0'},
{'0', '1', '1', '1','0'},
{'0', '0', '1', '0','0'},
{'0', '1', '1', '1','0'},
{'0', '0', '1', '0','0'},
};
int kernel[3][3] =
{
{'0', '1', '0'},
{'1', '1', '1'},
{'0', '1', '0'},
};
Then I want to pass them to my function (this doesn't compile, but it serves as an example of what I want).
int** erode(int image[][], int kernel[][]);
So far, I've read quite a bit about this. All I've read is that the columns can be of variable length, but the rows can't. So I should be change it to the following:
int** erode(int image[][5], int kernel[][3]);
But that's not really want I want either, because well, the image can be 10*10 and the kernel could be 5*5. So this isn't optimal in this situation either.
Then what I've read is creating a class, that internally stores the image as a 1d array and makes it look like a 2d array. Also I've read about using the Boost.MultiArray
class to do this. But well I'm not too happy about that either. Because then I'm forcing the people that use it to also use those classes. And I think it's creating a lot of complexity for something that seems really simple (at least it is in C#)
To be honest, I can't imagine there isn't an easier way to do this. Optimally I'd say only use classes/methods from the standard C++11
library. How would you solve this problem?
Timo
Upvotes: 1
Views: 707
Reputation: 8587
Returning a 2D vector is less complicated.
#include <vector>
#include <iostream>
using namespace std;
typedef std::vector<std::vector< int> > vector2D;
int image[5][5] =
{
{0, 0, 0, 0, 0},
{0, 1, 1, 1, 0},
{0, 0, 1, 0, 0},
{0, 1, 1, 1, 0},
{0, 0, 1, 0, 0},
};
int kernel[5][5] =
{
{0, 0, 0, 0, 0},
{0, 1, 1, 1, 0},
{0, 0, 1, 0, 0},
{0, 1, 1, 1, 0},
{0, 0, 1, 0, 0},
};
vector2D erode(int image[5][5], int kernel[5][5])
{
vector2D image_process_data ;
//create 2D vector array
image_process_data.resize(5);
for(int i = 0; i < 5; i++)
{
image_process_data[i].resize(5);
}
//perform calculations
for (int ix = 0; ix < 5; ix++)
{
for (int iy = 0; iy < 5; iy++)
{
image_process_data[ix][iy] = image[ix][iy] + kernel[ix][iy];
}
}
//return the 2D array
return image_process_data;
}
int main( )
{
vector2D new_image;
new_image = erode(image, kernel);
//display new_image
for (int ix = 0; ix < 5; ix++)
{
for (int iy = 0; iy < 5; iy++)
{
cout<<new_image[ix][iy]<<" ";
}
cout<<"\n";
}
return 0;
}
How to set up a 2D vector array:
#include <vector>
#include <iostream>
using std namespace;
#define HEIGHT 5
#define WIDTH 3
int main() {
vector<vector<double> > array2D;
// Set up sizes. (HEIGHT x WIDTH)
array2D.resize(HEIGHT);
for (int i = 0; i < HEIGHT; ++i)
array2D[i].resize(WIDTH);
// Put some values in
array2D[1][2] = 6.0;
array2D[3][1] = 5.5;
return 0;
}
Upvotes: 2
Reputation: 6647
I would pass the array as a pointer and also pass another parameter which defines the size of the array.
int** erode(int* image, size_t imageSize, int* kernel, size_t kernelSize);
An array is just a list of consecutive variables when in memory and the array variable such as image[5][5]
just points to the first element of the array. To pass the arrays you have shown into the function you would use.
int** ret = errode(image, 5, kernel, 3);
Be careful when using this however because it could easily cause a segmentation fault but when used correctly, it will be fine. Also this assumes that the array is square, to overcome this limitation just pass in another parameter which describes the other dimension of the array.
Update
I see Anycorn has posted a very nice solution. His is a safer solution however will require you knowing the size of the array at compile time. If this is the case I would recommend you use what he has said.
Upvotes: 3
Reputation: 39380
What you wrote about internally storing 1D array is essentially the best solution. Using 2-dimensional array is usually dangerous and can easily lead to errors. If you don't want to create whole class, consider:
const int SizeX = 10, SizeY = 10;
int Array[SizeX*SizeY];
inline int& Get (int x, int y) { return Array[SizeX*y+x]; }
Of course, it is the very simple example, but usually solves a lot of errors.
struct MyOwnArray
{
int SizeX, int SizeY;
int* Data;
inline int& Get (int x, int y) { return Data[SizeX*y+x]; }
}
The code above will allow you to pass references to this struct in a convenient way. And it's only 6 lines of code!
Of course, the code above would need some more code; for example for allocating memory. However, if you carefully program all these parts, you will be pretty safe to use it and it should be well-protected from bugs.
Upvotes: 2
Reputation: 51465
see if you can use templates, eg:
template<int N>
void f(int (&arr)[N][N]);
if arrays have compile time bounds. but really you should consider boost, armadillo, or tvmet
Upvotes: 4