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;
}
Exemple #4
0
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;
}
Exemple #7
0
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;
}
Exemple #10
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;
}
Exemple #12
0
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;
}