I am trying to implement Common Spatial Pattern (CSP) in my C++ application using OpenCV. I now have my CSPMat (14 x 14 dimension). I need to select the top two and last two rows of CSPMat and put it in some matrix, say rMat (which will be 4 x 14 dimension).
double dat[196] = {-0.40643,0.56297,0.24768,-0.21843,0.0020409,-0.071199,
-0.074495,-0.28166,0.04003,0.036224,0.35441,0.015098,-0.42881,0.07902,0.032339,
-0.35331,-0.31707,0.19567,-0.074969,0.14364,-0.15196,-0.29226,0.1002,0.036617,0.71354,
0.11413,-0.0096681,-0.25867,-0.21026,0.20852,-0.21658,0.12999,0.19116,-0.24877,
-0.072983,0.058441,-0.046685,-0.44232,-0.20248,0.58385,0.11024,-0.38662,-0.32475,
-0.20537,-0.24724,0.14887,0.018617,0.24893,-0.26009,0.13939,0.23181,-0.31028,
-0.36689,0.19742,-0.17256,0.51606,-0.042443,-0.19961,0.42573,0.045363,0.056726,0.12472,
-0.24221,0.012676,0.10124,-0.8274,0.015785,-0.031468,-0.036927,-0.0038262,
-0.10933,-0.27092,0.051624,0.11862,0.47132,-0.25812,0.37111,-0.014466,-0.39592,
-0.34548,0.059974,-0.3252,-0.10813,0.27212,-0.1103,0.058941,0.020318,-0.16778,0.1312,
0.10562,0.26175,0.088419,-0.13917,0.89163,0.17807,0.063471,-0.0121,-0.028945,
-0.31825,0.039514,0.11289,0.035025,0.041933,-0.23419,-0.5758,0.50917,-0.19886,0.3074,
0.21067,-0.15481,0.10478,0.14968,-0.013939,0.107,-0.1227,-0.062843,0.03534,-0.0043759,
0.11149,-0.0075394,-0.027777,-0.96728,0.094132,-0.065276,0.059764,0.050088,0.088534,
0.091467,0.040229,-0.22834,-0.043955,0.71084,-0.49935,0.16458,-0.22962,-0.23088,
0.079861,0.037212,-0.0189,-0.17949,-0.037697,-0.090957,-0.30513,0.2583,-0.058026,
0.37724,-0.14635,-0.13089,-0.4395,0.36025,-0.11406,0.36466,0.071945,0.41491,0.46926,
0.018588,-0.40528,-0.42208,0.083901,-0.31978,0.2096,0.076437,-0.088993,-0.42626,
0.17476,0.18124,-0.158,0.0011488,0.27109,-0.0087883,-0.49613,0.68606,0.10353,0.04053,
-0.29863,0.029601,0.13528,0.2491,-0.15446,-0.03623,-0.039323,-0.010952, 0.12699,
0.040763,0.048881,-0.5927,0.29154,0.047348,-0.31823,-0.19709,0.2386,0.51098,-0.055752,
-0.11963,0.11053,0.2313};
cv::Mat CSPMat(14,14, cv::DataType<float>::type);
int Ai =0;
int Aj = 0;
for (Ai = 0; Ai < 14; Ai++){
for (Aj = 0; Aj < 14; Aj++){
CSPMat.at<float>(Ai, Aj) = dat[Aj+(14*Ai)];
}
}
How can I construct the rMat matrix (4x14) taking the top two and bottom two rows of CSPMat?
Any help is greatly appreciated.
first, make your life easier, and have a float array for the data, then you can just setup a Mat constructor with it, and save the copy loopings.
then you can just grab some Mat.row(i), push them into a new Mat, and finally reshape to the desired row-count:
float dat[196] = {-0.40643,0.56297,0.24768,-0.21843,0.0020409,-0.071199,
-0.074495,-0.28166,0.04003,0.036224,0.35441,0.015098,-0.42881,0.07902,0.032339,
-0.35331,-0.31707,0.19567,-0.074969,0.14364,-0.15196,-0.29226,0.1002,0.036617,0.71354,
0.11413,-0.0096681,-0.25867,-0.21026,0.20852,-0.21658,0.12999,0.19116,-0.24877,
-0.072983,0.058441,-0.046685,-0.44232,-0.20248,0.58385,0.11024,-0.38662,-0.32475,
-0.20537,-0.24724,0.14887,0.018617,0.24893,-0.26009,0.13939,0.23181,-0.31028,
-0.36689,0.19742,-0.17256,0.51606,-0.042443,-0.19961,0.42573,0.045363,0.056726,0.12472,
-0.24221,0.012676,0.10124,-0.8274,0.015785,-0.031468,-0.036927,-0.0038262,
-0.10933,-0.27092,0.051624,0.11862,0.47132,-0.25812,0.37111,-0.014466,-0.39592,
-0.34548,0.059974,-0.3252,-0.10813,0.27212,-0.1103,0.058941,0.020318,-0.16778,0.1312,
0.10562,0.26175,0.088419,-0.13917,0.89163,0.17807,0.063471,-0.0121,-0.028945,
-0.31825,0.039514,0.11289,0.035025,0.041933,-0.23419,-0.5758,0.50917,-0.19886,0.3074,
0.21067,-0.15481,0.10478,0.14968,-0.013939,0.107,-0.1227,-0.062843,0.03534,-0.0043759,
0.11149,-0.0075394,-0.027777,-0.96728,0.094132,-0.065276,0.059764,0.050088,0.088534,
0.091467,0.040229,-0.22834,-0.043955,0.71084,-0.49935,0.16458,-0.22962,-0.23088,
0.079861,0.037212,-0.0189,-0.17949,-0.037697,-0.090957,-0.30513,0.2583,-0.058026,
0.37724,-0.14635,-0.13089,-0.4395,0.36025,-0.11406,0.36466,0.071945,0.41491,0.46926,
0.018588,-0.40528,-0.42208,0.083901,-0.31978,0.2096,0.076437,-0.088993,-0.42626,
0.17476,0.18124,-0.158,0.0011488,0.27109,-0.0087883,-0.49613,0.68606,0.10353,0.04053,
-0.29863,0.029601,0.13528,0.2491,-0.15446,-0.03623,-0.039323,-0.010952, 0.12699,
0.040763,0.048881,-0.5927,0.29154,0.047348,-0.31823,-0.19709,0.2386,0.51098,-0.055752,
-0.11963,0.11053,0.2313};
cv::Mat CSPMat(14,14, cv::DataType<float>::type, dat);
Mat top_bot_2;
top_bot_2.push_back( CSPMat.row(0) );
top_bot_2.push_back( CSPMat.row(1) );
top_bot_2.push_back( CSPMat.row(12) );
top_bot_2.push_back( CSPMat.row(13) );
Mat res = top_bot_2.reshape(1,4);
cerr << res << endl;
[-0.40643001, 0.56296998, 0.24767999, -0.21843, 0.0020409001, -0.071199, -0.074495003, -0.28165999, 0.040029999, 0.036224, 0.35440999, 0.015098, -0.42881, 0.079020001;
0.032338999, -0.35330999, -0.31707001, 0.19566999, -0.074969001, 0.14364, -0.15196, -0.29225999, 0.1002, 0.036617, 0.71354002, 0.11413, -0.0096680997, -0.25867;
0.27109, -0.0087882997, -0.49612999, 0.68606001, 0.10353, 0.04053, -0.29863, 0.029601, 0.13528, 0.2491, -0.15446, -0.036230002, -0.039322998, -0.010952;
0.12699001, 0.040762998, 0.048881002, -0.5927, 0.29154, 0.047348, -0.31823, -0.19709, 0.2386, 0.51098001, -0.055752002, -0.11963, 0.11053, 0.2313]
Related
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 have a questions about a for loop and its return value. This is C++ code, and I'm using openCV 2.4V.
Input to this function is max value of 600 images with pooling.
600 images << pooling << max value points.
The size of "res" matrix is 600x128 and vec.size() = 600.
For me, within the for loop, the res never get updated, however return value is not zeros.
I suspected
"ptmat.copyTo(subView)"
because, I thought that is not necessary line. However when I took that out, res did not get updated(being zero like initial Mat). Can anybody explain how does the res value get updated?
Also why does this function is called concatenate..?
Mat
concatenateMat(vector<vector<Mat> > &vec) {
int subFeatures = vec[0][0].rows * vec[0][0].cols;
int height = vec[0].size() * subFeatures;
int width = vec.size();
Mat res = Mat::zeros(height, width, CV_64FC1);
for (int i = 0; i<vec.size(); i++) {
for (int j = 0; j<vec[i].size(); j++) {
Rect roi = Rect(i, j * subFeatures, 1, subFeatures);
Mat subView = res(roi);
Mat ptmat = vec[i][j].reshape(0, subFeatures);
ptmat.copyTo(subView);
}
}
return res;
}
According to OpenCV documentation, the Mat::operator() does not make a copy of matrix data, thus any change to subView matrix object in the loop will be reflected in res matrix object as well. That's the line you've mentioned:
ptmat.copyTo(subView);
It's called concatenate because it concatenates 2D vector of Mat objects into a single one.
What is the fastest way of assigning a vector to a matrix row in a loop? I want to fill a data matrix along its rows with vectors. These vectors are computed in a loop. This loop last until all the entries of data matrix is filled those vectors.
Currently I am using cv::Mat::at<>() method for accessing the elements of the matrix and fill them with the vector, however, it seems this process is quite slow. I have tried another way by using cv::Mat::X.row(index) = data_vector, it works fast but fill my matrix X with some garbage values which I can not understand, why.
I read that there exists another way of using pointers (fastest way), however, I can not able to understand. Can somebody explain how to use them or other different methods?
Here is a part of my code:
#define OFFSET 2
cv::Mat im = cv::imread("001.png", CV_LOAD_IMAGE_GRAYSCALE);
cv::Mat X = cv::Mat((im.rows - 2*OFFSET)*(im.cols - 2*OFFSET), 25, CV_64FC1); // Holds the training data. Data contains image patches
cv::Mat patch = cv::Mat(5, 5, im.type()); // Holds a cropped image patch
typedef cv::Vec<float, 25> Vec25f;
int ind = 0;
for (int row = 0; row < (im.rows - 2*OFFSET); row++){
for (int col = 0; col < (im.cols - 2*OFFSET); col++){
cv::Mat temp_patch = im(cv::Rect(col, row, 5, 5)); // crop an image patch (5x5) at each pixel
patch = temp_patch.clone(); // Needs to do this because temp_patch is not continuous in memory
patch.convertTo(patch, CV_64FC1);
Vec25f data_vector = patch.reshape(0, 1); // make it row vector (1X25).
for (int i = 0; i < 25; i++)
{
X.at<float>(ind, i) = data_vector[i]; // Currently I am using this way (quite slow).
}
//X_train.row(ind) = patch.reshape(0, 1); // Tried this but it assigns some garbage values to the data matrix!
ind += 1;
}
}
To do it the regular opencv way you could do :-
ImageMat.row(RowIndex) = RowMat.clone();
or
RowMat.copyTo(ImageMat.row(RowIndex));
Haven't tested for correctness or speed.
Just a couple of edits in your code
double * xBuffer = X.ptr<double>(0);
for (int row = 0; row < (im.rows - 2*OFFSET); row++){
for (int col = 0; col < (im.cols - 2*OFFSET); col++){
cv::Mat temp_patch = im(cv::Rect(col, row, 5, 5)); // crop an image patch (5x5) at each pixel
patch = temp_patch.clone(); // Needs to do this because temp_patch is not continuous in memory
patch.convertTo(patch, CV_64FC1);
memcpy(xBuffer, patch.data, 25*sizeof(double));
xBuffer += 25;
}
}
Also, you dont seem to do any computation in patch just extract grey level values, so you can create X with the same type as im, and convert it to double at the end. In this way, you could memcpy each row of your patch, the address in memory beeing `unsigned char* buffer = im.ptr(row) + col
According to the docs:
if you need to process a whole row of matrix, the most efficient way is to get the pointer to the row first, and then just use plain C operator []:
// compute sum of positive matrix elements
// (assuming that M is double-precision matrix)
double sum=0;
for(int i = 0; i < M.rows; i++)
{
const double* Mi = M.ptr<double>(i);
for(int j = 0; j < M.cols; j++)
sum += std::max(Mi[j], 0.);
}
I am trying to implement the codebook foreground detection algorithm outlined here in the book Learning OpenCV.
The algorithm only describes a codebook based approach for each pixel of the picture. So I took the simplest approach that came to mind - to have a array of codebooks, one for each pixel, much like the matrix structure underlying IplImage. The length of the array is equal to the number of pixels in the image.
I wrote the following two loops to learn the background and segment the foreground. It uses my limited understanding of the matrix structure inside the src image, and uses pointer arithmetic to traverse the pixels.
void foreground(IplImage* src, IplImage* dst, codeBook* c, int* minMod, int* maxMod){
int height = src->height;
int width = src->width;
uchar* srcCurrent = (uchar*) src->imageData;
uchar* srcRowHead = srcCurrent;
int srcChannels = src->nChannels;
int srcRowWidth = src->widthStep;
uchar* dstCurrent = (uchar*) dst->imageData;
uchar* dstRowHead = dstCurrent;
// dst has 1 channel
int dstRowWidth = dst->widthStep;
for(int row = 0; row < height; row++){
for(int column = 0; column < width; column++){
(*dstCurrent) = find_foreground(srcCurrent, (*c), srcChannels, minMod, maxMod);
dstCurrent++;
c++;
srcCurrent += srcChannels;
}
srcCurrent = srcRowHead + srcRowWidth;
srcRowHead = srcCurrent;
dstCurrent = dstRowHead + dstRowWidth;
dstRowHead = dstCurrent;
}
}
void background(IplImage* src, codeBook* c, unsigned* learnBounds){
int height = src->height;
int width = src->width;
uchar* srcCurrent = (uchar*) src->imageData;
uchar* srcRowHead = srcCurrent;
int srcChannels = src->nChannels;
int srcRowWidth = src->widthStep;
for(int row = 0; row < height; row++){
for(int column = 0; column < width; column++){
update_codebook(srcCurrent, c[row*column], learnBounds, srcChannels);
srcCurrent += srcChannels;
}
srcCurrent = srcRowHead + srcRowWidth;
srcRowHead = srcCurrent;
}
}
The program works, but is very sluggish. Is there something obvious that is slowing it down? Or is it an inherent problem in the simple implementation? Is there anything I can do to speed it up? Each code book is sorted in no specific order, so it does take linear time to process each pixel. So double the background samples, and the program runs slower by 2 for each pixel, which is then magnified by the number of pixels. But as the implementation stands, I don't see any clear, logical way to sort the code element entries.
I am aware that there is an example implementation of the same algorithm in the opencv samples. However, that structure seems to be much more complex. I am looking more to understand the reasoning behind this method, I am aware that I can just modify the sample for real life applications.
Thanks
Operating on every pixel in an image is going to be slow, regardless of how you implement it.
I want to smooth a histogram.
Therefore I tried to smooth the internal matrix of cvHistogram.
typedef struct CvHistogram
{
int type;
CvArr* bins;
float thresh[CV_MAX_DIM][2]; /* for uniform histograms */
float** thresh2; /* for non-uniform histograms */
CvMatND mat; /* embedded matrix header for array histograms */
}
I tried to smooth the matrix like this:
cvCalcHist( planes, hist, 0, 0 ); // Compute histogram
(...)
// smooth histogram with Gaussian Filter
cvSmooth( hist->mat, hist_img, CV_GAUSSIAN, 3, 3, 0, 0 );
Unfortunately, this is not working because cvSmooth needs a CvMat as input instead of a CvMatND. I couldn't transform CvMatND into CvMat (CvMatND is 2-dim in my case).
Is there anybody who can help me? Thanks.
You can use the same basic algorithm used for Mean filter, just calculating the average.
for(int i = 1; i < NBins - 1; ++i)
{
hist[i] = (hist[i - 1] + hist[i] + hist[i + 1]) / 3;
}
Optionally you can use a slightly more flexible algorithm allowing you to easily change the window size.
int winSize = 5;
int winMidSize = winSize / 2;
for(int i = winMidSize; i < NBins - winMidSize; ++i)
{
float mean = 0;
for(int j = i - winMidSize; j <= (i + winMidSize); ++j)
{
mean += hist[j];
}
hist[i] = mean / winSize;
}
But bear in mind that this is just one simple technique.
If you really want to do it using OpenCv tools, I recommend you access the openCv forum: http://tech.groups.yahoo.com/group/OpenCV/join
You can dramatically change the "smoothness" of a histogram by changing the number of bins you use. A good rule of thumb is to have sqrt(n) bins if you have n data points. You might try applying this heuristic to your histogram and see if you get a better result.