pcb-image2schematic / detector.py
Sanjay1905's picture
Upload detector.py with huggingface_hub
3f9afd1 verified
Raw
History Blame Contribute Delete
6.89 kB
import cv2
import numpy as np
import os
import sys
import json
from collections import Counter
from inference_sdk import InferenceHTTPClient
# ─────────────────────────────────────────────
# CONFIGURATION
# ─────────────────────────────────────────────
ROBOFLOW_API_KEY = "bVPbU8TisRCASiURr0lb"
MODEL_ID = "printed-circuit-board/3"
CONFIDENCE_THRESH = 0.3
OVERLAP_THRESH = 0.3
LABEL_COLORS = {
"resistor": (0, 255, 0),
"capacitor": (255, 0, 0),
"inductor": (0, 0, 255),
"diode": (255, 255, 0),
"led": (0, 255, 255),
"ic": (255, 0, 255),
"transistor": (128, 255, 0),
"connector": (0, 128, 255),
"jumper": (255, 128, 0),
"emi_filter": (128, 0, 255),
"button": (0, 255, 128),
"clock": (255, 0, 128),
"transformer": (128, 128, 0),
"potentiometer": (0, 128, 128),
"heatsink": (128, 0, 128),
"fuse": (200, 200, 0),
"ferrite_bead": (0, 200, 200),
"buzzer": (200, 0, 200),
"display": (100, 200, 255),
"battery": (255, 200, 100),
}
DEFAULT_COLOR = (255, 255, 255)
def get_client():
return InferenceHTTPClient(
api_url="https://serverless.roboflow.com",
api_key=ROBOFLOW_API_KEY
)
def run_detection(image_path):
print(f"[->] Sending image to Roboflow API...")
client = get_client()
result = client.infer(image_path, model_id=MODEL_ID)
detections = []
predictions = result.get("predictions", [])
img_w = result.get("image", {}).get("width", 1)
img_h = result.get("image", {}).get("height", 1)
for pred in predictions:
confidence = pred.get("confidence", 0)
if confidence < CONFIDENCE_THRESH:
continue
label = pred.get("class", "unknown").lower()
cx = pred.get("x", 0)
cy = pred.get("y", 0)
w = pred.get("width", 0)
h = pred.get("height", 0)
x1 = max(0, int(cx - w / 2))
y1 = max(0, int(cy - h / 2))
x2 = min(img_w, int(cx + w / 2))
y2 = min(img_h, int(cy + h / 2))
detections.append({
'label': label,
'confidence': round(confidence, 3),
'bbox': (x1, y1, x2, y2)
})
print(f"[OK] Roboflow returned {len(predictions)} predictions, "
f"{len(detections)} above {CONFIDENCE_THRESH:.0%} confidence")
return detections
def detect_traces(img):
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower_copper = np.array([10, 50, 50])
upper_copper = np.array([30, 255, 255])
copper_mask = cv2.inRange(hsv, lower_copper, upper_copper)
lower_silver = np.array([0, 0, 180])
upper_silver = np.array([180, 30, 255])
silver_mask = cv2.inRange(hsv, lower_silver, upper_silver)
trace_mask = cv2.bitwise_or(copper_mask, silver_mask)
kernel = np.ones((2, 2), np.uint8)
trace_mask = cv2.morphologyEx(trace_mask, cv2.MORPH_OPEN, kernel, iterations=1)
trace_mask = cv2.morphologyEx(trace_mask, cv2.MORPH_CLOSE, kernel, iterations=1)
return trace_mask
def draw_detections(img, detections, trace_mask=None):
output = img.copy()
if trace_mask is not None:
trace_overlay = np.zeros_like(output)
trace_overlay[trace_mask > 0] = (255, 100, 0)
output = cv2.addWeighted(output, 1.0, trace_overlay, 0.3, 0)
for det in detections:
x1, y1, x2, y2 = det['bbox']
label = det['label']
conf = det['confidence']
color = LABEL_COLORS.get(label, DEFAULT_COLOR)
cv2.rectangle(output, (x1, y1), (x2, y2), color, 2)
text = f"{label} {conf:.0%}"
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 0.45
thickness = 1
(tw, th), _ = cv2.getTextSize(text, font, font_scale, thickness)
cv2.rectangle(output,
(x1, max(0, y1 - th - 8)),
(x1 + tw + 6, y1),
color, -1)
cv2.putText(output, text,
(x1 + 3, y1 - 4),
font, font_scale, (0, 0, 0), thickness, cv2.LINE_AA)
return output
def print_summary(detections):
counts = Counter(d['label'] for d in detections)
print("\n-- Detection Summary ---------------------")
for label, count in sorted(counts.items(), key=lambda x: -x[1]):
avg_conf = np.mean([d['confidence'] for d in detections
if d['label'] == label])
print(f" {label:<20} x{count} avg conf: {avg_conf:.0%}")
print(f" {'TOTAL':<20} x{len(detections)}")
print("------------------------------------------\n")
def save_json(detections, output_path):
data = {
"total_components": len(detections),
"components": [
{**d, "bbox": list(d["bbox"])}
for d in detections
]
}
with open(output_path, "w") as f:
json.dump(data, f, indent=2)
print(f"[OK] Results saved as JSON: {output_path}")
def detect_components(image_path, save_output=True, show_traces=True):
print(f"\n{'='*50}")
print(f" PCB Component Detector")
print(f" Image: {image_path}")
print(f"{'='*50}\n")
img = cv2.imread(image_path)
if img is None:
print(f"[X] Could not load image: {image_path}")
return []
print(f"[OK] Image loaded: {img.shape[1]}x{img.shape[0]} px")
detections = run_detection(image_path)
if not detections:
print("[!] No components detected. Try a clearer PCB image.")
return []
trace_mask = None
if show_traces:
trace_mask = detect_traces(img)
trace_px = np.count_nonzero(trace_mask)
print(f"[OK] Traces detected: {trace_px} pixels")
print_summary(detections)
if save_output:
base, ext = os.path.splitext(image_path)
annotated = draw_detections(img, detections, trace_mask)
img_out = base + "_detected" + ext
cv2.imwrite(img_out, annotated)
print(f"[OK] Annotated image saved: {img_out}")
json_out = base + "_results.json"
save_json(detections, json_out)
return detections
if __name__ == "__main__":
if len(sys.argv) < 2:
test_image = "sample 5.jpg"
print(f"No image specified. Using default: {test_image}")
else:
test_image = sys.argv[1]
detections = detect_components(test_image)