seg / app.py
eho69's picture
Update app.py
016e3a4 verified
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.
"""
# Convert PIL Image to OpenCV format (RGB -> BGR usually, but we keep RGB for display)
img_rgb = np.array(image_source)
img_h, img_w = img_rgb.shape[:2]
# Convert to Grayscale for detection
gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY)
# ── Step 1: Detect Circles (Bolt Holes) ─────────────────────────────────
circles = cv2.HoughCircles(
gray,
cv2.HOUGH_GRADIENT,
dp=1, # Resolution ratio
minDist=50, # Min distance between holes (prevents double detection)
param1=100, # Canny edge threshold (higher = fewer edges)
param2=30, # Accumulator threshold (lower = more circles)
minRadius=10, # Min size of bolt hole
maxRadius=40 # Max size of bolt hole
)
if circles is None:
return image_source, image_source, "❌ No bolt holes detected. Adjust lighting or focus."
# Convert coordinates to integers
circles = np.round(circles[0]).astype(int)
# ── Step 2: Separate into Top and Bottom Rows ───────────────────────────
# We sort all circles by their Y (height) coordinate
# The "Gap" in Y values tells us where the split between rows is.
ys = [c[1] for c in circles]
y_median = np.median(ys) # Find the middle point of the image's height logic
# Split circles based on if they are above or below the median height
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."
# ── Step 3: Calculate Straight Lines (Average Y) ────────────────────────
# We take the average Y of the top row to get a straight horizontal line
y_top_line = int(np.mean([c[1] for c in top_row]))
# We take the average Y of the bottom row
y_bot_line = int(np.mean([c[1] for c in bot_row]))
# ── Step 4: Visualization ───────────────────────────────────────────────
vis_img = img_rgb.copy()
# Colors
LINE_COLOR = (0, 255, 0) # Green
HOLE_COLOR = (255, 0, 0) # Red
CENTER_COLOR = (255, 255, 0) # Yellow dots
# Draw the straight horizontal lines across the full width
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)
# Draw the holes and their centers to show what we detected
for (x, y, r) in circles:
cv2.circle(vis_img, (x, y), r, HOLE_COLOR, 2) # The hole outline
cv2.circle(vis_img, (x, y), 3, CENTER_COLOR, -1) # The center dot
# ── Step 5: Crop the Region Between Lines ──────────────────────────────
# Slice the array: img[y_start : y_end, x_start : x_end]
# We add a tiny padding (e.g., +/- 0) or keep it exact.
# Ensure y_top is actually above y_bot
y_start, y_end = sorted([y_top_line, y_bot_line])
# Safety check for crop size
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]
# Generate Stats Text
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
# ── Gradio Interface ────────────────────────────────────────────────────────
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()