| import cv2 |
| import numpy as np |
| import matplotlib.pyplot as plt |
| import os |
| from deskew import determine_skew |
| from typing import Tuple, Union |
| import math |
| from loguru import logger |
|
|
| def preprocessImage(image): |
| """ |
| Preprocesses an image by applying various image processing steps such as denoising, thresholding, |
| and removal of horizontal and vertical lines, and saves the final processed image. |
| |
| Args: |
| - image_path (str): The file path to the input image to be processed. |
| - folder_path (str): The directory where the final processed image will be saved. |
| |
| Returns: |
| - str: The path of the final processed image. |
| """ |
|
|
| |
| gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) |
|
|
| |
| gray = cv2.fastNlMeansDenoising(gray, None, 10, 7, 21) |
|
|
| |
| thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1] |
|
|
| |
| removed = image.copy() |
|
|
| |
| vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 40)) |
| remove_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2) |
| cnts = cv2.findContours(remove_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) |
| cnts = cnts[0] if len(cnts) == 2 else cnts[1] |
| for c in cnts: |
| cv2.drawContours(removed, [c], -1, (255, 255, 255), 4) |
|
|
| |
| horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (40, 1)) |
| remove_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2) |
| cnts = cv2.findContours(remove_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) |
| cnts = cnts[0] if len(cnts) == 2 else cnts[1] |
| for c in cnts: |
| cv2.drawContours(removed, [c], -1, (255, 255, 255), 5) |
|
|
| |
| repair_kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)) |
| removed = 255 - removed |
| dilate = cv2.dilate(removed, repair_kernel, iterations=5) |
| dilate = cv2.cvtColor(dilate, cv2.COLOR_BGR2GRAY) |
| pre_result = cv2.bitwise_and(dilate, thresh) |
|
|
| |
| result = cv2.morphologyEx(pre_result, cv2.MORPH_CLOSE, repair_kernel, iterations=5) |
| final = cv2.bitwise_and(result, thresh) |
|
|
| |
| invert_final = 255 - final |
|
|
| |
| |
| |
| |
| return invert_final |
|
|
| def process_segment_and_crop_image(model, image, preprocess_image_path, padding=10, min_contour_area=100): |
| """ |
| Processes an image for segmentation using a U-Net model and crops the original image based on the largest contour. |
| |
| Args: |
| - model (tf.keras.Model): Trained U-Net model for image segmentation. |
| - img_path (str): Path to the original image. |
| - preprocess_image_path (str): Path to the preprocessed image. |
| - output_folder (str): Folder to save the cropped image. |
| - padding (int): Padding around the detected region. |
| - min_contour_area (int): Minimum contour area to be considered for cropping. |
| |
| Returns: |
| - str: The path of the cropped image. |
| """ |
| |
|
|
| img = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) |
|
|
| |
| _, img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV) |
|
|
| |
| img = cv2.resize(img, (512, 512)) |
|
|
| |
| img = np.expand_dims(img, axis=-1) |
| img_np = np.expand_dims(img, axis=0) |
|
|
| |
| pred = model.predict(img_np) |
| pred = np.squeeze(np.squeeze(pred, axis=0), axis=-1) |
|
|
| |
| |
| |
| |
| |
|
|
| |
| original_img = cv2.imread(preprocess_image_path) |
|
|
| |
| ori_height, ori_width = original_img.shape[:2] |
|
|
| |
| resized_mask = cv2.resize(pred, (ori_width, ori_height)) |
|
|
| |
| resized_mask = (resized_mask * 255).astype(np.uint8) |
|
|
| |
| _, binary_mask = cv2.threshold(resized_mask, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) |
|
|
| |
| kernel = np.ones((5, 5), np.uint8) |
| cleaned_mask = cv2.morphologyEx(binary_mask, cv2.MORPH_CLOSE, kernel) |
| cleaned_mask = cv2.morphologyEx(cleaned_mask, cv2.MORPH_OPEN, kernel) |
|
|
| |
| contours, _ = cv2.findContours(cleaned_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) |
|
|
| |
| valid_contours = [cnt for cnt in contours if cv2.contourArea(cnt) > min_contour_area] |
|
|
| if not valid_contours: |
| print("No valid text regions found.") |
| return None |
|
|
| |
| x_min, y_min = ori_width, ori_height |
| x_max, y_max = 0, 0 |
|
|
| for contour in valid_contours: |
| x, y, w, h = cv2.boundingRect(contour) |
| x_min = min(x_min, x) |
| y_min = min(y_min, y) |
| x_max = max(x_max, x + w) |
| y_max = max(y_max, y + h) |
|
|
| x_min = max(0, x_min - padding) |
| y_min = max(0, y_min - padding) |
| x_max = min(ori_width, x_max + padding) |
| y_max = min(ori_height, y_max + padding) |
|
|
| |
| cropped_img = original_img[y_min:y_max, x_min:x_max] |
|
|
| return cropped_img |
|
|
|
|
| def postProcessImage(cropped_image): |
| """ |
| Post-processes an image by deskewing, sharpening, and applying morphological dilation, then saves the final processed image. |
| |
| Args: |
| - image_path (str): Path to the original image. |
| - cropped_image_path (str): Path to the cropped image to be post-processed. |
| - output_folder (str): Directory where the final post-processed image will be saved. |
| |
| Returns: |
| - str: The path of the final post-processed image. |
| """ |
| def rotate( |
| image: np.ndarray, angle: float, background: Union[int, Tuple[int, int, int]] |
| ) -> np.ndarray: |
| old_width, old_height = image.shape[:2] |
| angle_radian = math.radians(angle) |
| width = abs(np.sin(angle_radian) * old_height) + abs(np.cos(angle_radian) * old_width) |
| height = abs(np.sin(angle_radian) * old_width) + abs(np.cos(angle_radian) * old_height) |
|
|
| image_center = tuple(np.array(image.shape[1::-1]) / 2) |
| rot_mat = cv2.getRotationMatrix2D(image_center, angle, 1.0) |
| rot_mat[1, 2] += (width - old_width) / 2 |
| rot_mat[0, 2] += (height - old_height) / 2 |
| return cv2.warpAffine(image, rot_mat, (int(round(height)), int(round(width))), borderValue=background) |
|
|
| |
| |
| |
| |
| rotated = cropped_image |
|
|
| |
| blurred = cv2.GaussianBlur(rotated, (1,1), sigmaX=3, sigmaY=3) |
| sharpened = cv2.addWeighted(rotated, 1.5, blurred, -0.5, 0) |
|
|
| |
| dilate_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 1)) |
| dilated = cv2.dilate(sharpened, dilate_kernel, iterations=1) |
|
|
| return sharpened |