views:

1473

answers:

7

Hi,

I am having quite a bit of trouble with trying to push_back an object of my custom class to a vector of pointers with my custom class as the type. Please see the code below along with the error received. I am using Eclipse with the CDT plugin and OpenCV on windows xp.

I have spent so much time trying to find an answer but to no avail! ps I am a student and pointers etc are not my thing!

    std:: vector<RoadLine>* LaneChangeDetector::roadLines(IplImage* img_8uc1, IplImage* img_8uc3, IplImage* img_edge, std::vector <RoadLine>* roadVector){

 CvMemStorage* storage = cvCreateMemStorage(0);
 CvSeq* lines = 0;
 CvMemStorage* roadStorage = cvCreateMemStorage(0);
 CvSeq* roadLines = 0;

 // Probabalistic Hough transform returns line segments from edge detected image
 lines = cvHoughLines2( img_edge, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 50, 200, 200 );

 // Sequence roadlines, lines with correct slope are added to this sequence
 roadLines = cvCreateSeq(0, lines->header_size, lines->elem_size, roadStorage);

 // slope
 double m = 0.0;

 // Point of intersection
 CvPoint poi;

 for(int i = 0; i < lines->total; i++ ){
  CvPoint* line = (CvPoint*)cvGetSeqElem(lines,i);
  CvPoint pt1 = line[0];
  CvPoint pt2 = line[1];

  double x1 = double(pt1.x);
  double y1 = double(pt1.y);
  double x2 = double(pt2.x);
  double y2 = double(pt2.y);

  if(pt1.x == pt2.x){
   m = 1.0;
  }
  else{
   m = (double(y2 - y1)/(double(x2 - x1)));
  }

  if( ((m>0.45) && (m<0.75)) || ((m<-0.45) && (m>-0.75)) ){

   // If the slope is between measured parameters add to roadLines sequence for further analysis
   cvSeqPush(roadLines, line);
  }
 }

 // otherRoadLine used for comparison
 CvPoint* otherRoadLine;

 for(int a=0; a<roadLines->total; a++){

  CvPoint* roadLine = (CvPoint*)cvGetSeqElem(roadLines,a);
  CvPoint rl1 = roadLine[0];
  CvPoint rl2 = roadLine[1];
  int lineCount = 0;

  if(a>0){

   // Test the current line against all the previous lines in the sequence.
   // If the current line is far enough away from all other lines then draw it
   for(int b=0; b<a; b++){
    otherRoadLine = (CvPoint*)cvGetSeqElem(roadLines,b);
    if((roadLine->x > ((otherRoadLine->x) + 200)) || (roadLine->x < ((otherRoadLine->x) - 200)) ){
     lineCount++;
    }
   }
   if(lineCount == a){
    cvLine(img_final, roadLine[0], roadLine[1], CV_RGB(0,0,255), 3, CV_AA, 0 );
    RoadLine myLine = RoadLine(roadLine, 1);
    roadVector->push_back(myLine); //ERROR OCCURS HERE
    cvShowImage("Plate Detection", img_final);
    cvWaitKey(0);
   }
  }
  else{
   cvLine(img_final, roadLine[0], roadLine[1], CV_RGB(0,0,255), 3, CV_AA, 0 );
   RoadLine myLine = RoadLine(roadLine, 1);
   roadVector->push_back(myLine //ERROR OCCURS HERE
   cvShowImage("Plate Detection", img_final);
   cvWaitKey(0);
  }
 }

 if(roadVector->size() >= 2){
  int pos = 0;
  RoadLine line1 = roadVector->at(pos);
  RoadLine line2 = roadVector->at(pos + 1);

  CvPoint* A = line1.line;
  CvPoint p1 = A[0];
  CvPoint p2 = A[1];

  int A1 = p1.y - p2.y;
  int B1 = p1.x - p2.x;
  int C1 = (p1.x*p2.y) - (p1.y*p2.x);

  CvPoint* B = line2.line;
  CvPoint p3 = B[0];
  CvPoint p4 = B[1];

  int A2 = p3.y - p4.y;
  int B2 = p3.x - p4.x;
  int C2 = (p3.x*p4.y) - (p3.y*p4.x);

  int det = A2*B1 - A1*B2;

  if(det == 0){
   printf("Lines are parallel");
  }
  else{
   int x = ( C1*(p3.x - p4.x) - (p1.x - p2.x)*C2 )/det;
   int y = ( C1*(p3.y - p4.y) - (p1.y - p2.y)*C2 )/det;

   poi.x = x;
   poi.y = y;

   horizon = poi.x;

   cvCircle(img_final, poi, 10, CV_RGB(255, 0, 0), 2, CV_AA, 0);
  }
 }

 cvShowImage("Plate Detection", img_final);
 cvWaitKey(0);

 return roadVector;
}

The custom class RoadLine can be seen here

    #include <cv.h>
class RoadLine{
private:
CvPoint* line;
int lane;
public:
RoadLine(CvPoint*, int);
};
RoadLine::RoadLine(CvPoint* aLine, int aLane){
line = aLine;
lane = aLane;
}

From debugging i can see that "std::vector <RoadLine>* roadVector" is being intialised correctly.

Here is what Eclipse tells me:

3 std::vector<RoadLine, std::allocator<RoadLine> >::push_back() F:\MinGW\include\c++\3.4.5\bits\stl_vector.h:560 0x0043e3f9

4 void std::_Construct<RoadLine, RoadLine>() F:\MinGW\include\c++\3.4.5\bits\stl_construct.h:81 0x0044015d

And the program jumps to this section of code in stl_construct.h

  template<typename _T1, typename _T2>
inline void
_Construct(_T1* __p, const _T2& __value)
{
  // _GLIBCXX_RESOLVE_LIB_DEFECTS
  // 402. wrong new expression in [some_]allocator::construct
  ::new(static_cast<void*>(__p)) _T1(__value); //DEBUG THROWS ME TO THIS LINE
}

Again any help would be greatly appreciated.

Cheers

Pat

+1  A: 

Your RoadLine class lacks a proper copy-ctor. Now, since you have a member that points to a CvPoint object you have create a copy of the pointer every time you push_back. This is probably not desirable.

RoadLine::RoadLine(const RoadLine & o){
     line = new CvPoint[ 2 ]; 
     line[ 0 ] = o.line[ 0 ];
     line[ 1 ] = o.line[ 1 ];
     lane = o.lane;
}

RoadLine& operator=(const RoadLine & o){
     if (this != &o) { //Remember to check for self-assignment.
      line = new CvPoint[ 2 ]; 
      line[ 0 ] = o.line[ 0 ];
      line[ 1 ] = o.line[ 1 ];
      lane = o.lane;
     }
     return *this;
}

Shorten your code: Try to isolate the problem:

int main() {
    CvPoint pa[] = { CvPoint(0, 0), CvPoint(100, 100) };
    RoadLine rl1(pa, 1);

    vector<RoadLine> v;
    v.push_back(rl1);

    return 0;
}

Does this crash?

dirkgently
I need to work on my typing speed :)
ya23
Actually, the copy-ctor is OK, and your version would have only worsen it, because line is an array, not one CvPoint
jpalecek
@jpalecek: That was sample code. But I get your point. Haven't looked at `CvPoint *` in detail.
dirkgently
Hi, so once i make a copy of the roadVector, i should call push_back on the copy? eg: roadVectorCopy->push_back(myLine); ???
@Pat Rohan: The `push_back` makes a copy that it stores in the vector. See to it that your class has a proper copy-ctor. The semantics will depend on what you want.
dirkgently
@dirkgently: at a high level i want to return roadVector and then modify ite objects in another function. i understand passing by reference is the way to do this. i have tried to show my new RoadLine class but it is not formatted. however i will post it anyway. thanks for your help thus far
i changed the class definition to be like this:#include <cv.h>class RoadLine{private: const CvPoint* line; int lane;public: RoadLine(const CvPoint*, int);};RoadLine::RoadLine(const CvPoint* aLine, int aLane){ line = aLine; lane = aLane;}
@Pat Rohan: I don't think line = aLine; is a good thing to do. This is shallow copy. Do a deep copy -- copy out the array.
dirkgently
@Pat Rohan: Also, this is your ordinary ctor, and not the copy-ctor.
dirkgently
@dirkgently: should i leave line = aLine present in the ordinary constructor?
@Pat Rohan: Yes that should be okay.
dirkgently
@dirkgently: Hi im still a little confused but im getting there! as push_back makes the copy it calls the copy constructor, is that right? and thus assigns the deep copy to roadVector. as a result when myLine goes out of scope and is deleted the RoadLine pushed into RoadVector remains unaffected?
@Pat Rohan: You are almost there :) The pointee will be deleted if it's on the stack; otherwise not. Looking at your updated class: I see that you are not creating a copy of CvPoint in the ctor/copy-ctor and assignment operator is missing.
dirkgently
@Pat Rohan: [contd] If you plan to share the same CvPoint object across RoadLine instances then line = aline; is okay. Otherwise, you need deep copy ie. line = new CvPoint(aline);
dirkgently
@Pat Rohan:[contd] if you are sharing pointers, then you can't have the delete in the dtor either -- it'll be disastrous.
dirkgently
@dirkgently: I have updated my response below to show how RoadLine is currently defined. Do you mean I need to define my own assignment operator? I have also shown how the code is now implemented, below.
@dirkgently: I see your assignment operator now and i will implement it. cheers
@Pat Rohan: My comments were made in light of your code posted below. They have a problem (see my earlier comments).
dirkgently
@dirkgently: Is the problem you are reffering to in this line: line[ 0 ] = o.line[ 0 ]; as this creates a shallow copy?
when i run the program and it crashes at push_back i get this in the eclipse console: Warning: F:\Eclipse\workspace\Contours/F: No such file or directory.Cannot access memory at address 0x450000Im not sure if thats any help
line[ 0 ] = o.line[ 0 ]; may or may not be a shallow copy depending on the definition of a CvPoint (unfortunately, I am not aware of this class).
dirkgently
Can you put a breakpoint and run your code through?
dirkgently
i do have a break point present. when i get to the "guilty" line i step over it and eclipse throws me to the line i have now highlighted in my original post. sorry for causing so much hassle! would be great if could get this sorted and continue with development
If line[ 0 ] = o.line[ 0 ]; crashes it may mean that there is some problem with the right hand side of your code.
dirkgently
@dirkgently: the test you asked to implement does work but I had to make one small syntax change: CvPoint pa[] = { cvPoint(0, 0), cvPoint(100, 100) }; it uses the ordinary constructor when calling push_back however. please see my updated post below
@Pat Rohan: If the code I provided works fine, should tell you a) how to use vectors and b) that since this works, then there must be some issue with the rest of the code.
dirkgently
@dirkgently: After finally getting to consult my supervisor today I have decided to use an actual vector of pointers which doesnt require any copy const etc. thank you for all your help i have a much better understanding now
+3  A: 

