private async Task CalibThread(FrameReadyEventArgs e)
        {
            Debug.WriteLine("Calibrating " + _calibrationStep + " " + _drawing);
            e.Frame.Bitmap.Save(@"C:\temp\daforge\src\img" + (_calibrationStep < 10?"0":"") + _calibrationStep + "-" + (_drawing?"1":"0") + "-" + _errors + ".jpg", ImageFormat.Jpeg);
            if (_errors > 100)
            {
                //calibration not possible
                return;
            }
            if (_calibrationStep == CalibrationFrames && !_drawing)
            {
                _cc.FrameReady -= BaseCalibration; // TODO
                //Grid.Calculate();
                _vs.Close();
                CalibrationCompleted(this, new EventArgs());
            }
            else
            {
                if (_calibrationStep > 2)
                {
                    if (_drawing)
                    {
                        // draw diffimage
                        _drawing = false;
                        _vs.Clear();
                        FillRects();
                        diffFilter.OverlayImage = e.Frame.Bitmap;
                    }
                    else
                    {
                        // analyse diffimage
                        _calibrationStep++;
                        _drawing = true;
                        _vs.Clear();
                        FillRects();
                        _calibrationStep--;
                        var gbm = diffFilter.Apply(e.Frame.Bitmap);
                        gbm.Save(@"C:\temp\daforge\diff\img" + _calibrationStep + ".jpg", ImageFormat.Jpeg);
#if DEBUG
                        actImg = (Bitmap)gbm.Clone();
#endif
                        var d     = UnmanagedImage.FromManagedImage(gbm);
                        var stats = new ImageStatistics(d);
                        var gcf   = new ColorFiltering(new IntRange(0, 255),
                                                       new IntRange((int)(stats.GreenWithoutBlack.Mean + stats.GreenWithoutBlack.StdDev + 5), 255),
                                                       new IntRange(0, 255));
                        var bcf = new ColorFiltering(new IntRange(0, 255),
                                                     new IntRange(0, 255),
                                                     new IntRange((int)(stats.BlueWithoutBlack.Mean + stats.BlueWithoutBlack.StdDev), 255));
                        //Debug.WriteLine("Green: " + stats.GreenWithoutBlack.Median + " Blue: " + stats.BlueWithoutBlack.Median);
                        //Debug.WriteLine("Green: " + stats.GreenWithoutBlack.Mean + " Blue: " + stats.BlueWithoutBlack.Mean);
                        var bf = new Difference(gcf.Apply(d));
                        bcf.ApplyInPlace(d);
                        bf.ApplyInPlace(d);
                        d.ToManagedImage().Save(@"C:\temp\daforge\diff\img" + _calibrationStep + ".jpg", ImageFormat.Jpeg);
                        stats = new ImageStatistics(d);
                        gcf   = new ColorFiltering(new IntRange(0, 255),
                                                   new IntRange((int)stats.GreenWithoutBlack.Mean, 255),
                                                   new IntRange(0, 255));
                        bcf = new ColorFiltering(new IntRange(0, 255),
                                                 new IntRange(0, 255),
                                                 new IntRange((int)stats.BlueWithoutBlack.Mean, 255));
                        // split channels
                        var bbm = bcf.Apply(d);
                        bbm.ToManagedImage().Save(@"C:\temp\daforge\bimg\img" + _calibrationStep + ".jpg", ImageFormat.Bmp);
                        gcf.ApplyInPlace(d);
                        d.ToManagedImage().Save(@"C:\temp\daforge\gimg\img" + _calibrationStep + ".jpg", ImageFormat.Bmp);
                        var gblobCounter = new BlobCounter
                        {
                            ObjectsOrder         = ObjectsOrder.YX,
                            MaxHeight            = 60,
                            MinHeight            = 15,
                            MaxWidth             = 60,
                            MinWidth             = 15,
                            FilterBlobs          = true,
                            CoupledSizeFiltering = false
                        };
                        gblobCounter.ProcessImage(d);
                        var bblobCounter = new BlobCounter
                        {
                            ObjectsOrder         = ObjectsOrder.YX,
                            MaxHeight            = 60,
                            MinHeight            = 15,
                            MaxWidth             = 60,
                            MinWidth             = 15,
                            FilterBlobs          = true,
                            CoupledSizeFiltering = false
                        };
                        bblobCounter.ProcessImage(bbm);
                        ProcessBlobs(gblobCounter, 0);
                        ProcessBlobs(bblobCounter, 1);
#if DEBUG
                        actImg.Save(@"C:\temp\daforge\squares\img" + _calibrationStep + ".jpg", ImageFormat.Jpeg);
#endif
                        _calibrationStep++;
                    }
                }
                else
                {
                    switch (_calibrationStep)
                    {
                    case 2:
                        var bm = UnmanagedImage.FromManagedImage(e.Frame.Bitmap);
                        //diffFilter.OverlayImage.Save(@"C:\temp\daforge\diff\src" + _errors + ".jpg", ImageFormat.Jpeg);
                        bm = diffFilter.Apply(bm);
                        var gf = new GaussianBlur(9.0, 3);
                        gf.ApplyInPlace(bm);
                        var cf = new ColorFiltering(new IntRange(10, 255), new IntRange(20, 255),
                                                    new IntRange(20, 255));
                        cf.ApplyInPlace(bm);
                        var blobCounter = new BlobCounter {
                            ObjectsOrder = ObjectsOrder.Size, BackgroundThreshold = Color.FromArgb(255, 15, 20, 20), FilterBlobs = true
                        };
                        blobCounter.ProcessImage(bm);
                        bm.ToManagedImage().Save(@"C:\temp\daforge\diff\img" + _calibrationStep + "-" + _errors + ".jpg", ImageFormat.Jpeg);
                        var blobs = blobCounter.GetObjectsInformation();
                        if (blobs.Any())
                        {
                            int             i = 0;
                            List <IntPoint> corners;
                            do
                            {
                                corners =
                                    PointsCloud.FindQuadrilateralCorners(blobCounter.GetBlobsEdgePoints(blobs[i++]));
                            } while (corners.Count != 4);
                            RecursiveAForgeCalibrator.GridBlobs.InPlaceSort(corners);
                            Grid.TopLeft     = new Point(corners[0].X, corners[0].Y);
                            Grid.TopRight    = new Point(corners[1].X, corners[1].Y);
                            Grid.BottomLeft  = new Point(corners[2].X, corners[2].Y);
                            Grid.BottomRight = new Point(corners[3].X, corners[3].Y);
                            if (Grid.TopLeft.X > 10 && Grid.TopRight.X < _vs.Width - 5 && Grid.BottomLeft.X > 5 &&
                                Grid.BottomRight.X < _vs.Width - 5 && Grid.TopLeft.Y > 10 && Grid.TopRight.Y > 5 &&
                                Grid.BottomLeft.Y < _vs.Height - 5 && Grid.BottomRight.Y < _vs.Height - 5 &&
                                Grid.TopLeft.X < Grid.BottomRight.X && blobs[i - 1].Area > 60000 &&
                                Grid.BottomLeft.X < Grid.TopRight.X && Grid.BottomLeft.X < Grid.BottomRight.X &&
                                Grid.TopLeft.Y < Grid.BottomLeft.Y && Grid.TopLeft.Y < Grid.BottomRight.Y &&
                                Grid.TopRight.Y < Grid.BottomLeft.Y && Grid.TopRight.Y < Grid.BottomRight.Y)
                            {
                                _calibrationStep++;
                                _drawing = true;
                                _vs.Clear();
                                FillRects();
                                Grid.AddPoint(new Point(), new Point(corners[0].X, corners[0].Y));
                                Grid.AddPoint(new Point(0, _vs.Height), new Point(corners[1].X, corners[1].Y));
                                Grid.AddPoint(new Point(_vs.Width, 0), new Point(corners[2].X, corners[2].Y));
                                Grid.AddPoint(new Point(_vs.Width, _vs.Height),
                                              new Point(corners[3].X, corners[3].Y));
                                //_mapper.PredictFromCorners();
                            }
                            else
                            {
                                _calibrationStep = 0;
                                _errors++;
                            }
                        }
                        else
                        {
                            _calibrationStep = 0;
                            _errors++;
                            _vs.Draw();
                        }
                        break;

                    case 1:
                        diffFilter.OverlayImage = e.Frame.Bitmap;
                        //diffFilter.OverlayImage.Save(@"C:\temp\daforge\diff\srcf" + _errors + ".jpg", ImageFormat.Jpeg);
                        //_vs.AddRect(0, 0, (int) _vs.Width, (int) _vs.Height, Color.FromArgb(255, 255, 255, 255));
                        _vs.Clear();
                        for (int y = 0; y < Columncount; y++)
                        {
                            for (int x = 0; x < Rowcount; x++)
                            {
                                if (!(y % 2 == 0 && x % 2 == 0 || y % 2 == 1 && x % 2 == 1))
                                {
                                    _vs.AddRect((int)(x * _sqrwidth), (int)(y * _sqrheight),
                                                (int)_sqrwidth, (int)_sqrheight,
                                                Color.FromArgb(255, 255, 255, 255));
                                }
                            }
                        }
                        _vs.Draw();
                        _calibrationStep++;
                        break;

                    case 0:
                        Grid            = new Grid(e.Frame.Bitmap.Width, e.Frame.Bitmap.Height);
                        Grid.ScreenSize = new Rectangle(0, 0, _vs.Width, _vs.Height);
                        _mapper         = new CornerBarycentricMapper(Grid);
                        var thread = new Thread(() =>
                        {
                            _vs.Show();
                            _vs.AddRect(0, 0, _vs.Width, _vs.Height, Color.FromArgb(255, 0, 0, 0));
                        });
                        thread.SetApartmentState(ApartmentState.STA);
                        thread.Start();
                        thread.Join();
                        for (int y = 0; y < Columncount; y++)
                        {
                            for (int x = 0; x < Rowcount; x++)
                            {
                                if (y % 2 == 0 && x % 2 == 0 || y % 2 == 1 && x % 2 == 1)
                                {
                                    _vs.AddRect((int)(x * _sqrwidth), (int)(y * _sqrheight),
                                                (int)_sqrwidth, (int)_sqrheight,
                                                Color.FromArgb(255, 255, 255, 255));
                                }
                            }
                        }
                        _vs.Draw();
                        _calibrationStep++;
                        break;
                    }
                }
            }
            Debug.WriteLine("Releasing");
            await Task.Delay(500);

            _sem.Release();
        }
        private async Task CalibThread(FrameReadyEventArgs e)
        {
            //Debug.WriteLine("Calibrating " + _calibrationStep + " " + _drawing);
            //e.Frame.Bitmap.Save(@"C:\temp\daforge\src\img" + (_calibrationStep<10?"0":"") + _calibrationStep + "-" + (_drawing?"1":"0") + "-" + _errors + ".jpg", ImageFormat.Jpeg);
            if (_errors > 100)
            {
                //calibration not possible
                return;
            }
            if (_calibrationStep == CalibrationFrames)
            {
                _cc.FrameReady -= BaseCalibration; // TODO
                //Grid.Calculate();
                //Grid.PredictFromCorners();
                _vs.Close();
                Console.WriteLine("Calibration complete");
                CalibrationCompleted(this, new EventArgs());
            }
            else
            {
                switch (_calibrationStep)
                {
                // get the corners from the difference image
                case 2:
                    var bm = UnmanagedImage.FromManagedImage(e.Frame.Bitmap);
                    //diffFilter.OverlayImage.Save(@"C:\temp\daforge\diff\src" + _errors + ".jpg", ImageFormat.Jpeg);
                    bm = diffFilter.Apply(bm);
                    var gf = new GaussianBlur(9.0, 3);
                    gf.ApplyInPlace(bm);
                    var cf = new ColorFiltering(new IntRange(10, 255), new IntRange(20, 255),
                                                new IntRange(20, 255));
                    cf.ApplyInPlace(bm);
                    var blobCounter = new BlobCounter
                    {
                        ObjectsOrder        = ObjectsOrder.Size,
                        BackgroundThreshold = Color.FromArgb(255, 15, 20, 20),
                        FilterBlobs         = true
                    };
                    blobCounter.ProcessImage(bm);
                    //bm.ToManagedImage().Save(@"C:\temp\daforge\diff\img" + _calibrationStep + "-" + _errors + ".jpg", ImageFormat.Jpeg);
                    var blobs = blobCounter.GetObjectsInformation();
                    if (blobs.Any())
                    {
                        int             i = 0;
                        List <IntPoint> corners;
                        do
                        {
                            corners =
                                PointsCloud.FindQuadrilateralCorners(blobCounter.GetBlobsEdgePoints(blobs[i++]));
                        } while (corners.Count != 4);
                        RecursiveAForgeCalibrator.GridBlobs.InPlaceSort(corners);
                        Grid.TopLeft     = new Point(corners[0].X, corners[0].Y);
                        Grid.TopRight    = new Point(corners[1].X, corners[1].Y);
                        Grid.BottomLeft  = new Point(corners[2].X, corners[2].Y);
                        Grid.BottomRight = new Point(corners[3].X, corners[3].Y);
                        if (Grid.TopLeft.X > 10 && Grid.TopRight.X < _vs.Width - 5 && Grid.BottomLeft.X > 5 &&
                            Grid.BottomRight.X < _vs.Width - 5 && Grid.TopLeft.Y > 10 && Grid.TopRight.Y > 5 &&
                            Grid.BottomLeft.Y < _vs.Height - 5 && Grid.BottomRight.Y < _vs.Height - 5 &&
                            Grid.TopLeft.X < Grid.BottomRight.X &&     //blobs[i - 1].Area > 60000 &&
                            Grid.BottomLeft.X < Grid.TopRight.X && Grid.BottomLeft.X < Grid.BottomRight.X &&
                            Grid.TopLeft.Y < Grid.BottomLeft.Y && Grid.TopLeft.Y < Grid.BottomRight.Y &&
                            Grid.TopRight.Y < Grid.BottomLeft.Y && Grid.TopRight.Y < Grid.BottomRight.Y)
                        {
                            _calibrationStep++;
                            _vs.Clear();
                            Grid.AddPoint(new Point(), new Point(corners[0].X, corners[0].Y));
                            Grid.AddPoint(new Point(0, _vs.Height), new Point(corners[1].X, corners[1].Y));
                            Grid.AddPoint(new Point(_vs.Width, 0), new Point(corners[2].X, corners[2].Y));
                            Grid.AddPoint(new Point(_vs.Width, _vs.Height),
                                          new Point(corners[3].X, corners[3].Y));
                        }
                        else
                        {
                            _calibrationStep = 0;
                            _errors++;
                        }
                    }
                    else
                    {
                        _calibrationStep = 0;
                        _errors++;
                        _vs.Draw();
                    }
                    break;

                case 1:     // draw second image, store the first
                    diffFilter.OverlayImage = e.Frame.Bitmap;
                    //diffFilter.OverlayImage.Save(@"C:\temp\daforge\diff\srcf" + _errors + ".jpg", ImageFormat.Jpeg);
                    //_vs.AddRect(0, 0, (int) _vs.Width, (int) _vs.Height, Color.FromArgb(255, 255, 255, 255));
                    _vs.Clear();
                    for (int y = 0; y < Columncount; y++)
                    {
                        for (int x = 0; x < Rowcount; x++)
                        {
                            if (!(y % 2 == 0 && x % 2 == 0 || y % 2 == 1 && x % 2 == 1))
                            {
                                _vs.AddRect((int)(x * _sqrwidth), (int)(y * _sqrheight),
                                            (int)_sqrwidth, (int)_sqrheight,
                                            Color.FromArgb(255, 255, 255, 255));
                            }
                        }
                    }
                    _vs.Draw();
                    _calibrationStep++;
                    break;

                //draw the first image
                case 0:
                    Grid            = new Grid(e.Frame.Bitmap.Width, e.Frame.Bitmap.Height);
                    Grid.ScreenSize = new Rectangle(0, 0, _vs.Width, _vs.Height);
                    var thread = new Thread(() =>
                    {
                        _vs.Show();
                        _vs.AddRect(0, 0, _vs.Width, _vs.Height, Color.FromArgb(255, 0, 0, 0));
                    });
                    thread.SetApartmentState(ApartmentState.STA);
                    thread.Start();
                    thread.Join();
                    for (int y = 0; y < Columncount; y++)
                    {
                        for (int x = 0; x < Rowcount; x++)
                        {
                            if (y % 2 == 0 && x % 2 == 0 || y % 2 == 1 && x % 2 == 1)
                            {
                                _vs.AddRect((int)(x * _sqrwidth), (int)(y * _sqrheight),
                                            (int)_sqrwidth, (int)_sqrheight,
                                            Color.FromArgb(255, 255, 255, 255));
                            }
                        }
                    }
                    _vs.Draw();
                    _calibrationStep++;
                    break;
                }
            }
            Debug.WriteLine("Releasing");
            await Task.Delay(500);

            _sem.Release();
        }
        private async Task CalibThread(FrameReadyEventArgs e)
        {
            try
            {
                if (_errors > MaxErrorCount)
                {
                    _cc.FrameReady -= BaseCalibration;
                    _vs.Close();
                    Console.WriteLine("Calibration impossible");
                }
                if (_calibrationStep == CalibrationFrames)
                {
                    _cc.FrameReady -= BaseCalibration;
                    _vs.Close();
                    Console.WriteLine("Calibration complete");
                    CalibrationCompleted(this, new EventArgs());
                }
                else
                {
                    switch (_calibrationStep)
                    {
                    // get the corners from the difference image
                    case 2:
                        var bm = UnmanagedImage.FromManagedImage(e.Frame.Bitmap);
                        bm = _diffFilter.Apply(bm);
                        var gf = new GaussianBlur(9.0, 3);
                        gf.ApplyInPlace(bm);
                        var cf = new ColorFiltering(new IntRange(10, 255), new IntRange(20, 255),
                                                    new IntRange(20, 255));
                        cf.ApplyInPlace(bm);
                        var blobCounter = new BlobCounter
                        {
                            ObjectsOrder        = ObjectsOrder.Size,
                            BackgroundThreshold = Color.FromArgb(255, 15, 20, 20),
                            FilterBlobs         = true
                        };
                        blobCounter.ProcessImage(bm);
                        var blobs = blobCounter.GetObjectsInformation();
                        if (blobs.Any())
                        {
                            int             i = 0;
                            List <IntPoint> corners;
                            do
                            {
                                corners =
                                    PointsCloud.FindQuadrilateralCorners(blobCounter.GetBlobsEdgePoints(blobs[i++]));
                            } while (corners.Count != 4);
                            InPlaceSort(corners);
                            Grid.TopLeft     = new Point(corners[0].X, corners[0].Y);
                            Grid.TopRight    = new Point(corners[1].X, corners[1].Y);
                            Grid.BottomLeft  = new Point(corners[2].X, corners[2].Y);
                            Grid.BottomRight = new Point(corners[3].X, corners[3].Y);
                            if (Grid.TopLeft.X > 10 && Grid.TopRight.X < _vs.Width - 5 && Grid.BottomLeft.X > 5 &&
                                Grid.BottomRight.X < _vs.Width - 5 && Grid.TopLeft.Y > 10 && Grid.TopRight.Y > 5 &&
                                Grid.BottomLeft.Y < _vs.Height - 5 && Grid.BottomRight.Y < _vs.Height - 5 &&
                                Grid.TopLeft.X < Grid.BottomRight.X &&     //blobs[i - 1].Area > 60000 &&
                                Grid.BottomLeft.X < Grid.TopRight.X && Grid.BottomLeft.X < Grid.BottomRight.X &&
                                Grid.TopLeft.Y < Grid.BottomLeft.Y && Grid.TopLeft.Y < Grid.BottomRight.Y &&
                                Grid.TopRight.Y < Grid.BottomLeft.Y && Grid.TopRight.Y < Grid.BottomRight.Y)
                            {
                                _calibrationStep++;
                                _vs.Clear();
                                Grid.AddPoint(new Point(), new Point(corners[0].X, corners[0].Y));
                                Grid.AddPoint(new Point(0, _vs.Height), new Point(corners[1].X, corners[1].Y));
                                Grid.AddPoint(new Point(_vs.Width, 0), new Point(corners[2].X, corners[2].Y));
                                Grid.AddPoint(new Point(_vs.Width, _vs.Height),
                                              new Point(corners[3].X, corners[3].Y));
                            }
                            else
                            {
                                _calibrationStep = 0;
                                _errors++;
                            }
                        }
                        else
                        {
                            _calibrationStep = 0;
                            _errors++;
                            _vs.Draw();
                        }
                        break;

                    case 1:     // draw second image, store the first
                        _diffFilter.OverlayImage = e.Frame.Bitmap;
                        _vs.Clear();
                        for (int y = 0; y < Columncount; y++)
                        {
                            for (int x = 0; x < Rowcount; x++)
                            {
                                if (!(y % 2 == 0 && x % 2 == 0 || y % 2 == 1 && x % 2 == 1))
                                {
                                    _vs.AddRect((int)(x * _sqrwidth), (int)(y * _sqrheight),
                                                (int)_sqrwidth, (int)_sqrheight,
                                                Color.FromArgb(255, 255, 255, 255));
                                }
                            }
                        }
                        _vs.Draw();
                        _calibrationStep++;
                        break;

                    //draw the first image
                    case 0:
                        Grid = new Grid(e.Frame.Bitmap.Width, e.Frame.Bitmap.Height)
                        {
                            ScreenSize = new Rectangle(0, 0, _vs.Width, _vs.Height)
                        };
                        var thread = new Thread(() =>
                        {
                            _vs.Show();
                            _vs.AddRect(0, 0, _vs.Width, _vs.Height, Color.FromArgb(255, 0, 0, 0));
                        });
                        thread.SetApartmentState(ApartmentState.STA);
                        thread.Start();
                        thread.Join();
                        for (int y = 0; y < Columncount; y++)
                        {
                            for (int x = 0; x < Rowcount; x++)
                            {
                                if (y % 2 == 0 && x % 2 == 0 || y % 2 == 1 && x % 2 == 1)
                                {
                                    _vs.AddRect((int)(x * _sqrwidth), (int)(y * _sqrheight),
                                                (int)_sqrwidth, (int)_sqrheight,
                                                Color.FromArgb(255, 255, 255, 255));
                                }
                            }
                        }
                        _vs.Draw();
                        _calibrationStep++;
                        break;
                    }
                }
                await Task.Delay(MillisecondsDelay);
            }
            finally
            {
                _sem.Release();
            }
        }
        private async Task CalibThread(FrameReadyEventArgs e)
        {
            Debug.WriteLine("Calibrating " + _calibrationStep);
            e.Frame.Bitmap.Save(@"C:\temp\aforge\src\img" + _calibrationStep + ".jpg");
            //var stats = new ImageStatistics(e.Frame.Bitmap);
            //        //var histogram = stats.Gray;
            ////Debug.WriteLine("Grey: Min: " + histogram.Min + " Max: " + histogram.Max + " Mean: " + histogram.Mean +
            ////    " Dev: " + histogram.StdDev + " Median: " + histogram.Median);
            //var histogram = stats.Green;
            //Debug.WriteLine("Green: Min: " + histogram.Min + " Max: " + histogram.Max + " Mean: " + histogram.Mean +
            //    " Dev: " + histogram.StdDev + " Median: " + histogram.Median);
            //histogram = stats.Blue;
            //Debug.WriteLine("Blue: Min: " + histogram.Min + " Max: " + histogram.Max + " Mean: " + histogram.Mean +
            //    " Dev: " + histogram.StdDev + " Median: " + histogram.Median);
            //histogram = stats.Red;
            //Debug.WriteLine("Red: Min: " + histogram.Min + " Max: " + histogram.Max + " Mean: " + histogram.Mean +
            //e.Frame.Bitmap.Save(@"C:\temp\aforge\src\img" + _calibrationStep + ".jpg");
            //    " Dev: " + histogram.StdDev + " Median: " + histogram.Median);
            //e.NewImage.Save(@"C:\temp\aforge\src\img" + _calibrationStep + ".jpg");
            if (_errors > 100)
            {
                //calibration not possible
                return;
            }
            if (_calibrationStep == 3)
            {
                _cc.FrameReady -= BaseCalibration; // TODO
                //Grid.Calculate();
                _vs.Close();
                CalibrationCompleted(this, new EventArgs());
            }
            else
            {
                //var diffBitmap = new Bitmap(1,1);
                //if(diffFilter.OverlayImage != null)
                //    diffBitmap = diffFilter.Apply(e.NewImage);
                //diffBitmap.Save(@"C:\temp\aforge\diff\img" + _calibrationStep + ".jpg");
                if (_calibrationStep > 2)
                {
                    _vs.Clear();
                    FillRects();
                    //var gf = new ColorFiltering(new IntRange(0, 255), //(int) stats.Red.Mean),
                    //                            new IntRange((int) (stats.Green.Mean + ColorDiff), 255),
                    //                            new IntRange(0, 255));//(int) stats.Blue.Mean));
                    //var bf = new ColorFiltering(new IntRange(0, 255),//(int)stats.Red.Mean),
                    //    new IntRange(0, 255),//(int) stats.Green.Mean),
                    //    new IntRange((int) stats.Blue.Mean + ColorDiff, 255));
                    // create color Channel images
                    var gbm = PartiallyApplyAvgFilter(e.Frame.Bitmap, Channels.Green, 8, 8, 8);
                    gbm.Save(@"C:\temp\aforge\gimg\img" + _calibrationStep + ".jpg");
                    var bbm = PartiallyApplyAvgFilter(e.Frame.Bitmap, Channels.Blue, 8, 8, 8);
                    bbm.Save(@"C:\temp\aforge\bimg\img" + _calibrationStep + ".jpg");
                    var gblobCounter = new BlobCounter {
                        ObjectsOrder = ObjectsOrder.YX,
                        MaxHeight    = 30, MinHeight = 15, MaxWidth = 30, MinWidth = 15, FilterBlobs = true, CoupledSizeFiltering = false
                    };
                    gblobCounter.ProcessImage(gbm);
                    var bblobCounter = new BlobCounter {
                        ObjectsOrder = ObjectsOrder.YX,
                        MaxHeight    = 30, MinHeight = 15, MaxWidth = 30, MinWidth = 15, FilterBlobs = true, CoupledSizeFiltering = false
                    };
                    bblobCounter.ProcessImage(bbm);
                    ProcessBlobs(gblobCounter, 0);
                    ProcessBlobs(bblobCounter, 1);
                    _calibrationStep++;
                }
                else
                {
                    switch (_calibrationStep)
                    {
                    case 2:     // identify screen bounds
                        //var thresholdFilter = new Threshold(50);
                        //thresholdFilter.ApplyInPlace(diffBitmap);
                        //var cf = new ColorFiltering(new IntRange(0, 255), //red is bad
                        //                            new IntRange((int) stats.Green.Mean + MeanDiff, 255),
                        //                            new IntRange((int) stats.Blue.Mean + MeanDiff, 255));
                        //var bm = cf.Apply(e.NewImage);
                        //var bm = PartiallyApplyAvgFilter(e.Frame.Bitmap, Channels.GreenAndBlue, 2, 2, MeanDiff);
                        var bm = UnmanagedImage.FromManagedImage(e.Frame.Bitmap);
                        bm = diffFilter.Apply(bm);
                        var gf = new GaussianBlur(9.0, 3);
                        gf.ApplyInPlace(bm);
                        var cf = new ColorFiltering(new IntRange(10, 255), new IntRange(20, 255),
                                                    new IntRange(20, 255));
                        cf.ApplyInPlace(bm);
                        var blobCounter = new BlobCounter {
                            ObjectsOrder = ObjectsOrder.Size, BackgroundThreshold = Color.FromArgb(255, 15, 20, 20), FilterBlobs = true
                        };
                        blobCounter.ProcessImage(bm);
                        bm.ToManagedImage().Save(@"C:\temp\aforge\diff.jpg");
                        var             blobs = blobCounter.GetObjectsInformation();
                        int             i     = 0;
                        List <IntPoint> corners;
                        do
                        {
                            corners =
                                PointsCloud.FindQuadrilateralCorners(blobCounter.GetBlobsEdgePoints(blobs[i++]));
                        } while (corners.Count != 4);
                        InPlaceSort(corners);
                        Grid.TopLeft     = new Point(corners[0].X, corners[0].Y);
                        Grid.TopRight    = new Point(corners[1].X, corners[1].Y);
                        Grid.BottomLeft  = new Point(corners[2].X, corners[2].Y);
                        Grid.BottomRight = new Point(corners[3].X, corners[3].Y);
                        if (Grid.TopLeft.X > 10 && Grid.TopRight.X < _vs.Width - 10 && Grid.BottomLeft.X > 10 &&
                            Grid.BottomRight.X < _vs.Width - 10 && Grid.TopLeft.Y > 10 && Grid.TopRight.Y > 10 &&
                            Grid.BottomLeft.Y < _vs.Height - 10 && Grid.BottomRight.Y < _vs.Height - 10 &&
                            Grid.TopLeft.X < Grid.BottomRight.X && blobs[i - 1].Area > 60000 &&
                            Grid.BottomLeft.X < Grid.TopRight.X && Grid.BottomLeft.X < Grid.BottomRight.X &&
                            Grid.TopLeft.Y < Grid.BottomLeft.Y && Grid.TopLeft.Y < Grid.BottomRight.Y &&
                            Grid.TopRight.Y < Grid.BottomLeft.Y && Grid.TopRight.Y < Grid.BottomRight.Y)
                        {
                            _calibrationStep++;
                            _vs.Clear();
                            FillRects();
                            Grid.AddPoint(new Point(), new Point(corners[0].X, corners[0].Y));
                            Grid.AddPoint(new Point(0, _vs.Height), new Point(corners[1].X, corners[1].Y));
                            Grid.AddPoint(new Point(_vs.Width, 0), new Point(corners[2].X, corners[2].Y));
                            Grid.AddPoint(new Point(_vs.Width, _vs.Height), new Point(corners[3].X, corners[3].Y));
                        }
                        else
                        {
                            _calibrationStep = 0;
                            _errors++;
                        }
                        break;

                    case 1:
                        diffFilter.OverlayImage = e.Frame.Bitmap;
                        //_vs.AddRect(0, 0, (int) _vs.Width, (int) _vs.Height, Color.FromArgb(255, 255, 255, 255));
                        _vs.Clear();
                        for (int y = 0; y < Columncount; y++)
                        {
                            for (int x = 0; x < Rowcount; x++)
                            {
                                if (!(y % 2 == 0 && x % 2 == 0 || y % 2 == 1 && x % 2 == 1))
                                {
                                    _vs.AddRect((int)(x * _sqrwidth), (int)(y * _sqrheight),
                                                (int)_sqrwidth, (int)_sqrheight,
                                                Color.FromArgb(255, 255, 255, 255));
                                }
                            }
                        }
                        _vs.Draw();
                        _calibrationStep++;
                        break;

                    case 0:
                        Grid = new Grid(e.Frame.Bitmap.Width, e.Frame.Bitmap.Height);
                        var thread = new Thread(() =>
                        {
                            _vs.Show();
                            _vs.AddRect(0, 0, (int)_vs.Width, (int)_vs.Height, Color.FromArgb(255, 0, 0, 0));
                        });
                        thread.SetApartmentState(ApartmentState.STA);
                        thread.Start();
                        thread.Join();
                        for (int y = 0; y < Columncount; y++)
                        {
                            for (int x = 0; x < Rowcount; x++)
                            {
                                if (y % 2 == 0 && x % 2 == 0 || y % 2 == 1 && x % 2 == 1)
                                {
                                    _vs.AddRect((int)(x * _sqrwidth), (int)(y * _sqrheight),
                                                (int)_sqrwidth, (int)_sqrheight,
                                                Color.FromArgb(255, 255, 255, 255));
                                }
                            }
                        }
                        _vs.Draw();
                        _calibrationStep++;
                        break;
                    }
                }
            }
            Debug.WriteLine("Releasing");
            await Task.Delay(100);

            _sem.Release();
        }
        private async Task CalibThread(NewImageEventArgs e)
        {
#if DEBUG
            e.NewImage.Save(@"C:\temp\srcimg\img" + _calibrationStep + ".jpg");
#endif
            //lock (_lockObj)
            {
                Debug.WriteLine("Calibrating");
                if (_errors > 100)
                {
                    //calibration not possible
                    return;
                }
                if (_calibrationStep == CalibrationFrames || !Grid.DataNeeded)
                {
                    //_cc.NewImage -= BaseCalibration; // TODO
                    _vs.Close();
                    CalibrationCompleted(this, new EventArgs());
                }
                //else if (_calibrationStep > CalibrationFrames/2)
                //{

                //}
                else if (_calibrationStep > 2)
                {
                    var tcb = ThreeChannelBitmap.FromBitmap(e.NewImage);
                    _vs.Clear();
                    FillRandomRects();
                    for (int j = 0; j < 3; j++)
                    {
                        var cChan = new OneChannelBitmap();
                        var diff  = new BinaryBitmap();
                        Debug.WriteLine(j);
                        switch (j)
                        {
                        case 0:
                            cChan = _blackImage.RChannelBitmap - tcb.RChannelBitmap;
                            diff  = cChan.GetBinary(RedDiff);
#if DEBUG
                            var s = new ThreeChannelBitmap(cChan, new OneChannelBitmap(diff.Width, diff.Height),
                                                           new OneChannelBitmap(diff.Width, diff.Height));
                            s.GetVisual().Save(@"C:\temp\rimg\img" + _calibrationStep + ".jpg");
#endif
                            break;

                        case 1:
                            cChan = _blackImage.GChannelBitmap - tcb.GChannelBitmap;
                            diff  = cChan.GetBinary(GreenDiff);
#if DEBUG
                            s = new ThreeChannelBitmap(new OneChannelBitmap(diff.Width, diff.Height), cChan,
                                                       new OneChannelBitmap(diff.Width, diff.Height));
                            s.GetVisual().Save(@"C:\temp\gimg\img" + _calibrationStep + ".jpg");
#endif
                            break;

                        case 2:
                            cChan = _blackImage.BChannelBitmap - tcb.BChannelBitmap;
                            diff  = cChan.GetBinary(BlueDiff);
#if DEBUG
                            s = new ThreeChannelBitmap(new OneChannelBitmap(diff.Width, diff.Height),
                                                       new OneChannelBitmap(diff.Width, diff.Height), cChan);
                            s.GetVisual().Save(@"C:\temp\bimg\img" + _calibrationStep + ".jpg");
#endif
                            break;
                        }
                        var topLeftCorner     = GetTopLeftCorner(diff);
                        var topRightCorner    = GetTopRightCorner(diff);
                        var bottomLeftCorner  = GetBottomLeftCorner(diff);
                        var bottomRightCorner = GetBottomRightCorner(diff);
                        if (topLeftCorner.X < topRightCorner.X && topLeftCorner.X > bottomLeftCorner.X &&
                            topRightCorner.Y < bottomRightCorner.Y && topRightCorner.Y < bottomLeftCorner.Y &&
                            topLeftCorner.Y < bottomRightCorner.Y && bottomLeftCorner.X < bottomRightCorner.X &&
                            topLeftCorner.X < bottomRightCorner.X && bottomLeftCorner.X < topRightCorner.X &&
                            IsValid(topLeftCorner) && IsValid(topRightCorner) && IsValid(bottomLeftCorner) &&
                            IsValid(bottomRightCorner) && true)
                        {
                            //Grid.AddPoint(_rects[j].Left, topLeftCorner);
                            //Grid.AddPoint(_rects[j].TopRight, topRightCorner);
                            //Grid.AddPoint(_rects[j].BottomLeft, bottomLeftCorner);
                            //Grid.AddPoint(_rects[j].BottomRight, bottomRightCorner);
                        }
                        else
                        {
                            _errors++;
                        }
                    }
                    _calibrationStep++;
                }
                switch (_calibrationStep)
                {
                case 2:
                    var diff = (_blackImage - ThreeChannelBitmap.FromBitmap(e.NewImage)).GetGrayscale();
                    var b    = diff.GetBinary(GreyDiff);
                    var s    = new ThreeChannelBitmap(diff,
                                                      diff, diff);
                    s.GetVisual().Save(@"C:\temp\diffs\img" + _errors + ".jpg");
                    Grid.TopLeft     = GetTopLeftCorner(b);
                    Grid.TopRight    = GetTopRightCorner(b);
                    Grid.BottomLeft  = GetBottomLeftCorner(b);
                    Grid.BottomRight = GetBottomRightCorner(b);
                    if (Grid.TopLeft.X < Grid.TopRight.X && Grid.TopLeft.X < Grid.BottomRight.X &&
                        Grid.BottomLeft.X < Grid.TopRight.X && Grid.BottomLeft.X < Grid.BottomRight.X &&
                        Grid.TopLeft.Y < Grid.BottomLeft.Y && Grid.TopLeft.Y < Grid.BottomRight.Y &&
                        Grid.TopRight.Y < Grid.BottomLeft.Y && Grid.TopRight.Y < Grid.BottomRight.Y &&
                        IsValid(Grid.TopLeft) && IsValid(Grid.TopRight) && IsValid(Grid.BottomLeft) &&
                        IsValid(Grid.BottomRight))
                    {
                        _calibrationStep++;
                        _vs.Clear();
                        FillRandomRects();
                        _sem = new SemaphoreSlim(3, 4);
                    }
                    else
                    {
                        _calibrationStep = 0;
                        _errors++;
                    }
                    break;

                case 1:
                    _blackImage = ThreeChannelBitmap.FromBitmap(e.NewImage);
                    _vs.AddRect(0, 0, _vs.Width, _vs.Height, DColor.FromArgb(255, 255, 255, 255));
                    _calibrationStep++;
                    break;

                case 0:
                    Grid = new Grid(e.NewImage.Width, e.NewImage.Height);
                    var thread = new Thread(() =>
                    {
                        _vs.Show();
                        //_vs.Draw();
                    });
                    thread.SetApartmentState(ApartmentState.STA);
                    thread.Start();
                    thread.Join();
                    _vs.AddRect(0, 0, _vs.Width, _vs.Height, DColor.FromArgb(255, 0, 0, 0));
                    _calibrationStep++;
                    break;
                }
            }
            await Task.Delay(500);

            Debug.WriteLine("releasing");
            _sem.Release();
        }
        private async Task CalibThread(FrameReadyEventArgs e)
        {
            Debug.WriteLine("Calibrating " + _calibrationStep + " ");
            e.Frame.Bitmap.Save(@"C:\temp\daforge\src\img" + (_calibrationStep < 10?"0":"") + _calibrationStep + "-" + _errors + ".jpg", ImageFormat.Jpeg);
            if (_errors > 100)
            {
                //calibration not possible
                return;
            }
            if (_calibrationStep > CalibrationFrames)
            {
                // A concurrency problem, do nothing
            }
            if (_calibrationStep == CalibrationFrames)
            {
                _cc.FrameReady -= BaseCalibration;
                _calibrationStep++;
                //Grid.Calculate();
                _vs.Close();
                CalibrationCompleted(this, new EventArgs());
            }
            else
            {
                if (_calibrationStep > 2)
                {
                    // analyse diffimage
                    _vs.Clear();
                    var img = diffFilter.Apply(e.Frame.Bitmap);
                    var mf  = new GaussianBlur(0.6, 5);
                    var gf  = Grayscale.CommonAlgorithms.BT709;
#if DEBUG
                    actImg = (Bitmap)img.Clone();
#endif
                    img = gf.Apply(img);
                    var stats = new ImageStatistics(img);
                    mf.ApplyInPlace(img);
                    mf = new GaussianBlur(0.7, 7);
                    mf.ApplyInPlace(img);
                    var tf = new Threshold((int)(stats.GrayWithoutBlack.Mean + stats.GrayWithoutBlack.StdDev * 2.0));
                    tf.ApplyInPlace(img);
                    img.Save(@"C:\temp\daforge\diff\img" + _calibrationStep + "-" + _errors + ".jpg", ImageFormat.MemoryBmp);
                    var blobCounter = new BlobCounter
                    {
                        ObjectsOrder         = ObjectsOrder.YX,
                        MaxHeight            = 30,
                        MinHeight            = 10,
                        MaxWidth             = 30,
                        MinWidth             = 10,
                        FilterBlobs          = true,
                        CoupledSizeFiltering = false
                    };
                    blobCounter.ProcessImage(img);
                    if (ProcessBlobs(blobCounter))
                    {
                        _calibrationStep++;
                    }
                    else
                    {
                        _errors++;
                    }
#if DEBUG
                    actImg.Save(@"C:\temp\daforge\squares\img" + _calibrationStep + ".jpg", ImageFormat.Jpeg);
#endif
                    DrawMarkers();
                }
                else
                {
                    switch (_calibrationStep)
                    {
                    case 2:
                        var bm = UnmanagedImage.FromManagedImage(e.Frame.Bitmap);
                        //diffFilter.OverlayImage.Save(@"C:\temp\daforge\diff\src" + _errors + ".jpg", ImageFormat.Jpeg);
                        bm = diffFilter.Apply(bm);
                        var gf = new GaussianBlur(0.8, 5);
                        gf.ApplyInPlace(bm);
                        var cf = new ColorFiltering(new IntRange(10, 255), new IntRange(20, 255),
                                                    new IntRange(20, 255));
                        cf.ApplyInPlace(bm);
                        var blobCounter = new BlobCounter
                        {
                            ObjectsOrder = ObjectsOrder.Size,
                            MinHeight    = 200,
                            MinWidth     = 300,
                            //BackgroundThreshold = Color.FromArgb(255, 20, 20, 50),
                            //CoupledSizeFiltering = false,
                            FilterBlobs = true
                        };
                        blobCounter.ProcessImage(bm);
                        bm.ToManagedImage()
                        .Save(@"C:\temp\daforge\diff\img" + _calibrationStep + "-" + _errors + ".jpg",
                              ImageFormat.Jpeg);
                        var blobs = blobCounter.GetObjectsInformation();
                        if (blobs.Any())
                        {
                            int             i = 0;
                            List <IntPoint> corners;
                            do
                            {
                                corners =
                                    PointsCloud.FindQuadrilateralCorners(blobCounter.GetBlobsEdgePoints(blobs[i++]));
                            } while (corners.Count != 4);
                            RecursiveAForgeCalibrator.GridBlobs.InPlaceSort(corners);
                            Grid.TopLeft     = new Point(corners[0].X, corners[0].Y);
                            Grid.TopRight    = new Point(corners[1].X, corners[1].Y);
                            Grid.BottomLeft  = new Point(corners[2].X, corners[2].Y);
                            Grid.BottomRight = new Point(corners[3].X, corners[3].Y);
                            if (Grid.TopLeft.X > 10 && Grid.TopRight.X < _vs.Width - 5 && Grid.BottomLeft.X > 5 &&
                                Grid.BottomRight.X < _vs.Width - 5 && Grid.TopLeft.Y > 10 && Grid.TopRight.Y > 5 &&
                                Grid.BottomLeft.Y < _vs.Height - 5 && Grid.BottomRight.Y < _vs.Height - 5 &&
                                Grid.TopLeft.X < Grid.BottomRight.X &&
                                Grid.BottomLeft.X < Grid.TopRight.X && Grid.BottomLeft.X < Grid.BottomRight.X &&
                                Grid.TopLeft.Y < Grid.BottomLeft.Y && Grid.TopLeft.Y < Grid.BottomRight.Y &&
                                Grid.TopRight.Y < Grid.BottomLeft.Y && Grid.TopRight.Y < Grid.BottomRight.Y)
                            {
                                _calibrationStep++;
                                _vs.Clear();
                                Grid.AddPoint(new Point(), new Point(corners[0].X, corners[0].Y));
                                Grid.AddPoint(new Point(0, _vs.Height), new Point(corners[1].X, corners[1].Y));
                                Grid.AddPoint(new Point(_vs.Width, 0), new Point(corners[2].X, corners[2].Y));
                                Grid.AddPoint(new Point(_vs.Width, _vs.Height),
                                              new Point(corners[3].X, corners[3].Y));
                                DrawMarkers();
                            }
                            else
                            {
                                _calibrationStep = 0;
                                _errors++;
                            }
                        }
                        else
                        {
                            _calibrationStep = 0;
                            _errors++;
                        }
                        break;

                    case 1:
                        diffFilter.OverlayImage = e.Frame.Bitmap;
                        //diffFilter.OverlayImage.Save(@"C:\temp\daforge\diff\srcf" + _errors + ".jpg", ImageFormat.Jpeg);
                        //_vs.AddRect(0, 0, (int) _vs.Width, (int) _vs.Height, Color.FromArgb(255, 255, 255, 255));
                        _vs.Clear();
                        DrawInverted(0, 0, _vs.Width, _vs.Height);
                        _vs.Draw();
                        _calibrationStep++;
                        break;

                    case 0:
                        Grid            = new Grid(e.Frame.Bitmap.Width, e.Frame.Bitmap.Height);
                        Grid.ScreenSize = new Rectangle(0, 0, _vs.Width, _vs.Height);
                        var thread = new Thread(() =>
                        {
                            _vs.Show();
                        });
                        thread.SetApartmentState(ApartmentState.STA);
                        thread.Start();
                        thread.Join();
                        DrawBackground();
                        _vs.Draw();
                        _calibrationStep++;
                        break;
                    }
                }
            }
            Debug.WriteLine("Releasing");
            await Task.Delay(500);

            _sem.Release();
        }