コード例 #1
0
ファイル: cc2.5DimEditor.cpp プロジェクト: ORNis/CloudCompare
bool cc2Point5DimEditor::RasterGrid::fillWith(	ccGenericPointCloud* cloud,
												unsigned char projectionDimension,
												cc2Point5DimEditor::ProjectionType projectionType,
												bool interpolateEmptyCells,
												cc2Point5DimEditor::ProjectionType sfInterpolation/*=INVALID_PROJECTION_TYPE*/,
												ccProgressDialog* progressDialog/*=0*/)
{
	if (!cloud)
	{
		assert(false);
		return false;
	}

	//current parameters
	unsigned gridTotalSize = width * height;
	
	//vertical dimension
	const unsigned char Z = projectionDimension;
	assert(Z >= 0 && Z <= 2);
	const unsigned char X = Z == 2 ? 0 : Z +1;
	const unsigned char Y = X == 2 ? 0 : X +1;

	//do we need to interpolate scalar fields?
	ccPointCloud* pc = cloud->isA(CC_TYPES::POINT_CLOUD) ? static_cast<ccPointCloud*>(cloud) : 0;
	bool interpolateSF = (sfInterpolation != INVALID_PROJECTION_TYPE);
	interpolateSF &= (pc && pc->hasScalarFields());
	if (interpolateSF)
	{
		unsigned sfCount = pc->getNumberOfScalarFields();

		bool memoryError = false;
		size_t previousCount = scalarFields.size();
		if (sfCount > previousCount)
		{
			try
			{
				scalarFields.resize(sfCount,0);
			}
			catch (const std::bad_alloc&)
			{
				//not enough memory
				memoryError = true;
			}
		}

		for (size_t i=previousCount; i<sfCount; ++i)
		{
			assert(scalarFields[i] == 0);
			scalarFields[i] = new double[gridTotalSize];
			if (!scalarFields[i])
			{
				//not enough memory
				memoryError = true;
				break;
			}
		}

		if (memoryError)
		{
			ccLog::Warning(QString("[Rasterize] Failed to allocate memory for scalar fields!"));
		}
	}

	//filling the grid
	unsigned pointCount = cloud->size();

	double gridMaxX = gridStep * width;
	double gridMaxY = gridStep * height;

	if (progressDialog)
	{
		progressDialog->setMethodTitle("Grid generation");
		progressDialog->setInfo(qPrintable(QString("Points: %1\nCells: %2 x %3").arg(pointCount).arg(width).arg(height)));
		progressDialog->start();
		progressDialog->show();
		QCoreApplication::processEvents();
	}
	CCLib::NormalizedProgress nProgress(progressDialog,pointCount);

	for (unsigned n=0; n<pointCount; ++n)
	{
		const CCVector3* P = cloud->getPoint(n);

		CCVector3d relativePos = CCVector3d::fromArray(P->u) - minCorner;
		int i = static_cast<int>(relativePos.u[X]/gridStep);
		int j = static_cast<int>(relativePos.u[Y]/gridStep);

		//specific case: if we fall exactly on the max corner of the grid box
		if (i == static_cast<int>(width) && relativePos.u[X] == gridMaxX)
			--i;
		if (j == static_cast<int>(height) && relativePos.u[Y] == gridMaxY)
			--j;

		//we skip points outside the box!
		if (	i < 0 || i >= static_cast<int>(width)
			||	j < 0 || j >= static_cast<int>(height) )
			continue;

		assert(i >= 0 && j >= 0);

		RasterCell* aCell = data[j]+i;
		unsigned& pointsInCell = aCell->nbPoints;
		if (pointsInCell)
		{
			if (P->u[Z] < aCell->minHeight)
			{
				aCell->minHeight = P->u[Z];
				if (projectionType == PROJ_MINIMUM_VALUE)
					aCell->pointIndex = n;
			}
			else if (P->u[Z] > aCell->maxHeight)
			{
				aCell->maxHeight = P->u[Z];
				if (projectionType == PROJ_MAXIMUM_VALUE)
					aCell->pointIndex = n;
			}
		}
		else
		{
			aCell->minHeight = aCell->maxHeight = P->u[Z];
			aCell->pointIndex = n;
		}
		// Sum the points heights
		aCell->avgHeight += P->u[Z];
		aCell->stdDevHeight += static_cast<double>(P->u[Z])*P->u[Z];

		//scalar fields
		if (interpolateSF)
		{
			int pos = j*static_cast<int>(width)+i; //pos in 2D SF grid(s)
			assert(pos < static_cast<int>(gridTotalSize));
			for (size_t k=0; k<scalarFields.size(); ++k)
			{
				if (scalarFields[k])
				{
					CCLib::ScalarField* sf = pc->getScalarField(static_cast<unsigned>(k));
					assert(sf);
					ScalarType sfValue = sf->getValue(n);
					ScalarType formerValue = static_cast<ScalarType>(scalarFields[k][pos]);

					if (pointsInCell && ccScalarField::ValidValue(formerValue))
					{
						if (ccScalarField::ValidValue(sfValue))
						{
							switch (sfInterpolation)
							{
							case PROJ_MINIMUM_VALUE:
								// keep the minimum value
								scalarFields[k][pos] = std::min<double>(formerValue,sfValue);
								break;
							case PROJ_AVERAGE_VALUE:
								//we sum all values (we will divide them later)
								scalarFields[k][pos] += sfValue;
								break;
							case PROJ_MAXIMUM_VALUE:
								// keep the maximum value
								scalarFields[k][pos] = std::max<double>(formerValue,sfValue);
								break;
							default:
								assert(false);
								break;
							}
						}
					}
					else
					{
						//for the first (vaild) point, we simply have to store its SF value (in any case)
						scalarFields[k][pos] = sfValue;
					}
				}
			}
		}

		pointsInCell++;

		if (!nProgress.oneStep())
		{
			//process cancelled by user
			return false;
		}
	}

	//update SF grids for 'average' cases
	if (sfInterpolation == PROJ_AVERAGE_VALUE)
	{
		for (size_t k=0; k<scalarFields.size(); ++k)
		{
			if (scalarFields[k])
			{
				double* _gridSF = scalarFields[k];
				for (unsigned j=0;j<height;++j)
				{
					RasterCell* cell = data[j];
					for (unsigned i=0; i<width; ++i,++cell,++_gridSF)
					{
						if (cell->nbPoints > 1)
						{
							ScalarType s = static_cast<ScalarType>(*_gridSF);
							if (ccScalarField::ValidValue(s)) //valid SF value
							{
								*_gridSF /= cell->nbPoints;
							}
						}
					}
				}
			}
		}
	}

	//update the main grid (average height and std.dev. computation + current 'height' value)
	{
		for (unsigned j=0; j<height; ++j)
		{
			RasterCell* cell = data[j];
			for (unsigned i=0; i<width; ++i,++cell)
			{
				if (cell->nbPoints > 1)
				{
					cell->avgHeight /= cell->nbPoints;
					cell->stdDevHeight = sqrt(fabs(cell->stdDevHeight/cell->nbPoints - cell->avgHeight*cell->avgHeight));
				}
				else
				{
					cell->stdDevHeight = 0;
				}

				if (cell->nbPoints != 0)
				{
					//set the right 'height' value
					switch (projectionType)
					{
					case PROJ_MINIMUM_VALUE:
						cell->h = cell->minHeight;
						break;
					case PROJ_AVERAGE_VALUE:
						cell->h = cell->avgHeight;
						break;
					case PROJ_MAXIMUM_VALUE:
						cell->h = cell->maxHeight;
						break;
					default:
						assert(false);
						break;
					}
				}
			}
		}
	}

	//compute the number of non empty cells
	nonEmptyCellCount = 0;
	{
		for (unsigned i=0; i<height; ++i)
			for (unsigned j=0; j<width; ++j)
				if (data[i][j].nbPoints)
					++nonEmptyCellCount;
	}

	//specific case: interpolate the empty cells
	if (interpolateEmptyCells)
	{
		std::vector<CCVector2> the2DPoints;
		if (nonEmptyCellCount < 3)
		{
			ccLog::Warning("[Rasterize] Not enough non-empty cells to interpolate!");
		}
		else if (nonEmptyCellCount < width * height) //otherwise it's useless!
		{
			try
			{
				the2DPoints.resize(nonEmptyCellCount);
			}
			catch (const std::bad_alloc&)
			{
				//out of memory
				ccLog::Warning("[Rasterize] Not enough memory to interpolate empty cells!");
			}
		}

		//fill 2D vector with non-empty cell indexes
		if (!the2DPoints.empty())
		{
			unsigned index = 0;
			for (unsigned j=0; j<height; ++j)
			{
				const RasterCell* cell = data[j];
				for (unsigned i=0; i<width; ++i, ++cell)
				{
					if (cell->nbPoints)
					{
						//we only use the non-empty cells to interpolate
						the2DPoints[index++] = CCVector2(static_cast<PointCoordinateType>(i),static_cast<PointCoordinateType>(j));
					}
				}
			}
			assert(index == nonEmptyCellCount);

			//mesh the '2D' points
			CCLib::Delaunay2dMesh delaunayMesh;
			char errorStr[1024];
			if (delaunayMesh.buildMesh(the2DPoints,0,errorStr))
			{
				unsigned triNum = delaunayMesh.size();
				//now we are going to 'project' all triangles on the grid
				delaunayMesh.placeIteratorAtBegining();
				for (unsigned k=0; k<triNum; ++k)
				{
					const CCLib::VerticesIndexes* tsi = delaunayMesh.getNextTriangleVertIndexes();
					//get the triangle bounding box (in grid coordinates)
					int P[3][2];
					int xMin = 0, yMin = 0, xMax = 0, yMax = 0;
					{
						for (unsigned j=0; j<3; ++j)
						{
							const CCVector2& P2D = the2DPoints[tsi->i[j]];
							P[j][0] = static_cast<int>(P2D.x);
							P[j][1] = static_cast<int>(P2D.y);
						}
						xMin = std::min(std::min(P[0][0],P[1][0]),P[2][0]);
						yMin = std::min(std::min(P[0][1],P[1][1]),P[2][1]);
						xMax = std::max(std::max(P[0][0],P[1][0]),P[2][0]);
						yMax = std::max(std::max(P[0][1],P[1][1]),P[2][1]);
					}
					//now scan the cells
					{
						//pre-computation for barycentric coordinates
						const double& valA = data[ P[0][1] ][ P[0][0] ].h;
						const double& valB = data[ P[1][1] ][ P[1][0] ].h;
						const double& valC = data[ P[2][1] ][ P[2][0] ].h;

						int det = (P[1][1]-P[2][1])*(P[0][0]-P[2][0]) + (P[2][0]-P[1][0])*(P[0][1]-P[2][1]);

						for (int j=yMin; j<=yMax; ++j)
						{
							RasterCell* cell = data[static_cast<unsigned>(j)];

							for (int i=xMin; i<=xMax; ++i)
							{
								//if the cell is empty
								if (!cell[i].nbPoints)
								{
									//we test if it's included or not in the current triangle
									//Point Inclusion in Polygon Test (inspired from W. Randolph Franklin - WRF)
									bool inside = false;
									for (int ti=0; ti<3; ++ti)
									{
										const int* P1 = P[ti];
										const int* P2 = P[(ti+1)%3];
										if ((P2[1] <= j &&j < P1[1]) || (P1[1] <= j && j < P2[1]))
										{
											int t = (i-P2[0])*(P1[1]-P2[1])-(P1[0]-P2[0])*(j-P2[1]);
											if (P1[1] < P2[1])
												t = -t;
											if (t < 0)
												inside = !inside;
										}
									}
									//can we interpolate?
									if (inside)
									{
										double l1 = static_cast<double>((P[1][1]-P[2][1])*(i-P[2][0])+(P[2][0]-P[1][0])*(j-P[2][1]))/det;
										double l2 = static_cast<double>((P[2][1]-P[0][1])*(i-P[2][0])+(P[0][0]-P[2][0])*(j-P[2][1]))/det;
										double l3 = 1.0-l1-l2;

										cell[i].h = l1 * valA + l2 * valB + l3 * valC;
										assert(cell[i].h == cell[i].h);

										//interpolate SFs as well!
										for (size_t sfIndex=0; sfIndex<scalarFields.size(); ++sfIndex)
										{
											if (scalarFields[sfIndex])
											{
												double* gridSF = scalarFields[sfIndex];
												const double& sfValA = gridSF[ P[0][0] + P[0][1]*width ];
												const double& sfValB = gridSF[ P[1][0] + P[1][1]*width ];
												const double& sfValC = gridSF[ P[2][0] + P[2][1]*width ];
												gridSF[i + j*width] = l1 * sfValA + l2 * sfValB + l3 * sfValC;
											}
										}
									}
								}
							}
						}
					}
				}
			}
			else
			{
				ccLog::Warning(QString("[Rasterize] Empty cells interpolation failed: Triangle lib. said '%1'").arg(errorStr));
			}
		}
	}

	//computation of the average and extreme height values in the grid
	{
		minHeight = 0;
		maxHeight = 0;
		meanHeight = 0;
		validCellCount = 0;

		for (unsigned i=0; i<height; ++i)
		{
			for (unsigned j=0; j<width; ++j)
			{
				if (data[i][j].h == data[i][j].h) //valid height
				{
					double h = data[i][j].h;

					if (validCellCount)
					{
						if (h < minHeight)
							minHeight = h;
						else if (h > maxHeight)
							maxHeight = h;
						
						meanHeight += h;
					}
					else
					{
						//first valid cell
						meanHeight = minHeight = maxHeight = h;
					}
					++validCellCount;
				}
			}
		}
		meanHeight /= validCellCount;
	}

	setValid(true);

	return true;
}
コード例 #2
0
ファイル: stereogramDlg.cpp プロジェクト: rotorliu/trunk
bool StereogramWidget::init(double angularStep_deg,
							ccHObject* entity,
							double resolution_deg/*=2.0*/)
{
	m_angularStep_deg = angularStep_deg;

	if (m_densityGrid)
		delete m_densityGrid;
	m_densityGrid = 0;

	if (!entity)
		return false;

	ccProgressDialog pDlg(true);
	pDlg.setMethodTitle("Stereogram");
	pDlg.setInfo("Preparing polar display...");
	pDlg.start();
	QApplication::processEvents();

	size_t count = 0;
	ccHObject::Container facets;
	ccPointCloud* cloud = 0;

	//a set of facets?
	if (entity->isA(CC_TYPES::HIERARCHY_OBJECT))
	{
		entity->filterChildren(facets,true,CC_TYPES::FACET);
		count = facets.size();
	}
	//or a cloud?
	else if (entity->isA(CC_TYPES::POINT_CLOUD))
	{
		cloud = static_cast<ccPointCloud*>(entity);
		if (cloud->hasNormals())
			count = cloud->size();
	}

	if (!count)
		return false;

	//pDlg.setMaximum(static_cast<int>(count));
	CCLib::NormalizedProgress nProgress(&pDlg,static_cast<unsigned>(count));

	//create the density grid
	FacetDensityGrid* densityGrid = new FacetDensityGrid();
	densityGrid->step_deg = resolution_deg;
	densityGrid->step_R = 0.02;

	//dip steps (dip in [0,90])
	//densityGrid->dSteps = static_cast<unsigned>(ceil(90.0 / densityGrid->step_deg));
	//R steps (R in [0,1])
	densityGrid->rSteps = static_cast<unsigned>(ceil(1.0 / densityGrid->step_R));
	//dip direction steps (dip dir. in [0,360])
	densityGrid->ddSteps = static_cast<unsigned>(ceil(360.0 / densityGrid->step_deg));

	unsigned cellCount = densityGrid->rSteps * densityGrid->ddSteps;
	densityGrid->grid = new double[cellCount];
	if (densityGrid->grid)
	{
		memset(densityGrid->grid,0,sizeof(double)*cellCount);

		CCVector3d Nmean(0,0,0);
		double surfaceSum = 0.0;

		for (size_t i=0; i<count; ++i)
		{
			CCVector3 N;
			double weight = 1.0;
			if (cloud)
			{
				N = cloud->getPointNormal(static_cast<unsigned>(i));
			}
			else
			{
				ccFacet* facet = static_cast<ccFacet*>(facets[i]);
				N = facet->getNormal();
				weight = facet->getSurface();
			}

			Nmean.x += static_cast<double>(N.x) * weight;
			Nmean.y += static_cast<double>(N.y) * weight;
			Nmean.z += static_cast<double>(N.z) * weight;
			surfaceSum += weight;

			PointCoordinateType dipDir = 0, dip = 0;
			ccNormalVectors::ConvertNormalToDipAndDipDir(N,dip,dipDir);

			//unsigned iDip = static_cast<unsigned>(floor(static_cast<double>(dip)/densityGrid->step_deg));
			//if (iDip == densityGrid->dSteps)
			//	iDip--;
			unsigned iDipDir = static_cast<unsigned>(floor(static_cast<double>(dipDir)/densityGrid->step_deg));
			if (iDipDir == densityGrid->ddSteps)
				iDipDir--;

			double dip_rad = dip * CC_DEG_TO_RAD;
			double R = sin(dip_rad) / (1.0 + cos(dip_rad));

			unsigned iR = static_cast<unsigned>(floor(static_cast<double>(R)/densityGrid->step_R));
			if (iR == densityGrid->rSteps)
				iR--;

			densityGrid->grid[iR + iDipDir * densityGrid->rSteps] += weight;

			//pDlg.setValue(static_cast<int>(i));
			if (!nProgress.oneStep())
			{
				//process cancelled by user
				delete densityGrid;
				return false;
			}
		}

		if (surfaceSum > 0)
		{
			Nmean.normalize();
			CCVector3 N(static_cast<PointCoordinateType>(Nmean.x),
				static_cast<PointCoordinateType>(Nmean.y),
				static_cast<PointCoordinateType>(Nmean.z));

			PointCoordinateType dipDir = 0, dip = 0;
			ccNormalVectors::ConvertNormalToDipAndDipDir(N,dip,dipDir);

			m_meanDipDir_deg = static_cast<double>(dipDir);
			m_meanDip_deg = static_cast<double>(dip);

			//set same value for the "filter" center
			m_clickDipDir_deg = m_meanDipDir_deg;
			m_clickDip_deg = m_meanDip_deg;
		}

		//compute min and max density
		{
			//DGM: only supported on C++x11!
			//std::pair<double*,double*> minmax = std::minmax_element(densityGrid->grid,densityGrid->grid+cellCount);
			//densityGrid->minMaxDensity[0] = *minmax.first;
			//densityGrid->minMaxDensity[1] = *minmax.second;

			densityGrid->minMaxDensity[0] = densityGrid->grid[0];
			densityGrid->minMaxDensity[1] = densityGrid->grid[0];
			for (unsigned j=1; j<cellCount; ++j)
			{
				if (densityGrid->grid[j] < densityGrid->minMaxDensity[0])
					densityGrid->minMaxDensity[0] = densityGrid->grid[j];
				else if (densityGrid->grid[j] > densityGrid->minMaxDensity[1])
					densityGrid->minMaxDensity[1] = densityGrid->grid[j];
			}
		}

		//pDlg.hide();
		pDlg.stop();
		QApplication::processEvents();
	}
	else
	{
		//not enough memory!
		delete densityGrid;
		densityGrid = 0;
	}

	//replace old grid by new one! (even in case of failure! See below)
	m_densityGrid = densityGrid;

	update();

	return true;
}
コード例 #3
0
bool DistanceMapGenerationTool::ComputeRadialDist(	ccPointCloud* cloud,
													ccPolyline* profile,
													bool storeRadiiAsSF/*=false*/,
													ccMainAppInterface* app/*=0*/)
{
	//check input cloud and profile/polyline
	if (!cloud || !profile)
	{
		if (app)
			app->dispToConsole(QString("Internal error: invalid input parameters"),ccMainAppInterface::ERR_CONSOLE_MESSAGE);
		return false;
	}
	assert(cloud && profile);

	//number of vertices for the profile
	CCLib::GenericIndexedCloudPersist* vertices = profile->getAssociatedCloud();
	unsigned vertexCount = vertices->size();
	if (vertexCount < 2)
	{
		if (app)
			app->dispToConsole(QString("Invalid polyline (not enough vertices)"),ccMainAppInterface::ERR_CONSOLE_MESSAGE);
		return false;
	}

	//profile meta-data
	ProfileMetaData profileDesc;
	if (!GetPoylineMetaData(profile, profileDesc))
	{
		if (app)
			app->dispToConsole(QString("Invalid polyline (bad or missing meta-data)"),ccMainAppInterface::ERR_CONSOLE_MESSAGE);
		return false;
	}

	//reserve a new scalar field (or take the old one if it already exists)
	int sfIdx = cloud->getScalarFieldIndexByName(RADIAL_DIST_SF_NAME);
	if (sfIdx < 0)
		sfIdx = cloud->addScalarField(RADIAL_DIST_SF_NAME);
	if (sfIdx < 0)
	{
		if (app)
			app->dispToConsole(QString("Failed to allocate a new scalar field for computing distances! Try to free some memory ..."),ccMainAppInterface::ERR_CONSOLE_MESSAGE);
		return false;
	}
	ccScalarField* sf = static_cast<ccScalarField*>(cloud->getScalarField(sfIdx));
	unsigned pointCount = cloud->size();
	sf->resize(pointCount); //should always be ok
	assert(sf);

	ccScalarField* radiiSf = 0;
	if (storeRadiiAsSF)
	{
		int sfIdxRadii = cloud->getScalarFieldIndexByName(RADII_SF_NAME);
		if (sfIdxRadii < 0)
			sfIdxRadii = cloud->addScalarField(RADII_SF_NAME);
		if (sfIdxRadii < 0)
		{
			if (app)
				app->dispToConsole(QString("Failed to allocate a new scalar field for storing radii! You should try to free some memory ..."),ccMainAppInterface::WRN_CONSOLE_MESSAGE);
			//return false;
		}
		else
		{
			radiiSf = static_cast<ccScalarField*>(cloud->getScalarField(sfIdxRadii));
			radiiSf->resize(pointCount); //should always be ok
		}
	}

	bool success = true;

	//now compute the distance between the cloud and the (implicit) surface of revolution
	{
		ccGLMatrix cloudToSurface = profileDesc.computeProfileToSurfaceTrans();

		//we deduce the horizontal dimensions from the revolution axis
		const unsigned char dim1 = static_cast<unsigned char>(profileDesc.revolDim < 2 ? profileDesc.revolDim+1 : 0);
		const unsigned char dim2 = (dim1 < 2 ? dim1+1 : 0);

		ccProgressDialog dlg(true, app ? app->getMainWindow() : 0);
		dlg.setMethodTitle("Cloud to profile radial distance");
		dlg.setInfo(qPrintable(QString("Polyline: %1 vertices\nCloud: %2 points").arg(vertexCount).arg(pointCount)));
		dlg.start();
		CCLib::NormalizedProgress nProgress(static_cast<CCLib::GenericProgressCallback*>(&dlg),pointCount);

		for (unsigned i=0; i<pointCount; ++i)
		{
			const CCVector3* P = cloud->getPoint(i);

			//relative point position
			CCVector3 Prel = cloudToSurface * (*P);

			//deduce point height and radius (i.e. in profile 2D coordinate system)
			double height = Prel.u[profileDesc.revolDim] - profileDesc.heightShift;
			//TODO FIXME: we assume the surface of revolution is smooth!
			double radius = sqrt(Prel.u[dim1]*Prel.u[dim1] + Prel.u[dim2]*Prel.u[dim2]);

			if (radiiSf)
			{
				ScalarType radiusVal = static_cast<ScalarType>(radius);
				radiiSf->setValue(i,radiusVal);
			}

			//search nearest "segment" in polyline
			ScalarType minDist = NAN_VALUE;
			for (unsigned j=1; j<vertexCount; ++j)
			{
				const CCVector3* A = vertices->getPoint(j-1);
				const CCVector3* B = vertices->getPoint(j);

				double alpha = (height - A->y)/(B->y - A->y);
				if (alpha >= 0.0 && alpha <= 1.0)
				{
					//we deduce the right radius by linear interpolation
					double radius_th = A->x + alpha * (B->x - A->x);
					double dist = radius - radius_th;

					//we look at the closest segment (if the polyline is concave!)
					if (!CCLib::ScalarField::ValidValue(minDist) || dist*dist < minDist*minDist)
					{
						minDist = static_cast<ScalarType>(dist);
					}
				}
			}

			sf->setValue(i,minDist);

			if (!nProgress.oneStep())
			{
				//cancelled by user
				for (unsigned j=i; j<pointCount; ++j)
					sf->setValue(j,NAN_VALUE);

				success = false;
				break;
			}

			//TEST
			//*const_cast<CCVector3*>(P) = Prel;
		}
		
		//TEST
		//cloud->invalidateBoundingBox();
	}

	sf->computeMinAndMax();
	cloud->setCurrentDisplayedScalarField(sfIdx);
	cloud->showSF(true);

	return success;
}
コード例 #4
0
bool ccKdTreeForFacetExtraction::FuseCells(	ccKdTree* kdTree,
											double maxError,
											CCLib::DistanceComputationTools::ERROR_MEASURES errorMeasure,
											double maxAngle_deg,
											PointCoordinateType overlapCoef/*=1*/,
											bool closestFirst/*=true*/,
											CCLib::GenericProgressCallback* progressCb/*=0*/)
{
	if (!kdTree)
		return false;

	ccGenericPointCloud* associatedGenericCloud = kdTree->associatedGenericCloud();
	if (!associatedGenericCloud || !associatedGenericCloud->isA(CC_TYPES::POINT_CLOUD) || maxError < 0.0)
		return false;

	//get leaves
	std::vector<ccKdTree::Leaf*> leaves;
	if (!kdTree->getLeaves(leaves) || leaves.empty())
		return false;

	//progress notification
	CCLib::NormalizedProgress nProgress(progressCb, static_cast<unsigned>(leaves.size()));
	if (progressCb)
	{
		progressCb->update(0);
		if (progressCb->textCanBeEdited())
		{
			progressCb->setMethodTitle("Fuse Kd-tree cells");
			progressCb->setInfo(qPrintable(QString("Cells: %1\nMax error: %2").arg(leaves.size()).arg(maxError)));
		}
		progressCb->start();
	}

	ccPointCloud* pc = static_cast<ccPointCloud*>(associatedGenericCloud);

	//sort cells based on their population size (we start by the biggest ones)
	SortAlgo(leaves.begin(), leaves.end(), DescendingLeafSizeComparison);

	//set all 'userData' to -1 (i.e. unfused cells)
	{
		for (size_t i=0; i<leaves.size(); ++i)
		{
			leaves[i]->userData = -1;
			//check by the way that the plane normal is unit!
			assert(static_cast<double>(fabs(CCVector3(leaves[i]->planeEq).norm2()) - 1.0) < 1.0e-6);
		}
	}

	// cosine of the max angle between fused 'planes'
	const double c_minCosNormAngle = cos(maxAngle_deg * CC_DEG_TO_RAD);

	//fuse all cells, starting from the ones with the best error
	const int unvisitedNeighborValue = -1;
	bool cancelled = false;
	int macroIndex = 1; //starts at 1 (0 is reserved for cells already above the max error)
	{
		for (size_t i=0; i<leaves.size(); ++i)
		{
			ccKdTree::Leaf* currentCell = leaves[i];
			if (currentCell->error >= maxError)
				currentCell->userData = 0; //0 = special group for cells already above the user defined threshold!

			//already fused?
			if (currentCell->userData != -1)
			{
				if (progressCb && !nProgress.oneStep()) //process canceled by user
				{
					cancelled = true;
					break;
				}
				continue;
			}

			//we create a new "macro cell" index
			currentCell->userData = macroIndex++;

			//we init the current set of 'fused' points with the cell's points
			CCLib::ReferenceCloud* currentPointSet = currentCell->points;
			//get current fused set centroid and normal
			CCVector3 currentCentroid = *CCLib::Neighbourhood(currentPointSet).getGravityCenter();
			CCVector3 currentNormal(currentCell->planeEq);

			//visited neighbors
			ccKdTree::LeafSet visitedNeighbors;
			//set of candidates
			std::list<Candidate> candidates;

			//we are going to iteratively look for neighbor cells that could be fused to this one
			ccKdTree::LeafVector cellsToTest;
			cellsToTest.push_back(currentCell);

			if (progressCb && !nProgress.oneStep()) //process canceled by user
			{
				cancelled = true;
				break;
			}

			while (!cellsToTest.empty() || !candidates.empty())
			{
				//get all neighbors around the 'waiting' cell(s)
				if (!cellsToTest.empty())
				{
					ccKdTree::LeafSet neighbors;
					while (!cellsToTest.empty())
					{
						if (!kdTree->getNeighborLeaves(cellsToTest.back(), neighbors, &unvisitedNeighborValue)) //we only consider unvisited cells!
						{
							//an error occurred
							return false;
						}
						cellsToTest.pop_back();
					}

					//add those (new) neighbors to the 'visitedNeighbors' set
					//and to the candidates set by the way if they are not yet there
					for (ccKdTree::LeafSet::iterator it=neighbors.begin(); it != neighbors.end(); ++it)
					{
						ccKdTree::Leaf* neighbor = *it;
						std::pair<ccKdTree::LeafSet::iterator,bool> ret = visitedNeighbors.insert(neighbor);
						//neighbour not already in the set?
						if (ret.second)
						{
							//we create the corresponding candidate
							try
							{
								candidates.push_back(Candidate(neighbor));
							}
							catch (const std::bad_alloc&)
							{
								//not enough memory!
								ccLog::Warning("[ccKdTreeForFacetExtraction] Not enough memory!");
								return false;
							}
						}
					}
				}

				//is there remaining candidates?
				if (!candidates.empty())
				{
					//update the set of candidates
					if (closestFirst && candidates.size() > 1)
					{
						for (std::list<Candidate>::iterator it = candidates.begin(); it !=candidates.end(); ++it)
							it->dist = (it->centroid-currentCentroid).norm2();

						//sort candidates by their distance
						candidates.sort(CandidateDistAscendingComparison);
					}
					
					//we will keep track of the best fused 'couple' at each pass
					std::list<Candidate>::iterator bestIt = candidates.end();
					CCLib::ReferenceCloud* bestFused = 0;
					CCVector3 bestNormal(0,0,0);
					double bestError = -1.0;

					unsigned skipCount = 0;
					for (std::list<Candidate>::iterator it = candidates.begin(); it != candidates.end(); /*++it*/)
					{
						assert(it->leaf && it->leaf->points);
						assert(currentPointSet->getAssociatedCloud() == it->leaf->points->getAssociatedCloud());

						//if the leaf orientation is too different
						if (fabs(CCVector3(it->leaf->planeEq).dot(currentNormal)) < c_minCosNormAngle)
						{
							it = candidates.erase(it);
							//++it;
							//++skipCount;
							continue;
						}

						//compute the minimum distance between the candidate centroid and the 'currentPointSet'
						PointCoordinateType minDistToMainSet = 0.0;
						{
							for (unsigned j=0; j<currentPointSet->size(); ++j)
							{
								const CCVector3* P = currentPointSet->getPoint(j);
								PointCoordinateType d2 = (*P-it->centroid).norm2();
								if (d2 < minDistToMainSet || j == 0)
									minDistToMainSet = d2;
							}
							minDistToMainSet = sqrt(minDistToMainSet);
						}
						
						//if the leaf is too far
						if (it->radius < minDistToMainSet / overlapCoef)
						{
							++it;
							++skipCount;
							continue;
						}

						//fuse the main set with the current candidate
						CCLib::ReferenceCloud* fused = new CCLib::ReferenceCloud(*currentPointSet);
						if (!fused->add(*(it->leaf->points)))
						{
							//not enough memory!
							ccLog::Warning("[ccKdTreeForFacetExtraction] Not enough memory!");
							delete fused;
							if (currentPointSet != currentCell->points)
								delete currentPointSet;
							return false;
						}

						//fit a plane and estimate the resulting error
						double error = -1.0;
						const PointCoordinateType* planeEquation = CCLib::Neighbourhood(fused).getLSPlane();
						if (planeEquation)
							error = CCLib::DistanceComputationTools::ComputeCloud2PlaneDistance(fused, planeEquation, errorMeasure);

						if (error < 0.0 || error > maxError)
						{
							//candidate is rejected
							it = candidates.erase(it);
						}
						else
						{
							//otherwise we keep track of the best one!
							if (bestError < 0.0 || error < bestError)
							{
								bestIt = it;
								bestError = error;
								if (bestFused)
									delete bestFused;
								bestFused = fused;
								bestNormal = CCVector3(planeEquation);
								fused = 0;
								
								if (closestFirst)
									break; //if we have found a good candidate, we stop here (closest first ;)
							}
							++it;
						}

						if (fused)
						{
							delete fused;
							fused = 0;
						}
					}

					//we have a (best) candidate for this pass?
					if (bestIt != candidates.end())
					{
						assert(bestFused && bestError >= 0.0);
						if (currentPointSet != currentCell->points)
							delete currentPointSet;
						currentPointSet = bestFused;
						{
							//update infos
							CCLib::Neighbourhood N(currentPointSet);
							//currentCentroid = *N.getGravityCenter(); //if we update it, the search will naturally shift along one dimension!
							//currentNormal = bestNormal; //same thing here for normals
						}

						bestIt->leaf->userData = currentCell->userData;
						//bestIt->leaf->userData = macroIndex++; //FIXME TEST

						//we will test this cell's neighbors as well
						cellsToTest.push_back(bestIt->leaf);

						if (progressCb && !nProgress.oneStep()) //process canceled by user
						{
							//premature end!
							candidates.clear();
							cellsToTest.clear();
							cancelled = true;
							break;
						}
						QApplication::processEvents();

						//we also remove it from the candidates list
						candidates.erase(bestIt);
					}

					if (skipCount == candidates.size() && cellsToTest.empty())
					{
						//only far leaves remain...
						candidates.clear();
					}

				}
			
			} //no more candidates or cells to test

			//end of the fusion process for the current leaf
			if (currentPointSet != currentCell->points)
				delete currentPointSet;
			currentPointSet = 0;

			if (cancelled)
				break;
		}
	}

	//convert fused indexes to SF
	if (!cancelled)
	{
		pc->enableScalarField();

		for (size_t i=0; i<leaves.size(); ++i)
		{
			CCLib::ReferenceCloud* subset = leaves[i]->points;
			if (subset)
			{
				ScalarType scalar = (ScalarType)leaves[i]->userData;
				if (leaves[i]->userData <= 0) //for unfused cells, we create new individual groups
					scalar = static_cast<ScalarType>(macroIndex++);
					//scalar = NAN_VALUE; //FIXME TEST
				for (unsigned j=0; j<subset->size(); ++j)
					subset->setPointScalarValue(j,scalar);
			}
		}

		//pc->setCurrentDisplayedScalarField(sfIdx);
	}

	return !cancelled;
}
コード例 #5
0
bool ccVolumeCalcTool::ComputeVolume(	ccRasterGrid& grid,
										ccGenericPointCloud* ground,
										ccGenericPointCloud* ceil,
										const ccBBox& gridBox,
										unsigned char vertDim,
										double gridStep,
										unsigned gridWidth,
										unsigned gridHeight,
										ccRasterGrid::ProjectionType projectionType,
										ccRasterGrid::EmptyCellFillOption emptyCellFillStrategy,
										ccVolumeCalcTool::ReportInfo& reportInfo,
										double groundHeight = std::numeric_limits<double>::quiet_NaN(),
										double ceilHeight = std::numeric_limits<double>::quiet_NaN(),
										QWidget* parentWidget/*=0*/)
{
	if (	gridStep <= 1.0e-8
		||	gridWidth == 0
		||	gridHeight == 0
		||	vertDim > 2)
	{
		assert(false);
		ccLog::Warning("[Volume] Invalid input parameters");
		return false;
	}

	if (!ground && !ceil)
	{
		assert(false);
		ccLog::Warning("[Volume] No valid input cloud");
		return false;
	}

	if (!gridBox.isValid())
	{
		ccLog::Warning("[Volume] Invalid bounding-box");
		return false;
	}

	//grid size
	unsigned gridTotalSize = gridWidth * gridHeight;
	if (gridTotalSize == 1)
	{
		if (parentWidget && QMessageBox::question(parentWidget, "Unexpected grid size", "The generated grid will only have 1 cell! Do you want to proceed anyway?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
			return false;
	}
	else if (gridTotalSize > 10000000)
	{
		if (parentWidget && QMessageBox::question(parentWidget, "Big grid size", "The generated grid will have more than 10.000.000 cells! Do you want to proceed anyway?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
			return false;
	}

	//memory allocation
	CCVector3d minCorner = CCVector3d::fromArray(gridBox.minCorner().u);
	if (!grid.init(gridWidth, gridHeight, gridStep, minCorner))
	{
		//not enough memory
		return SendError("Not enough memory", parentWidget);
	}

	//progress dialog
	QScopedPointer<ccProgressDialog> pDlg(0);
	if (parentWidget)
	{
		pDlg.reset(new ccProgressDialog(true, parentWidget));
	}

	ccRasterGrid groundRaster;
	if (ground)
	{
		if (!groundRaster.init(gridWidth, gridHeight, gridStep, minCorner))
		{
			//not enough memory
			return SendError("Not enough memory", parentWidget);
		}

		if (groundRaster.fillWith(	ground,
									vertDim,
									projectionType,
									emptyCellFillStrategy == ccRasterGrid::INTERPOLATE,
									ccRasterGrid::INVALID_PROJECTION_TYPE,
									pDlg.data()))
		{
			groundRaster.fillEmptyCells(emptyCellFillStrategy, groundHeight);
			ccLog::Print(QString("[Volume] Ground raster grid: size: %1 x %2 / heights: [%3 ; %4]").arg(groundRaster.width).arg(groundRaster.height).arg(groundRaster.minHeight).arg(groundRaster.maxHeight));
		}
		else
		{
			return false;
		}
	}

	//ceil
	ccRasterGrid ceilRaster;
	if (ceil)
	{
		if (!ceilRaster.init(gridWidth, gridHeight, gridStep, minCorner))
		{
			//not enough memory
			return SendError("Not enough memory", parentWidget);
		}

		if (ceilRaster.fillWith(ceil,
								vertDim,
								projectionType,
								emptyCellFillStrategy == ccRasterGrid::INTERPOLATE,
								ccRasterGrid::INVALID_PROJECTION_TYPE,
								pDlg.data()))
		{
			ceilRaster.fillEmptyCells(emptyCellFillStrategy, ceilHeight);
			ccLog::Print(QString("[Volume] Ceil raster grid: size: %1 x %2 / heights: [%3 ; %4]").arg(ceilRaster.width).arg(ceilRaster.height).arg(ceilRaster.minHeight).arg(ceilRaster.maxHeight));
		}
		else
		{
			return false;
		}
	}

	//update grid and compute volume
	{
		if (pDlg)
		{
			pDlg->setMethodTitle(QObject::tr("Volume computation"));
			pDlg->setInfo(QObject::tr("Cells: %1 x %2").arg(grid.width).arg(grid.height));
			pDlg->start();
			pDlg->show();
			QCoreApplication::processEvents();
		}
		CCLib::NormalizedProgress nProgress(pDlg.data(), grid.width * grid.height);
		
		size_t ceilNonMatchingCount = 0;
		size_t groundNonMatchingCount = 0;
		size_t cellCount = 0;

		//at least one of the grid is based on a cloud
		grid.nonEmptyCellCount = 0;
		for (unsigned i = 0; i < grid.height; ++i)
		{
			for (unsigned j = 0; j < grid.width; ++j)
			{
				ccRasterCell& cell = grid.rows[i][j];

				bool validGround = true;
				cell.minHeight = groundHeight;
				if (ground)
				{
					cell.minHeight = groundRaster.rows[i][j].h;
					validGround = std::isfinite(cell.minHeight);
				}

				bool validCeil = true;
				cell.maxHeight = ceilHeight;
				if (ceil)
				{
					cell.maxHeight = ceilRaster.rows[i][j].h;
					validCeil = std::isfinite(cell.maxHeight);
				}

				if (validGround && validCeil)
				{
					cell.h = cell.maxHeight - cell.minHeight;
					cell.nbPoints = 1;

					reportInfo.volume += cell.h;
					if (cell.h < 0)
					{
						reportInfo.removedVolume -= cell.h;
					}
					else if (cell.h > 0)
					{
						reportInfo.addedVolume += cell.h;
					}
					reportInfo.surface += 1.0;
					++grid.nonEmptyCellCount; //= matching count
					++cellCount;
				}
				else
				{
					if (validGround)
					{
						++cellCount;
						++groundNonMatchingCount;
					}
					else if (validCeil)
					{
						++cellCount;
						++ceilNonMatchingCount;
					}
					cell.h = std::numeric_limits<double>::quiet_NaN();
					cell.nbPoints = 0;
				}

				cell.avgHeight = (groundHeight + ceilHeight) / 2;
				cell.stdDevHeight = 0;

				if (pDlg && !nProgress.oneStep())
				{
					ccLog::Warning("[Volume] Process cancelled by the user");
					return false;
				}
			}
		}
		grid.validCellCount = grid.nonEmptyCellCount;

		//count the average number of valid neighbors
		{
			size_t validNeighborsCount = 0;
			size_t count = 0;
			for (unsigned i = 1; i < grid.height - 1; ++i)
			{
				for (unsigned j = 1; j < grid.width - 1; ++j)
				{
					ccRasterCell& cell = grid.rows[i][j];
					if (cell.h == cell.h)
					{
						for (unsigned k = i - 1; k <= i + 1; ++k)
						{
							for (unsigned l = j - 1; l <= j + 1; ++l)
							{
								if (k != i || l != j)
								{
									ccRasterCell& otherCell = grid.rows[k][l];
									if (std::isfinite(otherCell.h))
									{
										++validNeighborsCount;
									}
								}
							}
						}

						++count;
					}
				}
			}

			if (count)
			{
				reportInfo.averageNeighborsPerCell = static_cast<double>(validNeighborsCount) / count;
			}
		}

		reportInfo.matchingPrecent = static_cast<float>(grid.validCellCount * 100) / cellCount;
		reportInfo.groundNonMatchingPercent = static_cast<float>(groundNonMatchingCount * 100) / cellCount;
		reportInfo.ceilNonMatchingPercent = static_cast<float>(ceilNonMatchingCount * 100) / cellCount;
		float cellArea = static_cast<float>(grid.gridStep * grid.gridStep);
		reportInfo.volume *= cellArea;
		reportInfo.addedVolume *= cellArea;
		reportInfo.removedVolume *= cellArea;
		reportInfo.surface *= cellArea;
	}

	grid.setValid(true);

	return true;
}
コード例 #6
0
static bool ResolveNormalsWithMST(ccPointCloud* cloud, const Graph& graph, CCLib::GenericProgressCallback* progressCb = 0)
{
	assert(cloud && cloud->hasNormals());

//#define COLOR_PATCHES
#ifdef COLOR_PATCHES
	//Test: color patches
	cloud->setRGBColor(ccColor::white);
	cloud->showColors(true);

	//Test: arrival time
	int sfIdx = cloud->getScalarFieldIndexByName("MST arrival time");
	if (sfIdx < 0)
		sfIdx = cloud->addScalarField("MST arrival time");
	ccScalarField* sf = static_cast<ccScalarField*>(cloud->getScalarField(sfIdx));
	sf->fill(NAN_VALUE);
	cloud->setCurrentDisplayedScalarField(sfIdx);
#endif

	//reset
	std::priority_queue<Edge> priorityQueue;
	std::vector<bool> visited;
	unsigned visitedCount = 0;

	size_t vertexCount = graph.vertexCount();

	//instantiate the 'visited' table
	try
	{
		visited.resize(vertexCount,false);
	}
	catch (const std::bad_alloc&)
	{
		//not enough memory
		return false;
	}

	//progress notification
	CCLib::NormalizedProgress nProgress(progressCb,static_cast<unsigned>(vertexCount));
	if (progressCb)
	{
		progressCb->reset();
		progressCb->setMethodTitle("Orient normals (MST)");
		progressCb->setInfo(qPrintable(QString("Compute Minimum spanning tree\nPoints: %1\nEdges: %2").arg(vertexCount).arg(graph.edgeCount())));
		progressCb->start();
	}

	//while unvisited vertices remain...
	size_t firstUnvisitedIndex = 0;
	size_t patchCount = 0;
	size_t inversionCount = 0;
	while (visitedCount < vertexCount)
	{
		//find the first not-yet-visited vertex
		while (visited[firstUnvisitedIndex])
			++firstUnvisitedIndex;

		//set it as "visited"
		{
			visited[firstUnvisitedIndex] = true;
			++visitedCount;
			//add its neighbors to the priority queue
			const std::set<size_t>& neighbors = graph.getVertexNeighbors(firstUnvisitedIndex);
			for (std::set<size_t>::const_iterator it = neighbors.begin(); it != neighbors.end(); ++it)
				priorityQueue.push(Edge(firstUnvisitedIndex, *it, graph.weight(firstUnvisitedIndex, *it)));

			if (progressCb && !nProgress.oneStep())
				break;
		}

#ifdef COLOR_PATCHES
		ccColor::Rgb patchCol = ccColor::Generator::Random();
		cloud->setPointColor(static_cast<unsigned>(firstUnvisitedIndex), patchCol.rgb);
		sf->setValue(static_cast<unsigned>(firstUnvisitedIndex),static_cast<ScalarType>(visitedCount));
#endif

		while(!priorityQueue.empty() && visitedCount < vertexCount)
		{
			//process next edge (with the lowest 'weight')
			Edge element = priorityQueue.top();
			priorityQueue.pop();

			//there should only be (at most) one unvisited vertex in the edge
			size_t v = 0;
			if (!visited[element.v1()])
				v = element.v1();
			else if (!visited[element.v2()])
				v = element.v2();
			else
				continue;

			//invert normal if necessary (DO THIS BEFORE SETTING THE VERTEX AS 'VISITED'!)
			const CCVector3& N1 = cloud->getPointNormal(static_cast<unsigned>(element.v1()));
			const CCVector3& N2 = cloud->getPointNormal(static_cast<unsigned>(element.v2()));
			if (N1.dot(N2) < 0)
			{
				if (!visited[element.v1()])
				{
					cloud->setPointNormal(static_cast<unsigned>(v), -N1);
				}
				else
				{
					cloud->setPointNormal(static_cast<unsigned>(v), -N2);
				}
				++inversionCount;
			}

			//set it as "visited"
			{
				visited[v] = true;
				++visitedCount;
				//add its neighbors to the priority queue
				const std::set<size_t>& neighbors = graph.getVertexNeighbors(v);
				for (std::set<size_t>::const_iterator it = neighbors.begin(); it != neighbors.end(); ++it)
					priorityQueue.push(Edge(v, *it, graph.weight(v,*it)));
			}

#ifdef COLOR_PATCHES
			cloud->setPointColor(static_cast<unsigned>(v), patchCol);
			sf->setValue(static_cast<unsigned>(v),static_cast<ScalarType>(visitedCount));
#endif
			if (progressCb && !nProgress.oneStep())
			{
				visitedCount = static_cast<unsigned>(vertexCount); //early stop
				break;
			}
		}

		//new patch
		++patchCount;
	}

#ifdef COLOR_PATCHES
	sf->computeMinAndMax();
	cloud->showSF(true);
#endif

	if (progressCb)
	{
		progressCb->stop();
	}

	ccLog::Print(QString("[ResolveNormalsWithMST] Patches = %1 / Inversions: %2").arg(patchCount).arg(inversionCount));

	return true;
}
コード例 #7
0
bool ccVolumeCalcTool::updateGrid()
{
    if (!m_cloud2)
    {
        assert(false);
        return false;
    }

    //cloud bounding-box --> grid size
    ccBBox box = getCustomBBox();
    if (!box.isValid())
    {
        return false;
    }

    unsigned gridWidth = 0, gridHeight = 0;
    if (!getGridSize(gridWidth, gridHeight))
    {
        return false;
    }

    //grid size
    unsigned gridTotalSize = gridWidth * gridHeight;
    if (gridTotalSize == 1)
    {
        if (QMessageBox::question(this, "Unexpected grid size", "The generated grid will only have 1 cell! Do you want to proceed anyway?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
            return false;
    }
    else if (gridTotalSize > 10000000)
    {
        if (QMessageBox::question(this, "Big grid size", "The generated grid will have more than 10.000.000 cells! Do you want to proceed anyway?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
            return false;
    }

    //grid step
    double gridStep = getGridStep();
    assert(gridStep != 0);

    //memory allocation
    CCVector3d minCorner = CCVector3d::fromArray(box.minCorner().u);
    if (!m_grid.init(gridWidth, gridHeight, gridStep, minCorner))
    {
        //not enough memory
        ccLog::Error("Not enough memory");
        return false;
    }

    //ground
    ccGenericPointCloud* groundCloud = 0;
    double groundHeight = 0;
    switch (groundComboBox->currentIndex())
    {
    case 0:
        groundHeight =	groundEmptyValueDoubleSpinBox->value();
        break;
    case 1:
        groundCloud = m_cloud1 ? m_cloud1 : m_cloud2;
        break;
    case 2:
        groundCloud = m_cloud2;
        break;
    default:
        assert(false);
        return false;
    }

    //vertical dimension
    const unsigned char Z = getProjectionDimension();
    assert(Z >= 0 && Z <= 2);
    //per-cell Z computation
    ccRasterGrid::ProjectionType projectionType = getTypeOfProjection();

    ccProgressDialog pDlg(true, this);

    ccRasterGrid groundRaster;
    if (groundCloud)
    {
        if (!groundRaster.init(gridWidth, gridHeight, gridStep, minCorner))
        {
            //not enough memory
            ccLog::Error("Not enough memory");
            return false;
        }

        if (groundRaster.fillWith(	groundCloud,
                                    Z,
                                    projectionType,
                                    getFillEmptyCellsStrategy(fillGroundEmptyCellsComboBox) == ccRasterGrid::INTERPOLATE,
                                    ccRasterGrid::INVALID_PROJECTION_TYPE,
                                    &pDlg))
        {
            groundRaster.fillEmptyCells(getFillEmptyCellsStrategy(fillGroundEmptyCellsComboBox), groundEmptyValueDoubleSpinBox->value());
            ccLog::Print(QString("[Volume] Ground raster grid: size: %1 x %2 / heights: [%3 ; %4]").arg(m_grid.width).arg(m_grid.height).arg(m_grid.minHeight).arg(m_grid.maxHeight));
        }
        else
        {
            return false;
        }
    }

    //ceil
    ccGenericPointCloud* ceilCloud = 0;
    double ceilHeight = 0;
    switch (ceilComboBox->currentIndex())
    {
    case 0:
        ceilHeight = ceilEmptyValueDoubleSpinBox->value();
        break;
    case 1:
        ceilCloud = m_cloud1 ? m_cloud1 : m_cloud2;
        break;
    case 2:
        ceilCloud = m_cloud2;
        break;
    default:
        assert(false);
        return false;
    }

    ccRasterGrid ceilRaster;
    if (ceilCloud)
    {
        if (!ceilRaster.init(gridWidth, gridHeight, gridStep, minCorner))
        {
            //not enough memory
            ccLog::Error("Not enough memory");
            return false;
        }

        if (ceilRaster.fillWith(ceilCloud,
                                Z,
                                projectionType,
                                getFillEmptyCellsStrategy(fillCeilEmptyCellsComboBox) == ccRasterGrid::INTERPOLATE,
                                ccRasterGrid::INVALID_PROJECTION_TYPE,
                                &pDlg))
        {
            ceilRaster.fillEmptyCells(getFillEmptyCellsStrategy(fillCeilEmptyCellsComboBox), ceilEmptyValueDoubleSpinBox->value());
            ccLog::Print(QString("[Volume] Ceil raster grid: size: %1 x %2 / heights: [%3 ; %4]").arg(m_grid.width).arg(m_grid.height).arg(m_grid.minHeight).arg(m_grid.maxHeight));
        }
        else
        {
            return false;
        }
    }

    //update grid and compute volume
    {
        pDlg.setMethodTitle(tr("Volume computation"));
        pDlg.setInfo(tr("Cells: %1 x %2").arg(m_grid.width).arg(m_grid.height));
        pDlg.start();
        pDlg.show();
        QCoreApplication::processEvents();
        CCLib::NormalizedProgress nProgress(&pDlg, m_grid.width*m_grid.height);

        ReportInfo repotInfo;
        size_t ceilNonMatchingCount = 0;
        size_t groundNonMatchingCount = 0;
        size_t cellCount = 0;

        //at least one of the grid is based on a cloud
        m_grid.nonEmptyCellCount = 0;
        for (unsigned i = 0; i < m_grid.height; ++i)
        {
            for (unsigned j = 0; j < m_grid.width; ++j)
            {
                ccRasterCell& cell = m_grid.rows[i][j];

                bool validGround = true;
                cell.minHeight = groundHeight;
                if (groundCloud)
                {
                    cell.minHeight = groundRaster.rows[i][j].h;
                    validGround = std::isfinite(cell.minHeight);
                }

                bool validCeil = true;
                cell.maxHeight = ceilHeight;
                if (ceilCloud)
                {
                    cell.maxHeight = ceilRaster.rows[i][j].h;
                    validCeil = std::isfinite(cell.maxHeight);
                }

                if (validGround && validCeil)
                {
                    cell.h = cell.maxHeight - cell.minHeight;
                    cell.nbPoints = 1;

                    repotInfo.volume += cell.h;
                    if (cell.h < 0)
                    {
                        repotInfo.removedVolume -= cell.h;
                    }
                    else if (cell.h > 0)
                    {
                        repotInfo.addedVolume += cell.h;
                    }
                    repotInfo.surface += 1.0;
                    ++m_grid.nonEmptyCellCount; //= matching count
                    ++cellCount;
                }
                else
                {
                    if (validGround)
                    {
                        ++cellCount;
                        ++groundNonMatchingCount;
                    }
                    else if (validCeil)
                    {
                        ++cellCount;
                        ++ceilNonMatchingCount;
                    }
                    cell.h = std::numeric_limits<double>::quiet_NaN();
                    cell.nbPoints = 0;
                }

                cell.avgHeight = (groundHeight + ceilHeight) / 2;
                cell.stdDevHeight = 0;

                if (!nProgress.oneStep())
                {
                    ccLog::Warning("[Volume] Process cancelled by the user");
                    return false;
                }
            }
        }
        m_grid.validCellCount = m_grid.nonEmptyCellCount;

        //count the average number of valid neighbors
        {
            size_t validNeighborsCount = 0;
            size_t count = 0;
            for (unsigned i = 1; i < m_grid.height - 1; ++i)
            {
                for (unsigned j = 1; j < m_grid.width - 1; ++j)
                {
                    ccRasterCell& cell = m_grid.rows[i][j];
                    if (cell.h == cell.h)
                    {
                        for (unsigned k = i - 1; k <= i + 1; ++k)
                        {
                            for (unsigned l = j - 1; l <= j + 1; ++l)
                            {
                                if (k != i || l != j)
                                {
                                    ccRasterCell& otherCell = m_grid.rows[k][l];
                                    if (std::isfinite(otherCell.h))
                                    {
                                        ++validNeighborsCount;
                                    }
                                }
                            }
                        }

                        ++count;
                    }
                }
            }

            if (count)
            {
                repotInfo.averageNeighborsPerCell = static_cast<double>(validNeighborsCount) / count;
            }
        }

        repotInfo.matchingPrecent = static_cast<float>(m_grid.validCellCount * 100) / cellCount;
        repotInfo.groundNonMatchingPercent = static_cast<float>(groundNonMatchingCount * 100) / cellCount;
        repotInfo.ceilNonMatchingPercent = static_cast<float>(ceilNonMatchingCount * 100) / cellCount;
        float cellArea = static_cast<float>(m_grid.gridStep * m_grid.gridStep);
        repotInfo.volume *= cellArea;
        repotInfo.addedVolume *= cellArea;
        repotInfo.removedVolume *= cellArea;
        repotInfo.surface *= cellArea;

        outputReport(repotInfo);
    }

    m_grid.setValid(true);

    return true;
}
コード例 #8
0
int FastMarchingForFacetExtraction::init(	ccGenericPointCloud* cloud,
											CCLib::DgmOctree* theOctree,
											unsigned char level,
											ScalarType maxError,
											CCLib::DistanceComputationTools::ERROR_MEASURES errorMeasure,
											bool useRetroProjectionError,
											CCLib::GenericProgressCallback* progressCb/*=0*/)
{
	m_maxError = maxError;
	m_errorMeasure = errorMeasure;
	m_useRetroProjectionError = useRetroProjectionError;

	if (progressCb)
	{
		if (progressCb->textCanBeEdited())
		{
			progressCb->setMethodTitle("Fast Marching grid initialization");
			progressCb->setInfo(qPrintable(QString("Level: %1").arg(level)));
		}
		progressCb->update(0);
		progressCb->start();
	}

	int result = initGridWithOctree(theOctree, level);
	if (result < 0)
		return result;

	//fill the grid with the octree
	CCLib::DgmOctree::cellCodesContainer cellCodes;
	theOctree->getCellCodes(level,cellCodes,true);
	size_t cellCount = cellCodes.size();

	CCLib::NormalizedProgress nProgress(progressCb, static_cast<unsigned>(cellCount));
	if (progressCb)
	{
		progressCb->setInfo(qPrintable(QString("Level: %1\nCells: %2").arg(level).arg(cellCount)));
	}

	CCLib::ReferenceCloud Yk(theOctree->associatedCloud());
	while (!cellCodes.empty())
	{
		if (theOctree->getPointsInCell(cellCodes.back(),level,&Yk,true))
		{
			//convert the octree cell code to grid position
			Tuple3i cellPos;
			theOctree->getCellPos(cellCodes.back(),level,cellPos,true);

			CCVector3 N,C;
			ScalarType error;
			if (ComputeCellStats(&Yk,N,C,error,m_errorMeasure))
			{
				//convert octree cell pos to FM cell pos index
				unsigned gridPos = pos2index(cellPos);

				//create corresponding cell
				PlanarCell* aCell = new PlanarCell;
				aCell->cellCode = cellCodes.back();
				aCell->N = N;
				aCell->C = C;
				aCell->planarError = error;
				m_theGrid[gridPos] = aCell;
			}
			else
			{
				//an error occurred?!
				return -10;
			}
		}

		cellCodes.pop_back();

		if (progressCb && !nProgress.oneStep())
		{
			//process cancelled by user
			progressCb->stop();
			return -1;
		}
	}

	if (progressCb)
	{
		progressCb->stop();
	}
		
	m_initialized = true;

	return 0;
}
コード例 #9
0
void ccRasterizeTool::generateContours()
{
	if (!m_grid.isValid() || !m_rasterCloud)
	{
		ccLog::Error("Need a valid raster/cloud to compute contours!");
		return;
	}

	ccScalarField* activeLayer = m_rasterCloud->getCurrentDisplayedScalarField();
	if (!activeLayer)
	{
		ccLog::Error("No valid/active layer!");
		return;
	}

	double startValue = contourStartDoubleSpinBox->value();
	if (startValue > activeLayer->getMax())
	{
		ccLog::Error("Start value is above the layer maximum value!");
		return;
	}
	double step = contourStepDoubleSpinBox->value();
	assert(step > 0);
	unsigned levelCount = 1 + static_cast<unsigned>(floor((activeLayer->getMax()-startValue)/step));

	removeContourLines();
	bool ignoreBorders = ignoreContourBordersCheckBox->isChecked();

	unsigned xDim = m_grid.width;
	unsigned yDim = m_grid.height;
	int margin = 0;
	if (!ignoreBorders)
	{
		margin = 1;
		xDim += 2;
		yDim += 2;
	}
	std::vector<double> grid;
	try
	{
		grid.resize(xDim * yDim, 0);
	}
	catch (const std::bad_alloc&)
	{
		ccLog::Error("Not enough memory!");
		if (m_window)
			m_window->redraw();
		return;
	}

	//fill grid
	{
		bool sparseLayer = (activeLayer->currentSize() != m_grid.height * m_grid.width);
		double emptyCellsValue = activeLayer->getMin()-1.0;

		unsigned layerIndex = 0;
		for (unsigned j=0; j<m_grid.height; ++j)
		{
			RasterCell* cell = m_grid.data[j];
			double* row = &(grid[(j+margin)*xDim + margin]);
			for (unsigned i=0; i<m_grid.width; ++i)
			{
				if (cell[i].nbPoints || !sparseLayer)
				{
					ScalarType value = activeLayer->getValue(layerIndex++);
					row[i] = ccScalarField::ValidValue(value) ? value : emptyCellsValue;
				}
				else
				{
					row[i] = emptyCellsValue;
				}
			}
		}
	}

	bool memoryError = false;

	try
	{
		Isolines<double> iso(static_cast<int>(xDim),static_cast<int>(yDim));
		if (!ignoreBorders)
			iso.createOnePixelBorder(&(grid.front()),activeLayer->getMin()-1.0);
		//bounding box
		ccBBox box = getCustomBBox();
		assert(box.isValid());

		//vertical dimension
		const unsigned char Z = getProjectionDimension();
		assert(Z >= 0 && Z <= 2);
		const unsigned char X = Z == 2 ? 0 : Z +1;
		const unsigned char Y = X == 2 ? 0 : X +1;

		int minVertexCount = minVertexCountSpinBox->value();
		assert(minVertexCount >= 3);

		ccProgressDialog pDlg(true,this);
		pDlg.setMethodTitle("Contour plot");
		pDlg.setInfo(qPrintable(QString("Levels: %1\nCells: %2 x %3").arg(levelCount).arg(m_grid.width).arg(m_grid.height)));
		pDlg.start();
		pDlg.show();
		QApplication::processEvents();
		CCLib::NormalizedProgress nProgress(&pDlg,levelCount);

		int lineWidth = contourWidthSpinBox->value();
		bool colorize = colorizeContoursCheckBox->isChecked();

		double v = startValue;
		while (v <= activeLayer->getMax() && !memoryError)
		{
			//extract contour lines for the current level
			iso.setThreshold(v);
			int lineCount = iso.find(&(grid.front()));

			ccLog::PrintDebug(QString("[Rasterize][Isolines] value=%1 : %2 lines").arg(v).arg(lineCount));

			//convert them to poylines
			int realCount = 0;
			for (int i=0; i<lineCount; ++i)
			{
				int vertCount = iso.getContourLength(i);
				if (vertCount >= minVertexCount)
				{
					ccPointCloud* vertices = new ccPointCloud("vertices");
					ccPolyline* poly = new ccPolyline(vertices);
					poly->addChild(vertices);
					bool isClosed = iso.isContourClosed(i);
					if (poly->reserve(vertCount) && vertices->reserve(vertCount))
					{
						unsigned localIndex = 0;
						for (int vi=0; vi<vertCount; ++vi)
						{
							double x = iso.getContourX(i,vi) - margin + 0.5;
							double y = iso.getContourY(i,vi) - margin + 0.5;

							CCVector3 P;
							P.u[X] = static_cast<PointCoordinateType>(x * m_grid.gridStep + box.minCorner().u[X]);
							P.u[Y] = static_cast<PointCoordinateType>(y * m_grid.gridStep + box.minCorner().u[Y]);
							P.u[Z] = static_cast<PointCoordinateType>(v);

							vertices->addPoint(P);
							assert(localIndex < vertices->size());
							poly->addPointIndex(localIndex++);
						}

						assert(poly);
						if (poly->size() > 1)
						{
							poly->setName(QString("Contour line value=%1 (#%2)").arg(v).arg(++realCount));
							poly->setGlobalScale(m_cloud->getGlobalScale());
							poly->setGlobalShift(m_cloud->getGlobalShift());
							poly->setWidth(lineWidth);
							poly->setClosed(isClosed); //if we have less vertices, it means we have 'chopped' the original contour
							poly->setColor(ccColor::darkGrey);
							if (colorize)
							{
								const ColorCompType* col = activeLayer->getColor(v);
								if (col)
									poly->setColor(ccColor::Rgb(col));
							}
							poly->showColors(true);
							vertices->setEnabled(false);
							//add the 'const altitude' meta-data as well
							poly->setMetaData(ccPolyline::MetaKeyConstAltitude(),QVariant(v));
						
							if (m_window)
								m_window->addToOwnDB(poly);

							m_contourLines.push_back(poly);
						}
						else
						{
							delete poly;
							poly = 0;
						}
					}
					else
					{
						delete poly;
						poly = 0;
						ccLog::Error("Not enough memory!");
						memoryError = true; //early stop
						break;
					}
				}
			}
			v += step;

			if (!nProgress.oneStep())
			{
				//process cancelled by user
				break;
			}
		}
	}
	catch (const std::bad_alloc&)
	{
		ccLog::Error("Not enough memory!");
	}

	ccLog::Print(QString("[Rasterize] %1 iso-lines generated (%2 levels)").arg(m_contourLines.size()).arg(levelCount));

	if (!m_contourLines.empty())
	{
		if (memoryError)
		{
			removeContourLines();
		}
		else
		{
			exportContoursPushButton->setEnabled(true);
			clearContoursPushButton->setEnabled(true);
		}
	}

	if (m_window)
		m_window->redraw();
}