//! \exception std::runtime_error will be thrown if \a inRegion overlaps an //! existing region. //! //! \todo Need to investigate if we can use the STL sort algorithm at all. Even //! though we're doing merges too, we could sort first then examine the list //! for merges. void StExecutableImage::insertOrMergeRegion(MemoryRegion &inRegion) { uint32_t newStart = inRegion.m_address; uint32_t newEnd = newStart + inRegion.m_length; MemoryRegionList::iterator it = m_image.begin(); for (; it != m_image.end(); ++it) { MemoryRegion ®ion = *it; uint32_t thisStart = region.m_address; uint32_t thisEnd = thisStart + region.m_length; // keep track of where to insert it to retain sort order if (thisStart >= newEnd) { break; } // region types and flags must match in order to merge if (region.m_type == inRegion.m_type && region.m_flags == inRegion.m_flags) { if (newStart == thisEnd || newEnd == thisStart) { mergeRegions(region, inRegion); return; } else if ((newStart >= thisStart && newStart < thisEnd) || (newEnd >= thisStart && newEnd < thisEnd)) { throw std::runtime_error("new region overlaps existing region"); } } } // not merged, so just insert it in the sorted position m_image.insert(it, inRegion); }
static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegionSize, unsigned short& maxRegionId, rcCompactHeightfield& chf, unsigned short* srcReg) { const int w = chf.width; const int h = chf.height; const int nreg = maxRegionId+1; rcRegion* regions = (rcRegion*)rcAlloc(sizeof(rcRegion)*nreg, RC_ALLOC_TEMP); if (!regions) { ctx->log(RC_LOG_ERROR, "filterSmallRegions: Out of memory 'regions' (%d).", nreg); return false; } // Construct regions for (int i = 0; i < nreg; ++i) new(®ions[i]) rcRegion((unsigned short)i); // Find edge of a region and find connections around the contour. for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { const rcCompactCell& c = chf.cells[x+y*w]; for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) { unsigned short r = srcReg[i]; if (r == 0 || r >= nreg) continue; rcRegion& reg = regions[r]; reg.spanCount++; // Update floors. for (int j = (int)c.index; j < ni; ++j) { if (i == j) continue; unsigned short floorId = srcReg[j]; if (floorId == 0 || floorId >= nreg) continue; addUniqueFloorRegion(reg, floorId); } // Have found contour if (reg.connections.size() > 0) continue; reg.areaType = chf.areas[i]; // Check if this cell is next to a border. int ndir = -1; for (int dir = 0; dir < 4; ++dir) { if (isSolidEdge(chf, srcReg, x, y, i, dir)) { ndir = dir; break; } } if (ndir != -1) { // The cell is at border. // Walk around the contour to find all the neighbours. walkContour(x, y, i, ndir, chf, srcReg, reg.connections); } } } } // Remove too small regions. rcIntArray stack(32); rcIntArray trace(32); for (int i = 0; i < nreg; ++i) { rcRegion& reg = regions[i]; if (reg.id == 0 || (reg.id & RC_BORDER_REG)) continue; if (reg.spanCount == 0) continue; if (reg.visited) continue; // Count the total size of all the connected regions. // Also keep track of the regions connects to a tile border. bool connectsToBorder = false; int spanCount = 0; stack.resize(0); trace.resize(0); reg.visited = true; stack.push(i); while (stack.size()) { // Pop int ri = stack.pop(); rcRegion& creg = regions[ri]; spanCount += creg.spanCount; trace.push(ri); for (int j = 0; j < creg.connections.size(); ++j) { if (creg.connections[j] & RC_BORDER_REG) { connectsToBorder = true; continue; } rcRegion& neireg = regions[creg.connections[j]]; if (neireg.visited) continue; if (neireg.id == 0 || (neireg.id & RC_BORDER_REG)) continue; // Visit stack.push(neireg.id); neireg.visited = true; } } // If the accumulated regions size is too small, remove it. // Do not remove areas which connect to tile borders // as their size cannot be estimated correctly and removing them // can potentially remove necessary areas. if (spanCount < minRegionArea && !connectsToBorder) { // Kill all visited regions. for (int j = 0; j < trace.size(); ++j) { regions[trace[j]].spanCount = 0; regions[trace[j]].id = 0; } } } // Merge too small regions to neighbour regions. int mergeCount = 0 ; do { mergeCount = 0; for (int i = 0; i < nreg; ++i) { rcRegion& reg = regions[i]; if (reg.id == 0 || (reg.id & RC_BORDER_REG)) continue; if (reg.spanCount == 0) continue; // Check to see if the region should be merged. if (reg.spanCount > mergeRegionSize && isRegionConnectedToBorder(reg)) continue; // Small region with more than 1 connection. // Or region which is not connected to a border at all. // Find smallest neighbour region that connects to this one. int smallest = 0xfffffff; unsigned short mergeId = reg.id; for (int j = 0; j < reg.connections.size(); ++j) { if (reg.connections[j] & RC_BORDER_REG) continue; rcRegion& mreg = regions[reg.connections[j]]; if (mreg.id == 0 || (mreg.id & RC_BORDER_REG)) continue; if (mreg.spanCount < smallest && canMergeWithRegion(reg, mreg) && canMergeWithRegion(mreg, reg)) { smallest = mreg.spanCount; mergeId = mreg.id; } } // Found new id. if (mergeId != reg.id) { unsigned short oldId = reg.id; rcRegion& target = regions[mergeId]; // Merge neighbours. if (mergeRegions(target, reg)) { // Fixup regions pointing to current region. for (int j = 0; j < nreg; ++j) { if (regions[j].id == 0 || (regions[j].id & RC_BORDER_REG)) continue; // If another region was already merged into current region // change the nid of the previous region too. if (regions[j].id == oldId) regions[j].id = mergeId; // Replace the current region with the new one if the // current regions is neighbour. replaceNeighbour(regions[j], oldId, mergeId); } mergeCount++; } } } } while (mergeCount > 0); // Compress region Ids. for (int i = 0; i < nreg; ++i) { regions[i].remap = false; if (regions[i].id == 0) continue; // Skip nil regions. if (regions[i].id & RC_BORDER_REG) continue; // Skip external regions. regions[i].remap = true; } unsigned short regIdGen = 0; for (int i = 0; i < nreg; ++i) { if (!regions[i].remap) continue; unsigned short oldId = regions[i].id; unsigned short newId = ++regIdGen; for (int j = i; j < nreg; ++j) { if (regions[j].id == oldId) { regions[j].id = newId; regions[j].remap = false; } } } maxRegionId = regIdGen; // Remap regions. for (int i = 0; i < chf.spanCount; ++i) { if ((srcReg[i] & RC_BORDER_REG) == 0) srcReg[i] = regions[srcReg[i]].id; } for (int i = 0; i < nreg; ++i) regions[i].~rcRegion(); rcFree(regions); return true; }
static bool filterSmallRegions(int minRegionSize, int mergeRegionSize, unsigned short& maxRegionId, rcCompactHeightfield& chf, unsigned short* src) { const int w = chf.width; const int h = chf.height; int nreg = maxRegionId+1; rcRegion* regions = new rcRegion[nreg]; if (!regions) { if (rcGetLog()) rcGetLog()->log(RC_LOG_ERROR, "filterSmallRegions: Out of memory 'regions' (%d).", nreg); return false; } for (int i = 0; i < nreg; ++i) regions[i].id = (unsigned short)i; // Find edge of a region and find connections around the contour. for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { const rcCompactCell& c = chf.cells[x+y*w]; for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) { unsigned short r = src[i*2]; if (r == 0 || r >= nreg) continue; rcRegion& reg = regions[r]; reg.count++; // Update floors. for (int j = (int)c.index; j < ni; ++j) { if (i == j) continue; unsigned short floorId = src[j*2]; if (floorId == 0 || floorId >= nreg) continue; addUniqueFloorRegion(reg, floorId); } // Have found contour if (reg.connections.size() > 0) continue; // Check if this cell is next to a border. int ndir = -1; for (int dir = 0; dir < 4; ++dir) { if (isSolidEdge(chf, src, x, y, i, dir)) { ndir = dir; break; } } if (ndir != -1) { // The cell is at border. // Walk around the contour to find all the neighbours. walkContour(x, y, i, ndir, chf, src, reg.connections); } } } } // Remove too small unconnected regions. for (int i = 0; i < nreg; ++i) { rcRegion& reg = regions[i]; if (reg.id == 0 || (reg.id & RC_BORDER_REG)) continue; if (reg.count == 0) continue; if (reg.connections.size() == 1 && reg.connections[0] == 0) { if (reg.count < minRegionSize) { // Non-connected small region, remove. reg.count = 0; reg.id = 0; } } } // Merge too small regions to neighbour regions. int mergeCount = 0 ; do { mergeCount = 0; for (int i = 0; i < nreg; ++i) { rcRegion& reg = regions[i]; if (reg.id == 0 || (reg.id & RC_BORDER_REG)) continue; if (reg.count == 0) continue; // Check to see if the region should be merged. if (reg.count > mergeRegionSize && isRegionConnectedToBorder(reg)) continue; // Small region with more than 1 connection. // Or region which is not connected to a border at all. // Find smallest neighbour region that connects to this one. int smallest = 0xfffffff; unsigned short mergeId = reg.id; for (int j = 0; j < reg.connections.size(); ++j) { if (reg.connections[j] & RC_BORDER_REG) continue; rcRegion& mreg = regions[reg.connections[j]]; if (mreg.id == 0 || (mreg.id & RC_BORDER_REG)) continue; if (mreg.count < smallest && canMergeWithRegion(reg, mreg.id) && canMergeWithRegion(mreg, reg.id)) { smallest = mreg.count; mergeId = mreg.id; } } // Found new id. if (mergeId != reg.id) { unsigned short oldId = reg.id; rcRegion& target = regions[mergeId]; // Merge neighbours. if (mergeRegions(target, reg)) { // Fixup regions pointing to current region. for (int j = 0; j < nreg; ++j) { if (regions[j].id == 0 || (regions[j].id & RC_BORDER_REG)) continue; // If another region was already merged into current region // change the nid of the previous region too. if (regions[j].id == oldId) regions[j].id = mergeId; // Replace the current region with the new one if the // current regions is neighbour. replaceNeighbour(regions[j], oldId, mergeId); } mergeCount++; } } } } while (mergeCount > 0); // Compress region Ids. for (int i = 0; i < nreg; ++i) { regions[i].remap = false; if (regions[i].id == 0) continue; // Skip nil regions. if (regions[i].id & RC_BORDER_REG) continue; // Skip external regions. regions[i].remap = true; } unsigned short regIdGen = 0; for (int i = 0; i < nreg; ++i) { if (!regions[i].remap) continue; unsigned short oldId = regions[i].id; unsigned short newId = ++regIdGen; for (int j = i; j < nreg; ++j) { if (regions[j].id == oldId) { regions[j].id = newId; regions[j].remap = false; } } } maxRegionId = regIdGen; // Remap regions. for (int i = 0; i < chf.spanCount; ++i) { if ((src[i*2] & RC_BORDER_REG) == 0) src[i*2] = regions[src[i*2]].id; } delete [] regions; return true; }
static dtStatus filterSmallRegions(dtTileCacheAlloc* alloc, dtTileCacheLayer& layer, int minRegionArea, int mergeRegionSize, unsigned short& maxRegionId, unsigned short* srcReg) { const int w = (int)layer.header->width; const int h = (int)layer.header->height; const int nreg = maxRegionId+1; dtFixedArray<dtLayerRegion> regions(alloc, nreg); if (!regions) { return DT_FAILURE | DT_OUT_OF_MEMORY; } // Construct regions regions.set(0); for (int i = 0; i < nreg; ++i) regions[i] = dtLayerRegion((unsigned short)i); // Find edge of a region and find connections around the contour. for (int y = 0; y < h; ++y) { const bool borderY = (y == 0) || (y == (h - 1)); for (int x = 0; x < w; ++x) { const int i = x+y*w; unsigned short r = srcReg[i]; if (r == DT_TILECACHE_NULL_AREA || r >= nreg) continue; dtLayerRegion& reg = regions[r]; reg.cellCount++; reg.border |= borderY || (x == 0) || (x == (w - 1)); // Have found contour if (reg.connections.size() > 0) continue; reg.areaType = layer.areas[i]; // Check if this cell is next to a border. int ndir = -1; for (int dir = 0; dir < 4; ++dir) { if (isSolidEdge(layer, srcReg, x, y, i, dir)) { ndir = dir; break; } } if (ndir != -1) { // The cell is at border. // Walk around the contour to find all the neighbours. walkContour(x, y, i, ndir, layer, srcReg, reg.connections); } } } // Remove too small regions. dtIntArray stack(32); dtIntArray trace(32); for (int i = 0; i < nreg; ++i) { dtLayerRegion& reg = regions[i]; if (reg.id == 0) continue; if (reg.cellCount == 0) continue; if (reg.visited) continue; // Count the total size of all the connected regions. // Also keep track of the regions connects to a tile border. bool connectsToBorder = false; int cellCount = 0; stack.resize(0); trace.resize(0); reg.visited = true; stack.push(i); while (stack.size()) { // Pop int ri = stack.pop(); dtLayerRegion& creg = regions[ri]; connectsToBorder |= creg.border; cellCount += creg.cellCount; trace.push(ri); for (int j = 0; j < creg.connections.size(); ++j) { dtLayerRegion& neireg = regions[creg.connections[j]]; if (neireg.visited) continue; if (neireg.id == 0) continue; // Visit stack.push(neireg.id); neireg.visited = true; } } // If the accumulated regions size is too small, remove it. // Do not remove areas which connect to tile borders // as their size cannot be estimated correctly and removing them // can potentially remove necessary areas. if (cellCount < minRegionArea && !connectsToBorder) { // Kill all visited regions. for (int j = 0; j < trace.size(); ++j) { regions[trace[j]].cellCount = 0; regions[trace[j]].id = 0; } } } // Merge too small regions to neighbour regions. int mergeCount = 0 ; do { mergeCount = 0; for (int i = 0; i < nreg; ++i) { dtLayerRegion& reg = regions[i]; if (reg.id == 0) continue; if (reg.cellCount == 0) continue; // Check to see if the region should be merged. if (reg.cellCount > mergeRegionSize && reg.border) continue; // Small region with more than 1 connection. // Or region which is not connected to a border at all. // Find smallest neighbour region that connects to this one. int smallest = 0xfffffff; unsigned short mergeId = reg.id; for (int j = 0; j < reg.connections.size(); ++j) { dtLayerRegion& mreg = regions[reg.connections[j]]; if (mreg.id == 0) continue; if (mreg.cellCount < smallest && canMergeWithRegion(reg, mreg) && canMergeWithRegion(mreg, reg)) { smallest = mreg.cellCount; mergeId = mreg.id; } } // Found new id. if (mergeId != reg.id) { unsigned short oldId = reg.id; dtLayerRegion& target = regions[mergeId]; // Merge neighbours. if (mergeRegions(target, reg)) { // Fixup regions pointing to current region. for (int j = 0; j < nreg; ++j) { if (regions[j].id == 0) continue; // If another region was already merged into current region // change the nid of the previous region too. if (regions[j].id == oldId) regions[j].id = mergeId; // Replace the current region with the new one if the // current regions is neighbour. replaceNeighbour(regions[j], oldId, mergeId); } mergeCount++; } } } } while (mergeCount > 0); // Compress region Ids. for (int i = 0; i < nreg; ++i) { regions[i].remap = false; if (regions[i].id == DT_TILECACHE_NULL_AREA) continue; // Skip nil regions. regions[i].remap = true; } unsigned short regIdGen = 0; for (int i = 0; i < nreg; ++i) { if (!regions[i].remap) continue; unsigned short oldId = regions[i].id; unsigned short newId = ++regIdGen; for (int j = i; j < nreg; ++j) { if (regions[j].id == oldId) { regions[j].id = newId; regions[j].remap = false; } } } maxRegionId = regIdGen; // Remap regions. for (int i = w*h-1; i >= 0; i--) { srcReg[i] = regions[srcReg[i]].id; } for (int i = 0; i < nreg; ++i) regions[i].~dtLayerRegion(); return DT_SUCCESS; }
void RegionMerger::initialApproximateMerge(){ // const int batchSize=40; const int initialStop=400; int batchnr=0; while((int)regionList.size() > initialStop){ // cout << "beginning batch of initialApproximateMerge" << endl; // cout <<"regionCount="<<regionList.size() << endl; set<int> region; set<int> neighbours; set<regionDiff> joins; int batchSize=(regionList.size()-numberOfRegions)/2; //int batchSize=500; const int l=regionList.size(); vector<int> labelsInRL; map<int,Region>::iterator rit; for(rit=regionList.begin();rit!=regionList.end();rit++) labelsInRL.push_back(rit->first); // cout << labelsInRL.size() << " labels collected:( l=" << l<<")"<<endl; // for(size_t i=0;i<labelsInRL.size();i++) // cout<<labelsInRL[i]<<endl; for(int i=0;i<batchSize;i++){ int j; int r=random(l-1); //cout << "r="<<r<<endl; j=labelsInRL[r]; if(region.count(j)||neighbours.count(j)) continue; //cout << "region set: "<<formVirtualSegmentLabel(region)<<endl; //cout << "neighbours: "<<formVirtualSegmentLabel(neighbours)<<endl; region.insert(j); //char jstr[80]; //sprintf(jstr,"%d",j); //if(regionList.count(j)==0) // throw string("accepted region ")+jstr+ " not in regionlist"; const set<int> &nbr=regionList[j].nList; //cout << "accepted region "<<j<<" w/ neighbours " << endl // <<" "<< formVirtualSegmentLabel(nbr)<<endl; neighbours.insert(nbr.begin(),nbr.end()); regionDiff tmp; tmp.r1=j; tmp.r2=findClosest(j); //cout << "closest region: "<<tmp.r2<<endl; tmp.dist=regionList[j].count*regionDist(tmp.r1,tmp.r2); tmp.dist=regionDist(tmp.r1,tmp.r2); joins.insert(tmp); } // cout <<"joins.size()="<<joins.size()<<endl; // sort(joins.begin(),joins.end()); int i=0; map<int,int> joinedTo; int njoins=joins.size()/2; // arbitrary choice, quite aggressive if(njoins<1) njoins=1; for(set<regionDiff>::iterator it=joins.begin();i<njoins; i++,it++){ regionDiff d=*it; //cout << "i= "<<i<<" dist="<<it->dist<<endl; while(joinedTo.count(d.r1)) d.r1=joinedTo[d.r1]; while(joinedTo.count(d.r2)) d.r2=joinedTo[d.r2]; if(d.r1==d.r2) continue; mergeRegions(d.r1,d.r2,full_update); // if(regionList.size() != countRegions()){ // cout << "batch nr " << batchnr << endl; // cout << "joined regions "<<d.r1<<" and "<<d.r2<<endl; // cout << "regionList.size()="<<regionList.size()<<" (real="<<countRegions() // << ") -> batchSize="<<batchSize<<endl; //} if(d.r1<d.r2) joinedTo[d.r2]=d.r1; else joinedTo[d.r1]=d.r2; } batchnr++; } }
void RegionMerger::mergeTopRegions(){ //regionDiff d=queue.top(); //queue.pop(); pop_heap(queue_vector.begin(),queue_vector.end()); regionDiff d=queue_vector.back(); queue_vector.pop_back(); if(regionList.count(d.r1)==0 ||regionList.count(d.r2)==0){ // cout << "popped inexistent merge" << endl; return; } if(d.r1>d.r2){ int tmp=d.r1; d.r1=d.r2; d.r2=tmp; } Region *r1=&(regionList[d.r1]); Region *r2=&(regionList[d.r2]); if(d.timestamp < r1->timestamp || d.timestamp < r2->timestamp) return; currentTime++; int i=d.r1,i2=d.r2; // cout << "Merging regions " << d.r1 <<" and " << d.r2 // << " w\\ d=" << d.dist << endl; // cout << "Feature vectors:" << endl << " r1: "; // Feature::printFeatureVector(cout,r1->fV); // cout << endl << " r2: "; // Feature::printFeatureVector(cout,r2->fV); // cout << "Neighbours of r1: " << endl; set<int>::iterator it; // for(it=r1->nList.begin(); it != r1->nList.end(); it++){ // if(regionList[*it].count){ // cout << " #"<<*it<<" (size "<<regionList[*it].count<<"):"; // Feature::printFeatureVector(cout,regionList[*it].fV); // cout << endl; // } // } // cout << "Neighbours of r2: " << endl; // for(it=r2->nList.begin(); it != r2->nList.end(); it++){ // if(regionList[*it].count){ // cout << " #"<<*it<<" (size "<<regionList[*it].count<<"):"; // Feature::printFeatureVector(cout,regionList[*it].fV); // cout << endl; // } // } mergeRegions(d.r1,d.r2,full_update); // now pointer r2 becomes invalid for(it=r1->nList.begin(); it != r1->nList.end(); it++){ Region *r=&(regionList[*it]); bool otherBefore = (r->closestNeighbour != i && r->closestNeighbour != i2); r->closestNeighbour=findClosest(*it); if(r->closestNeighbour != i && otherBefore) continue; // other relevant merges remain in the queue regionDiff d; d.timestamp=currentTime; d.r1=*it; d.r2=r->closestNeighbour; d.dist = -regionDist(d.r1,d.r2); //queue.push(d); // cout << "pushed merge: " << d.print() << endl; queue_vector.push_back(d); push_heap(queue_vector.begin(),queue_vector.end()); } if(r1->nList.size()){ r1->closestNeighbour=findClosest(i); regionDiff dn; dn.timestamp=currentTime; dn.r1=i; dn.r2=r1->closestNeighbour; dn.dist = -regionDist(dn.r1,dn.r2); //queue.push(d); queue_vector.push_back(dn); //cout << "pushed merge: " << dn.print() << endl; push_heap(queue_vector.begin(),queue_vector.end()); r1->timestamp=currentTime; //r2->timestamp=currentTime; } // dumpRegionList(); }
bool RegionMerger::joinSmallRegions() { // first look for small regions that have only one neighbour int nregions=0; // dumpRegionList(); map<int,Region>::iterator rit=regionList.begin(); bool first=true; while(rit!=regionList.end()&&(int)regionList.size()>numberOfRegions){ if((int)rit->second.count<=sizeThreshold ){ nregions++; if((int)rit->second.nList.size()==1){ // some tricks to avoid iterators going invalid if(first){ mergeRegions(rit->first,*rit->second.nList.begin(),no_update); rit=regionList.begin(); continue; } else{ const map<int,Region>::iterator ritwas=rit; rit--; mergeRegions(ritwas->first, *ritwas->second.nList.begin(),no_update); } } } rit++; first=false; } // while if(Verbose()>1){ cout << nregions <<" small regions." << endl; cout << "regions w/ one neighbour joined." << endl; cout << "region count now " << regionList.size() << endl; } // then merge all small regions to their closest neighbours int smallmerges=0; bool approx_update=!useAveraging; bool fromFirst=false; rit=regionList.begin(); while(rit!=regionList.end()&&(int)regionList.size()>numberOfRegions){ while((int)rit->second.count<=sizeThreshold){ const int n=findClosest(rit->first); bool isInvalidated= rit->first > n; fromFirst= isInvalidated && (rit==regionList.begin()); const map<int,Region>::iterator ritwas=rit; if(isInvalidated) rit--; mergeRegions(ritwas->first,n, approx_update?choose_larger:full_update); smallmerges++; if(Verbose()>1 && smallmerges%100==0) cout << smallmerges <<" small regions merged." << endl; if(isInvalidated) break; } // while if(fromFirst){ rit=regionList.begin(); fromFirst=false; } else{ rit++; } } // while if(approx_update){ // all merges done, update remaining feature vectors for(rit=regionList.begin();rit!=regionList.end();rit++) updateFeatureValues(rit->first); } // cout << "small regions joined" << endl; return true; }