[SOLVED] EXC_BAD_ACCESS when passing image to class method and trying to set instance ofImage with setFromPixels

I’m pretty new to OpenFrameworks and C++, so this comes with the usual caveats that I’m probably not understanding pointers and references correctly… or something along those lines.

I’m building an openCV app that recognizes letter shapes on a wall.

I have a Letter class that is managed by ofxCV’s RectFollower, so that a new instance is created to track each blob/contour. I want to pass the webcam image through to a class method, where I will crop it and save to a local ofImage variable for later display/classification.

Here’s the relevant code from the class and ofApp.

Letter.h

#include "ofMain.h"
#include "ofxOpenCv.h"
#include "ofxCv.h"

using namespace ofxCv;
using namespace cv;

class Letter : public ofxCv::RectFollower {
protected:
    ofImage img;
    cv::Rect rect;
public:
    Letter(){}
    void setup(const cv::Rect& track);
    void setImage(const ofxCvColorImage * camImage);
    void classify();
    void update(const cv::Rect& track);
    void kill();
    void draw();
};

Letter.cpp

#include "letter.h"

void Letter::setup(const cv::Rect& track) {
    rect = track;
    // Does img need to be allocated?
    // img.allocate(640, 480, OF_IMAGE_COLOR);
}

void Letter::setImage(const ofxCvColorImage * camImage) {
    img.setFromPixels(camImage->getPixels());
    img.crop(rect.x, rect.y, rect.width, rect.height);
}

// … rest of class methods ignored for brevity …

ofApp.cpp

// … rest of file ignored for brevity …
//--------------------------------------------------------------
void ofApp::update(){
    if (!bPause) {
    cam.update();
    }
    if(cam.isFrameNew())
    {
        // get grayscale image and threshold
        colorImage.setFromPixels(cam.getPixels());
       // …other CV stuff ignored for brevity …

        letterTracker.track(contourFinder.getBoundingRects());
        
        // update new Letters with image
        vector<Letter>& letters = letterTracker.getFollowers();
        const vector<unsigned int>& newLabels = letterTracker.getNewLabels();
        for(int i = 0; i < newLabels.size(); i++) {
            const int index = letterTracker.getIndexFromLabel(newLabels[i]);
            // HERE'S WHERE THIS METHOD IS CALLED:
            letters[index].setImage(&colorImage);
        }
    }
}
// … rest of file ignored for brevity …

It looks like usually the first instance works ok, and then on a subsequent instance I get Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT) when img.setFromPixels(camImage->getPixels()); is called. If I trace it through the thread you see the following:

What am I missing?!

Full code is on GitHub here: https://github.com/johndryan/Sugraph

Thanks in advance for any help or pointers (excuse the pun).

Hi,

Not a really good answer because I don’t know what happens…
I can give some basic tests to do, but you probably have already tried.

const int index = letterTracker.getIndexFromLabel(newLabels[i]);
if( index < 0 || index >= letters.size() {
    cout << "Hum...." << endl;
    continue;
}
letters[index].setImage(&colorImage);

I don’t think the problem is here, but it is always dangerous to access a vector element like that without checking the index.

Another one:

if(cam.isFrameNew()) {
    colorImage.setFromPixels(cam.getPixels());
    // declare an ofImage imgTest in ofApp.h, then
    imgTest.setFromPixels(colorImage.getPixels()); // does it works ?

And by the way, a general advice, I prefer to avoid pointers when it is possible:

void Letter::setImage(const ofxCvColorImage * camImage){
    // Better to test here that camImage is not nullptr, before to use it

void Letter::setImage(const ofxCvColorImage & camImage){
    // Nothing to test

Not sure I’m helping :slight_smile:

Thanks for the tips, I’ve updated my code accordingly.

The following works correctly:
imgTest.setFromPixels(colorImage.getPixels());

It just seems to be when I call it inside the class method that I get the error?

I have downloaded your code and tested it. After all my first tip was good, the indices returned by
letterTracker.getIndexFromLabel(newLabels[i]) are always -1.
(but I never used the ofxCv tracker, so I don’t know what to do. Perhaps read the addon example source)

1 Like

Thanks @lilive for spending the time on this. After following your line of investigation, I found the same problem, and tracked it to a known issue with ofxCv's getLabelFromIndex. I’ve made some changes to the add-on (taking some ideas from this comment) and I’m getting there.

I also allocated the ‘ofImage’ explicitly during the Letter initialization and did the resizing before I passed the ofImage. I’m not sure which has solved the problem, but I’m getting there!

Thanks for your help. I’ll share the project when it gets a little further. :slight_smile:


Here’s where I landed for reference:

Letter.cpp

#include "letter.h"

void Letter::setup(const cv::Rect& track) {
    rect = track;
    img.allocate(224, 224, OF_IMAGE_COLOR);
}

void Letter::setImage(const ofImage & _img) {
    img.setFromPixels(_img.getPixels());
}

ofApp.cpp

// … rest of file ignored for brevity …
//--------------------------------------------------------------
void ofApp::update(){
    if (!bPause) {
    cam.update();
    }
    if(cam.isFrameNew())
    {
        // get grayscale image and threshold
        colorImage.setFromPixels(cam.getPixels());
       // …other CV stuff ignored for brevity …

        letterTracker.track(contourFinder.getBoundingRects());
        
        // update new Letters with image, then classify
        vector<Letter>& letters = letterTracker.getFollowers();
        const vector<unsigned int>& newLabels = letterTracker.getNewLabels();
        for(int i = 0; i < newLabels.size(); i++) {
            int index = letterTracker.getIndexFromLabel(newLabels[i]);
            if (index >= 0 && index < letters.size()) {
                ofImage camTempCopy;
                camTempCopy.setFromPixels(colorImage.getPixels());
                cv::Rect cropRect = letters[index].getRect();
                if (cropRect.x < camTempCopy.getWidth()
                    && cropRect.y < camTempCopy.getHeight()
                    && cropRect.width < camTempCopy.getWidth()
                    && cropRect.height < camTempCopy.getHeight()) {
                    camTempCopy.crop(letters[index].getRect().x, letters[index].getRect().y, letters[index].getRect().width, letters[index].getRect().height);
                    camTempCopy.resize(224, 224);
                    letters[index].setImage(camTempCopy);
                }
            }
        }
    }
}
// … rest of file ignored for brevity …