import cv2 import numpy as np from pdf2image import pdf2image from vidocp.utils import draw_contours, show_mpl, draw_rectangles, remove_included, remove_overlapping, show_cv2 def is_large_enough(cont, min_area=10000): return cv2.contourArea(cont, False) > min_area def has_acceptable_format(cont, max_width_to_hight_ratio=6): _, _, w, h = cv2.boundingRect(cont) return max_width_to_hight_ratio >= w / h >= (1 / max_width_to_hight_ratio) def is_likely_figure(cont, min_area=5000, max_width_to_hight_ratio=6): return is_large_enough(cont, min_area) and has_acceptable_format(cont, max_width_to_hight_ratio) def remove_primary_text_regions(image): """Removes regions of primary text, meaning no figure descriptions for example, but main text body paragraphs. Args: image: Image to remove primary text from. Returns: Image with primary text removed. References: https://stackoverflow.com/questions/58349726/opencv-how-to-remove-text-from-background """ def filter_likely_primary_text_segments(cnts): for c in cnts: area = cv2.contourArea(c) if area > 800 and area < 15000: yield cv2.boundingRect(c) image = image.copy() gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) thresh = cv2.threshold(gray, 253, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1] close_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (20, 3)) close = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, close_kernel, iterations=1) dilate_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 3)) dilate = cv2.dilate(close, dilate_kernel, iterations=1) cnts, _ = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) for rect in filter_likely_primary_text_segments(cnts): x, y, w, h = rect cv2.rectangle(image, (x, y), (x + w, y + h), (255, 255, 255), -1) return image def __detect_large_coherent_structures(image: np.array): """Detects large coherent structures on an image. References: https://stackoverflow.com/questions/60259169/how-to-group-nearby-contours-in-opencv-python-zebra-crossing-detection """ gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) thresh = cv2.threshold(gray, 253, 255, cv2.THRESH_BINARY)[1] dilate_kernel = cv2.getStructuringElement(cv2.MORPH_OPEN, (5, 5)) dilate = cv2.dilate(~thresh, dilate_kernel, iterations=4) close_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (20, 20)) close = cv2.morphologyEx(dilate, cv2.MORPH_CLOSE, close_kernel, iterations=1) cnts, _ = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) return cnts def detect_figures(image: np.array): image = image.copy() image = remove_primary_text_regions(image) cnts = __detect_large_coherent_structures(image) cnts = filter(is_likely_figure, cnts) rects = map(cv2.boundingRect, cnts) rects = remove_included(rects) return rects def remove_text_in_pdf(pdf_path, page_index=1): page = pdf2image.convert_from_path(pdf_path, first_page=page_index + 1, last_page=page_index + 1)[0] page = np.array(page) redaction_contours = detect_figures(page) page = draw_rectangles(page, redaction_contours) show_mpl(page)