import cv2 as cv from transformers import TrOCRProcessor, VisionEncoderDecoderModel import numpy as np from concurrent.futures import ProcessPoolExecutor from openai import OpenAI import gradio as gr processor = TrOCRProcessor.from_pretrained('microsoft/trocr-large-handwritten') model = VisionEncoderDecoderModel.from_pretrained('microsoft/trocr-large-handwritten') def preprocess_image(image): gray_image = cv.cvtColor(image, cv.COLOR_BGR2GRAY) ret, bin_image = cv.threshold(gray_image, 127, 255, cv.THRESH_OTSU) bin_image = cv.copyMakeBorder(bin_image, int(0.10 * image.shape[0]), int(0.05 * image.shape[0]), int(0.05 * image.shape[1]), int(0.10 * image.shape[1]), cv.BORDER_CONSTANT, value=(255, 255, 255)) return bin_image #bin_image = preprocess_image(image) def split_image_into_lines(image): lines = [] while (image.shape[0] > 20): flag1 = 0 flag2 = 0 for i in range(image.shape[0]): if flag1 == 0: for j in range(image.shape[1]): pixel_value = image[i][j] if (pixel_value == 0) & (flag1 == 0): start = i flag1 = 1 flag2 = 1 if flag2 == 1: num_white_pixels = np.sum(image[i + 1] == 255) if (num_white_pixels > 0.98 * image.shape[1]): end = i + 1 break line = image[int(start - 0.2 * (end - start + 1)): int(end + 1 + 0.2 * (end - start + 1))][:] if line.shape[0] > 20: line_rgb = cv.cvtColor(line, cv.COLOR_GRAY2RGB) lines.append(line_rgb) pads = 255 * np.ones((20, image.shape[1]), dtype='uint8') new_image = image[int(end + 2 -(0.2 * (end - start + 1))):][:] new_image = np.concatenate((pads, new_image)) image = new_image return lines #lines = split_image_into_lines(bin_image) def generate_text(line): pixel_values = processor(images=line, return_tensors="pt").pixel_values generated_ids = model.generate(pixel_values, max_new_tokens=50) generated_text = processor.batch_decode(generated_ids, skip_special_tokens=True)[0] return generated_text def get_improved_result(lines): with ProcessPoolExecutor() as executor: results = ' '.join(executor.map(generate_text, lines)) #improve results with llm client = OpenAI() completion = client.chat.completions.create( model="gpt-4o", messages=[ { "role": "user", "content": f"I have a string that was extracted from an image of handwritten text. The extraction process introduced minor grammatical, spelling, and punctuation errors. Please carefully review the text below and make any necessary corrections to improve readability and accuracy while preserving the original meaning. Do not change the content or style beyond necessary corrections. Return the corrected text only without adding any headings, explanations, or extra formatting. Text: {results}" } ] ) improved_text = completion.choices[0].message.content return improved_text def put_text(text, font, font_scale, color, thickness, max_width, out_image_width, top_margin): words = text.split(" ") lines = [] current_line = "" for word in words: if cv.getTextSize(current_line + " " + word, font, font_scale, thickness)[0][0] <= (max_width * out_image_width): current_line += " " + word else: lines.append(current_line) current_line = word lines.append(current_line) out_image_height = sum([cv.getTextSize(line, font, font_scale, thickness)[0][1] for line in lines]) + 2 * top_margin + 20 * (len(lines) - 1) #20 is the gap between two consecutive lines out_image = 255 * (np.ones((out_image_height, out_image_width, 3), dtype=np.uint8)) top = top_margin for line in lines: cv.putText(out_image, line.strip(), (int(((1 - max_width) * out_image_width) / 2), top), font, font_scale, 0, thickness, lineType=cv.LINE_AA) top += cv.getTextSize(line.strip(), font, font_scale, thickness)[0][1] + 20 return out_image font = cv.FONT_HERSHEY_DUPLEX font_scale = 2 color = 0 thickness = 2 max_width = 0.9 out_image_width = 1500 top_margin = 100 #out_image = put_text(improved_text, font, font_scale, color, thickness, max_width, out_image_width, top_margin) def predict(input_img): bin_image = preprocess_image(input_img) lines = split_image_into_lines(bin_image) improved_text = get_improved_result(lines) out_image = put_text(improved_text, font, font_scale, color, thickness, max_width, out_image_width, top_margin) return out_img gradio_app = gr.Interface( predict, inputs=gr.Image(label="Image with handwritten text", sources=['upload']), outputs=[gr.Image(label="Output Image")], title="Extract Handwritten Text", ) if __name__ == "__main__": gradio_app.launch()