public static async Task <List <Bitmap>[]> Extract(Bitmap processedImage, Bitmap originalImage, byte[,] binarizedImage, List <int[]> lines, bool strip = true)
        {
            var extractedFromProcessed = new List <Bitmap>();
            var extractedFromOriginal  = new List <Bitmap>();

            await Task.Run(() =>
            {
                var combinations = new List <int[]>();
                var bottomLines  = GetLinesByAngle(90, lines);
                var leftLines    = GetLinesByAngle(30, lines);
                var rightLines   = GetLinesByAngle(150, lines).Concat(GetLinesByAngle(330, lines)).ToList();

                foreach (var bottomLine in bottomLines)
                {
                    foreach (var leftLine in leftLines)
                    {
                        combinations.AddRange(rightLines.Select(rightLine => new[] { bottomLine, leftLine, rightLine }));
                    }
                }

                var rectangles = new List <Rectangle>();

                foreach (var combination in combinations)
                {
                    var firstLine   = lines[combination[0]];
                    var secondtLine = lines[combination[1]];
                    var thirdLine   = lines[combination[2]];

                    var firstCrossing  = GetPointOfCrossing(firstLine, secondtLine);
                    var secondCrossing = GetPointOfCrossing(firstLine, thirdLine);
                    var thirdCrossing  = GetPointOfCrossing(secondtLine, thirdLine);

                    if (!IsValidLine(firstLine, firstCrossing, secondCrossing, binarizedImage, 0.98, 2) ||
                        !IsValidLine(secondtLine, firstCrossing, thirdCrossing, binarizedImage, 0.7, 5) ||
                        !IsValidLine(thirdLine, secondCrossing, thirdCrossing, binarizedImage, 0.7, 5))
                    {
                        continue;
                    }

                    var minX = new[] { firstCrossing[0], secondCrossing[0], thirdCrossing[0] }.Min();
                    var minY = new[] { firstCrossing[1], secondCrossing[1], thirdCrossing[1] }.Min();

                    var maxX = new[] { firstCrossing[0], secondCrossing[0], thirdCrossing[0] }.Max();
                    var maxY = new[] { firstCrossing[1], secondCrossing[1], thirdCrossing[1] }.Max();

                    rectangles.Add(new Rectangle(minX, minY, maxX - minX, maxY - minY));
                }

                var filteredRectangles = new List <Rectangle>();
                foreach (var rectangle in rectangles)
                {
                    if (rectangles.Any(r => r != rectangle && r.Contains(rectangle)))
                    {
                        continue;
                    }
                    filteredRectangles.Add(rectangle);
                }

                foreach (var rectangle in filteredRectangles)
                {
                    var croppedImage         = processedImage.Clone(rectangle, processedImage.PixelFormat);
                    var croppedOriginalImage = originalImage.Clone(rectangle, originalImage.PixelFormat);
                    if (strip)
                    {
                        croppedImage         = StripImage(croppedImage, Color.Black);
                        croppedOriginalImage = StripImage(croppedOriginalImage, Color.Black, false);
                    }
                    extractedFromProcessed.Add(BilinearInterpolation.Resize(croppedImage, 300, 300));
                    extractedFromOriginal.Add(croppedOriginalImage);
                }
            });

            return(new[] { extractedFromProcessed, extractedFromOriginal });
        }
        public static async Task <List <Bitmap>[]> Extract(Bitmap processedImage, Bitmap originalImage, byte[,] binarizedImage, List <int[]> lines, bool strip = true)
        {
            var extractedFromProcessed = new List <Bitmap>();
            var extractedFromOriginal  = new List <Bitmap>();

            await Task.Run(() =>
            {
                var horizontalLines = GetLinesByAngle(90, lines);
                var verticalLines   = GetLinesByAngle(0, lines);

                var combinations = new List <int[]>();
                foreach (var topLine in horizontalLines)
                {
                    foreach (var bottomLine in horizontalLines)
                    {
                        if (topLine == bottomLine ||
                            lines[topLine][0] >= (lines[bottomLine][0] - 20))
                        {
                            continue;
                        }

                        foreach (var leftLine in verticalLines)
                        {
                            foreach (var rightLine in verticalLines)
                            {
                                if (leftLine == rightLine ||
                                    lines[leftLine][0] >= (lines[rightLine][0] - 20))
                                {
                                    continue;
                                }
                                combinations.Add(new[] { bottomLine, leftLine, topLine, rightLine });
                            }
                        }
                    }
                }

                var rectangles = new List <Rectangle>();
                foreach (var combination in combinations)
                {
                    var bottomLine = lines[combination[0]];
                    var leftLine   = lines[combination[1]];
                    var topLine    = lines[combination[2]];
                    var rightLine  = lines[combination[3]];

                    var bottomLeftCrossing  = GetPointOfCrossing(bottomLine, leftLine);
                    var leftTopCrossing     = GetPointOfCrossing(leftLine, topLine);
                    var topRightCrossing    = GetPointOfCrossing(topLine, rightLine);
                    var rightBottomCrossing = GetPointOfCrossing(rightLine, bottomLine);

                    if (!IsValidLine(bottomLine, rightBottomCrossing, bottomLeftCrossing, binarizedImage, 0.9, 4) ||
                        !IsValidLine(leftLine, bottomLeftCrossing, leftTopCrossing, binarizedImage, 0.9, 4) ||
                        !IsValidLine(topLine, leftTopCrossing, topRightCrossing, binarizedImage, 0.9, 4) ||
                        !IsValidLine(rightLine, topRightCrossing, rightBottomCrossing, binarizedImage, 0.9, 4))
                    {
                        continue;
                    }

                    var minX = new[] { leftTopCrossing[0], bottomLeftCrossing[0] }.Min();
                    var minY = new[] { leftTopCrossing[1], bottomLeftCrossing[1] }.Min();

                    var maxX = new[] { rightBottomCrossing[0], topRightCrossing[0] }.Max();
                    var maxY = new[] { rightBottomCrossing[1], topRightCrossing[1] }.Max();

                    rectangles.Add(new Rectangle(minX, minY, maxX - minX, maxY - minY));
                }

                var filteredRectangles = new List <Rectangle>();
                foreach (var rectangle in rectangles)
                {
                    if (rectangles.Any(r => r != rectangle && r.Contains(rectangle)))
                    {
                        continue;
                    }
                    filteredRectangles.Add(rectangle);
                }

                foreach (var rectangle in filteredRectangles)
                {
                    var croppedImage         = processedImage.Clone(rectangle, processedImage.PixelFormat);
                    var croppedOriginalImage = originalImage.Clone(rectangle, originalImage.PixelFormat);
                    if (strip)
                    {
                        croppedImage         = StripImage(croppedImage, Color.Black);
                        croppedOriginalImage = StripImage(croppedOriginalImage, Color.Black, false);
                    }
                    extractedFromProcessed.Add(BilinearInterpolation.Resize(croppedImage, 300, 300));
                    extractedFromOriginal.Add(croppedOriginalImage);
                }
            });

            return(new[] { extractedFromProcessed, extractedFromOriginal });
        }