ModelTracker::Transform ModelTracker::position(const ModelTracker::ImgPoint imagePoints[],const ModelTracker::Transform::Rotation& orientation) const
	{
	/* Build the least-squares linear system: */
	Math::Matrix ata(3,3,0.0);
	Math::Matrix atb(3,1,0.0);
	const Projection::Matrix& pm=projection.getMatrix();
	for(unsigned int mpi=0;mpi<numModelPoints;++mpi)
		{
		/* Transform the model point with the known orientation and projection matrix: */
		Projection::HVector pmp=projection.transform(Projection::HVector(orientation.transform(modelPoints[mpi])));
		
		/* Create the two equations for the model/image point pair: */
		double eq[2][4];
		for(int i=0;i<2;++i)
			{
			for(int j=0;j<3;++j)
				eq[i][j]=pm(i,j)-imagePoints[mpi][i]*pm(3,j);
			eq[i][3]=imagePoints[mpi][i]*pmp[3]-pmp[i];
			}
		
		/* Add the two equations to the least-squares system: */
		for(int i=0;i<3;++i)
			{
			for(int j=0;j<3;++j)
				ata(i,j)+=eq[0][i]*eq[0][j]+eq[1][i]*eq[1][j];
			atb(i)+=eq[0][i]*eq[0][3]+eq[1][i]*eq[1][3];
			}
		}
	
	/* Solve the least-squares system: */
	Math::Matrix x=atb.divideFullPivot(ata);
	
	/* Return the result transformation: */
	return Transform(Transform::Vector(x(0),x(1),x(2)),orientation);
	}
void DepthCorrectionTool::buttonCallback(int buttonSlotIndex,Vrui::InputDevice::ButtonCallbackData* cbData)
	{
	if(cbData->newButtonState)
		{
		if(buttonSlotIndex==0)
			{
			/* Add a new averaged depth frame and calculate the best-fitting plane: */
			DepthFrame df;
			df.frame=Kinect::FrameBuffer(application->depthFrameSize[0],application->depthFrameSize[1],application->depthFrameSize[0]*application->depthFrameSize[1]*sizeof(float));
			float foregroundCutoff=float(application->averageNumFrames)*0.5f;
			float* afdPtr=application->averageFrameDepth;
			float* affPtr=application->averageFrameForeground;
			float* dfPtr=static_cast<float*>(df.frame.getBuffer());
			typedef Geometry::PCACalculator<3>::Point PPoint;
			typedef Geometry::PCACalculator<3>::Vector PVector;
			Geometry::PCACalculator<3> pca;
			for(unsigned int y=0;y<application->depthFrameSize[1];++y)
				for(unsigned int x=0;x<application->depthFrameSize[0];++x,++afdPtr,++affPtr,++dfPtr)
					{
					if(*affPtr>=foregroundCutoff)
						{
						/* Calculate the average depth value: */
						*dfPtr=(*afdPtr)/(*affPtr);
						
						/* Add the depth pixel to the PCA calculator: */
						pca.accumulatePoint(PPoint(double(x)+0.5,double(y)+0.5,double(*dfPtr)));
						}
					else
						*dfPtr=2047.0f;
					}
			
			/* Calculate the best-fitting plane: */
			PPoint centroid=pca.calcCentroid();
			pca.calcCovariance();
			double evs[3];
			pca.calcEigenvalues(evs);
			PVector normal=pca.calcEigenvector(evs[2]);
			df.plane=Plane(normal,centroid);
			depthFrames.push_back(df);
			}
		else
			{
			/* Calculate the per-pixel affine depth correction coefficients: */
			Kinect::FrameSource::PixelDepthCorrection* coefficients=new Kinect::FrameSource::PixelDepthCorrection[application->depthFrameSize[1]*application->depthFrameSize[0]];
			Kinect::FrameSource::PixelDepthCorrection* cPtr=coefficients;
			unsigned int pixelOffset=0;
			for(unsigned int y=0;y<application->depthFrameSize[1];++y)
				{
				for(unsigned int x=0;x<application->depthFrameSize[0];++x,++cPtr,++pixelOffset)
					{
					/* Build the least-squares linear regression system: */
					Math::Matrix ata(2,2,0.0);
					Math::Matrix atb(2,1,0.0);
					unsigned int numFrames=0;
					for(std::vector<DepthFrame>::iterator dfIt=depthFrames.begin();dfIt!=depthFrames.end();++dfIt)
						{
						double actual=double(static_cast<float*>(dfIt->frame.getBuffer())[pixelOffset]);
						if(actual!=2047.0)
							{
							ata(0,0)+=actual*actual;
							ata(0,1)+=actual;
							ata(1,0)+=actual;
							ata(1,1)+=1.0;
							double expected=(dfIt->plane.getOffset()-(double(x)+0.5)*dfIt->plane.getNormal()[0]-(double(y)+0.5)*dfIt->plane.getNormal()[1])/dfIt->plane.getNormal()[2];
							atb(0)+=actual*expected;
							atb(1)+=expected;
							++numFrames;
							}
						}
					
					if(numFrames>=2)
						{
						/* Solve for the regression coefficients: */
						Math::Matrix x=atb/ata;
						cPtr->scale=float(x(0));
						cPtr->offset=float(x(1));
						}
					else
						{
						/* Use identity correction if the pixel is underdetermined: */
						cPtr->scale=1.0f;
						cPtr->offset=0.0f;
						}
					}
				}
			
			/* Save the depth correction image: */
			std::string depthCorrectionFileName=KINECT_CONFIG_DIR;
			depthCorrectionFileName.push_back('/');
			depthCorrectionFileName.append(KINECT_CAMERA_DEPTHCORRECTIONFILENAMEPREFIX);
			depthCorrectionFileName.push_back('-');
			depthCorrectionFileName.append(application->camera->getSerialNumber());
			depthCorrectionFileName.append(".dat");
			std::cout<<"Writing depth correction file "<<depthCorrectionFileName<<std::endl;
			IO::FilePtr depthCorrectionFile(Vrui::openFile(depthCorrectionFileName.c_str(),IO::File::WriteOnly));
			depthCorrectionFile->setEndianness(Misc::LittleEndian);
			cPtr=coefficients;
			for(unsigned int y=0;y<application->depthFrameSize[1];++y)
				for(unsigned int x=0;x<application->depthFrameSize[0];++x,++cPtr)
					{
					depthCorrectionFile->write<float>(cPtr->scale);
					depthCorrectionFile->write<float>(cPtr->offset);
					}
			
			/* Clean up: */
			delete[] coefficients;
			}
		}
	}
