ketanmore's picture
Upload folder using huggingface_hub
2720487 verified
from typing import List
import cv2
import numpy as np
from PIL import Image, ImageDraw
from surya.postprocessing.util import get_line_angle, rescale_bbox
from surya.schema import ColumnLine
def get_detected_lines_sobel(image, vertical=True):
# Apply Sobel operator with a kernel size of 3 to detect vertical edges
if vertical:
dx = 1
dy = 0
else:
dx = 0
dy = 1
sobelx = cv2.Sobel(image, cv2.CV_32F, dx, dy, ksize=3)
# Absolute Sobel (to capture both edges)
abs_sobelx = np.absolute(sobelx)
# Convert to 8-bit image
scaled_sobel = np.uint8(255 * abs_sobelx / np.max(abs_sobelx))
kernel = np.ones((20, 1), np.uint8)
eroded = cv2.erode(scaled_sobel, kernel, iterations=1)
scaled_sobel = cv2.dilate(eroded, kernel, iterations=3)
return scaled_sobel
def get_detected_lines(image, slope_tol_deg=2, vertical=False, horizontal=False) -> List[ColumnLine]:
assert not (vertical and horizontal)
new_image = image.astype(np.float32) * 255 # Convert to 0-255 range
if vertical or horizontal:
new_image = get_detected_lines_sobel(new_image, vertical)
new_image = new_image.astype(np.uint8)
edges = cv2.Canny(new_image, 150, 200, apertureSize=3)
if vertical:
max_gap = 100
min_length = 10
else:
max_gap = 10
min_length = 4
lines = cv2.HoughLinesP(edges, 1, np.pi / 180, threshold=150, minLineLength=min_length, maxLineGap=max_gap)
line_info = []
if lines is not None:
for line in lines:
vertical_line = False
horizontal_line = False
x1, y1, x2, y2 = line[0]
bbox = [x1, y1, x2, y2]
if x2 == x1:
vertical_line = True
else:
line_angle = get_line_angle(x1, y1, x2, y2)
if 90 - slope_tol_deg < line_angle < 90 + slope_tol_deg:
vertical_line = True
elif -90 - slope_tol_deg < line_angle < -90 + slope_tol_deg:
vertical_line = True
elif -slope_tol_deg < line_angle < slope_tol_deg:
horizontal_line = True
if bbox[3] < bbox[1]:
bbox[1], bbox[3] = bbox[3], bbox[1]
if bbox[2] < bbox[0]:
bbox[0], bbox[2] = bbox[2], bbox[0]
row = ColumnLine(bbox=bbox, vertical=vertical_line, horizontal=horizontal_line)
line_info.append(row)
if vertical:
line_info = [line for line in line_info if line.vertical]
if horizontal:
line_info = [line for line in line_info if line.horizontal]
return line_info
def draw_lines_on_image(line_info: List[ColumnLine], img):
draw = ImageDraw.Draw(img)
for line in line_info:
divisor = 20
if line.horizontal:
divisor = 200
x1, y1, x2, y2 = [x // divisor * divisor for x in line.bbox]
if line.vertical:
draw.line((x1, y1, x2, y2), fill="red", width=3)
return img
def get_vertical_lines(image, processor_size, image_size, divisor=20, x_tolerance=40, y_tolerance=20) -> List[ColumnLine]:
vertical_lines = get_detected_lines(image, vertical=True)
for line in vertical_lines:
line.rescale_bbox(processor_size, image_size)
vertical_lines = sorted(vertical_lines, key=lambda x: x.bbox[0])
for line in vertical_lines:
line.round_bbox(divisor)
# Merge adjacent line segments together
to_remove = []
for i, line in enumerate(vertical_lines):
for j, line2 in enumerate(vertical_lines):
if j <= i:
continue
if line.bbox[0] != line2.bbox[0]:
continue
expanded_line1 = [line.bbox[0], line.bbox[1] - y_tolerance, line.bbox[2],
line.bbox[3] + y_tolerance]
line1_points = set(range(int(expanded_line1[1]), int(expanded_line1[3])))
line2_points = set(range(int(line2.bbox[1]), int(line2.bbox[3])))
intersect_y = len(line1_points.intersection(line2_points)) > 0
if intersect_y:
vertical_lines[j].bbox[1] = min(line.bbox[1], line2.bbox[1])
vertical_lines[j].bbox[3] = max(line.bbox[3], line2.bbox[3])
to_remove.append(i)
vertical_lines = [line for i, line in enumerate(vertical_lines) if i not in to_remove]
# Remove redundant segments
to_remove = []
for i, line in enumerate(vertical_lines):
if i in to_remove:
continue
for j, line2 in enumerate(vertical_lines):
if j <= i or j in to_remove:
continue
close_in_x = abs(line.bbox[0] - line2.bbox[0]) < x_tolerance
line1_points = set(range(int(line.bbox[1]), int(line.bbox[3])))
line2_points = set(range(int(line2.bbox[1]), int(line2.bbox[3])))
intersect_y = len(line1_points.intersection(line2_points)) > 0
if close_in_x and intersect_y:
# Keep the longer line and extend it
if len(line2_points) > len(line1_points):
vertical_lines[j].bbox[1] = min(line.bbox[1], line2.bbox[1])
vertical_lines[j].bbox[3] = max(line.bbox[3], line2.bbox[3])
to_remove.append(i)
else:
vertical_lines[i].bbox[1] = min(line.bbox[1], line2.bbox[1])
vertical_lines[i].bbox[3] = max(line.bbox[3], line2.bbox[3])
to_remove.append(j)
vertical_lines = [line for i, line in enumerate(vertical_lines) if i not in to_remove]
if len(vertical_lines) > 0:
# Always start with top left of page
vertical_lines[0].bbox[1] = 0
return vertical_lines