def boundary_detection(binary,
                       min_obj_area=C.THRESHOLD_OBJ_MIN_AREA, min_obj_perimeter=C.THRESHOLD_OBJ_MIN_PERIMETER,
                       line_thickness=C.THRESHOLD_LINE_THICKNESS, min_rec_evenness=C.THRESHOLD_REC_MIN_EVENNESS,
                       max_dent_ratio=C.THRESHOLD_REC_MAX_DENT_RATIO, show=False, write_boundary=False):
    """
    :param binary: Binary image from pre-processing
    :param min_obj_area: If not pass then ignore the small object
    :param min_obj_perimeter: If not pass then ignore the small object
    :param line_thickness: If not pass then ignore the slim object
    :param min_rec_evenness: If not pass then this object cannot be rectangular
    :param max_dent_ratio: If not pass then this object cannot be rectangular
    :return: boundary: [top, bottom, left, right]
                        -> up, bottom: list of (column_index, min/max row border)
                        -> left, right: list of (row_index, min/max column border) detect range of each row
    """
    mark = np.full(binary.shape, 0, dtype=np.uint8)
    boundary_all = []
    boundary_rec = []
    boundary_nonrec = []
    row, column = binary.shape[0], binary.shape[1]

    for i in range(row):
        for j in range(column):
            if binary[i, j] == 255 and mark[i, j] == 0:
                # get connected area
                area = util.boundary_bfs_connected_area(binary, i, j, mark)
                # ignore small area
                if len(area) < min_obj_area:
                    continue

                # calculate the boundary of the connected area
                boundary = util.boundary_get_boundary(area)
                # ignore small area
                perimeter = np.sum([len(b) for b in boundary])
                if perimeter < min_obj_perimeter:
                    continue

                # check if it is line by checking the length of edges
                if util.boundary_is_line(boundary, line_thickness):
                    continue

                # rectangle check
                if util.boundary_is_rectangle(boundary, min_rec_evenness, max_dent_ratio):
                    boundary_rec.append(boundary)
                else:
                    boundary_nonrec.append(boundary)
                boundary_all.append(boundary)

                if show:
                    print('Area:%d, Perimeter:%d' % (len(area), perimeter))
                    draw.draw_boundary(boundary_all, binary.shape, show=True)

    if write_boundary:
        cv2.imwrite('data/output/boundary.png', draw.draw_boundary(boundary_all, binary.shape))

    return boundary_rec, boundary_nonrec
                                   line=-1)
if is_ocr:
    draw_bounding, word = ocr.text_detection(org, img_clean)
else:
    draw_bounding = org
img_clean = draw.draw_bounding_box(img_clean,
                                   corners_compo,
                                   color=(255, 255, 255),
                                   line=-1)

# *** Step 6 *** post-processing: remove img elements from original image and segment into smaller size
if is_segment:
    seg.segment_img(img_clean, 600, 'output/segment')
# draw results
draw_bounding = draw.draw_bounding_box_class(
    draw_bounding, corners_block, ['block' for i in range(len(corners_block))])
draw_bounding = draw.draw_bounding_box_class(
    draw_bounding, corners_img, ['img' for j in range(len(corners_img))])
draw_bounding = draw.draw_bounding_box_class(
    draw_bounding, corners_compo, ['compo' for j in range(len(corners_compo))])
draw_boundary = draw.draw_boundary(boundary_rec, org.shape)
# save results
if is_save:
    cv2.imwrite('output/org.png', org)
    cv2.imwrite('output/labeled.png', draw_bounding)
    cv2.imwrite('output/boundary.png', draw_boundary)
    cv2.imwrite('output/gradient.png', binary)
    cv2.imwrite('output/clean.png', img_clean)

end = file.timer(start)