public GlobalScalingLocalOffsetCalibrator(Dictionary <GazePoint, CalibrationPoint> calibrationMapping) : base(calibrationMapping) { //Assume most of the projection can be explained globally by stretching the screen in both x and y directions separately. double meanGazeX = calibrationMapping.Select(kvp => kvp.Key.X).Average(); meanScalingX = calibrationMapping .Where(kvp => kvp.Key.X >= meanGazeX * .01) //Ignore any points too close to the left side of the screen for X scaling .Select(kvp => kvp.Value.X / kvp.Key.X) //Get ratio .Average(); double meanGazeY = calibrationMapping.Select(kvp => kvp.Key.Y).Average(); meanScalingY = calibrationMapping .Where(kvp => kvp.Key.Y >= meanGazeY * .01) //Ignore any points too close to the top side of the screen for Y scaling .Select(kvp => kvp.Value.Y / kvp.Key.Y) //Get ratio .Average(); //Then model the offset that needs to be applied to each of the calibration points as a local phenomenon. if (false) { double radius = 2000; //pixels xOffsetSurface = Surfaces.CreateKernelSmoothingFittedSurface2D( Surfaces.SurfaceFitting2DType.KernelSmoothing, calibrationMapping.Select(kvp => new Surfaces.CalibrationPoint3d(kvp.Key.X, kvp.Key.Y, kvp.Value.X - kvp.Key.X * meanScalingX)), radius); yOffsetSurface = Surfaces.CreateKernelSmoothingFittedSurface2D( Surfaces.SurfaceFitting2DType.KernelSmoothing, calibrationMapping.Select(kvp => new Surfaces.CalibrationPoint3d(kvp.Key.X, kvp.Key.Y, kvp.Value.Y - kvp.Key.Y * meanScalingY)), radius); } else if (false) { xOffsetSurface = Surfaces.CreateBilinearInterpolatedSurface2D( calibrationMapping.Select(kvp => new Surfaces.CalibrationPoint3d(kvp.Key.X, kvp.Key.Y, kvp.Value.X - kvp.Key.X * meanScalingX)) ); yOffsetSurface = Surfaces.CreateBilinearInterpolatedSurface2D( calibrationMapping.Select(kvp => new Surfaces.CalibrationPoint3d(kvp.Key.X, kvp.Key.Y, kvp.Value.Y - kvp.Key.Y * meanScalingY)) ); } else { xOffsetSurface = Surfaces.CreateZeroSurface2D(); yOffsetSurface = Surfaces.CreateZeroSurface2D(); } }