You do not use vector of pointers, but vector of objects. In that case, your class needs to have a copy constructor, as push_back stores a copy of object.

As a general debugging advice, try to boil down the problem by removing as much code as you can and still see incorrect behaviour. Try to find the simplest example that fails.

ya23
A: 

These kinds of errors are usually caused by incorrect memory-management. Sadly, you haven't posted the way how do you manage your memory.

If you can get it run on a linux system, you can try running your program under valgrind, which helps to track down incorrect memory accesses/freeing. Unfortunately, valgrind is not available under windows, but there may be substitutes.

jpalecek
A: 

Hi all,

i have changed my class definition of RoadLine to:

#include <cv.h>

class RoadLine{

private:
    int lane;
public:
    CvPoint* line;
    RoadLine(CvPoint*, int);
    RoadLine(const RoadLine &);
    ~RoadLine();
    RoadLine& operator=(const RoadLine & o);
};

RoadLine::RoadLine(CvPoint* aLine, int aLane){
    line = aLine;
    lane = aLane;
}

RoadLine::RoadLine(const RoadLine & myRoadLine){
    line = new CvPoint[ 2 ]; // CRASHES HERE
    line[ 0 ] = myRoadLine.line[ 0 ];
    line[ 1 ] = myRoadLine.line[ 1 ];
    //line = new CvPoint(*myRoadLine.line);
    lane = myRoadLine.lane;
}

