static ImageCellCollection DetectRawCells(CrossPoints crossPoints, int[,] crossPointIndexes, int spacingMin, double widthRatio, double heightRatio)
        {
            ImageCellCollection rawCells = new ImageCellCollection();

            if (crossPoints.Count > 0)
            {
                int        rowIndex, columnIndex, leftColumnIndex, topRowIndex, leftTopIndex;
                int        leftX, rightX, topY, bottomY, width, height, rowSize, columnSize, index = 0;
                CrossPoint leftTop;

                foreach (CrossPoint current in crossPoints)
                {
                    if (current.RowIndex > 0 && current.ColumnIndex > 0 && current.ExistLeftLine && current.ExistTopLine)
                    {
                        leftColumnIndex = FindLeftColumnIndex(rowIndex: current.RowIndex, columnIndex: current.ColumnIndex, crossPointIndexes: ref crossPointIndexes, rawCrossPoints: ref crossPoints);
                        topRowIndex     = FindTopRowIndex(rowIndex: current.RowIndex, columnIndex: current.ColumnIndex, crossPointIndexes: ref crossPointIndexes, rawCrossPoints: ref crossPoints);

                        if (leftColumnIndex > -1 && topRowIndex > -1)
                        {
                            leftTopIndex = crossPointIndexes[topRowIndex, leftColumnIndex];

                            if (leftTopIndex > -1)
                            {
                                leftTop = crossPoints[leftTopIndex];

                                leftX   = leftTop.X;
                                rightX  = current.X;
                                topY    = leftTop.Y;
                                bottomY = current.Y;
                                width   = rightX - leftX;
                                height  = bottomY - topY;

                                // 가로 간격이나 세로 간격이 최소 간격 작은 것은 cell로 인정하지 않는다.
                                // 셀의 가로, 세로가 모두 페이지의 절반을 넘어서면 cell로 인정하지 않는다.
                                if (width > spacingMin && height > spacingMin &&
                                    !(width * widthRatio > LIMIT_CELL_WIDTH && height * heightRatio > LIMIT_CELL_HEIGHT))
                                {
                                    rowIndex    = leftTop.RowIndex;
                                    columnIndex = leftTop.ColumnIndex;
                                    columnSize  = current.ColumnIndex - leftTop.ColumnIndex;
                                    rowSize     = current.RowIndex - leftTop.RowIndex;

                                    // 최종적으로 좌표는 백분율로 된 상대값이기 때문에 여기서 바꿔줘야 한다.
                                    rawCells.Add(new ImageCell(index: index++, rowIndex: rowIndex, columnIndex: columnIndex, rowSize: rowSize, columnSize: columnSize, topY: topY, bottomY: bottomY, leftX: leftX, rightX: rightX));
                                }
                            }
                        }
                    }
                }
            }

            // 병합된 경우 index가 뒤죽박죽이 되므로 여기서 정렬한다.
            return(new ImageCellCollection(rawCells.OrderBy(cell => cell.RowIndex).ThenBy(cell => cell.ColumnIndex)));
        }
        static CrossPoints DetectRawCrossPoints(CellLines horizontals, CellLines verticals, int lineDistance, out int[,] crossPointIndexes)
        {
            crossPointIndexes = new int[horizontals.Count, verticals.Count];

            CrossPoints rawCrossPoints = new CrossPoints();

            bool     existTopLine, existLeftLine;
            int      index = 0;
            CellLine horizontal, vertical;

            for (int i = 0; i < horizontals.Count; i++)
            {
                horizontal = horizontals[i];

                for (int j = 0; j < verticals.Count; j++)
                {
                    vertical = verticals[j];

                    // 두선이 교차하는지 확인
                    if (horizontal.StartX <= vertical.StartX && horizontal.EndX >= vertical.EndX && horizontal.StartY >= vertical.StartY && horizontal.EndY <= vertical.EndY)
                    {
                        // 현재 점이 위로 선이 있는지, 좌로 선이 있는지 확인. 향후 cell 구성할 때 사용
                        existTopLine  = Math.Abs(vertical.StartY - horizontal.StartY) > lineDistance;
                        existLeftLine = Math.Abs(horizontal.StartX - vertical.StartX) > lineDistance;

                        rawCrossPoints.Add(new CrossPoint(index: index, existTopLine: existTopLine, existLeftLine: existLeftLine, rowIndex: i, columnIndex: j, x: vertical.StartX, y: horizontal.StartY, horizontal: horizontal, vertical: vertical));
                        crossPointIndexes[i, j] = index;
                        index++;
                    }
                    else
                    {
                        crossPointIndexes[i, j] = -1;
                    }
                }
            }

            return(rawCrossPoints);
        }
        static int FindTopRowIndex(int rowIndex, int columnIndex, ref int[,] crossPointIndexes, ref CrossPoints rawCrossPoints)
        {
            CrossPoint topPoint;
            int        index = 0;

            for (int i = rowIndex - 1; i > -1; i--)
            {
                index = crossPointIndexes[i, columnIndex];

                if (index > -1)
                {
                    topPoint = rawCrossPoints[index];

                    if (topPoint.ExistLeftLine)
                    {
                        return(topPoint.RowIndex);
                    }
                }
            }

            return(-1);
        }
        static int FindLeftColumnIndex(int rowIndex, int columnIndex, ref int[,] crossPointIndexes, ref CrossPoints rawCrossPoints)
        {
            CrossPoint leftPoint;
            int        index;

            for (int i = columnIndex - 1; i > -1; i--)
            {
                index = crossPointIndexes[rowIndex, i];

                if (index > -1)
                {
                    leftPoint = rawCrossPoints[index];

                    if (leftPoint.ExistTopLine)
                    {
                        return(leftPoint.ColumnIndex);
                    }
                }
            }

            return(-1);
        }