detect-contours / scalingtestupdated.py
umairahmad1789's picture
Update scalingtestupdated.py
3a66ca9 verified
import cv2
import numpy as np
import os
import argparse
from typing import Union
from matplotlib import pyplot as plt
class ScalingSquareDetector:
def __init__(self, feature_detector="ORB", debug=False):
"""
Initialize the detector with the desired feature matching algorithm.
:param feature_detector: "ORB" or "SIFT" (default is "ORB").
:param debug: If True, saves intermediate images for debugging.
"""
self.feature_detector = feature_detector
self.debug = debug
self.detector = self._initialize_detector()
def _initialize_detector(self):
"""
Initialize the chosen feature detector.
:return: OpenCV detector object.
"""
if self.feature_detector.upper() == "SIFT":
return cv2.SIFT_create()
elif self.feature_detector.upper() == "ORB":
return cv2.ORB_create()
else:
raise ValueError("Invalid feature detector. Choose 'ORB' or 'SIFT'.")
def find_scaling_square(
self, reference_image_path, target_image, known_size_mm, roi_margin=30
):
"""
Detect the scaling square in the target image based on the reference image.
:param reference_image_path: Path to the reference image of the square.
:param target_image_path: Path to the target image containing the square.
:param known_size_mm: Physical size of the square in millimeters.
:param roi_margin: Margin to expand the ROI around the detected square (in pixels).
:return: Scaling factor (mm per pixel).
"""
contours, _ = cv2.findContours(
target_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE
)
if not contours:
raise ValueError("No contours found in the cropped ROI.")
# # Select the largest square-like contour
largest_square = None
largest_square_area = 0
for contour in contours:
x_c, y_c, w_c, h_c = cv2.boundingRect(contour)
aspect_ratio = w_c / float(h_c)
if 0.9 <= aspect_ratio <= 1.1:
peri = cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, 0.02 * peri, True)
if len(approx) == 4:
area = cv2.contourArea(contour)
if area > largest_square_area:
largest_square = contour
largest_square_area = area
# if largest_square is None:
# raise ValueError("No square-like contour found in the ROI.")
# Draw the largest contour on the original image
target_image_color = cv2.cvtColor(target_image, cv2.COLOR_GRAY2BGR)
cv2.drawContours(
target_image_color, largest_square, -1, (255, 0, 0), 3
)
# if self.debug:
cv2.imwrite("largest_contour.jpg", target_image_color)
# Calculate the bounding rectangle of the largest contour
x, y, w, h = cv2.boundingRect(largest_square)
square_width_px = w
square_height_px = h
# Calculate the scaling factor
avg_square_size_px = (square_width_px + square_height_px) / 2
scaling_factor = 0.5 / avg_square_size_px # mm per pixel
return scaling_factor #, square_height_px, square_width_px, roi_binary
def draw_debug_images(self, output_folder):
"""
Save debug images if enabled.
:param output_folder: Directory to save debug images.
"""
if self.debug:
if not os.path.exists(output_folder):
os.makedirs(output_folder)
debug_images = ["largest_contour.jpg"]
for img_name in debug_images:
if os.path.exists(img_name):
os.rename(img_name, os.path.join(output_folder, img_name))
def calculate_scaling_factor(
reference_image_path,
target_image,
known_square_size_mm=12.7,
feature_detector="ORB",
debug=False,
roi_margin=30,
):
# Initialize detector
detector = ScalingSquareDetector(feature_detector=feature_detector, debug=debug)
# Find scaling square and calculate scaling factor
scaling_factor = detector.find_scaling_square(
reference_image_path=reference_image_path,
target_image=target_image,
known_size_mm=known_square_size_mm,
roi_margin=roi_margin,
)
# Save debug images
if debug:
detector.draw_debug_images("debug_outputs")
return scaling_factor
# Example usage:
if __name__ == "__main__":
import os
from PIL import Image
from ultralytics import YOLO
from app import yolo_detect, shrink_bbox
from ultralytics.utils.plotting import save_one_box
for idx, file in enumerate(os.listdir("./sample_images")):
img = np.array(Image.open(os.path.join("./sample_images", file)))
img = yolo_detect(img, ['box'])
model = YOLO("./last.pt")
res = model.predict(img, conf=0.6)
box_img = save_one_box(res[0].cpu().boxes.xyxy, im=res[0].orig_img, save=False)
# img = shrink_bbox(box_img, 1.20)
cv2.imwrite(f"./outputs/{idx}_{file}", box_img)
print("File: ",f"./outputs/{idx}_{file}")
try:
scaling_factor = calculate_scaling_factor(
reference_image_path="./Reference_ScalingBox.jpg",
target_image=box_img,
known_square_size_mm=12.7,
feature_detector="ORB",
debug=False,
roi_margin=90,
)
# cv2.imwrite(f"./outputs/{idx}_binary_{file}", roi_binary)
# Square size in mm
# square_size_mm = 12.7
# # Compute the calculated scaling factors and compare
# calculated_scaling_factor = square_size_mm / height_px
# discrepancy = abs(calculated_scaling_factor - scaling_factor)
# import pprint
# pprint.pprint({
# "height_px": height_px,
# "width_px": width_px,
# "given_scaling_factor": scaling_factor,
# "calculated_scaling_factor": calculated_scaling_factor,
# "discrepancy": discrepancy,
# })
print(f"Scaling Factor (mm per pixel): {scaling_factor:.6f}")
except Exception as e:
from traceback import print_exc
print(print_exc())
print(f"Error: {e}")