RoadLine::~RoadLine(){
    delete line;
}

RoadLine& RoadLine::operator=(const RoadLine & o){
     if (this != &o) { //Remember to check for self-assignment.
      line = new CvPoint[ 2 ];
      line[ 0 ] = o.line[ 0 ];
      line[ 1 ] = o.line[ 1 ];
      lane = o.lane;
     }
     return *this;
}

This is the current version of the RoadLine class

This is how I am implementing the class:

else{
        cvLine(img_final, roadLine[0], roadLine[1], CV_RGB(0,0,255), 3, CV_AA, 0 );
     RoadLine myLine(roadLine, 1);
     roadVector->push_back(myLine); // FROM HERE
     cvShowImage("Plate Detection", img_final);
     cvWaitKey(0);
}

When push_back is called it calls the copy constructor but the program crashes where highlighted above

What difference is made by the fact that my vector is defined;

std::vector<RoadLine>* roadVector

and that i have a CvPoint* not CvPoint[]

sorry if these seem very basic questions

+2  A: 

Your new RoadLine class will certainly lead to disaster :

RoadLine::RoadLine(CvPoint* aLine, int aLane){
    line = aLine;
    lane = aLane;
}

RoadLine::RoadLine(const RoadLine & myRoadLine){
    line = myRoadLine.line;
    lane = 1;
}

