0

The code below was wrtting to access the elements of 3 RGB images converted to matrices and place them in a bigger matrix. However, I have know realised that CV_MAT_ELEM(mat, type, row, col) is used only for accessing elements of single channel matrices, whereas my images are all 3 channel. Would would I go about fixing this code then, to access elements of a 3 channel matrix instead of a single channel matrix?

#include "cv.h" 
#include "highgui.h" 
#include "iostream" 

using namespace std; 

void cvDoubleMatPrint (const CvMat* mat)
{
int i, j;
for (i = 0; i < mat->rows; i++)
{
for (j = 0 ; j < mat->cols; j++)
{
    printf ( "%f ", cvGet2D (mat, i , j));
}
printf ( "\n" );
}
}


int main( int argc, char* argv ) 
{ 
CvMat *img0, *img1, *img2,  *img0_mat, *img1_mat, *img2_mat, *col0, *col1, *col2, *superMat = NULL;

img0 = cvLoadImageM("C:\\small\\walk mii.jpg", CV_LOAD_IMAGE_UNCHANGED);    
img1 = cvLoadImageM("C:\\small\\wave mii.jpg", CV_LOAD_IMAGE_UNCHANGED);  
img2 = cvLoadImageM("C:\\small\\fantasy.jpg", CV_LOAD_IMAGE_UNCHANGED); 

CvMat img0_header ,img1_header, img2_header;

col0 = cvReshape(img0, &img0_header, 0, 4800);
col1 = cvReshape(img1, &img1_header, 0, 4800);
col2 = cvReshape(img2, &img2_header, 0, 4800);

superMat = cvCreateMat(4800, 3, CV_8UC1);
cvSetZero(superMat);

for(int i=0; i<col0->height; i++)
{
CV_MAT_ELEM( *superMat, double, i, 0 ) = CV_MAT_ELEM( *col0, double, i, 0 );
}

for(int j=0; j<col1->height; j++)
{
CV_MAT_ELEM( *superMat, double, j, 1 ) = CV_MAT_ELEM( *col1, double, j, 0 );
}

 for(int k=0; k<col2->height; k++)
{
CV_MAT_ELEM( *superMat, double, k, 2 ) = CV_MAT_ELEM( *col2, double, k, 0 );
}


cvDoubleMatPrint(superMat);

cvWaitKey(0);
return 0;
sue-ling
  • 399
  • 2
  • 11
  • 21
  • Hi, thanks for the help everyone. I changed all the images to grayscale and hence, 1 channel so therefore i can use the CV_MAT_ELEM in the code. The images were black and white so converting to grayscale made no difference to the visual output. – sue-ling Mar 15 '12 at 02:53

2 Answers2

2

I'm not as familiar with the C API, but here is how you can accomplish what you are doing with the C++ API.

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

#include <iostream>
#include <vector>

using namespace cv;
using namespace std;

int main(int /*argc*/, char** /*argv*/)
{
    cv::RNG rng = cv::theRNG();

    // create 3 random 10x10 matrices to simulate your situation.
    vector<Mat> matrices;
    for(int i = 0; i < 3; i++)
    {
        Mat temp(Size(10, 10), CV_64FC3);
        rng.fill(temp, RNG::UNIFORM, 0.0, 1.0);
        matrices.push_back(temp);
    }

    // reshape the matrices to have 1 row and 1 channel (i.e., 10x10x3 -> 300x1).
    vector<Mat> singleRows;
    vector<Mat>::iterator i;
    for(i = matrices.begin(); i != matrices.end(); i++)
    {
        singleRows.push_back(i->reshape(1, 1));
    }

    // finally, concatenate the matrices to make a 300x3 matrix.
    Mat concatenated;
    for(i = singleRows.begin(); i != singleRows.end(); i++)
    {
        concatenated.push_back(i->clone());
    }

    cout << concatenated.size().width << "x" << concatenated.size().height << endl;

    return 0;
}

If you need to access an RGB value in the C++ API see my other post on how to use the cv::Vec3b class. In general, I would recommend using the C++ API over the C API as it has more features and is much easier to use. Also, have a look at OpenCV functions split and merge. These functions are useful for multi-channel management. Hope that helps!

Community
  • 1
  • 1
mevatron
  • 13,911
  • 4
  • 55
  • 72
0

I think what you may be looking for is CvtPixToPlane, which can divide a multi-channel array into several single-channel arrays, or extracts a single channel from the array.

CvtPlaneToPix is also useful for combining several single-channel arrays into a multi-channel array, or inserting a single channel into an array.

Some documentation on these two functions.

Here is an example of splitting a multi-channel array into three single-channel arrays, extracted from Shervin Emami's Detect Blobs tutorial -

// Get the separate HSV color components of the color input image.
    IplImage* planeH = cvCreateImage( cvGetSize(imageBGR), 8, 1);   // Hue component.
    IplImage* planeS = cvCreateImage( cvGetSize(imageBGR), 8, 1);   // Saturation component.
    IplImage* planeV = cvCreateImage( cvGetSize(imageBGR), 8, 1);   // Brightness component.
    cvCvtPixToPlane(imageHSV, planeH, planeS, planeV, 0);   // Extract the 3 color components.

As you can see, he creates images with the correct attributes by getting the size of his original image (imageBGR) and setting them up to be single-channel images. Then cvCvtPixToPlane is used to split his multi-channel image (imageHSV) into three single-channel images (planeH, planeS, planeV)

Hope this helps :)

Community
  • 1
  • 1
Eilidh
  • 1,354
  • 5
  • 21
  • 43
  • Thanks for replying. However, I am trying to work with matrices instead of images. Would the function you referred to be able to work on cvMat elements as well as IplImage? – sue-ling Mar 15 '12 at 02:57
  • CvArr* is a meta-type - it's used only to specify that a function will accept arrays of multiple types. Any function which takes a CvArr* parameter will accept IplImage* and CvMat*, and sometimes CvSeq*. So, to answer your question, yes, you can pass in a CvMat :) Sorry to take so long to get back to you! – Eilidh Mar 18 '12 at 11:40
  • No problem - I hope it helps - I find those functions quite useful in image detection – Eilidh Mar 20 '12 at 14:18