views:

2171

answers:

3

I need some C++/pointer help. When I create an RGB IplImage and I want to access i,j I use the following C++ class taken from: http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html

template<class T> class Image
{
private:
    IplImage* imgp;

public:
    Image(IplImage* img=0) {imgp=img;}
    ~Image(){imgp=0;}
    void operator=(IplImage* img) {imgp=img;}
    inline T* operator[](const int rowIndx) {
     return ((T *)(imgp->imageData + rowIndx*imgp->widthStep));}
};

typedef struct{
  unsigned char b,g,r;
} RgbPixel;

typedef struct{
  float b,g,r;
} RgbPixelFloat;

typedef Image<RgbPixel>       RgbImage;
typedef Image<RgbPixelFloat>  RgbImageFloat;
typedef Image<unsigned char>  BwImage;
typedef Image<float>          BwImageFloat;

I've been working with CUDA so sometimes I have to put all the data into an array, I like to keep every channel in its own array, seems easier to handle the data that way. So I would usually do something like this:

IplImage *image = cvLoadImage("whatever.tif");
RgbImageFloat img(image);
for(int i = 0; i < exrIn->height; i++)
{
    for(int j = 0; j < exrIn->width; j++)
    {
        hostr[j*data->height+i] = img[i][j].r;
        hostg[j*data->height+i] = img[i][j].g;
        hostb[j*data->height+i] = img[i][j].b;
    }
}

I would then copy my data to the device, do some stuff with it, get it back to the host and then loop, yet again, through the array assigning the data back to the IplImage and saving my results.

It seems like I'm looping to much there has to be a faster way to do this with pointers but I'm lost, there has to be a more efficient way to do it. Is there a way I can simply use a pointer for every channel? I tried doing something like this but it didn't work:

float *hostr = &img[0][0].r
float *hostg = &img[0][0].b
float *hostb = &img[0][0].g

Any suggestions? Thanks!

EDIT: Thanks everyone for answering. Maybe I wasn't very clear on my question. I am familiar on how to access channels and their data. What I am interested is in increasing the performance and efficiency of completely copying data off the IplImage to a standard array, more along the lines of what csl said so far. The problem I see is that the way data in an IplImage is arranged is "rgbrgbrgbrgb".

+1  A: 

There is room for a lot of improvement here. So much, that you should read up on how people access bitmaps.

First of all, increase memory locality as much as possible. This will increase cache hits, and performance. I.e., don't use three separate arrays for each color channel. Store each together, since you probably will be working mostly on pixels.

Secondly, don't do that y*width calculation for every pixel. When done in an inner loop, it consumes a lot of cycles.

Lastly, if you just want a complete copy of the image, then you could simply do a memcpy(), which is very fast. I couldn't deduce if you converted from floats to integers, but if not, use memcpy() for non-overlapping regions.

If you wonder how you can do this with pointers (kind of pseudo-code, and also not tested):

float *dst = &hostg[0][0];
RgbPixelFloat *src = &img[0][0];
RgbPixelFloat *end = &img[HEIGHT][WIDTH] + 1;

// copy green channel of whole image
while ( src != end )  {
    *dst = src->g;
    ++dst;
    ++src;
}
csl
I don't think cache applies to CUDA. Even if it did, a lot of image algorithms operate on channels independently, and will get even *better* locality when they don't have to skip over the unused parts of the triplet.
Mark Ransom
The OP wants to know how to separate out `img` into channels `r`, `g` and `b`
Jacob
+4  A: 

Firstly, if you're comfortable with C++, you should consider using OpenCV 2.0 which does away with different data types for images and matrices (IplImage* and CvMat*) and uses one structure (Mat) to handle both. Apart from automatic memory management and a truckload of useful routines to handle channels, etc. and some MATLAB-esque ones as well, it's really fun to use.

For your specific problem, you access the channels of an IplImage* with Mat, like this:

 IplImage *image = cvLoadImage("lena.bmp");
 Mat Lena(image);
 vector<Mat> Channels;
 split(Lena,Channels);
 namedWindow("LR",CV_WINDOW_AUTOSIZE);
 imshow("LR",Channels[0]);
 waitKey();

Now you have the copies of each channel in the vector Channels.

If you don't want to use OpenCV2.0 and extract channels, note the following. OpenCV orders multi-channel images in the following manner:

x(1,1,1) x(1,1,2) x(1,1,3) x(1,2,1) x(1,2,2) x(1,2,3) ...

where x(i,j,k) = an element in row i of column j in channel k

Also, OpenCV pads it's images .. so don't forget to jump rows with widthStep which accounts for these padding gaps. And along the lines of what csl said, increase your row pointer in the outer loop (using widthStep) and increment this pointer to access elements in a row.

NOTE:

Since you're using 2.0 now, you can bypass IplImage* with Mat Lena = imread("Lena.bmp");.

Jacob
Thanks, I downloaded openCV 2.0 the other day I haven't had time to take a look at it yet, we're using 64bit so I'm gonna have to look into re-compiling 2.0 to 64bit so I've been avoiding it :p
rem7
I think it's worth the effort. The makefiles are quite easy to use (CMake) and coding with Mat and all the new operators has made my code much cleaner and simpler.
Jacob
Jacob, thanks, I just compiled 2.0 and did what you suggested. Pretty cool. I'll have to look at whats new in here... is there any documentation for the C++ stuff or will I just have to go through the header files looking for stuff?
rem7
No way, check out the `doc\opencv.pdf` - they've documented the new interface pretty well. Also, feel free to accept my answer :)
Jacob
A: 

i'm wearking in PCA algorithim i have some answer 1- how i loade some image for diffrent person? 2- i load 5 image for one person and calculat face spase matrix how i transform CvMat back to IplImage to see the operation on image

hythem