RoadLine::~RoadLine(){
    delete line;
}

code using it :

                        if(lineCount == a){
                                cvLine(img_final, roadLine[0], roadLine[1], CV_RGB(0,0,255), 3, CV_AA, 0 );
                                RoadLine myLine = RoadLine(roadLine, 1);//create object on the Stack
                                roadVector->push_back(myLine); //Push COPY of myLine
                                cvShowImage("Plate Detection", img_final);
                                cvWaitKey(0);
                        }//Stack-based object "myLine" is automatically destroyed here (leaves scope)

the automatic destruction of "myLine" will delete "myLine.line" (in RoadLine's dtor) but "myLine.line" is still referenced in the vector (you just pushed it).

You have to either make a DEEP COPY of line (as others suggested), something like this :

RoadLine::RoadLine(const RoadLine & myRoadLine){
    line = new CvPoint(*myRoadLine.line);//assuming CvPoint can be copy-constructed
    lane = 1;
}

Or use a CvLine object rather than a pointer (or something else, need more context)

EDIT : Dirk Gently's copy-ctorhas a bug, because it leaks memory to the former "line"-member should be :

RoadLine& operator=(const RoadLine & o){
     if (this != &o) { //Remember to check for self-assignment.
      delete []line;//delete[] vs. delete !
      line = 0;//if next line throws at least we won't double-delete line
      line = new CvPoint[ 2 ]; //this might throw ! should catch (or redesign to get rid of new (prefered)
      line[ 0 ] = o.line[ 0 ];
      line[ 1 ] = o.line[ 1 ];
      lane = o.lane;
     }
     return *this;
}
//consistent constructor !
RoadLine::RoadLine(CvPoint* aLine, int aLane)
    :line(new CvPoint[2]),//might throw, but its better to throw in initializer ! (if you just have one pointer it might be ok to do it like this)
    lane(aLane)
{
     line[0] = aLine[0];
     line[1] = aLine[1];
}
RoadLine::~RoadLine(){
    delete[] line;//also use delete[] vs. normal delete here !
}

EDIT 2 : I almost forgot that I had an idea why it crashes ! maybe you try to build a pair with last and last+1 CvPoint (like this obviously false code)?

CvPoint Pnts[2] = {CvPoint(0,0),CvPoint(1,1)};
Roadline Line(&Pnts[1],1);//tries to access Pnts[2] which is one past end !
qwerty
Hi qwerty,Thanks for your help. I have a quick question regarding your answer. Are you saying that I only need to change the copy const? or do i need to change how the code is implemented? Also in the copy cont line is of type CvPoint* so should that line read something like:
line = new CvPoint*(*myRoadLine.line);
sorry i now see how line = new CvPoint(*myRoadLine.line);is correct. however i am still unsure about how it is used. thanks again
CvPoint* line = (CvPoint*)cvGetSeqElem(lines,i);We need more info on this function.I guess it just returns a pointer to a part of a larger array ?(which would change the whole "ownership" scenario)
qwerty
yes a sequence is essentially a vector, to the best of my understanding anyway. the address of line at index i=0 is 0x012f8858 and the address of the sequence lines is 0x012f8808. so it seems it does return a pointer to the larger sequence
adresses of i=0 and i=1 would be interesting (difference of sizeof(CvPoint) ?),also that means you don't have access to the source of cvGetSeqElem function ?
qwerty
@qwerty: I am trying to implement what you have done above. i need a copy constructor too, right? also i dont understand how i could/should redesign so i dont use "new" please explain
i=0 is 0x012f8858, i=1 is 0x012f8868. cvGetSeqElem Returns a pointer to a sequence element by its index.
A: 

The trick with C++ is to imagine the "~" key as big and red and that alarm bells will sound whenever you press it, ie. whenever you're thinking of adding a destructor to a class.

If you're adding a destructor then you NEED a copy constructor and assignment operator. No exceptions. Even if you're not going to copy the object you should still declare them in the private section so the compiler will give errors if they're used accidentally.

You should also use a reference counted pointer instead of a raw C-style pointer whenever the lifetime of an object is being controlled (in C++-speak this is "RAII"). If you did this the destructor would vanish from RoadLine, and, magically, so would your problem.

Jimmy J
A: 

You don't have a vector of pointers.

std::vector<RoadLine>* roadVector

is a pointer to a vector of RoadLine objects. If you want a vector of pointers, you should do:

std::vector<RoadLine*> roadVector

That may help you (since the vector won't be invoking copy constructors any more), but you should still look at sorting those out as others have suggested.

Peter