| import gradio as gr |
| import torch |
| import torch.nn as nn |
| from torchvision import models, transforms |
| from PIL import Image |
| import numpy as np |
| import pickle |
| import os |
| import cv2 |
| import logging |
| from sklearn.cluster import KMeans |
|
|
| def detect_connect_and_crop(image_source: Image.Image): |
| """ |
| 1. Detects bolt holes using Hough Transform. |
| 2. Separates them into Top and Bottom rows. |
| 3. Fits a straight horizontal line through the center of each row. |
| 4. Crops the region strictly between these two lines. |
| |
| """ |
| |
| img_rgb = np.array(image_source) |
| img_h, img_w = img_rgb.shape[:2] |
| |
| |
| gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY) |
| |
| |
| circles = cv2.HoughCircles( |
| gray, |
| cv2.HOUGH_GRADIENT, |
| dp=1, |
| minDist=50, |
| param1=100, |
| param2=30, |
| minRadius=10, |
| maxRadius=40 |
| ) |
| |
| if circles is None: |
| return image_source, image_source, "β No bolt holes detected. Adjust lighting or focus." |
| |
| |
| circles = np.round(circles[0]).astype(int) |
| |
| |
| |
| |
| |
| ys = [c[1] for c in circles] |
| y_median = np.median(ys) |
| |
| |
| top_row = [c for c in circles if c[1] < y_median] |
| bot_row = [c for c in circles if c[1] >= y_median] |
| |
| if len(top_row) < 2 or len(bot_row) < 2: |
| return image_source, image_source, "β οΈ Need at least 2 holes in both top and bottom rows to draw lines." |
|
|
| |
| |
| y_top_line = int(np.mean([c[1] for c in top_row])) |
| |
| |
| y_bot_line = int(np.mean([c[1] for c in bot_row])) |
|
|
|
|
| |
| |
| vis_img = img_rgb.copy() |
| |
| |
| LINE_COLOR = (0, 255, 0) |
| HOLE_COLOR = (255, 0, 0) |
| CENTER_COLOR = (255, 255, 0) |
| |
| |
| cv2.line(vis_img, (0, y_top_line), (img_w, y_top_line), LINE_COLOR, 3) |
| cv2.line(vis_img, (0, y_bot_line), (img_w, y_bot_line), LINE_COLOR, 3) |
| |
| |
| for (x, y, r) in circles: |
| cv2.circle(vis_img, (x, y), r, HOLE_COLOR, 2) |
| cv2.circle(vis_img, (x, y), 3, CENTER_COLOR, -1) |
|
|
| |
| |
| |
| |
| |
| y_start, y_end = sorted([y_top_line, y_bot_line]) |
| |
| |
| if y_end - y_start < 10: |
| return Image.fromarray(vis_img), Image.fromarray(img_rgb), "β οΈ Calculated lines are too close." |
| |
| cropped_img = img_rgb[y_start:y_end, 0:img_w] |
| |
| |
| stats_text = ( |
| f"β
**Processing Complete**\n" |
| f"β’ Top Row Holes: {len(top_row)}\n" |
| f"β’ Bottom Row Holes: {len(bot_row)}\n" |
| f"β’ Top Line Y-Pos: {y_top_line} px\n" |
| f"β’ Bottom Line Y-Pos: {y_bot_line} px\n" |
| f"β’ Cropped Height: {y_end - y_start} px" |
| ) |
|
|
|
|
|
|
| return Image.fromarray(vis_img), Image.fromarray(cropped_img), stats_text |
|
|
| |
| with gr.Blocks(title="Engine Line Connector", theme=gr.themes.Default(primary_hue="blue")) as demo: |
| gr.Markdown( |
| """ |
| # π Bolt-Center Line Connector |
| ### Precision Cropping Tool |
| 1. Detects bolt holes. |
| 2. Draws a straight line through the average center of the top & bottom rows. |
| 3. Crops the saddle area exactly between these two lines. |
| """ |
| ) |
| |
| with gr.Row(): |
| input_image = gr.Image(type="pil", label="π· Upload Engine Block Image") |
|
|
|
|
|
|
|
|
| |
| run_button = gr.Button("π Connect Centers & Crop", variant="primary") |
| |
| with gr.Row(): |
| output_vis = gr.Image(label="βοΈ Visualized Lines & Centers") |
| output_crop = gr.Image(label="βοΈ Final Cropped Area") |
| |
| stats_output = gr.Markdown() |
| |
| run_button.click( |
| fn=detect_connect_and_crop, |
| inputs=[input_image], |
| outputs=[output_vis, output_crop, stats_output] |
| ) |
|
|
| if __name__ == "__main__": |
| demo.launch() |
|
|