|
import gradio as gr |
|
import logging |
|
from roboflow import Roboflow |
|
from PIL import Image, ImageDraw |
|
import cv2 |
|
import numpy as np |
|
from selenium import webdriver |
|
from selenium.webdriver.chrome.service import Service |
|
from selenium.webdriver.common.by import By |
|
from selenium.webdriver.support.ui import WebDriverWait |
|
from selenium.webdriver.support import expected_conditions as EC |
|
from selenium.webdriver import ActionChains |
|
from selenium.webdriver.support.ui import Select |
|
import time |
|
import os |
|
from math import atan2, degrees |
|
|
|
|
|
logging.basicConfig( |
|
level=logging.DEBUG, |
|
format="%(asctime)s - %(levelname)s - %(message)s", |
|
handlers=[logging.FileHandler("debug.log"), logging.StreamHandler()], |
|
) |
|
|
|
|
|
ROBOFLOW_API_KEY = "KUP9w62eUcD5PrrRMJsV" |
|
PROJECT_NAME = "model_verification_project" |
|
VERSION_NUMBER = 2 |
|
|
|
|
|
CHROME_PATH = "/home/user/.cache/selenium/chrome/linux64/132.0.6834.83/chrome" |
|
CHROMEDRIVER_PATH = "/home/user/.cache/selenium/chromedriver/linux64/132.0.6834.83/chromedriver" |
|
|
|
|
|
def get_calligrapher(): |
|
calli_url = "https://www.calligrapher.ai" |
|
|
|
options = webdriver.ChromeOptions() |
|
options.add_argument("--headless") |
|
options.add_argument("--no-sandbox") |
|
options.add_argument("--disable-dev-shm-usage") |
|
options.binary_location = CHROME_PATH |
|
|
|
service = Service(CHROMEDRIVER_PATH) |
|
driver = webdriver.Chrome(service=service, options=options) |
|
driver.maximize_window() |
|
driver.get(calli_url) |
|
|
|
|
|
speed_slider = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.ID, 'speed-slider'))) |
|
ActionChains(driver).drag_and_drop_by_offset(speed_slider, 40, 0).perform() |
|
|
|
bias_slider = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.ID, 'bias-slider'))) |
|
ActionChains(driver).drag_and_drop_by_offset(bias_slider, 20, 0).perform() |
|
|
|
width_slider = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.ID, 'width-slider'))) |
|
ActionChains(driver).drag_and_drop_by_offset(width_slider, 20, 0).perform() |
|
|
|
|
|
select = Select(driver.find_element(By.ID, 'select-style')) |
|
select.select_by_visible_text('9') |
|
return driver |
|
|
|
def get_calligrapher_text(driver, text, save_path): |
|
text_input = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.ID, 'text-input'))) |
|
text_input.clear() |
|
text_input.send_keys(text) |
|
|
|
draw_button = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.ID, 'draw-button'))) |
|
draw_button.click() |
|
time.sleep(3) |
|
|
|
|
|
canvas = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.ID, 'draw-area'))) |
|
canvas.screenshot(save_path) |
|
print(f"Handwriting saved to: {save_path}") |
|
|
|
|
|
def detect_paper_angle(image, bounding_box): |
|
x1, y1, x2, y2 = bounding_box |
|
roi = np.array(image)[y1:y2, x1:x2] |
|
gray = cv2.cvtColor(roi, cv2.COLOR_RGBA2GRAY) |
|
edges = cv2.Canny(gray, 50, 150) |
|
lines = cv2.HoughLinesP(edges, 1, np.pi / 180, threshold=100, minLineLength=50, maxLineGap=10) |
|
if lines is not None: |
|
longest_line = max(lines, key=lambda line: np.linalg.norm((line[0][2] - line[0][0], line[0][3] - line[0][1]))) |
|
x1, y1, x2, y2 = longest_line[0] |
|
dx = x2 - x1 |
|
dy = y2 - y1 |
|
angle = degrees(atan2(dy, dx)) |
|
return angle |
|
else: |
|
return 0 |
|
|
|
|
|
def process_image(image, text): |
|
try: |
|
|
|
save_path = "/tmp/handwriting.png" |
|
driver = get_calligrapher() |
|
get_calligrapher_text(driver, text, save_path) |
|
driver.quit() |
|
|
|
|
|
handwriting_image = Image.open(save_path).convert("RGBA") |
|
|
|
|
|
rf = Roboflow(api_key=ROBOFLOW_API_KEY) |
|
logging.debug("Initialized Roboflow API.") |
|
project = rf.workspace().project(PROJECT_NAME) |
|
logging.debug("Accessed project in Roboflow.") |
|
model = project.version(VERSION_NUMBER).model |
|
logging.debug("Loaded model from Roboflow.") |
|
|
|
|
|
input_image_path = "/tmp/input_image.jpg" |
|
image.save(input_image_path) |
|
logging.debug(f"Input image saved to {input_image_path}.") |
|
|
|
|
|
logging.debug("Performing inference on the image...") |
|
prediction = model.predict(input_image_path, confidence=70, overlap=50).json() |
|
logging.debug(f"Inference result: {prediction}") |
|
|
|
|
|
pil_image = image.convert("RGBA") |
|
logging.debug("Converted image to RGBA mode.") |
|
|
|
|
|
for obj in prediction['predictions']: |
|
white_paper_width = obj['width'] |
|
white_paper_height = obj['height'] |
|
|
|
padding_x = int(white_paper_width * 0.1) |
|
padding_y = int(white_paper_height * 0.1) |
|
|
|
box_width = white_paper_width - 2 * padding_x |
|
box_height = white_paper_height - 2 * padding_y |
|
|
|
x1_padded = int(obj['x'] - white_paper_width / 2 + padding_x) |
|
y1_padded = int(obj['y'] - white_paper_height / 2 + padding_y) |
|
|
|
|
|
resized_handwriting = handwriting_image.resize((box_width, box_height), Image.ANTIALIAS) |
|
|
|
|
|
pil_image.paste(resized_handwriting, (x1_padded, y1_padded), resized_handwriting) |
|
|
|
|
|
output_image_path = "/tmp/output_image.png" |
|
pil_image.convert("RGB").save(output_image_path) |
|
logging.debug(f"Output image saved to {output_image_path}.") |
|
return output_image_path |
|
|
|
except Exception as e: |
|
logging.error(f"Error during image processing: {e}") |
|
return None |
|
|
|
|
|
def gradio_inference(image, text): |
|
logging.debug("Starting Gradio inference.") |
|
result_path = process_image(image, text) |
|
if result_path: |
|
logging.debug("Gradio inference successful.") |
|
return result_path, result_path, "Processing complete! Download the image below." |
|
logging.error("Gradio inference failed.") |
|
return None, None, "An error occurred while processing the image. Please check the logs." |
|
|
|
|
|
interface = gr.Interface( |
|
fn=gradio_inference, |
|
inputs=[ |
|
gr.Image(type="pil", label="Upload an Image"), |
|
gr.Textbox(label="Enter Text to Overlay"), |
|
], |
|
outputs=[ |
|
gr.Image(label="Processed Image Preview"), |
|
gr.File(label="Download Processed Image"), |
|
gr.Textbox(label="Status"), |
|
], |
|
title="Roboflow Detection with Calligrapher Text Overlay", |
|
description=( |
|
"Upload an image, enter text to overlay, and let the Roboflow model process the image. " |
|
"Handwritten text is generated using Calligrapher.ai and overlaid on the detected white paper areas. " |
|
"Preview or download the output image below." |
|
), |
|
allow_flagging="never", |
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
logging.debug("Launching Gradio interface.") |
|
interface.launch(share=True) |
|
|
|
|
|
|