ModelTracker::Transform ModelTracker::posit(ModelTracker::ImgPoint imagePoints[],unsigned int maxNumIterations)
	{
	/* Pre-transform the image points by the image transformation: */
	for(unsigned int ipi=0;ipi<numModelPoints;++ipi)
		imagePoints[ipi]=imgTransform.transform(imagePoints[ipi]);
	
	/* Assign initial homogeneous weights to the model points: */
	for(unsigned int mpi=0;mpi<numModelPoints;++mpi)
		mpws[mpi]=1.0;
	
	/* Iterate until convergence: */
	Vector r1,r2,t;
	for(unsigned int iteration=0;iteration<maxNumIterations;++iteration)
		{
		/*******************************************************************
		Estimate the object's pose by approximating the perspective
		projection with a scaled orthographic projection:
		*******************************************************************/
		
		/* Build the least-squares linear system: */
		Math::Matrix atb(4,2,0.0);
		for(unsigned int pi=0;pi<numModelPoints;++pi)
			{
			/* Right-hand side matrix contains image-space point positions multiplied by estimated homogeneous weights: */
			for(int i=0;i<3;++i)
				for(int j=0;j<2;++j)
					atb(i,j)+=modelPoints[pi][i]*imagePoints[pi][j]*mpws[pi];
			for(int j=0;j<2;++j)
				atb(3,j)+=imagePoints[pi][j]*mpws[pi];
			}
		
		/* Solve the least-squares linear system: */
		Math::Matrix x=invModelMat*atb;
		
		/* Calculate the scale factor and the full rotation matrix and translation vector: */
		for(int i=0;i<3;++i)
			{
			r1[i]=x(i,0);
			r2[i]=x(i,1);
			}
		double s1=r1.mag();
		double s2=r2.mag();
		
		/* Orthogonalize the orientation vectors with minimum displacement: */
		Vector r3=Geometry::normalize(r1^r2);
		Vector mid=r1/s1+r2/s2;
		mid/=mid.mag()*Math::sqrt(2.0);
		Vector mid2=r3^mid;
		r1=mid-mid2;
		r2=mid+mid2;
		
		double s=Math::sqrt(s1*s2);
		t[0]=x(3,0)/s;
		t[1]=x(3,1)/s;
		t[2]=-f/s;
		
		/* Update the homogeneous weights of the object points: */
		for(unsigned int mpi=0;mpi<numModelPoints;++mpi)
			mpws[mpi]=(r3*modelPoints[mpi])/t[2]+1.0;
		}
	
	/* Return the result transformation: */
	return Transform(t,Geometry::invert(Transform::Rotation::fromBaseVectors(r1,r2)));
	}