views:

184

answers:

5

I'm using OpenCV to iterate through an image and find the colour of each pixel, here's some of the code I'm using:

IplImage* img = cvLoadImage("c:\\test.png");

int pixels = img->height * img->width;
int channels = img->nChannels;

for (int i = 0; i < pixels*channels; i+= channels)
{

    unsigned char red = img->imageData[i + 2];
    unsigned char green = img->imageData[i + 1];
    unsigned char blue = img->imageData[i];

    outputRGBValues(red, green, blue);
    if (red == REDCOLOUR && green == GREENCOLOUR && blue == BLUECOLOUR)
    {
        count++;
    }
}
cvReleaseImage(&img);

When I run it, it outputRGBValues outputs negative values. Most often R, G, B = -1, but occasionally other negative numbers and a handful of positive numbers. I've heard something about uninitialized memory contents, and pixels not being allocated to the memory properly. But I'm don't really understand it and definitely don't know how to fix it. What am I doing wrong and how can I fix it?

UPDATE

After fixing the code (as above) with fschmitt's changes, I'm a bit closer. This is the image I'm using, if it's any help. Quite hard to see, but it's just a 5*3 'V' of black pixels with one green one at the bottom.

Running the code on it, I get this output:

0 0 0
255 255 255
255 255 255
255 255 255
0 0 0
255 255 186
0 0 255
255 255 0
And it continues

The first 5 lines are fine, exactly what they should be. That's the top row of the image. The next row, 6th line onwards is wrong. It should be:

255 255 255
255 255 255
0 0 0

I'm not sure what's causing this. How can it work for the top line and not the second? Is there some sort of mismatch, and its taking the value one bit to the left of where it should?

+1  A: 

I've never used OpenCV, but I would imagine that cvCreateImage doesn't initialise the contents of the image, and certainly not to anything meaningful.

Oli Charlesworth
+1  A: 

Quite some issues here:

  • You need to fill the image with meaningful data
  • Depending on how the image was constructed, you may have swapped red and blue
  • You should use unsigned char red
  • You declare a vector for each pixel and don't use it
fschmitt
I thought that pixel colour was stored as BGR, not RGB in imageData? Thanks for picking those things up, I'll edit with the changes.
JBenson
It depends on how you construct the image, but indeed you are right, the default is BGR, I edited the answer.
fschmitt
Okay, I'm getting closer. Post updated, the first row of pixels is found correctly, but it's wrong from the second row onwards
JBenson
@JBenson: your method for accessing pixel data is incorrect, as it does not take into account the difference between `width` and `widthStep` (there may be padding at the end of each row, hence the difference). Recommend reading O'Reilly "Learning OpenCV" which covers the basics such as this in detail.
Paul R
Thanks for the recommendation fschmitt, I'll go read up.
JBenson
+1  A: 

Try this:

IplImage* img = cvLoadImage("c:\\test.png");

for (int i = 0; i < img->height; i++)
{
    for (int j = 0; j < img->width; j += img->nChannels)
    {
        unsigned char red = img->imageData[i * img->widthStep + j + 2];
        unsigned char green = img->imageData[i * img->widthStep + j + 1];
        unsigned char blue = img->imageData[i * img->widthStep + j];

        outputRGBValues(red, green, blue);
        if (red == REDCOLOUR && green == GREENCOLOUR && blue == BLUECOLOUR)
        {
            count++;
        }
    }
}
cvReleaseImage(&img);
Paul R
do note widthstep is member of IplImage, instead of instance variable in given code.
YeenFei
@YeenFei: thanks for the correction - now edited accordingly - not sure what you mean about byte alignment in your second comment though ?
Paul R
@Paul R, i mistaken that you are incrementing pointer offset by nChannels, bad mistake. Deleted my second reply.
YeenFei
A: 

Since you didn't test the success of cvLoadImage() you don't know if it really worked.

As far as we can tell, you could just be printing memory garbage. So, its better be safe than sorry:

IplImage* pRGBImg = cvLoadImage("c:\\test.png", CV_LOAD_IMAGE_UNCHANGED); 
if (!pRGBImg)
{
  std::cout << "!!! cvLoadImage failed !!!" << std::endl;
  exit(1);
}

int width = pRGBImg->width; 
int height = pRGBImg->height;
int bpp = pRGBImg->nChannels; 
for (int i=0; i < width*height*bpp; i+=bpp) 
{
  if (!(i % (width*bpp))) // print empty line for better readability
      std::cout << std::endl;

  std::cout << std::dec << "R:" << (int) pRGBImg->imageData[i] <<  
                          " G:" << (int) pRGBImg->imageData[i+1] <<  
                          " B:" << (int) pRGBImg->imageData[i+2] << " "; 
}
karlphillip
-1, your claim is not possible since a failed cvLoadImage returns 0 and dereferencing null pointer causes access violation.
YeenFei
@YennFei Really? -1 for recommending good programming practices? Good job.
karlphillip
its directed to the claim on printing memory garbage :(
YeenFei
+1  A: 

I don't understand why you are accessing pixel data blindly (by 1D index) while you already know the image dimension (proved by the usage of IplImage::height and IplImage::width) and opencv does provide function to access pixel value (cvGet*D).

You are getting odd values in the direct pointer access because you did not factor in the byte padding performed by OpenCV. You may find in 8 bit images, (width*nChannels <= widthStep) for this reason.

A quick revised code can be as follow:

IplImage* img = cvLoadImage("c:\\test.png");
for(int iRow=0; iRow<img->height; ++iRow)
{
    for(int iCol=0; iCol<img->width; ++iCol)
    {
        CvScalar pixelVal = cvGet2D( img, iRow, iCol);
        unsigned char red = pixelVal.val[2];
        unsigned char green = pixelVal.val[1];
        unsigned char blue = pixelVal.val[0];

        outputRGBValues(red, green, blue);
        if (red == REDCOLOUR && green == GREENCOLOUR && blue == BLUECOLOUR)
        {
            count++;
        }
    }
}
cvReleaseImage(&img);

Also, note that usually OpenCV arrange data in BGR format, which can verified at IplImage::channelSeq. Do not confused by IplImage::colorModel, which tells the color model.

I'll go with Paul R suggestion that you should find a reference and understand the library before attempting to use them.

YeenFei