Creating a crossword puzzle generator. The grid size is chosen by input and the grid will be generated. I'd like the grid to be an object with rows and columns but also a 2d array which will allow me to divide the grid into smaller sections for randomising between blank and numbered squares. I am not sure where to implement it.
It has to be a 2d array as I will do dividing and inverting the layout.
Here is my Grid class with some methods. (And the rest)
class Grid
{
int rows; //x
int columns; //y
Square field;
public:
void SetXY(int x, int y)
{
rows = x;
columns = y;
return;
}
public:
void DisplaySize()
{
cout << "Rows = ", rows, "Columns = ", columns;
}
};
The simplest way to implement a 2D array is to use a std::array<std::array<>> or std::vector<std::vector<>> - depending on whether it needs to be a static or dynamically sized array.
But, you can also just use a one dimensional std::array or std::vector and then just get the second dimension by indexing like row*size_of_row+column.
In Python I normally use functions like vstack, stack, etc to easily create a 3D array by stacking 2D arrays one onto another.
Is there any way to do this in C++?
In particular, I have loaded a image into a Mat variable with OpenCV like:
cv::Mat im = cv::imread("image.png", 0);
I would like to make a 3D array/Mat of N layers by stacking copies of that Mat variable.
EDIT: This new 3D matrix has to be "travellable" by adding an integer to any of its components, such that if I am in the position (x1,y1,1) and I add +1 to the last component, I arrive to (x1,y1,2). Similarly for any of the coordinates/components of the 3D matrix.
SOLVED: Both answers from #Aram and #Nejc do exactly what expected. I set #Nejc 's answer as the correct one for his shorter code.
The Numpy function vstack returns a contiguous array. Any C++ solution that produces vectors or arrays of cv::Mat objects does not reflect the behaviour of vstack in this regard, becase separate "layers" belonging to individual cv::Mat objects will not be stored in contiguous buffer (unless a careful allocation of underlying buffers is done in advance of course).
I present the solution that copies all arrays into a three-dimensional cv::Mat object with a contiguous buffer. As far as the idea goes, this answer is similar to Aram's answer. But instead of assigning pixel values one by one, I take advantage of OpenCV functions. At the beginning I allocate the matrix which has a size N X ROWS X COLS, where N is the number of 2D images I want to "stack" and ROWS x COLS are dimensions of each of these images.
Then I make N steps. On every step, I obtain the pointer to the location of the first element along the "outer" dimension. I pass that pointer to the constructor of temporary Mat object that acts as a kind of wrapper around the memory chunk of size ROWS x COLS (but no copies are made) that begins at the address that is pointed-at by pointer. I then use copyTo method to copy i-th image into that memory chunk. Code for N = 2:
cv::Mat img0 = cv::imread("image0.png", CV_IMREAD_GRAYSCALE);
cv::Mat img1 = cv::imread("image1.png", CV_IMREAD_GRAYSCALE);
cv::Mat images[2] = {img0, img1}; // you can also use vector or some other container
int dims[3] = { 2, img0.rows, img0.cols }; // dimensions of new image
cv::Mat joined(3, dims, CV_8U); // same element type (CV_8U) as input images
for(int i = 0; i < 2; ++i)
{
uint8_t* ptr = &joined.at<uint8_t>(i, 0, 0); // pointer to first element of slice i
cv::Mat destination(img0.rows, img0.cols, CV_8U, (void*)ptr); // no data copy, see documentation
images[i].copyTo(destination);
}
This answer is in response to the question above of:
In Python I normally use functions like vstack, stack, etc to easily create a 3D array by stacking 2D arrays one onto another.
This is certainly possible, you can add matrices into a vector which would be your "stack"
For instance you could use a
std::vector<cv::Mat>>
This would give you a vector of mats, which would be one slice, and then you could "layer" those by adding more slices vector
If you then want to have multiple stacks you can add that vector into another vector:
std::vector<std::vector<cv::Mat>>
To add matrix to an array you do:
myVector.push_back(matrix);
Edit for question below
In such case, could I travel from one position (x1, y1, z1) to an immediately upper position doing (x1,y1,z1+1), such that my new position in the matrix would be (x1,y1,z2)?
You'll end up with something that looks a lot like this. If you have a matrix at element 1 in your vector, it doesn't really have any relationship to the element[2] except for the fact that you have added it into that point. If you want to build relationships then you will need to code that in yourself.
You can actually create a 3D or ND mat with opencv, you need to use the constructor that takes the dimensions as input. Then copy each matrix into (this case) the 3D array
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main() {
// Dimensions for the constructor... set dims[0..2] to what you want
int dims[] = {5, 5, 5}; // 5x5x5 3d mat
Mat m = Mat::zeros(5, 5, CV_8UC1);
for (size_t i = 0; i < 5; i++) {
for (size_t k = 0; k < 5; k++) {
m.at<uchar>(i, k) = i + k;
}
}
// Mat with constructor specifying 3 dimensions with dimensions sizes in dims.
Mat 3DMat = Mat(3, dims, CV_8UC1);
// We fill our 3d mat.
for (size_t i = 0; i < m2.size[0]; i++) {
for (size_t k = 0; k < m2.size[1]; k++) {
for (size_t j = 0; j < m2.size[2]; j++) {
3DMat.at<uchar>(i, k, j) = m.at<uchar>(k, j);
}
}
}
// We print it to show the 5x5x5 array.
for (size_t i = 0; i < m2.size[0]; i++) {
for (size_t k = 0; k < m2.size[1]; k++) {
for (size_t j = 0; j < m2.size[2]; j++) {
std::cout << (int) 3DMat.at<uchar>(i, k, j) << " ";
}
std::cout << endl;
}
std::cout << endl;
}
return 0;
}
Based on the question and comments, I think you are looking for something like this:
std::vector<cv::Mat> vec_im;
//In side for loop:
vec_im.push_back(im);
Then, you can access it by:
Scalar intensity_1 = vec_im[z1].at<uchar>(y, x);
Scalar intensity_2 = vec_im[z2].at<uchar>(y, x);
This assumes that the image is single channel.
I've got a minor problem.
I'm using multidimensional-vectors and I want to insert some values to it at a given position. I'm making a sudoku in wxWidgets and i'm getting the tiles the player have put in and wanting to store them in my mVector.
The mVector looks like this.
vector< vector<string> > board{9, vector<string>(9)};
And at first i've added values just like this.
board[row][col] = value;
"value" is a string and row/col are ints.
Is this a legit way of adding values to the mVector? I'm asking this because when I update the board, by doing this above, I for some reason can't run my other functions where i'm solving the board, giving a hint to the board and so on. Before i store the new values to it all the functions works correkt. Do I maby need to use some other type of build in functions for the vector like insert, push_back or something instead?
Since you declared the vector as size 9x9, yes that is a valid way of assigning values.
Otherwise you could declare the board as
vector<vector<string>> board;
Then fill it with
for (int i = 0; i < 9; ++i)
{
vector<string> row;
for (int j = 0; j < 9; ++j)
{
row.push_back(value); // where value is whatever you want
}
board.push_back(row);
}
But again, once the board is of size 9x9, you can simply assign a value at any cell for example
board[2][4] = "hello";
Working example
I created a dynamic array.
typedef float* DynamicMatrix[MAT_SIZE];
DynamicMatrix matDyn;
// allocate rows and initialize to 0
for (r = 0; r < MAT_SIZE; r++) {
matDyn[r] = new float[MAT_SIZE];
for (c = 0; c < MAT_SIZE; c++) {
(matDyn[r])[c] = 0; // IS THIS CORRECT???
}
}
The whole idea was that I create an array of pointers which is my spine of a matrix I am creating. This spine is the leftmost-vertical part of the matrix. Each row of this array will point to another array of floats, therefore making it a dynamic 2d array.
I am unsure how to access an element of this matrix. Please let me know how to.
You can access it just like a "normal" matrix:
matDyn[r][c].
You can just access it by stating matDyn[r][c]
My error was that later in my program(not shown), I was trying to set matDyn[MAT_SIZE][MAT_SIZE] which isn't possible (it can only be matDyn[MAT_SIZE-1][MAT_SIZE-1]) and that is why I was getting my segmentation error.
2D arrays such as:Cell **scoreTable.After allocating:
scoreTable = new Ceil*[10];
for(int i = 0 ;i<10;i++)
scoreTable[i] = new Ceil[9];
And I want to save the value like this:scoreTable[i][j]= new Ceil(i,j) in heap,and this can not work in c++.Thanks for help.
scoreTable[i][j]= new Ceil(i,j). You are trying to put Cell* into Cell.
You have to create 2d array of pointers:
auto scoreTable = new Ceil**[10];
for(int i = 0 ;i<10;i++)
scoreTable[i] = new Ceil*[9];
But much better is to use vector:
std::vector< std::vector<Ceil*> > table;
table.resize(10);
for (int i = 0; i < 10; ++i)
{
table[i].resize(9, NULL);
}
table[3][4] = new Ceil();
Once you've allocated your array like this, you'll already have a 2D array of Cell, so you don't need to use the new keyword to change the value.
If you give your Cell class a method like SetValue(i,j), then you can use it like this: scoreTable[i][j].SetValue(i,j);
I want to suggest using std::vector instead. It is much easier to keep track of.
You can replace all of the code above with
std::vector< std::vector< Cell> > scoreTable(10, std::vector<Cell>(9));
This will create scoreTable, which is a vector containing 10 elements of vector<Cell>, each containing 9 cells. In other word, the desired 2D table.
You access the elements in the same way. scoreTable[i][j], where i goes fron 0 to 9, and j from 0 to 8.
If you want to expand with a new row, just say:
scoreTable.push_bach(std::vector<Cell>(9));
For a new column:
for(size_t row = 0; row < scoreTable.size(); ++row) {
scoreTable[row].push_back(Cell());
}
No need for new or delete.