Vector of pointers problem - c++

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

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.

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 !

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?

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.

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.

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.

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

Related

bad memory alloc error - segment tree

I am trying to create a segmented tree,
Here is my struct for the node of tree:
struct Node{
int x1, x2; // x coordinates
int y1, y2; // y coordinates
Node * v1;
Node * v2;
Node * v3;
Node * v4;
bool oBo; //check if 1 by 1
bool O;
bool F;
int dimens;
Node(int myx1, int myx2, int myy1, int myy2){
this->x1 = myx1;
this->x2 = myx2;
this->y1 = myy1;
this->y2 = myy2;
this->dimens = abs(x2 - x1);
if (dimens == 1)
{
this->oBo = true;
}
else
this->oBo = false;
this->O = false;
this->F = false;
this->v1 = NULL;
this->v2 = NULL;
this->v3 = NULL;
this->v4 = NULL;
}
};
This is my constructor for the Map
MapTree::MapTree(int iSize)
{
this->size = iSize;
root = new Node(0, size, 0, size);
segment(root);
}
and I am using the this segment function to make sub-segments of the root and then this is function is called recursively on the sub-nodes of root and so on. I get a bad memory alloc on the second segment. i.e when dimens = 2 and I have no idea why this is happening. I tried to fix it by changing the values and size but visual studio is not providing any clear error except bad memory alloc at certain memory location.
here is the segment function:
void MapTree::segment(Node * node)
{
while (node->oBo != true)
{
int dimension = node->dimens;
node->v1 = new Node(0, dimension/2, 0 , dimension/2);
node->v2 = new Node(dimension/ 2, dimension, 0, dimension/ 2);
node->v3 = new Node(0, dimension / 2 , dimension / 2, dimension);
node->v4 = new Node(dimension / 2, dimension, dimension / 2, dimension);
segment(node->v1);
segment(node->v2);
segment(node->v3);
segment(node->v4);
}
and last but not the least the size given for the tree is always the power of 2 so the segments are always going to end up being the size of one by one
Never mind, I figured out what was wrong, I think I did not worded my question here correctly. but after some debugging I found the error, the loop was being called again again from the same position and hence infinite memory allocation. since root node->oBo will never be true hence infinite loop and bad memory alloc.

Intersection in box2d not working right

I'm trying to use the code here
http://www.iforce2d.net/b2dtut/raycasting
So I can know whether a line cross a box2d object or not. It half works, in that when the line crosses the object it does show as an intersection, however, if you click before the object, it still shows as an intersection, as if it doesn't know the line stops before the object. From reading about this code, it should not do this.
Here's a screen shot of the issue.
And here's the method I'm using for the check
-(b2Vec2)rayCheckWithInput:(b2Vec2)p1 andX:(b2Vec2)p2
{
b2RayCastInput input;
input.p1 = p1;
input.p2 = p2;
input.maxFraction = 1;
//check every fixture of every body to find closest
float closestFraction = 1; //start with end of line as p2
b2Vec2 intersectionNormal(0,0);
//for (b2Body* b = self.world.world->GetBodyList(); b; b = b->GetNext()) {
for (b2Fixture* f = box.body->GetFixtureList(); f; f = f->GetNext()) {
b2RayCastOutput output;
if ( ! f->RayCast( &output, input, 0 ) )
{
NSLog(#"Not Intersected");
continue;
}
if ( output.fraction < closestFraction ) {
closestFraction = output.fraction;
intersectionNormal = output.normal;
NSLog(#"Intersected");
NSLog(#"%f %f,", output.normal.x, output.normal.y);
NSLog(#"%f", output.fraction);
}
else{
NSLog(#"Intersected2");
NSLog(#"%f %f,", output.normal.x, output.normal.y);
NSLog(#"%f", output.fraction);
}
}
//}
b2Vec2 intersectionPoint = p1 + closestFraction * (p2 - p1);
NSLog(#"I point %f, %f", intersectionPoint.x, intersectionPoint.y);
return intersectionPoint;
}
I can't see how in that check I can tell if the click point is before (no intersection) or beyond (intersection) the bird, it seems to give the same result regardless of either of those possibilities.
Any ideas?
It's a problem of unit of measure: you should convert p1 and p2 coordinates (pixel or points) to box2d coordinates (meters).

StereoBM OpenCV bad allocation in release without debugging

I have a client/server application, my server manages the opencv library to do for example disparity mapping, the application works fine with stereoSGBM, but with stereoBM I have random crash with ctrl + f5 release, so launching it without debugging.
The crash is random, with a try/catch sometimes I can get bad allocation memory, failed to allocate 1k bytes. Instead with the call stack I'm not able to catch anything relevant because the crash is not always in the same point, sometimes is in a imgread, sometimes is a malloc, a free a mat.release, so every time is different, but always involves memory in some way.
The code is pretty simple:
void disparity_mapping(std::vector<std::string> & _return, const StereoBmValue& BmValue, const ClientShowSelection& clientShowSelection, const std::string& filenameL, const std::string& filenameR)
{
int ch;
alg = BmValue.algorithmSelection;
if((filenameL == "0" || filenameR == "0"))
_return.push_back("0");
if((filenameL != "0" && filenameR != "0"))
{
imgL = imread(filenameL , CV_LOAD_IMAGE_GRAYSCALE );
imgR = imread(filenameR, CV_LOAD_IMAGE_GRAYSCALE );
_return.push_back("1");
ch = imgL.channels();
setAlgValue(BmValue, methodSelection, ch); //Setting the value for StereoBM or SGBM
disp = calculateDisparity(imgL, imgR, alg); //calculating disparity
normalize(disp, disp8, 0, 255, CV_MINMAX, CV_8U);
string matAsStringL(imgL.begin<unsigned char>(), imgL.end<unsigned char>());
_return.push_back(matAsStringL);
string matAsStringR(imgR.begin<unsigned char>(), imgR.end<unsigned char>());
_return.push_back(matAsStringR);
string matAsStringD(disp8.begin<unsigned char>(), disp8.end<unsigned char>());
_return.push_back(matAsStringD);
}
the two function that are called:
void setAlgValue(const StereoBmValue BmValue, int methodSelection, int ch)
{
if (initDisp)
initDisparity(methodSelection); //inizializing bm.init(...) and find remap informations from steroRect, etc.
//Select 0 == StereoSGBM, 1 == StereoBM
int alg = BmValue.algorithmSelection;
//storing alg value.
stereoValue.minDisparity = BmValue.minDisparity;
stereoValue.disp12MaxDiff = BmValue.disp12MaxDiff;
stereoValue.SADWindowSize = BmValue.SADWindowSize;
stereoValue.textureThreshold = BmValue.textureThreshold;
stereoValue.uniquenessRatio = BmValue.uniquenessRatio;
stereoValue.numberOfDisparities = BmValue.numberOfDisparities;
stereoValue.preFilterCap = BmValue.preFilterCap;
stereoValue.speckleWindowSize = BmValue.speckleWindowSize;
stereoValue.speckleRange = BmValue.speckleRange;
stereoValue.preFilterSize = BmValue.preFilterSize;
if (alg == 1) //set of the values in the bm state
{
bm.state->roi1 = roi1;
bm.state->roi2 = roi2;
bm.state->preFilterCap = stereoValue.preFilterCap;
bm.state->SADWindowSize = stereoValue.SADWindowSize;
bm.state->minDisparity = stereoValue.minDisparity;
bm.state->numberOfDisparities = stereoValue.numberOfDisparities;
bm.state->textureThreshold = stereoValue.textureThreshold;
bm.state->uniquenessRatio = stereoValue.uniquenessRatio;
bm.state->speckleWindowSize = stereoValue.speckleWindowSize;
bm.state->speckleRange = stereoValue.speckleRange;
bm.state->disp12MaxDiff = stereoValue.disp12MaxDiff;
bm.state->preFilterSize = stereoValue.preFilterSize;
}
else if(alg == 0) //same for SGBM
{
sgbm.P1 = 8*ch*sgbm.SADWindowSize*sgbm.SADWindowSize;
sgbm.P2 = 32*ch*sgbm.SADWindowSize*sgbm.SADWindowSize;
sgbm.preFilterCap = stereoValue.preFilterCap;
sgbm.SADWindowSize = stereoValue.SADWindowSize;
sgbm.minDisparity = stereoValue.minDisparity;
sgbm.numberOfDisparities = stereoValue.numberOfDisparities;
sgbm.uniquenessRatio = stereoValue.uniquenessRatio;
sgbm.speckleWindowSize = stereoValue.speckleWindowSize;
sgbm.speckleRange = stereoValue.speckleRange;
sgbm.disp12MaxDiff = stereoValue.disp12MaxDiff;
}
}
and the other one:
Mat calculateDisparity(Mat& imgL, Mat& imgR, int alg)
{
Mat disparity;
//remap for rectification
remap(imgL, imgL, map11, map12, INTER_LINEAR,BORDER_CONSTANT, Scalar());
remap(imgR, imgR, map21, map22, INTER_LINEAR,BORDER_CONSTANT, Scalar());
//disp
if (alg == 1)
bm( imgL , imgR , disparity);
else if (alg == 0)
sgbm(imgL, imgR, disparity);
return disparity;
}
So as you can see the code is really simple, but using bm make all crash. I'm using the last OpenCV library build for VS9 updated. Is also linked with thrift apache, pcl, eigen, vtk and boost. The bm/sgbm value are controlled by the client and are correct, i don't get any error in debug/release with debug.
What can be? Why one works and the other one make the entire application to crash? Why just in release without debug?
I was having this same issue, and just found out that with high values of bm.state->textureThreshold it would crash. Values from ~50+ are crashing for me.

OpenCV groupRectangles - getting grouped and ungrouped rectangles

I'm using OpenCV and want to group together rectangles that have significant overlap. I've tried using groupRectangles for this, which takes a group threshold argument. With a threshold of 0 it doesn't do any grouping at all, and with a threshold of 1 is only returns rectangles that were the result of at least 2 rectangles. For example, given the rectangles on the left in the image below you end up with the 2 rectangles on the right:
What I'd like to end up with is 3 rectangles. The 2 on the right in the image above, plus the rectangle in the top right of the image to the left that doesn't overlap with any other rectangles. What's the best way to achieve this?
The solution I ended up going with was to duplicate all of the initial rectangles before calling groupRectangles. That way every input rectangle is guaranteed to be grouped with at least one other rectangle, and will appear in the output:
int size = rects.size();
for( int i = 0; i < size; i++ )
{
rects.push_back(Rect(rects[i]));
}
groupRectangles(rects, 1, 0.2);
A little late to the party, however "duplicating" solution did not properly work for me. I also had another problem where merged rectangles would overlap and would need to be merged.
So I came up with an overkill solution (might require C++14 compiler). Here's usage example:
std::vector<cv::Rect> rectangles, test1, test2, test3;
rectangles.push_back(cv::Rect(cv::Point(5, 5), cv::Point(15, 15)));
rectangles.push_back(cv::Rect(cv::Point(14, 14), cv::Point(26, 26)));
rectangles.push_back(cv::Rect(cv::Point(24, 24), cv::Point(36, 36)));
rectangles.push_back(cv::Rect(cv::Point(37, 20), cv::Point(40, 40)));
rectangles.push_back(cv::Rect(cv::Point(20, 37), cv::Point(40, 40)));
test1 = rectangles;
test2 = rectangles;
test3 = rectangles;
//Output format: {Rect(x, y, width, height), ...}
//Merge once
mergeRectangles(test1);
//Output rectangles: test1 = {Rect(5, 5, 31, 31), Rect(20, 20, 20, 20)}
//Merge until there are no rectangles to merge
mergeRectangles(test2, true);
//Output rectangles: test2 = {Rect(5, 5, 35, 35)}
//Override default merge (intersection) function to merge all rectangles
mergeRectangles(test3, false, [](const cv::Rect& r1, const cv::Rect& r2) {
return true;
});
//Output rectangles: test3 = {Rect(5, 5, 35, 35)}
Function:
void mergeRectangles(std::vector<cv::Rect>& rectangles, bool recursiveMerge = false, std::function<bool(const cv::Rect& r1, const cv::Rect& r2)> mergeFn = nullptr) {
static auto defaultFn = [](const cv::Rect& r1, const cv::Rect& r2) {
return (r1.x < (r2.x + r2.width) && (r1.x + r1.width) > r2.x && r1.y < (r2.y + r2.height) && (r1.y + r1.height) > r2.y);
};
static auto innerMerger = [](std::vector<cv::Rect>& rectangles, std::function<bool(const cv::Rect& r1, const cv::Rect& r2)>& mergeFn) {
std::vector<std::vector<std::vector<cv::Rect>::const_iterator>> groups;
std::vector<cv::Rect> mergedRectangles;
bool merged = false;
static auto findIterator = [&](std::vector<cv::Rect>::const_iterator& iteratorToFind) {
for (auto groupIterator = groups.begin(); groupIterator != groups.end(); ++groupIterator) {
auto foundIterator = std::find(groupIterator->begin(), groupIterator->end(), iteratorToFind);
if (foundIterator != groupIterator->end()) {
return groupIterator;
}
}
return groups.end();
};
for (auto rect1_iterator = rectangles.begin(); rect1_iterator != rectangles.end(); ++rect1_iterator) {
auto groupIterator = findIterator(rect1_iterator);
if (groupIterator == groups.end()) {
groups.push_back({rect1_iterator});
groupIterator = groups.end() - 1;
}
for (auto rect2_iterator = rect1_iterator + 1; rect2_iterator != rectangles.end(); ++rect2_iterator) {
if (mergeFn(*rect1_iterator, *rect2_iterator)) {
groupIterator->push_back(rect2_iterator);
merged = true;
}
}
}
for (auto groupIterator = groups.begin(); groupIterator != groups.end(); ++groupIterator) {
auto groupElement = groupIterator->begin();
int x1 = (*groupElement)->x;
int x2 = (*groupElement)->x + (*groupElement)->width;
int y1 = (*groupElement)->y;
int y2 = (*groupElement)->y + (*groupElement)->height;
while (++groupElement != groupIterator->end()) {
if (x1 > (*groupElement)->x)
x1 = (*groupElement)->x;
if (x2 < (*groupElement)->x + (*groupElement)->width)
x2 = (*groupElement)->x + (*groupElement)->width;
if (y1 >(*groupElement)->y)
y1 = (*groupElement)->y;
if (y2 < (*groupElement)->y + (*groupElement)->height)
y2 = (*groupElement)->y + (*groupElement)->height;
}
mergedRectangles.push_back(cv::Rect(cv::Point(x1, y1), cv::Point(x2, y2)));
}
rectangles = mergedRectangles;
return merged;
};
if (!mergeFn)
mergeFn = defaultFn;
while (innerMerger(rectangles, mergeFn) && recursiveMerge);
}
By checking out groupRectangles() in opencv-3.3.0 source code:
if( groupThreshold <= 0 || rectList.empty() )
{
// ......
return;
}
I saw that if groupThreshold is set to less than or equal to 0, the function would just return without doing any grouping.
On the other hand, the following code removed all rectangles which don't have more than groupThreshold similarities.
// filter out rectangles which don't have enough similar rectangles
if( n1 <= groupThreshold )
continue;
That explains why with groupThreshold=1 only rectangles with at least 2 overlappings are in the output.
One possible solution could be to modify the source code shown above (replacing n1 <= groupThreshold with n1 < groupThreshold) and re-compile OpenCV.

How to replace an instance with another instance via pointer?

I'm doing online destructive clustering (clusters replace clustered objects) on a list of class instances (stl::list).
Background
My list of current percepUnits is: stl::list<percepUnit> units; and for each iteration I get a new list of input percepUnits stl::list<percepUnit> scratch; that need to be clustered with the units.
I want to maintain a fixed number of percepUnits (so units.size() is constant), so for each new scratch percepUnit I need to merge it with the nearest percepUnit in units. Following is a code snippet that builds a list (dists) of structures (percepUnitDist) that contain pointers to each pair of items in scratch and units percepDist.scratchUnit = &(*scratchUnit); and percepDist.unit = &(*unit); and their distance. Additionally, for each item in scratch I keep track of which item in units has the least distance minDists.
// For every scratch percepUnit:
for (scratchUnit = scratch.begin(); scratchUnit != scratch.end(); scratchUnit++) {
float minDist=2025.1172; // This is the max possible distance in unnormalized CIELuv, and much larger than the normalized dist.
// For every percepUnit:
for (unit = units.begin(); unit != units.end(); unit++) {
// compare pairs
float dist = featureDist(*scratchUnit, *unit, FGBG);
//cout << "distance: " << dist << endl;
// Put pairs in a structure that caches their distances
percepUnitDist percepDist;
percepDist.scratchUnit = &(*scratchUnit); // address of where scratchUnit points to.
percepDist.unit = &(*unit);
percepDist.dist = dist;
// Figure out the percepUnit that is closest to this scratchUnit.
if (dist < minDist)
minDist = dist;
dists.push_back(percepDist); // append dist struct
}
minDists.push_back(minDist); // append the min distance to the nearest percepUnit for this particular scratchUnit.
}
So now I just need to loop through the percepUnitDist items in dists and match the distances with the minimum distances to figure out which percepUnit in scratch should be merged with which percepUnit in units. The merging process mergePerceps() creates a new percepUnit which is a weighted average of the "parent" percepUnits in scratch and units.
Question
I want to replace the instance in the units list with the new percepUnit constructed by mergePerceps(), but I would like to do so in the context of looping through the percepUnitDists. This is my current code:
// Loop through dists and merge all the closest pairs.
// Loop through all dists
for (distIter = dists.begin(); distIter != dists.end(); distIter++) {
// Loop through all minDists for each scratchUnit.
for (minDistsIter = minDists.begin(); minDistsIter != minDists.end(); minDistsIter++) {
// if this is the closest cluster, and the closest cluster has not already been merged, and the scratch has not already been merged.
if (*minDistsIter == distIter->dist and not distIter->scratchUnit->remove) {
percepUnit newUnit;
mergePerceps(*(distIter->scratchUnit), *(distIter->unit), newUnit, FGBG);
*(distIter->unit) = newUnit; // replace the cluster with the new merged version.
distIter->scratchUnit->remove = true;
}
}
}
I thought that I could replace the instance in units via the percepUnitDist pointer with the new percepUnit instance using *(distIter->unit) = newUnit;, but that does not seem to be working as I'm seeing a memory leak, implying the instances in the units are not getting replaced.
How do I delete the percepUnit in the units list and replace it with a new percepUnit instance such that the new unit is located in the same location?
EDIT1
Here is the percepUnit class. Note the cv::Mat members. Following is the mergePerceps() function and the mergeImages() function on which it depends:
// Function to construct an accumulation.
void clustering::mergeImages(Mat &scratch, Mat &unit, cv::Mat &merged, const string maskOrImage, const string FGBG, const float scratchWeight, const float unitWeight) {
int width, height, type=CV_8UC3;
Mat scratchImagePad, unitImagePad, scratchImage, unitImage;
// use the resolution and aspect of the largest of the pair.
if (unit.cols > scratch.cols)
width = unit.cols;
else
width = scratch.cols;
if (unit.rows > scratch.rows)
height = unit.rows;
else
height = scratch.rows;
if (maskOrImage == "mask")
type = CV_8UC1; // single channel mask
else if (maskOrImage == "image")
type = CV_8UC3; // three channel image
else
cout << "maskOrImage is not 'mask' or 'image'\n";
merged = Mat(height, width, type, Scalar::all(0));
scratchImagePad = Mat(height, width, type, Scalar::all(0));
unitImagePad = Mat(height, width, type, Scalar::all(0));
// weight images before summation.
// because these pass by reference, they mess up the images in memory!
scratch *= scratchWeight;
unit *= unitWeight;
// copy images into padded images.
scratch.copyTo(scratchImagePad(Rect((scratchImagePad.cols-scratch.cols)/2,
(scratchImagePad.rows-scratch.rows)/2,
scratch.cols,
scratch.rows)));
unit.copyTo(unitImagePad(Rect((unitImagePad.cols-unit.cols)/2,
(unitImagePad.rows-unit.rows)/2,
unit.cols,
unit.rows)));
merged = scratchImagePad+unitImagePad;
}
// Merge two perceps and return a new percept to replace them.
void clustering::mergePerceps(percepUnit scratch, percepUnit unit, percepUnit &mergedUnit, const string FGBG) {
Mat accumulation;
Mat accumulationMask;
Mat meanColour;
int x, y, w, h, area;
float l,u,v;
int numMerges=0;
std::vector<float> featuresVar; // Normalized, Sum, Variance.
//float featuresVarMin, featuresVarMax; // min and max variance accross all features.
float scratchWeight, unitWeight;
if (FGBG == "FG") {
// foreground percepts don't get merged as much.
scratchWeight = 0.65;
unitWeight = 1-scratchWeight;
} else {
scratchWeight = 0.85;
unitWeight = 1-scratchWeight;
}
// Images TODO remove the meanColour if needbe.
mergeImages(scratch.image, unit.image, accumulation, "image", FGBG, scratchWeight, unitWeight);
mergeImages(scratch.mask, unit.mask, accumulationMask, "mask", FGBG, scratchWeight, unitWeight);
mergeImages(scratch.meanColour, unit.meanColour, meanColour, "image", "FG", scratchWeight, unitWeight); // merge images
// Position and size.
x = (scratch.x1*scratchWeight) + (unit.x1*unitWeight);
y = (scratch.y1*scratchWeight) + (unit.y1*unitWeight);
w = (scratch.w*scratchWeight) + (unit.w*unitWeight);
h = (scratch.h*scratchWeight) + (unit.h*unitWeight);
// area
area = (scratch.area*scratchWeight) + (unit.area*unitWeight);
// colour
l = (scratch.l*scratchWeight) + (unit.l*unitWeight);
u = (scratch.u*scratchWeight) + (unit.u*unitWeight);
v = (scratch.v*scratchWeight) + (unit.v*unitWeight);
// Number of merges
if (scratch.numMerges < 1 and unit.numMerges < 1) { // both units are patches
numMerges = 1;
} else if (scratch.numMerges < 1 and unit.numMerges >= 1) { // unit A is a patch, B a percept
numMerges = unit.numMerges + 1;
} else if (scratch.numMerges >= 1 and unit.numMerges < 1) { // unit A is a percept, B a patch.
numMerges = scratch.numMerges + 1;
cout << "merged scratch??" <<endl;
// TODO this may be an impossible case.
} else { // both units are percepts
numMerges = scratch.numMerges + unit.numMerges;
cout << "Merging two already merged Percepts" <<endl;
// TODO this may be an impossible case.
}
// Create unit.
mergedUnit = percepUnit(accumulation, accumulationMask, x, y, w, h, area); // time is the earliest value in times?
mergedUnit.l = l; // members not in the constrcutor.
mergedUnit.u = u;
mergedUnit.v = v;
mergedUnit.numMerges = numMerges;
mergedUnit.meanColour = meanColour;
mergedUnit.pActivated = unit.pActivated; // new clusters retain parent's history of activation.
mergedUnit.scratch = false;
mergedUnit.habituation = unit.habituation; // we inherent the habituation of the cluster we merged with.
}
EDIT2
Changing the copy and assignment operators had performance side-effects and did not seem to resolve the problem. So I've added a custom function to do the replacement, which just like the copy operator makes copies of each member and make's sure those copies are deep. The problem is that I still end up with a leak.
So I've changed this line: *(distIter->unit) = newUnit;
to this: (*(distIter->unit)).clone(newUnit)
Where the clone method is as follows:
// Deep Copy of members
void percepUnit::clone(const percepUnit &source) {
// Deep copy of Mats
this->image = source.image.clone();
this->mask = source.mask.clone();
this->alphaImage = source.alphaImage.clone();
this->meanColour = source.meanColour.clone();
// shallow copies of everything else
this->alpha = source.alpha;
this->fadingIn = source.fadingIn;
this->fadingHold = source.fadingHold;
this->fadingOut = source.fadingOut;
this->l = source.l;
this->u = source.u;
this->v = source.v;
this->x1 = source.x1;
this->y1 = source.y1;
this->w = source.w;
this->h = source.h;
this->x2 = source.x2;
this->y2 = source.y2;
this->cx = source.cx;
this->cy = source.cy;
this->numMerges = source.numMerges;
this->id = source.id;
this->area = source.area;
this->features = source.features;
this->featuresNorm = source.featuresNorm;
this->remove = source.remove;
this->fgKnockout = source.fgKnockout;
this->colourCalculated = source.colourCalculated;
this->normalized = source.normalized;
this->activation = source.activation;
this->activated = source.activated;
this->pActivated = source.pActivated;
this->habituation = source.habituation;
this->scratch = source.scratch;
this->FGBG = source.FGBG;
}
And yet, I still see a memory increase. The increase does not happen if I comment out that single replacement line. So I'm still stuck.
EDIT3
I can prevent memory from increasing if I disable the cv::Mat cloning code in the function above:
// Deep Copy of members
void percepUnit::clone(const percepUnit &source) {
/* try releasing Mats first?
// No effect on memory increase, but the refCount is decremented.
this->image.release();
this->mask.release();
this->alphaImage.release();
this->meanColour.release();*/
/* Deep copy of Mats
this->image = source.image.clone();
this->mask = source.mask.clone();
this->alphaImage = source.alphaImage.clone();
this->meanColour = source.meanColour.clone();*/
// shallow copies of everything else
this->alpha = source.alpha;
this->fadingIn = source.fadingIn;
this->fadingHold = source.fadingHold;
this->fadingOut = source.fadingOut;
this->l = source.l;
this->u = source.u;
this->v = source.v;
this->x1 = source.x1;
this->y1 = source.y1;
this->w = source.w;
this->h = source.h;
this->x2 = source.x2;
this->y2 = source.y2;
this->cx = source.cx;
this->cy = source.cy;
this->numMerges = source.numMerges;
this->id = source.id;
this->area = source.area;
this->features = source.features;
this->featuresNorm = source.featuresNorm;
this->remove = source.remove;
this->fgKnockout = source.fgKnockout;
this->colourCalculated = source.colourCalculated;
this->normalized = source.normalized;
this->activation = source.activation;
this->activated = source.activated;
this->pActivated = source.pActivated;
this->habituation = source.habituation;
this->scratch = source.scratch;
this->FGBG = source.FGBG;
}
EDIT4
While I still can't explain this issue, I did notice another hint. I realized that this leak can also be stopped if I don't normalize those features I use to cluster via featureDist() (but continue to clone cv::Mats). The really odd thing is that I rewrote that code entirely and still the problem persists.
Here is the featureDist function:
float clustering::featureDist(percepUnit unitA, percepUnit unitB, const string FGBG) {
float distance=0;
if (FGBG == "BG") {
for (unsigned int i=0; i<unitA.featuresNorm.rows; i++) {
distance += pow(abs(unitA.featuresNorm.at<float>(i) - unitB.featuresNorm.at<float>(i)),0.5);
//cout << "unitA.featuresNorm[" << i << "]: " << unitA.featuresNorm[i] << endl;
//cout << "unitB.featuresNorm[" << i << "]: " << unitB.featuresNorm[i] << endl;
}
// for FG, don't use normalized colour features.
// TODO To include the area use i=4
} else if (FGBG == "FG") {
for (unsigned int i=4; i<unitA.features.rows; i++) {
distance += pow(abs(unitA.features.at<float>(i) - unitB.features.at<float>(i)),0.5);
}
} else {
cout << "FGBG argument was not FG or BG, returning 0." <<endl;
return 0;
}
return pow(distance,2);
}
Features used to be a vector of floats, and thus the normalization code was as follows:
void clustering::normalize(list<percepUnit> &scratch, list<percepUnit> &units) {
list<percepUnit>::iterator unit;
list<percepUnit*>::iterator unitPtr;
vector<float> min,max;
list<percepUnit*> masterList; // list of pointers.
// generate pointers
for (unit = scratch.begin(); unit != scratch.end(); unit++)
masterList.push_back(&(*unit)); // add pointer to where unit points to.
for (unit = units.begin(); unit != units.end(); unit++)
masterList.push_back(&(*unit)); // add pointer to where unit points to.
int numFeatures = masterList.front()->features.size(); // all percepts have the same number of features.
min.resize(numFeatures); // allocate for the number of features we have.
max.resize(numFeatures);
// Loop through all units to get feature values
for (int i=0; i<numFeatures; i++) {
min[i] = masterList.front()->features[i]; // starting point.
max[i] = min[i];
// calculate min and max for each feature.
for (unitPtr = masterList.begin(); unitPtr != masterList.end(); unitPtr++) {
if ((*unitPtr)->features[i] < min[i])
min[i] = (*unitPtr)->features[i];
if ((*unitPtr)->features[i] > max[i])
max[i] = (*unitPtr)->features[i];
}
}
// Normalize features according to min/max.
for (int i=0; i<numFeatures; i++) {
for (unitPtr = masterList.begin(); unitPtr != masterList.end(); unitPtr++) {
(*unitPtr)->featuresNorm[i] = ((*unitPtr)->features[i]-min[i]) / (max[i]-min[i]);
(*unitPtr)->normalized = true;
}
}
}
I changed the features type to a cv::Mat so I could use the opencv normalization function, so I rewrote the normalization function as follows:
void clustering::normalize(list<percepUnit> &scratch, list<percepUnit> &units) {
Mat featureMat = Mat(1,units.size()+scratch.size(), CV_32FC1, Scalar(0));
list<percepUnit>::iterator unit;
// For each feature
for (int i=0; i< units.begin()->features.rows; i++) {
// for each unit in units
int j=0;
float value;
for (unit = units.begin(); unit != units.end(); unit++) {
// Populate featureMat j is the unit index, i is the feature index.
value = unit->features.at<float>(i);
featureMat.at<float>(j) = value;
j++;
}
// for each unit in scratch
for (unit = scratch.begin(); unit != scratch.end(); unit++) {
// Populate featureMat j is the unit index, i is the feature index.
value = unit->features.at<float>(i);
featureMat.at<float>(j) = value;
j++;
}
// Normalize this featureMat in place
cv::normalize(featureMat, featureMat, 0, 1, NORM_MINMAX);
// set normalized values in percepUnits from featureMat
// for each unit in units
j=0;
for (unit = units.begin(); unit != units.end(); unit++) {
// Populate percepUnit featuresNorm, j is the unit index, i is the feature index.
value = featureMat.at<float>(j);
unit->featuresNorm.at<float>(i) = value;
j++;
}
// for each unit in scratch
for (unit = scratch.begin(); unit != scratch.end(); unit++) {
// Populate percepUnit featuresNorm, j is the unit index, i is the feature index.
value = featureMat.at<float>(j);
unit->featuresNorm.at<float>(i) = value;
j++;
}
}
}
I can't understand what the interaction between mergePercepts and normalization, especially since normalization is an entirely rewritten function.
Update
Massif and my /proc memory reporting don't agree. Massif says there is no effect of normalization on memory usage, only commenting out the percepUnit::clone() operation bypasses the leak.
Here is all the code, in case the interaction is somewhere else I am missing.
Here is another version of the same code with the dependence on OpenCV GPU removed, to facilitate testing...
It was recommended by Nghia (on the opencv forum) that I try and make the percepts a constant size. Sure enough, if I fix the dimensions and type of the cv::Mat members of percepUnit, then the leak disappears.
So it seems to me this is a bug in OpenCV that effects calling clone() and copyTo() on Mats of different sizes that are class members. So far unable to reproduce in a simple program. The leak does seem small enough that it may be the headers leaking, rather than the underlying image data.