unsigned FastMarchingForFacetExtraction::updateFlagsTable( ccGenericPointCloud* theCloud, GenericChunkedArray<1,unsigned char> &flags, unsigned facetIndex) { if (!m_initialized || !m_currentFacetPoints) return 0; unsigned pointCount = m_currentFacetPoints->size(); for (unsigned k=0; k<pointCount; ++k) { unsigned index = m_currentFacetPoints->getPointGlobalIndex(k); flags.setValue(index,1); theCloud->setPointScalarValue(index,static_cast<ScalarType>(facetIndex)); } if (m_currentFacetPoints) m_currentFacetPoints->clear(false); /*for (size_t i=0; i<m_activeCells.size(); ++i) { //we remove the processed cell so as to be sure not to consider them again! CCLib::FastMarching::Cell* cell = m_theGrid[m_activeCells[i]]; m_theGrid[m_activeCells[i]] = 0; if (cell) delete cell; } //*/ //unsigned pointCount = 0; CCLib::ReferenceCloud Yk(m_octree->associatedCloud()); for (size_t i=0; i<m_activeCells.size(); ++i) { PlanarCell* aCell = static_cast<PlanarCell*>(m_theGrid[m_activeCells[i]]); if (!m_octree->getPointsInCell(aCell->cellCode,m_gridLevel,&Yk,true)) continue; for (unsigned k=0; k<Yk.size(); ++k) { unsigned index = Yk.getPointGlobalIndex(k); assert(flags.getValue(index) == 1); //flags.setValue(index,1); //++pointCount; } m_theGrid[m_activeCells[i]] = 0; delete aCell; } return pointCount; }
unsigned ccFastMarchingForNormsDirection::updateResolvedTable( ccGenericPointCloud* cloud, GenericChunkedArray<1,unsigned char> &resolved, NormsIndexesTableType* theNorms) { if (!m_initialized || !m_octree || m_gridLevel > CCLib::DgmOctree::MAX_OCTREE_LEVEL) return 0; CCLib::ReferenceCloud Yk(m_octree->associatedCloud()); unsigned count = 0; for (size_t i=0; i<m_activeCells.size(); ++i) { DirectionCell* aCell = static_cast<DirectionCell*>(m_theGrid[m_activeCells[i]]); if (!m_octree->getPointsInCell(aCell->cellCode,m_gridLevel,&Yk,true)) { //not enough memory return 0; } for (unsigned k=0; k<Yk.size(); ++k) { unsigned index = Yk.getPointGlobalIndex(k); resolved.setValue(index,1); const CompressedNormType& norm = theNorms->getValue(index); const CCVector3& N = ccNormalVectors::GetNormal(norm); //inverse point normal if necessary if (N.dot(aCell->N) < 0) { theNorms->setValue(index,ccNormalVectors::GetNormIndex(-N)); } #ifdef QT_DEBUG cloud->setPointScalarValue(index,aCell->T); //cloud->setPointScalarValue(index,aCell->signConfidence); //cloud->setPointScalarValue(index,aCell->scalar); #endif ++count; } } return count; }
int ccFastMarchingForNormsDirection::updateResolvedTable(ccGenericPointCloud* theCloud, GenericChunkedArray<1,uchar> &resolved, NormsIndexesTableType* theNorms) { if (!initialized) return -1; int count=0; for (unsigned i=0;i<activeCells.size();++i) { DirectionCell* aCell = (DirectionCell*)theGrid[activeCells[i]]; CCLib::ReferenceCloud* Yk = theOctree->getPointsInCell(aCell->cellCode,gridLevel,true); if (!Yk) continue; Yk->placeIteratorAtBegining(); for (unsigned k=0;k<Yk->size();++k) { unsigned index = Yk->getCurrentPointGlobalIndex(); resolved.setValue(index,1); //resolvedValue=1 const normsType& norm = theNorms->getValue(index); if (CCVector3::vdot(ccNormalVectors::GetNormal(norm),aCell->N)<0.0) { PointCoordinateType newN[3]; const PointCoordinateType* N = ccNormalVectors::GetNormal(norm); newN[0]=-N[0]; newN[1]=-N[1]; newN[2]=-N[2]; theNorms->setValue(index,ccNormalVectors::GetNormIndex(newN)); } //norm = NormalVectors::getNormIndex(aCell->N); //theNorms->setValue(index,&norm); theCloud->setPointScalarValue(index,aCell->T); //theCloud->setPointScalarValue(index,aCell->v); Yk->forwardIterator(); ++count; } } return count; }
ccPointCloud* ccGenericMesh::samplePoints( bool densityBased, double samplingParameter, bool withNormals, bool withRGB, bool withTexture, CCLib::GenericProgressCallback* pDlg/*=0*/) { bool withFeatures = (withNormals || withRGB || withTexture); GenericChunkedArray<1,unsigned>* triIndices = (withFeatures ? new GenericChunkedArray<1,unsigned> : 0); CCLib::SimpleCloud* sampledCloud = 0; if (densityBased) { sampledCloud = CCLib::MeshSamplingTools::samplePointsOnMesh(this,samplingParameter,pDlg,triIndices); } else { sampledCloud = CCLib::MeshSamplingTools::samplePointsOnMesh(this,static_cast<unsigned>(samplingParameter),pDlg,triIndices); } //convert to real point cloud ccPointCloud* cloud = 0; if (sampledCloud) { cloud = ccPointCloud::From(sampledCloud); delete sampledCloud; sampledCloud = 0; } if (!cloud) { if (triIndices) triIndices->release(); ccLog::Warning("[ccGenericMesh::samplePoints] Not enough memory!"); return 0; } if (withFeatures && triIndices && triIndices->currentSize() >= cloud->size()) { //generate normals if (withNormals && hasNormals()) { if (cloud->reserveTheNormsTable()) { for (unsigned i=0; i<cloud->size(); ++i) { unsigned triIndex = triIndices->getValue(i); const CCVector3* P = cloud->getPoint(i); CCVector3 N(0,0,1); interpolateNormals(triIndex,*P,N); cloud->addNorm(N); } cloud->showNormals(true); } else { ccLog::Warning("[ccGenericMesh::samplePoints] Failed to interpolate normals (not enough memory?)"); } } //generate colors if (withTexture && hasMaterials()) { if (cloud->reserveTheRGBTable()) { for (unsigned i=0; i<cloud->size(); ++i) { unsigned triIndex = triIndices->getValue(i); const CCVector3* P = cloud->getPoint(i); colorType C[3]={MAX_COLOR_COMP,MAX_COLOR_COMP,MAX_COLOR_COMP}; getColorFromMaterial(triIndex,*P,C,withRGB); cloud->addRGBColor(C); } cloud->showColors(true); } else { ccLog::Warning("[ccGenericMesh::samplePoints] Failed to export texture colors (not enough memory?)"); } } else if (withRGB && hasColors()) { if (cloud->reserveTheRGBTable()) { for (unsigned i=0; i<cloud->size(); ++i) { unsigned triIndex = triIndices->getValue(i); const CCVector3* P = cloud->getPoint(i); colorType C[3] = { MAX_COLOR_COMP, MAX_COLOR_COMP, MAX_COLOR_COMP }; interpolateColors(triIndex,*P,C); cloud->addRGBColor(C); } cloud->showColors(true); } else { ccLog::Warning("[ccGenericMesh::samplePoints] Failed to interpolate colors (not enough memory?)"); } } } //we rename the resulting cloud cloud->setName(getName()+QString(".sampled")); cloud->setDisplay(getDisplay()); cloud->prepareDisplayForRefresh(); //copy 'shift on load' information if (getAssociatedCloud()) { const CCVector3d& shift = getAssociatedCloud()->getGlobalShift(); cloud->setGlobalShift(shift); double scale = getAssociatedCloud()->getGlobalScale(); cloud->setGlobalScale(scale); } if (triIndices) triIndices->release(); return cloud; }
int ccFastMarchingForNormsDirection::OrientNormals( ccPointCloud* cloud, unsigned char octreeLevel, ccProgressDialog* progressCb) { if (!cloud || !cloud->normals()) { ccLog::Warning(QString("[orientNormalsWithFM] Cloud '%1' is invalid (or cloud has no normals)").arg(cloud->getName())); assert(false); } NormsIndexesTableType* theNorms = cloud->normals(); unsigned numberOfPoints = cloud->size(); if (numberOfPoints == 0) return -1; //we need the octree if (!cloud->getOctree()) { if (!cloud->computeOctree(progressCb)) { ccLog::Warning(QString("[orientNormalsWithFM] Could not compute octree on cloud '%1'").arg(cloud->getName())); return false; } } ccOctree::Shared octree = cloud->getOctree(); assert(octree); //temporary SF bool sfWasDisplayed = cloud->sfShown(); int oldSfIdx = cloud->getCurrentDisplayedScalarFieldIndex(); int sfIdx = cloud->getScalarFieldIndexByName("FM_Propagation"); if (sfIdx < 0) sfIdx = cloud->addScalarField("FM_Propagation"); if (sfIdx >= 0) { cloud->setCurrentScalarField(sfIdx); } else { ccLog::Warning("[orientNormalsWithFM] Couldn't create temporary scalar field! Not enough memory?"); return -3; } if (!cloud->enableScalarField()) { ccLog::Warning("[orientNormalsWithFM] Couldn't enable temporary scalar field! Not enough memory?"); cloud->deleteScalarField(sfIdx); cloud->setCurrentScalarField(oldSfIdx); return -4; } //flags indicating if each point has been processed or not GenericChunkedArray<1,unsigned char>* resolved = new GenericChunkedArray<1,unsigned char>(); if (!resolved->resize(numberOfPoints,true,0)) //defaultResolvedValue = 0 { ccLog::Warning("[orientNormalsWithFM] Not enough memory!"); cloud->deleteScalarField(sfIdx); cloud->setCurrentScalarField(oldSfIdx); resolved->release(); return -5; } //Fast Marching propagation ccFastMarchingForNormsDirection fm; int result = fm.init(cloud, theNorms, octree.data(), octreeLevel); if (result < 0) { ccLog::Error("[orientNormalsWithFM] Something went wrong during initialization..."); cloud->deleteScalarField(sfIdx); cloud->setCurrentScalarField(oldSfIdx); resolved->release(); return -6; } //progress notification if (progressCb) { if (progressCb->textCanBeEdited()) { progressCb->setMethodTitle("Norms direction"); progressCb->setInfo(qPrintable(QString("Octree level: %1\nPoints: %2").arg(octreeLevel).arg(numberOfPoints))); } progressCb->update(0); progressCb->start(); } const int octreeWidth = (1<<octreeLevel)-1; //enable 26-connectivity //fm.setExtendedConnectivity(true); //while non-processed points remain... unsigned resolvedPoints = 0; int lastProcessedPoint = -1; bool success = true; while (success) { //find the next non-processed point do { ++lastProcessedPoint; } while (lastProcessedPoint < static_cast<int>(numberOfPoints) && resolved->getValue(lastProcessedPoint) != 0); //all points have been processed? Then we can stop. if (lastProcessedPoint == static_cast<int>(numberOfPoints)) break; //we start the propagation from this point //its corresponding cell in fact ;) const CCVector3 *thePoint = cloud->getPoint(lastProcessedPoint); Tuple3i cellPos; octree->getTheCellPosWhichIncludesThePoint(thePoint, cellPos, octreeLevel); //clipping (in case the octree is not 'complete') cellPos.x = std::min(octreeWidth, cellPos.x); cellPos.y = std::min(octreeWidth, cellPos.y); cellPos.z = std::min(octreeWidth, cellPos.z); //set corresponding FM cell as 'seed' fm.setSeedCell(cellPos); //launch propagation int propagationResult = fm.propagate(); //if it's a success if (propagationResult >= 0) { //compute the number of points processed during this pass unsigned count = fm.updateResolvedTable(cloud,*resolved,theNorms); if (count != 0) { resolvedPoints += count; if (progressCb) progressCb->update(static_cast<float>(resolvedPoints)/static_cast<float>(numberOfPoints)*100.0f); } fm.cleanLastPropagation(); } else { ccLog::Error("An error occurred during front propagation! Process cancelled..."); success = false; } } if (progressCb) progressCb->stop(); resolved->release(); resolved = 0; cloud->showNormals(true); #ifdef QT_DEBUG cloud->setCurrentDisplayedScalarField(sfIdx); cloud->getCurrentDisplayedScalarField()->computeMinAndMax(); cloud->showSF(true); #else cloud->deleteScalarField(sfIdx); cloud->setCurrentScalarField(oldSfIdx); cloud->showSF(sfWasDisplayed); #endif return success; }
CC_FILE_ERROR DepthMapFileFilter::saveToFile(QString filename, ccGBLSensor* sensor) { assert(sensor); if (!sensor) { return CC_FERR_BAD_ARGUMENT; } //the depth map associated to this sensor const ccGBLSensor::DepthBuffer& db = sensor->getDepthBuffer(); if (db.zBuff.empty()) { ccLog::Warning(QString("[DepthMap] sensor '%1' has no associated depth map (you must compute it first)").arg(sensor->getName())); return CC_FERR_NO_SAVE; //this is not a severe error (the process can go on) } ccGenericPointCloud* cloud = ccHObjectCaster::ToGenericPointCloud(sensor->getParent()); if (!cloud) { ccLog::Warning(QString("[DepthMap] sensor '%1' is not associated to a point cloud!").arg(sensor->getName())); //this is not a severe error (the process can go on) } //opening file FILE* fp = fopen(qPrintable(filename),"wt"); if (!fp) { ccLog::Error(QString("[DepthMap] Can't open file '%1' for writing!").arg(filename)); return CC_FERR_WRITING; } fprintf(fp,"// SENSOR DEPTH MAP\n"); fprintf(fp,"// Associated cloud: %s\n",qPrintable(cloud ? cloud->getName() : "none")); fprintf(fp,"// Pitch = %f [ %f : %f ]\n", sensor->getPitchStep(), sensor->getMinPitch(), sensor->getMaxPitch()); fprintf(fp,"// Yaw = %f [ %f : %f ]\n", sensor->getYawStep(), sensor->getMinYaw(), sensor->getMaxYaw()); fprintf(fp,"// Range = %f\n",sensor->getSensorRange()); fprintf(fp,"// L = %i\n",db.width); fprintf(fp,"// H = %i\n",db.height); fprintf(fp,"/////////////////////////\n"); //an array of projected normals (same size a depth map) ccGBLSensor::NormalGrid* theNorms = NULL; //an array of projected colors (same size a depth map) ccGBLSensor::ColorGrid* theColors = NULL; //if the sensor is associated to a "ccPointCloud", we may also extract //normals and color! if (cloud && cloud->isA(CC_TYPES::POINT_CLOUD)) { ccPointCloud* pc = static_cast<ccPointCloud*>(cloud); unsigned nbPoints = cloud->size(); if (nbPoints == 0) { ccLog::Warning(QString("[DepthMap] sensor '%1' is associated to an empty cloud?!").arg(sensor->getName())); //this is not a severe error (the process can go on) } else { //if possible, we create the array of projected normals if (pc->hasNormals()) { NormsTableType* decodedNorms = new NormsTableType; if (decodedNorms->reserve(nbPoints)) { for (unsigned i=0; i<nbPoints; ++i) decodedNorms->addElement(pc->getPointNormal(i).u); theNorms = sensor->projectNormals(pc,*decodedNorms); decodedNorms->clear(); } else { ccLog::Warning(QString("[DepthMap] not enough memory to load normals on sensor '%1'!").arg(sensor->getName())); } decodedNorms->release(); decodedNorms = 0; } //if possible, we create the array of projected colors if (pc->hasColors()) { GenericChunkedArray<3,ColorCompType>* rgbColors = new GenericChunkedArray<3,ColorCompType>(); rgbColors->reserve(nbPoints); for (unsigned i=0; i<nbPoints; ++i) { //conversion from ColorCompType[3] to unsigned char[3] const ColorCompType* col = pc->getPointColor(i); rgbColors->addElement(col); } theColors = sensor->projectColors(pc,*rgbColors); rgbColors->clear(); rgbColors->release(); rgbColors = 0; } } } const ScalarType* _zBuff = &(db.zBuff.front()); if (theNorms) theNorms->placeIteratorAtBegining(); if (theColors) theColors->placeIteratorAtBegining(); for (unsigned k=0; k<db.height; ++k) { for (unsigned j=0; j<db.width; ++j, ++_zBuff) { //grid index and depth fprintf(fp,"%u %u %.12f",j,k,*_zBuff); //color if (theColors) { const ColorCompType* C = theColors->getCurrentValue(); fprintf(fp," %i %i %i",C[0],C[1],C[2]); theColors->forwardIterator(); } //normal if (theNorms) { const PointCoordinateType* N = theNorms->getCurrentValue(); fprintf(fp," %f %f %f",N[0],N[1],N[2]); theNorms->forwardIterator(); } fprintf(fp,"\n"); } } fclose(fp); fp = 0; if (theNorms) { theNorms->release(); theNorms = 0; } if (theColors) { theColors->release(); theColors = 0; } return CC_FERR_NO_ERROR; }
ccGBLSensor::ColorGrid* ccGBLSensor::projectColors( CCLib::GenericCloud* cloud, const ColorGrid& theColors) const { if (!cloud || !theColors.isAllocated()) return 0; unsigned gridSize = m_depthBuffer.height*m_depthBuffer.width; if (gridSize == 0) return 0; //depth buffer empty or not initialized! //number of points per cell of the depth map std::vector<size_t> pointPerDMCell; try { pointPerDMCell.resize(gridSize,0); } catch(std::bad_alloc) { //not enough memory return 0; } //temp. array for accumulation GenericChunkedArray<3,float>* colorAccumGrid = new GenericChunkedArray<3,float>; { float blackF[3] = {0,0,0}; if (!colorAccumGrid->resize(gridSize,true,blackF)) return 0; //not enough memory } //final array ColorsTableType* colorGrid = new ColorsTableType; { if (!colorGrid->resize(gridSize,true,ccColor::black.rgba)) { colorAccumGrid->release(); return 0; //not enough memory } } //project colors { unsigned pointCount = cloud->size(); cloud->placeIteratorAtBegining(); { for (unsigned i=0; i<pointCount; ++i) { const CCVector3 *P = cloud->getNextPoint(); CCVector2 Q; PointCoordinateType depth; projectPoint(*P,Q,depth,m_activeIndex); unsigned x,y; if (convertToDepthMapCoords(Q.x,Q.y,x,y)) { unsigned index = y*m_depthBuffer.width+x; //accumulate color const colorType* srcC = theColors.getValue(i); float* destC = colorAccumGrid->getValue(index); destC[0] += srcC[0]; destC[1] += srcC[1]; destC[2] += srcC[2]; ++pointPerDMCell[index]; } else { //shouldn't happen! assert(false); } } } } //normalize { for (unsigned i=0; i<gridSize; ++i) { if (pointPerDMCell[i] != 0) { const float* srcC = colorAccumGrid->getValue(i); colorType* destC = colorGrid->getValue(i); destC[0] = static_cast<colorType>( srcC[0] / pointPerDMCell[i] ); destC[1] = static_cast<colorType>( srcC[1] / pointPerDMCell[i] ); destC[2] = static_cast<colorType>( srcC[2] / pointPerDMCell[i] ); } } } colorAccumGrid->release(); return colorGrid; }
int ccFastMarchingForNormsDirection::ResolveNormsDirectionByFrontPropagation( ccPointCloud* theCloud, NormsIndexesTableType* theNorms, uchar octreeLevel, CCLib::GenericProgressCallback* progressCb, CCLib::DgmOctree* inputOctree) { assert(theCloud); unsigned numberOfPoints = theCloud->size(); if (numberOfPoints == 0) return -1; //we compute the octree if none is provided CCLib::DgmOctree* theOctree = inputOctree; if (!theOctree) { theOctree = new CCLib::DgmOctree(theCloud); if (theOctree->build(progressCb)<1) { delete theOctree; return -2; } } //temporary SF int oldSfIdx = theCloud->getCurrentDisplayedScalarFieldIndex(); int sfIdx = theCloud->getScalarFieldIndexByName("FM_Propagation"); if (sfIdx < 0) sfIdx = theCloud->addScalarField("FM_Propagation"); if (sfIdx >= 0) { theCloud->setCurrentScalarField(sfIdx); } else { ccLog::Warning("[ccFastMarchingForNormsDirection] Couldn't create temporary scalar field! Not enough memory?"); if (!inputOctree) delete theOctree; return -3; } if (!theCloud->enableScalarField()) { ccLog::Warning("[ccFastMarchingForNormsDirection] Couldn't enable temporary scalar field! Not enough memory?"); theCloud->deleteScalarField(sfIdx); theCloud->setCurrentScalarField(oldSfIdx); if (!inputOctree) delete theOctree; return -4; } //flags indicating if each point has been processed or not GenericChunkedArray<1,uchar>* resolved = new GenericChunkedArray<1,uchar>(); if (!resolved->resize(numberOfPoints,true,0)) //defaultResolvedValue = 0 { ccLog::Warning("[ccFastMarchingForNormsDirection] Not enough memory!"); theCloud->deleteScalarField(sfIdx); theCloud->setCurrentScalarField(oldSfIdx); if (!inputOctree) delete theOctree; resolved->release(); return -5; } //Fast Marching propagation ccFastMarchingForNormsDirection fm; int result = fm.init(theCloud,theNorms,theOctree,octreeLevel); if (result < 0) { ccLog::Error("[ccFastMarchingForNormsDirection] Something went wrong during initialization..."); theCloud->deleteScalarField(sfIdx); theCloud->setCurrentScalarField(oldSfIdx); resolved->release(); if (!inputOctree) delete theOctree; return -6; } //progress notification if (progressCb) { progressCb->reset(); progressCb->setMethodTitle("Norms direction"); progressCb->setInfo(qPrintable(QString("Octree level: %1\nPoints: %2").arg(octreeLevel).arg(numberOfPoints))); progressCb->start(); } const int octreeWidth = (1<<octreeLevel)-1; //enable 26-connectivity //fm.setExtendedConnectivity(true); //while non-processed points remain... unsigned resolvedPoints = 0; int lastProcessedPoint = -1; while (true) { //find the next non-processed point do { ++lastProcessedPoint; } while (lastProcessedPoint < static_cast<int>(numberOfPoints) && resolved->getValue(lastProcessedPoint) != 0); //all points have been processed? Then we can stop. if (lastProcessedPoint == static_cast<int>(numberOfPoints)) break; //we start the propagation from this point //its corresponding cell in fact ;) const CCVector3 *thePoint = theCloud->getPoint(lastProcessedPoint); int pos[3]; theOctree->getTheCellPosWhichIncludesThePoint(thePoint,pos,octreeLevel); //clipping (in case the octree is not 'complete') pos[0] = std::min(octreeWidth,pos[0]); pos[1] = std::min(octreeWidth,pos[1]); pos[2] = std::min(octreeWidth,pos[2]); //set corresponding FM cell as 'seed' fm.setSeedCell(pos); //launch propagation int propagationResult = fm.propagate(); //if it's a success if (propagationResult >= 0) { //compute the number of points processed during this pass unsigned count = fm.updateResolvedTable(theCloud,*resolved,theNorms); if (count != 0) { resolvedPoints += count; if (progressCb) progressCb->update(static_cast<float>(resolvedPoints)/static_cast<float>(numberOfPoints)*100.0f); } fm.cleanLastPropagation(); } else { ccLog::Error("An error occurred during front propagation! Process cancelled..."); break; } } if (progressCb) progressCb->stop(); resolved->release(); resolved = 0; if (!inputOctree) delete theOctree; theCloud->showNormals(true); #ifdef _DEBUG theCloud->setCurrentDisplayedScalarField(sfIdx); theCloud->getCurrentDisplayedScalarField()->computeMinAndMax(); theCloud->showSF(true); #else theCloud->deleteScalarField(sfIdx); theCloud->setCurrentScalarField(oldSfIdx); #endif return 0; }
int ccFastMarchingForNormsDirection::ResolveNormsDirectionByFrontPropagation(ccPointCloud* theCloud, NormsIndexesTableType* theNorms, uchar octreeLevel, CCLib::GenericProgressCallback* progressCb, CCLib::DgmOctree* _theOctree) { assert(theCloud); int i,numberOfPoints = theCloud->size(); if (numberOfPoints<1) return -2; //on calcule l'octree si besoin CCLib::DgmOctree* theOctree = _theOctree; if (!theOctree) { theOctree = new CCLib::DgmOctree(theCloud); if (theOctree->build(progressCb)<1) { delete theOctree; return -3; } } //temporaire int oldSfIdx = theCloud->getCurrentInScalarFieldIndex(); int sfIdx = theCloud->getScalarFieldIndexByName("FM_Propagation"); if (sfIdx<0) sfIdx=theCloud->addScalarField("FM_Propagation",true); if (sfIdx>=0) theCloud->setCurrentScalarField(sfIdx); else { ccConsole::Warning("[ccFastMarchingForNormsDirection] Couldn't create temporary scalar field! Not enough memory?"); if (!_theOctree) delete theOctree; return -5; } theCloud->enableScalarField(); //vecteur indiquant si le point a été traité GenericChunkedArray<1,uchar>* resolved = new GenericChunkedArray<1,uchar>(); resolved->resize(numberOfPoints,true,0); //defaultResolvedValue=0 //on va faire la propagation avec l'algorithme de Fast Marching ccFastMarchingForNormsDirection* fm = new ccFastMarchingForNormsDirection(); int result = fm->init(theCloud,theNorms,theOctree,octreeLevel); if (result<0) { ccConsole::Error("[ccFastMarchingForNormsDirection] Something went wrong during initialization ...\n"); theCloud->deleteScalarField(sfIdx); theCloud->setCurrentScalarField(oldSfIdx); if (!_theOctree) delete theOctree; delete fm; return -4; } int resolvedPoints=0; if (progressCb) { progressCb->reset(); progressCb->setMethodTitle("Norms direction"); char buffer[256]; sprintf(buffer,"Octree level: %i\nNumber of points: %i",octreeLevel,numberOfPoints); progressCb->setInfo(buffer); progressCb->start(); } int octreeLength = (1<<octreeLevel)-1; while (true) { //on cherche un point non encore traité resolved->placeIteratorAtBegining(); for (i=0;i<numberOfPoints;++i) { if (resolved->getCurrentValue()==0) break; resolved->forwardIterator(); } //si tous les points ont été traités, on peut arréter ! if (i==numberOfPoints) break; //on lance la propagation à partir du point trouvé const CCVector3 *thePoint = theCloud->getPoint(i); int pos[3]; theOctree->getTheCellPosWhichIncludesThePoint(thePoint,pos,octreeLevel); //clipping (important !) pos[0] = std::min(octreeLength,pos[0]); pos[1] = std::min(octreeLength,pos[1]); pos[2] = std::min(octreeLength,pos[2]); fm->setSeedCell(pos); int result = fm->propagate(); //si la propagation s'est bien passée if (result>=0) { int count = fm->updateResolvedTable(theCloud,*resolved,theNorms); if (progressCb) { if (count>=0) { resolvedPoints += count; progressCb->update(float(resolvedPoints)/float(numberOfPoints)*100.0); } } fm->endPropagation(); } } if (progressCb) progressCb->stop(); delete fm; resolved->release(); resolved=0; if (!_theOctree) delete theOctree; theCloud->deleteScalarField(sfIdx); theCloud->setCurrentScalarField(oldSfIdx); return 0; }
bool ccGenericMesh::laplacianSmooth(unsigned nbIteration, float factor, CCLib::GenericProgressCallback* progressCb/*=0*/) { if (!m_associatedCloud) return false; //vertices unsigned vertCount = m_associatedCloud->size(); //triangles unsigned faceCount = size(); if (!vertCount || !faceCount) return false; GenericChunkedArray<3,PointCoordinateType>* verticesDisplacement = new GenericChunkedArray<3,PointCoordinateType>; if (!verticesDisplacement->resize(vertCount)) { //not enough memory verticesDisplacement->release(); return false; } //compute the number of edges to which belong each vertex unsigned* edgesCount = new unsigned[vertCount]; if (!edgesCount) { //not enough memory verticesDisplacement->release(); return false; } memset(edgesCount, 0, sizeof(unsigned)*vertCount); placeIteratorAtBegining(); for(unsigned j=0; j<faceCount; j++) { const CCLib::TriangleSummitsIndexes* tri = getNextTriangleIndexes(); edgesCount[tri->i1]+=2; edgesCount[tri->i2]+=2; edgesCount[tri->i3]+=2; } //progress dialog CCLib::NormalizedProgress* nProgress = 0; if (progressCb) { unsigned totalSteps = nbIteration; nProgress = new CCLib::NormalizedProgress(progressCb,totalSteps); progressCb->setMethodTitle("Laplacian smooth"); progressCb->setInfo(qPrintable(QString("Iterations: %1\nVertices: %2\nFaces: %3").arg(nbIteration).arg(vertCount).arg(faceCount))); progressCb->start(); } //repeat Laplacian smoothing iterations for(unsigned iter = 0; iter < nbIteration; iter++) { verticesDisplacement->fill(0); //for each triangle placeIteratorAtBegining(); for(unsigned j=0; j<faceCount; j++) { const CCLib::TriangleSummitsIndexes* tri = getNextTriangleIndexes(); const CCVector3* A = m_associatedCloud->getPoint(tri->i1); const CCVector3* B = m_associatedCloud->getPoint(tri->i2); const CCVector3* C = m_associatedCloud->getPoint(tri->i3); CCVector3 dAB = (*B-*A); CCVector3 dAC = (*C-*A); CCVector3 dBC = (*C-*B); CCVector3* dA = (CCVector3*)verticesDisplacement->getValue(tri->i1); (*dA) += dAB+dAC; CCVector3* dB = (CCVector3*)verticesDisplacement->getValue(tri->i2); (*dB) += dBC-dAB; CCVector3* dC = (CCVector3*)verticesDisplacement->getValue(tri->i3); (*dC) -= dAC+dBC; } if (nProgress && !nProgress->oneStep()) { //cancelled by user break; } //apply displacement verticesDisplacement->placeIteratorAtBegining(); for (unsigned i=0; i<vertCount; i++) { //this is a "persistent" pointer and we know what type of cloud is behind ;) CCVector3* P = const_cast<CCVector3*>(m_associatedCloud->getPointPersistentPtr(i)); const CCVector3* d = (const CCVector3*)verticesDisplacement->getValue(i); (*P) += (*d)*(factor/(PointCoordinateType)edgesCount[i]); } } m_associatedCloud->updateModificationTime(); if (hasNormals()) computeNormals(); if (verticesDisplacement) verticesDisplacement->release(); verticesDisplacement=0; if (edgesCount) delete[] edgesCount; edgesCount=0; if (nProgress) delete nProgress; nProgress=0; return true; }
ReferenceCloud* CloudSamplingTools::resampleCloudSpatially(GenericIndexedCloudPersist* theCloud, PointCoordinateType minDistance, DgmOctree* theOctree/*=0*/, GenericProgressCallback* progressCb/*=0*/) { assert(theCloud); unsigned cloudSize = theCloud->size(); DgmOctree *_theOctree=theOctree; if (!_theOctree) { _theOctree = new DgmOctree(theCloud); if (_theOctree->build()<(int)cloudSize) { delete _theOctree; return 0; } } ReferenceCloud* sampledCloud = new ReferenceCloud(theCloud); if (!sampledCloud->reserve(cloudSize)) { if (!theOctree) delete _theOctree; return 0; } GenericChunkedArray<1,bool>* markers = new GenericChunkedArray<1,bool>(); //DGM: upgraded from vector, as this can be quite huge! if (!markers->resize(cloudSize,true,true)) { markers->release(); if (!theOctree) delete _theOctree; delete sampledCloud; return 0; } NormalizedProgress* normProgress=0; if (progressCb) { progressCb->setInfo("Spatial resampling"); normProgress = new NormalizedProgress(progressCb,cloudSize); progressCb->reset(); progressCb->start(); } //for each point in the cloud that is still 'marked', we look //for its neighbors and remove their own marks DgmOctree::NearestNeighboursSphericalSearchStruct nss; nss.level = _theOctree->findBestLevelForAGivenNeighbourhoodSizeExtraction(minDistance); markers->placeIteratorAtBegining(); for (unsigned i=0; i<cloudSize; i++, markers->forwardIterator()) { //progress indicator if (normProgress && !normProgress->oneStep()) { //cancel process delete sampledCloud; sampledCloud = 0; break; } //no mark? we skip this point if (!markers->getCurrentValue()) continue; //init neighbor search structure theCloud->getPoint(i,nss.queryPoint); bool inbounds = false; _theOctree->getTheCellPosWhichIncludesThePoint(&nss.queryPoint, nss.cellPos, nss.level, inbounds); nss.truncatedCellCode = (inbounds ? _theOctree->generateTruncatedCellCode(nss.cellPos, nss.level) : DgmOctree::INVALID_CELL_CODE); _theOctree->computeCellCenter(nss.cellPos, nss.level, nss.cellCenter); //add the points that lie in the same cell (faster) { ReferenceCloud* Y = _theOctree->getPointsInCell(nss.truncatedCellCode, nss.level, true); unsigned count = Y->size(); try { nss.pointsInNeighbourhood.resize(count); } catch (std::bad_alloc) //out of memory { //stop process delete sampledCloud; sampledCloud = 0; break; } unsigned realCount = 0; DgmOctree::NeighboursSet::iterator it = nss.pointsInNeighbourhood.begin(); for (unsigned j=0; j<count; ++j) { unsigned index = Y->getPointGlobalIndex(j); if (index != i && markers->getValue(index)) //no need to add the point itself and those already flagged off { it->point = Y->getPointPersistentPtr(j); it->pointIndex = index; ++it; ++realCount; } } nss.pointsInNeighbourhood.resize(realCount); //should be ok as realCount<=count nss.alreadyVisitedNeighbourhoodSize = 1; } #ifdef TEST_CELLS_FOR_SPHERICAL_NN nss.pointsInSphericalNeighbourhood.clear(); #endif nss.prepare(minDistance,_theOctree->getCellSize(nss.level)); //look for neighbors and 'de-mark' them { unsigned nbNeighbors = _theOctree->findNeighborsInASphereStartingFromCell(nss, minDistance, false); DgmOctree::NeighboursSet::iterator it = nss.pointsInNeighbourhood.begin(); for (unsigned j=0; j<nbNeighbors; ++j, ++it) if (it->pointIndex != i) markers->setValue(it->pointIndex,false); } //At this stage, the ith point is the only one marked in a radius of <minDistance>. //Therefore it will necessarily be in the final cloud! if (!sampledCloud->addPointIndex(i)) //not enough memory { //stop process delete sampledCloud; sampledCloud = 0; break; } } if(normProgress) { delete normProgress; normProgress = 0; } if (!theOctree) delete _theOctree; markers->release(); return sampledCloud; }
CC_FILE_ERROR DepthMapFileFilter::saveToOpenedFile(FILE* fp, ccGBLSensor* sensor) { assert(fp && sensor); if (!sensor->getParent()->isKindOf(CC_TYPES::POINT_CLOUD)) { ccLog::Warning(QString("[DepthMap] sensor '%1' is not associated to a point cloud!").arg(sensor->getName())); return CC_FERR_NO_ERROR; //this is not a severe error (the process can go on) } ccGenericPointCloud* cloud = ccHObjectCaster::ToGenericPointCloud(sensor->getParent()); //the depth map associated to this sensor const ccGBLSensor::DepthBuffer& db = sensor->getDepthBuffer(); fprintf(fp,"// CLOUDCOMPARE DEPTH MAP\n"); fprintf(fp,"// Associated cloud: %s\n",qPrintable(cloud->getName())); fprintf(fp,"// dPhi = %f [ %f : %f ]\n", sensor->getDeltaPhi(), sensor->getPhiMin(), sensor->getPhiMax()); fprintf(fp,"// dTheta = %f [ %f : %f ]\n", sensor->getDeltaTheta(), sensor->getThetaMin(), sensor->getThetaMax()); fprintf(fp,"// pMax = %f\n",sensor->getSensorRange()); fprintf(fp,"// L = %i\n",db.width); fprintf(fp,"// H = %i\n",db.height); fprintf(fp,"/////////////////////////\n"); //an array of projected normals (same size a depth map) PointCoordinateType* theNorms = NULL; //an array of projected colors (same size a depth map) colorType* theColors = NULL; //if the sensor is associated to a "ccPointCloud", we may also extract //normals and color! if (cloud->isA(CC_TYPES::POINT_CLOUD)) { ccPointCloud* pc = static_cast<ccPointCloud*>(cloud); unsigned nbPoints = cloud->size(); if (nbPoints == 0) { ccLog::Warning(QString("[DepthMap] sensor '%1' is associated to an empty cloud!").arg(sensor->getName())); return CC_FERR_NO_ERROR; //this is not a severe error (the process can go on) } else { //if possible, we create the array of projected normals if (pc->hasNormals()) { NormsTableType* decodedNorms = new NormsTableType; if (decodedNorms->reserve(nbPoints)) { for (unsigned i=0; i<nbPoints; ++i) decodedNorms->addElement(pc->getPointNormal(i).u); theNorms = sensor->projectNormals(pc,*decodedNorms); decodedNorms->clear(); } else { ccLog::Warning(QString("[DepthMap] not enough memory to load normals on sensor '%1'!").arg(sensor->getName())); } decodedNorms->release(); decodedNorms = 0; } //if possible, we create the array of projected colors if (pc->hasColors()) { GenericChunkedArray<3,colorType>* rgbColors = new GenericChunkedArray<3,colorType>(); rgbColors->reserve(nbPoints); for (unsigned i=0; i<nbPoints; ++i) { //conversion from colorType[3] to unsigned char[3] const colorType* col = pc->getPointColor(i); rgbColors->addElement(col); } theColors = sensor->projectColors(pc,*rgbColors); rgbColors->clear(); rgbColors->release(); rgbColors = 0; } } } PointCoordinateType* _theNorms = theNorms; colorType* _theColors = theColors; ScalarType* _zBuff = db.zBuff; for (unsigned k=0; k<db.height; ++k) { for (unsigned j=0; j<db.width; ++j) { //grid index and depth fprintf(fp,"%i %i %.12f",j,k,*_zBuff++); //color if (_theColors) { fprintf(fp," %i %i %i",_theColors[0],_theColors[1],_theColors[2]); _theColors += 3; } //normal if (_theNorms) { fprintf(fp," %f %f %f",_theNorms[0],_theNorms[1],_theNorms[2]); _theNorms += 3; } fprintf(fp,"\n"); } } if (theNorms) delete[] theNorms; if (theColors) delete[] theColors; return CC_FERR_NO_ERROR; }
ReferenceCloud* CloudSamplingTools::resampleCloudSpatially(GenericIndexedCloudPersist* inputCloud, PointCoordinateType minDistance, const SFModulationParams& modParams, DgmOctree* inputOctree/*=0*/, GenericProgressCallback* progressCb/*=0*/) { assert(inputCloud); unsigned cloudSize = inputCloud->size(); DgmOctree* octree = inputOctree; if (!octree) { octree = new DgmOctree(inputCloud); if (octree->build() < static_cast<int>(cloudSize)) { delete octree; return 0; } } assert(octree && octree->associatedCloud() == inputCloud); //output cloud ReferenceCloud* sampledCloud = new ReferenceCloud(inputCloud); const unsigned c_reserveStep = 65536; if (!sampledCloud->reserve(std::min(cloudSize,c_reserveStep))) { if (!inputOctree) delete octree; return 0; } GenericChunkedArray<1,char>* markers = new GenericChunkedArray<1,char>(); //DGM: upgraded from vector, as this can be quite huge! if (!markers->resize(cloudSize,true,1)) //true by default { markers->release(); if (!inputOctree) delete octree; delete sampledCloud; return 0; } //best octree level (there may be several of them if we use parameter modulation) std::vector<unsigned char> bestOctreeLevel; bool modParamsEnabled = modParams.enabled; ScalarType sfMin = 0, sfMax = 0; try { if (modParams.enabled) { //compute min and max sf values ScalarFieldTools::computeScalarFieldExtremas(inputCloud,sfMin,sfMax); if (!ScalarField::ValidValue(sfMin)) { //all SF values are NAN?! modParamsEnabled = false; } else { //compute min and max 'best' levels PointCoordinateType dist0 = static_cast<PointCoordinateType>(sfMin * modParams.a + modParams.b); PointCoordinateType dist1 = static_cast<PointCoordinateType>(sfMax * modParams.a + modParams.b); unsigned char level0 = octree->findBestLevelForAGivenNeighbourhoodSizeExtraction(dist0); unsigned char level1 = octree->findBestLevelForAGivenNeighbourhoodSizeExtraction(dist1); bestOctreeLevel.push_back(level0); if (level1 != level0) { //add intermediate levels if necessary size_t levelCount = (level1 < level0 ? level0-level1 : level1-level0) + 1; assert(levelCount != 0); for (size_t i=1; i<levelCount-1; ++i) //we already know level0 and level1! { ScalarType sfVal = sfMin + i*((sfMax-sfMin)/levelCount); PointCoordinateType dist = static_cast<PointCoordinateType>(sfVal * modParams.a + modParams.b); unsigned char level = octree->findBestLevelForAGivenNeighbourhoodSizeExtraction(dist); bestOctreeLevel.push_back(level); } } bestOctreeLevel.push_back(level1); } } else { unsigned char defaultLevel = octree->findBestLevelForAGivenNeighbourhoodSizeExtraction(minDistance); bestOctreeLevel.push_back(defaultLevel); } } catch (const std::bad_alloc&) { //not enough memory markers->release(); if (!inputOctree) { delete octree; } delete sampledCloud; return 0; } //progress notification NormalizedProgress normProgress(progressCb, cloudSize); if (progressCb) { if (progressCb->textCanBeEdited()) { progressCb->setMethodTitle("Spatial resampling"); char buffer[256]; sprintf(buffer, "Points: %u\nMin dist.: %f", cloudSize, minDistance); progressCb->setInfo(buffer); } progressCb->update(0); progressCb->start(); } //for each point in the cloud that is still 'marked', we look //for its neighbors and remove their own marks markers->placeIteratorAtBegining(); bool error = false; //default octree level assert(!bestOctreeLevel.empty()); unsigned char octreeLevel = bestOctreeLevel.front(); //default distance between points PointCoordinateType minDistBetweenPoints = minDistance; for (unsigned i=0; i<cloudSize; i++, markers->forwardIterator()) { //no mark? we skip this point if (markers->getCurrentValue() != 0) { //init neighbor search structure const CCVector3* P = inputCloud->getPoint(i); //parameters modulation if (modParamsEnabled) { ScalarType sfVal = inputCloud->getPointScalarValue(i); if (ScalarField::ValidValue(sfVal)) { //modulate minDistance minDistBetweenPoints = static_cast<PointCoordinateType>(sfVal * modParams.a + modParams.b); //get (approximate) best level size_t levelIndex = static_cast<size_t>(bestOctreeLevel.size() * (sfVal / (sfMax-sfMin))); if (levelIndex == bestOctreeLevel.size()) --levelIndex; octreeLevel = bestOctreeLevel[levelIndex]; } else { minDistBetweenPoints = minDistance; octreeLevel = bestOctreeLevel.front(); } } //look for neighbors and 'de-mark' them { DgmOctree::NeighboursSet neighbours; octree->getPointsInSphericalNeighbourhood(*P,minDistBetweenPoints,neighbours,octreeLevel); for (DgmOctree::NeighboursSet::iterator it = neighbours.begin(); it != neighbours.end(); ++it) if (it->pointIndex != i) markers->setValue(it->pointIndex,0); } //At this stage, the ith point is the only one marked in a radius of <minDistance>. //Therefore it will necessarily be in the final cloud! if (sampledCloud->size() == sampledCloud->capacity() && !sampledCloud->reserve(sampledCloud->capacity() + c_reserveStep)) { //not enough memory error = true; break; } if (!sampledCloud->addPointIndex(i)) { //not enough memory error = true; break; } } //progress indicator if (progressCb && !normProgress.oneStep()) { //cancel process error = true; break; } } //remove unnecessarily allocated memory if (!error) { if (sampledCloud->capacity() > sampledCloud->size()) sampledCloud->resize(sampledCloud->size()); } else { delete sampledCloud; sampledCloud = 0; } if (progressCb) { progressCb->stop(); } if (!inputOctree) { //locally computed octree delete octree; octree = 0; } markers->release(); markers = 0; return sampledCloud; }
int FastMarchingForFacetExtraction::ExtractPlanarFacets( ccPointCloud* theCloud, unsigned char octreeLevel, ScalarType maxError, CCLib::DistanceComputationTools::ERROR_MEASURES errorMeasure, bool useRetroProjectionError/*=true*/, CCLib::GenericProgressCallback* progressCb/*=0*/, CCLib::DgmOctree* _theOctree/*=0*/) { assert(theCloud); unsigned numberOfPoints = theCloud->size(); if (numberOfPoints == 0) return -1; if (!theCloud->getCurrentOutScalarField()) return -2; if (progressCb) { //just spawn the dialog so that we can see the //octree computation (in case the CPU charge prevents //the dialog from being shown) progressCb->start(); QApplication::processEvents(); } //we compute the octree if none is provided CCLib::DgmOctree* theOctree = _theOctree; if (!theOctree) { theOctree = new CCLib::DgmOctree(theCloud); if (theOctree->build(progressCb)<1) { delete theOctree; return -3; } } if (progressCb) { if (progressCb->textCanBeEdited()) { progressCb->setMethodTitle("Fast Marching for facets extraction"); progressCb->setInfo("Initializing..."); } progressCb->start(); QApplication::processEvents(); } if (!theCloud->enableScalarField()) { ccLog::Warning("[FastMarchingForFacetExtraction] Couldn't enable scalar field! Not enough memory?"); if (!_theOctree) delete theOctree; return -4; } //raz SF values { for (unsigned i=0; i!=theCloud->size(); ++i) theCloud->setPointScalarValue(i,0); } //flags indicating if each point has been processed or not GenericChunkedArray<1,unsigned char>* flags = new GenericChunkedArray<1,unsigned char>(); if (!flags->resize(numberOfPoints,true,0)) //defaultFlagValue = 0 { ccLog::Warning("[FastMarchingForFacetExtraction] Not enough memory!"); if (!_theOctree) delete theOctree; flags->release(); return -5; } //Fast Marching propagation FastMarchingForFacetExtraction fm; int result = fm.init( theCloud, theOctree, octreeLevel, maxError, errorMeasure, useRetroProjectionError, progressCb); if (result < 0) { ccLog::Error("[FastMarchingForFacetExtraction] Something went wrong during initialization..."); flags->release(); if (!_theOctree) delete theOctree; return -6; } //progress notification if (progressCb) { progressCb->update(0); if (progressCb->textCanBeEdited()) { progressCb->setMethodTitle("Facets extraction"); progressCb->setInfo(qPrintable(QString("Octree level: %1\nPoints: %2").arg(octreeLevel).arg(numberOfPoints))); } progressCb->start(); QApplication::processEvents(); } const int octreeWidth = (1<<octreeLevel)-1; //enable 26-connectivity mode //fm.setExtendedConnectivity(true); //while non-processed points remain... unsigned resolvedPoints = 0; int lastProcessedPoint = -1; unsigned facetIndex = 0; while (true) { //find the next non-processed point do { ++lastProcessedPoint; } while (lastProcessedPoint < static_cast<int>(numberOfPoints) && flags->getValue(lastProcessedPoint) != 0); //all points have been processed? Then we can stop. if (lastProcessedPoint == static_cast<int>(numberOfPoints)) break; //we start the propagation from this point //its corresponding cell in fact ;) const CCVector3 *thePoint = theCloud->getPoint(lastProcessedPoint); Tuple3i pos; theOctree->getTheCellPosWhichIncludesThePoint(thePoint, pos, octreeLevel); //clipping (in case the octree is not 'complete') pos.x = std::min(octreeWidth, pos.x); pos.y = std::min(octreeWidth, pos.y); pos.z = std::min(octreeWidth, pos.z); //set corresponding FM cell as 'seed' if (!fm.setSeedCell(pos)) { //an error occurred?! //result = -7; //break; continue; } //launch propagation int propagationResult = fm.propagate(); //compute the number of points processed during this pass unsigned count = fm.updateFlagsTable(theCloud,*flags,propagationResult >= 0 ? ++facetIndex : 0); //0 = invalid facet index if (count != 0) { resolvedPoints += count; if (progressCb) { if (progressCb->isCancelRequested()) { result = -7; break; } progressCb->update(static_cast<float>(resolvedPoints)/static_cast<float>(numberOfPoints)*100.0f); } } fm.cleanLastPropagation(); } if (progressCb) progressCb->stop(); flags->release(); flags = 0; if (!_theOctree) delete theOctree; return result; }