public static PointF[] PhineTune(Projector projector, Camera camera, PointF[] cameraCorners, PointF[] rough, int subdiv) { var color = Color.Green; projector.DrawBackground(Color.Black); var nolight = new Image<Bgr, byte>(camera.TakePicture(2)).Split()[1]; projector.DrawBackground(color); var fulllight = new Image<Bgr, byte>(camera.TakePicture(2)).Split()[1]; Func<int, bool, Image<Gray, byte>[]> takePics = (step, vertical) => { Image<Gray, byte>[] pics = new Image<Gray, byte>[3]; projector.DrawBackground(Color.Black); camera.TakePicture(5).Dispose(); projector.DrawPhaseMod(step, (float)(-2.0f * Math.PI / 3.0f), vertical, color); pics[0] = new Image<Bgr, byte>(camera.TakePicture(2)).Split()[1]; projector.DrawBackground(Color.Black); camera.TakePicture(5).Dispose(); projector.DrawPhaseMod(step, 0f, vertical, color); pics[1] = new Image<Bgr, byte>(camera.TakePicture(2)).Split()[1]; projector.DrawBackground(Color.Black); camera.TakePicture(5).Dispose(); projector.DrawPhaseMod(step, (float)(2.0f * Math.PI / 3.0f), vertical, color); pics[2] = new Image<Bgr, byte>(camera.TakePicture(2)).Split()[1]; return pics; }; Func<Image<Gray, byte>[], double[]> itop = (pics) => { return cameraCorners.Select(c => { var mini = nolight[(int)c.Y, (int)c.X].Intensity; var maxi = fulllight[(int)c.Y, (int)c.X].Intensity; var i1 = pics[0][(int)c.Y, (int)c.X].Intensity; i1 = (i1 - mini) / (maxi - mini); var i2 = pics[1][(int)c.Y, (int)c.X].Intensity; i2 = (i2 - mini) / (maxi - mini); var i3 = pics[2][(int)c.Y, (int)c.X].Intensity; i3 = (i3 - mini) / (maxi - mini); return PhaseModulation.IntensityToPhase(i1, i2, i3); }).ToArray(); }; int w = projector.Size.Width; int h = projector.Size.Height; var xphs = itop(takePics(subdiv, true)); var yphs = itop(takePics(subdiv, false)); var ids = new int[cameraCorners.Length]; int idx = 0; ids = ids.Select(i => idx++).ToArray(); return ids.Select(i => { var v = rough[i]; var denom = (double)subdiv; var xph = xphs[i] * (w / denom); var yph = yphs[i] * (h / denom); var phsx = Math.Floor(v.X / denom) * denom; var phsy = Math.Floor(v.Y / denom) * denom; double apx = v.X / w; double phx = xphs[i]; apx = Math.Floor(apx / (1.0 / denom)) * (1.0 / denom) + (phx < 1 ? phx / denom : 0); apx = Math.Round(apx, 5) * w; double apy = v.Y / h; double phy = yphs[i]; apy = Math.Floor(apy / (1.0 / denom)) * (1.0 / denom) + (phy < 1 ? phy / denom : 0); apy = Math.Round(apy, 5) * h; return new PointF((float)(apx), (float)(apy)); }).ToArray(); }
public static PointF[] PhaseCalib(Projector projector, Camera camera, PointF[] cameraCorners, int steps = 7) { var color = Color.Green; projector.DrawBackground(Color.Black); var nolight = new Image<Bgr, byte>(camera.TakePicture(2)).Split()[1]; projector.DrawBackground(color); var fulllight = new Image<Bgr, byte>(camera.TakePicture(2)).Split()[1]; Func<int, bool, Image<Gray, byte>[]> takePics = (step, vertical) => { Image<Gray, byte>[] pics = new Image<Gray, byte>[3]; projector.DrawBackground(Color.Black); camera.TakePicture(5).Dispose(); projector.DrawPhaseMod(step, (float)(-2.0f * Math.PI / 3.0f), vertical, color); pics[0] = new Image<Bgr, byte>(camera.TakePicture(2)).Split()[1]; projector.DrawBackground(Color.Black); camera.TakePicture(5).Dispose(); projector.DrawPhaseMod(step, 0f, vertical, color); pics[1] = new Image<Bgr, byte>(camera.TakePicture(2)).Split()[1]; projector.DrawBackground(Color.Black); camera.TakePicture(5).Dispose(); projector.DrawPhaseMod(step, (float)(2.0f * Math.PI / 3.0f), vertical, color); pics[2] = new Image<Bgr, byte>(camera.TakePicture(2)).Split()[1]; return pics; }; Func<Image<Gray, byte>[], double[]> itop = (pics) => { return cameraCorners.Select(c => { var mini = nolight[(int)c.Y, (int)c.X].Intensity; var maxi = fulllight[(int)c.Y, (int)c.X].Intensity; var i1 = pics[0][(int)c.Y, (int)c.X].Intensity; i1 = (i1 - mini) / (i1 - maxi); var i2 = pics[1][(int)c.Y, (int)c.X].Intensity; i2 = (i2 - mini) / (i2 - maxi); var i3 = pics[2][(int)c.Y, (int)c.X].Intensity; i3 = (i3 - mini) / (i3 - maxi); return PhaseModulation.IntensityToPhase(i1, i2, i3); }).ToArray(); }; int w = projector.Size.Width; int h = projector.Size.Height; int subdiv = 1; int[] xs = new int[cameraCorners.Length]; int[] ys = new int[cameraCorners.Length]; int[] ids = Range.OfInts(cameraCorners.Length).ToArray(); for (int i = 0; i < steps; i++) { var xpics = takePics(subdiv, true); var xphs = itop(xpics); var xh = ids.Select(id => xphs[id] > 0.5).ToArray(); var qd = QuickDraw.Start(xpics[1].Bitmap); var thrash = ids.Select(id => { qd.Color(xh[id] ? Color.White : Color.Gray); qd.DrawPoint(cameraCorners[id].X, cameraCorners[id].Y, 5); return id; }).ToArray(); qd.Finish(); DebugWindow.DrawBitmap(xpics[1].Bitmap); var ypics = takePics(subdiv, false); var yphs = itop(ypics); var yh = ids.Select(id => yphs[id] > 0.5).ToArray(); qd = QuickDraw.Start(ypics[1].Bitmap); thrash = ids.Select(id => { qd.Color(yh[id] ? Color.White : Color.Gray); qd.DrawPoint(cameraCorners[id].X, cameraCorners[id].Y, 5); return id; }).ToArray(); qd.Finish(); DebugWindow.DrawBitmap(ypics[1].Bitmap); xs = ids.Select(id => (xs[id] << 1) | (xh[id] ? 1 : 0)).ToArray(); ys = ids.Select(id => (ys[id] << 1) | (yh[id] ? 1 : 0)).ToArray(); subdiv = subdiv << 1; } var fxs = ids.Select(id => ((double)xs[id] / (double)subdiv) * w).ToArray(); var fys = ids.Select(id => ((double)ys[id] / (double)subdiv) * h).ToArray(); return fxs.Zip(fys, (x,y) => new PointF((float)x,(float)y)).ToArray(); }
public static PointF[] PhaseMod(Projector projector, Camera camera, PointF[] cameraCorners) { var color = Color.FromArgb(0, 255, 0); projector.DrawBackground(Color.Black); var nolight = new Image<Bgr, byte>(camera.TakePicture(2)).Split()[1]; projector.DrawBackground(color); var fulllight = new Image<Bgr, byte>(camera.TakePicture(2)).Split()[1]; Func<int, bool, Image<Gray, byte>[]> takePics = (step, vertical) => { Image<Gray, byte>[] pics = new Image<Gray, byte>[3]; projector.DrawBackground(Color.Black); camera.TakePicture(5).Dispose(); projector.DrawPhaseMod(step, (float)(-2.0f * Math.PI / 3.0f), vertical, color); pics[0] = new Image<Bgr, byte>(camera.TakePicture(2)).Split()[1]; projector.DrawBackground(Color.Black); camera.TakePicture(5).Dispose(); projector.DrawPhaseMod(step, 0f, vertical, color); pics[1] = new Image<Bgr, byte>(camera.TakePicture(2)).Split()[1]; projector.DrawBackground(Color.Black); camera.TakePicture(5).Dispose(); projector.DrawPhaseMod(step, (float)(2.0f * Math.PI / 3.0f), vertical, color); pics[2] = new Image<Bgr, byte>(camera.TakePicture(2)).Split()[1]; return pics; }; Func<Image<Gray, byte>[], PointF[], double[]> itop = (pics, corners) => { return corners.Select(c => { var mini = nolight[(int)c.Y, (int)c.X].Intensity; var maxi = fulllight[(int)c.Y, (int)c.X].Intensity; var i1 = pics[0][(int)c.Y, (int)c.X].Intensity; i1 = (i1 - mini) / (i1 - maxi); var i2 = pics[1][(int)c.Y, (int)c.X].Intensity; i2 = (i2 - mini) / (i2 - maxi); var i3 = pics[2][(int)c.Y, (int)c.X].Intensity; i3 = (i3 - mini) / (i3 - maxi); return PhaseModulation.IntensityToPhase(i1, i2, i3); }).ToArray(); }; var ids = new int[cameraCorners.Length]; int idx = 0; ids = ids.Select(i => idx++).ToArray(); int steps = 7; int[] stepa = new int[steps]; idx = 1; stepa = stepa.Select(i => idx++).ToArray(); int[] stepx = stepa.Select(row => (int)Math.Pow(2, row - 1)).ToArray(); double[][] verticals = new double[steps][]; for (var i = 1; i <= steps; i++) { var pics = takePics(stepx[i - 1], true); verticals[i - 1] = itop(pics, cameraCorners); } var apv = ids.Select(i => PhaseModulation.AbsolutePhase( stepa.Select(s => verticals[s - 1][i]).ToArray(), stepa)) .Select(ph => ph * projector.Size.Width) .ToArray(); double[][] horizontals = new double[steps][]; for (var i = 1; i <= steps; i++) { var pics = takePics(stepx[i - 1], false); horizontals[i - 1] = itop(pics, cameraCorners); } var aph = ids.Select(i => PhaseModulation.AbsolutePhase( stepa.Select(s => horizontals[s - 1][i]).ToArray(), stepx)) .Select(ph => ph * projector.Size.Height) .ToArray(); return ids.Select(i => new PointF((float)apv[i], (float)aph[i])).ToArray(); }
public static CalibrationData GetLocalCorners(PointF[] cameraCorners, Projector projector, Camera camera, Size pattern) { if (false) { projector.DrawBackground(Color.Black); var noi = new Image<Bgr, byte>(camera.TakePicture(5)); projector.DrawBackground(Color.FromArgb(0, 255, 0)); var fulli = new Image<Bgr, byte>(camera.TakePicture(5)); projector.DrawPhaseMod(4, 0f, true, Color.FromArgb(0, 255, 0)); var img = new Image<Bgr, byte>(camera.TakePicture(5)); var max = (fulli - noi).Split()[1]; var cur = (img - noi).Split()[1]; var map = new Bitmap(cur.Width, cur.Height); DebugWindow.DrawBitmap(max.Bitmap); DebugWindow.DrawBitmap(cur.Bitmap); using (var fast = new FastBitmap(map)) { for (int y = 0; y < cur.Height; y++) { for (int x = 0; x < cur.Width; x++) { var ii = cur[(int)y, (int)x].Intensity / max[(int)y, (int)x].Intensity; if (ii > 1) ii = 1; var i = (byte)(ii * 255); fast[x, y] = Color.FromArgb(i, i, i); } } } if (DebugWindow != null) DebugWindow.DrawBitmap(map); } //Determine rough checkerboard coordinates in projector space with graycode or binary structured light var rough = DetectRoughCorners(cameraCorners, projector, camera, Color.Green); //Improve accuracy with PhaseModulation //var phine = PhineTune(projector, camera, cameraCorners, rough, 32); //var phine2 = PhineTune(projector, camera, cameraCorners, rough, 64); //var phine3 = PhineTune(projector, camera, cameraCorners, phine2, 128); projector.DrawPoints(rough, 5); //Determine corners in projector space //var projectorCorners = DetectProjectorCorners(nolight, cameraCorners, projOutline, projector, camera); //Save corners in camera and projector space and store along side global coordinates that matches current checkerboard var data = new CalibrationData() { CameraCorners = cameraCorners, ProjectorCorners = rough }; return data; }