Spaces:
Running
Running
Update scalingtestupdated.py
Browse files- scalingtestupdated.py +200 -85
scalingtestupdated.py
CHANGED
@@ -2,7 +2,7 @@ import cv2
|
|
2 |
import numpy as np
|
3 |
import os
|
4 |
import argparse
|
5 |
-
from typing import Union
|
6 |
from matplotlib import pyplot as plt
|
7 |
|
8 |
class ScalingSquareDetector:
|
@@ -33,8 +33,7 @@ class ScalingSquareDetector:
|
|
33 |
):
|
34 |
"""
|
35 |
Detect the scaling square in the target image based on the reference image.
|
36 |
-
:param
|
37 |
-
:param target_image_path: Path to the target image containing the square.
|
38 |
:param known_size_mm: Physical size of the square in millimeters.
|
39 |
:param roi_margin: Margin to expand the ROI around the detected square (in pixels).
|
40 |
:return: Scaling factor (mm per pixel).
|
@@ -44,54 +43,38 @@ class ScalingSquareDetector:
|
|
44 |
)
|
45 |
|
46 |
if not contours:
|
47 |
-
raise ValueError("No contours found in the
|
48 |
|
49 |
-
#
|
50 |
print(f"No of contours: {len(contours)}")
|
51 |
-
|
52 |
-
|
53 |
-
#
|
54 |
-
# x_c, y_c, w_c, h_c = cv2.boundingRect(contour)
|
55 |
-
# aspect_ratio = w_c / float(h_c)
|
56 |
-
# if 0.9 <= aspect_ratio <= 1.1:
|
57 |
-
# peri = cv2.arcLength(contour, True)
|
58 |
-
# approx = cv2.approxPolyDP(contour, 0.02 * peri, True)
|
59 |
-
# if len(approx) == 4:
|
60 |
-
# area = cv2.contourArea(contour)
|
61 |
-
# if area > largest_square_area:
|
62 |
-
# largest_square = contour
|
63 |
-
# largest_square_area = area
|
64 |
-
|
65 |
-
for contour in contours:
|
66 |
-
largest_square = contour
|
67 |
-
|
68 |
-
# if largest_square is None:
|
69 |
-
# raise ValueError("No square-like contour found in the ROI.")
|
70 |
-
|
71 |
-
# Draw the largest contour on the original image
|
72 |
target_image_color = cv2.cvtColor(target_image, cv2.COLOR_GRAY2BGR)
|
73 |
cv2.drawContours(
|
74 |
-
target_image_color,
|
75 |
)
|
76 |
|
77 |
-
|
78 |
-
|
79 |
|
80 |
# Calculate the bounding rectangle of the largest contour
|
81 |
-
x, y, w, h = cv2.boundingRect(
|
82 |
square_width_px = w
|
83 |
square_height_px = h
|
84 |
-
|
85 |
-
print(f"
|
86 |
-
print(f"
|
|
|
87 |
|
88 |
-
# Calculate the scaling factor
|
89 |
avg_square_size_px = (square_width_px + square_height_px) / 2
|
90 |
-
print(f"
|
|
|
91 |
scaling_factor = known_size_mm / avg_square_size_px # mm per pixel
|
92 |
-
print(f"scaling factor: {scaling_factor} mm per pixel")
|
93 |
|
94 |
-
return scaling_factor
|
95 |
|
96 |
def draw_debug_images(self, output_folder):
|
97 |
"""
|
@@ -113,7 +96,17 @@ def calculate_scaling_factor(
|
|
113 |
feature_detector="ORB",
|
114 |
debug=False,
|
115 |
roi_margin=30,
|
116 |
-
):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
117 |
# Initialize detector
|
118 |
detector = ScalingSquareDetector(feature_detector=feature_detector, debug=debug)
|
119 |
|
@@ -131,54 +124,176 @@ def calculate_scaling_factor(
|
|
131 |
return scaling_factor
|
132 |
|
133 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
134 |
# Example usage:
|
135 |
if __name__ == "__main__":
|
136 |
import os
|
137 |
from PIL import Image
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
|
|
|
|
|
|
|
|
|
|
|
2 |
import numpy as np
|
3 |
import os
|
4 |
import argparse
|
5 |
+
from typing import Union, Tuple
|
6 |
from matplotlib import pyplot as plt
|
7 |
|
8 |
class ScalingSquareDetector:
|
|
|
33 |
):
|
34 |
"""
|
35 |
Detect the scaling square in the target image based on the reference image.
|
36 |
+
:param target_image: Binary image containing the square.
|
|
|
37 |
:param known_size_mm: Physical size of the square in millimeters.
|
38 |
:param roi_margin: Margin to expand the ROI around the detected square (in pixels).
|
39 |
:return: Scaling factor (mm per pixel).
|
|
|
43 |
)
|
44 |
|
45 |
if not contours:
|
46 |
+
raise ValueError("No contours found in the target image.")
|
47 |
|
48 |
+
# Select the largest contour (assuming it's the reference object)
|
49 |
print(f"No of contours: {len(contours)}")
|
50 |
+
largest_contour = max(contours, key=cv2.contourArea)
|
51 |
+
|
52 |
+
# Draw the largest contour on the original image for debugging
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
53 |
target_image_color = cv2.cvtColor(target_image, cv2.COLOR_GRAY2BGR)
|
54 |
cv2.drawContours(
|
55 |
+
target_image_color, [largest_contour], -1, (255, 0, 0), 3
|
56 |
)
|
57 |
|
58 |
+
if self.debug:
|
59 |
+
cv2.imwrite("largest_contour.jpg", target_image_color)
|
60 |
|
61 |
# Calculate the bounding rectangle of the largest contour
|
62 |
+
x, y, w, h = cv2.boundingRect(largest_contour)
|
63 |
square_width_px = w
|
64 |
square_height_px = h
|
65 |
+
|
66 |
+
print(f"Reference object size: {known_size_mm} mm")
|
67 |
+
print(f"Detected width: {square_width_px} px")
|
68 |
+
print(f"Detected height: {square_height_px} px")
|
69 |
|
70 |
+
# Calculate the scaling factor using average of width and height
|
71 |
avg_square_size_px = (square_width_px + square_height_px) / 2
|
72 |
+
print(f"Average square size: {avg_square_size_px} px")
|
73 |
+
|
74 |
scaling_factor = known_size_mm / avg_square_size_px # mm per pixel
|
75 |
+
print(f"Calculated scaling factor: {scaling_factor:.6f} mm per pixel")
|
76 |
|
77 |
+
return scaling_factor
|
78 |
|
79 |
def draw_debug_images(self, output_folder):
|
80 |
"""
|
|
|
96 |
feature_detector="ORB",
|
97 |
debug=False,
|
98 |
roi_margin=30,
|
99 |
+
) -> float:
|
100 |
+
"""
|
101 |
+
Calculate scaling factor from reference object in image.
|
102 |
+
|
103 |
+
:param target_image: Input image (numpy array)
|
104 |
+
:param reference_obj_size_mm: Known size of reference object in millimeters
|
105 |
+
:param feature_detector: Feature detector to use ("ORB" or "SIFT")
|
106 |
+
:param debug: Enable debug output
|
107 |
+
:param roi_margin: ROI margin in pixels
|
108 |
+
:return: Scaling factor in mm per pixel
|
109 |
+
"""
|
110 |
# Initialize detector
|
111 |
detector = ScalingSquareDetector(feature_detector=feature_detector, debug=debug)
|
112 |
|
|
|
124 |
return scaling_factor
|
125 |
|
126 |
|
127 |
+
def convert_units(value: float, from_unit: str, to_unit: str) -> float:
|
128 |
+
"""
|
129 |
+
Convert between mm and inches.
|
130 |
+
|
131 |
+
:param value: Value to convert
|
132 |
+
:param from_unit: Source unit ("mm" or "inches")
|
133 |
+
:param to_unit: Target unit ("mm" or "inches")
|
134 |
+
:return: Converted value
|
135 |
+
"""
|
136 |
+
if from_unit == to_unit:
|
137 |
+
return value
|
138 |
+
|
139 |
+
if from_unit == "inches" and to_unit == "mm":
|
140 |
+
return value * 25.4
|
141 |
+
elif from_unit == "mm" and to_unit == "inches":
|
142 |
+
return value / 25.4
|
143 |
+
else:
|
144 |
+
raise ValueError(f"Unsupported unit conversion: {from_unit} to {to_unit}")
|
145 |
+
|
146 |
+
|
147 |
+
def calculate_scaling_factor_with_units(
|
148 |
+
target_image,
|
149 |
+
reference_obj_size: float,
|
150 |
+
reference_unit: str = "mm",
|
151 |
+
output_unit: str = "mm",
|
152 |
+
feature_detector="ORB",
|
153 |
+
debug=False,
|
154 |
+
roi_margin=30,
|
155 |
+
) -> Tuple[float, str]:
|
156 |
+
"""
|
157 |
+
Calculate scaling factor with proper unit handling.
|
158 |
+
|
159 |
+
:param target_image: Input image (numpy array)
|
160 |
+
:param reference_obj_size: Known size of reference object
|
161 |
+
:param reference_unit: Unit of reference object size ("mm" or "inches")
|
162 |
+
:param output_unit: Desired unit for scaling factor ("mm" or "inches")
|
163 |
+
:param feature_detector: Feature detector to use ("ORB" or "SIFT")
|
164 |
+
:param debug: Enable debug output
|
165 |
+
:param roi_margin: ROI margin in pixels
|
166 |
+
:return: Tuple of (scaling_factor, unit_string)
|
167 |
+
"""
|
168 |
+
# Convert reference size to mm for internal calculation
|
169 |
+
reference_size_mm = convert_units(reference_obj_size, reference_unit, "mm")
|
170 |
+
|
171 |
+
# Calculate scaling factor in mm/px
|
172 |
+
scaling_factor_mm = calculate_scaling_factor(
|
173 |
+
target_image=target_image,
|
174 |
+
reference_obj_size_mm=reference_size_mm,
|
175 |
+
feature_detector=feature_detector,
|
176 |
+
debug=debug,
|
177 |
+
roi_margin=roi_margin,
|
178 |
+
)
|
179 |
+
|
180 |
+
# Convert scaling factor to desired output unit
|
181 |
+
if output_unit == "inches":
|
182 |
+
scaling_factor = scaling_factor_mm / 25.4 # Convert mm/px to inches/px
|
183 |
+
unit_string = "inches per pixel"
|
184 |
+
else:
|
185 |
+
scaling_factor = scaling_factor_mm
|
186 |
+
unit_string = "mm per pixel"
|
187 |
+
|
188 |
+
print(f"Final scaling factor: {scaling_factor:.6f} {unit_string}")
|
189 |
+
|
190 |
+
return scaling_factor, unit_string
|
191 |
+
|
192 |
+
|
193 |
+
# Paper size configurations (in mm and inches)
|
194 |
+
PAPER_SIZES = {
|
195 |
+
"A4": {"width_mm": 210, "height_mm": 297, "width_inches": 8.27, "height_inches": 11.69},
|
196 |
+
"A3": {"width_mm": 297, "height_mm": 420, "width_inches": 11.69, "height_inches": 16.54},
|
197 |
+
"US Letter": {"width_mm": 215.9, "height_mm": 279.4, "width_inches": 8.5, "height_inches": 11.0}
|
198 |
+
}
|
199 |
+
|
200 |
+
|
201 |
+
def calculate_paper_scaling_factor(
|
202 |
+
paper_contour: np.ndarray,
|
203 |
+
paper_size: str,
|
204 |
+
output_unit: str = "mm"
|
205 |
+
) -> Tuple[float, str]:
|
206 |
+
"""
|
207 |
+
Calculate scaling factor based on detected paper dimensions with proper unit handling.
|
208 |
+
|
209 |
+
:param paper_contour: Detected paper contour
|
210 |
+
:param paper_size: Paper size identifier ("A4", "A3", "US Letter")
|
211 |
+
:param output_unit: Desired unit for scaling factor ("mm" or "inches")
|
212 |
+
:return: Tuple of (scaling_factor, unit_string)
|
213 |
+
"""
|
214 |
+
# Get paper dimensions in the desired unit
|
215 |
+
if output_unit == "inches":
|
216 |
+
expected_width = PAPER_SIZES[paper_size]["width_inches"]
|
217 |
+
expected_height = PAPER_SIZES[paper_size]["height_inches"]
|
218 |
+
unit_string = "inches per pixel"
|
219 |
+
else:
|
220 |
+
expected_width = PAPER_SIZES[paper_size]["width_mm"]
|
221 |
+
expected_height = PAPER_SIZES[paper_size]["height_mm"]
|
222 |
+
unit_string = "mm per pixel"
|
223 |
+
|
224 |
+
# Calculate bounding rectangle of paper contour
|
225 |
+
rect = cv2.boundingRect(paper_contour)
|
226 |
+
detected_width_px = rect[2]
|
227 |
+
detected_height_px = rect[3]
|
228 |
+
|
229 |
+
# Calculate scaling factors for both dimensions
|
230 |
+
scale_x = expected_width / detected_width_px
|
231 |
+
scale_y = expected_height / detected_height_px
|
232 |
+
|
233 |
+
# Use the minimum scale to ensure the object fits within expected dimensions
|
234 |
+
scaling_factor = min(scale_x, scale_y)
|
235 |
+
|
236 |
+
print(f"Paper detection: {detected_width_px}x{detected_height_px} px")
|
237 |
+
print(f"Expected paper size: {expected_width}x{expected_height} {output_unit}")
|
238 |
+
print(f"Scale X: {scale_x:.6f}, Scale Y: {scale_y:.6f}")
|
239 |
+
print(f"Final scaling factor: {scaling_factor:.6f} {unit_string}")
|
240 |
+
|
241 |
+
return scaling_factor, unit_string
|
242 |
+
|
243 |
+
|
244 |
# Example usage:
|
245 |
if __name__ == "__main__":
|
246 |
import os
|
247 |
from PIL import Image
|
248 |
+
|
249 |
+
# Test with different units
|
250 |
+
sample_dir = "./sample_images"
|
251 |
+
if os.path.exists(sample_dir):
|
252 |
+
for idx, file in enumerate(os.listdir(sample_dir)):
|
253 |
+
if file.lower().endswith(('.jpg', '.jpeg', '.png')):
|
254 |
+
img_path = os.path.join(sample_dir, file)
|
255 |
+
img = np.array(Image.open(img_path))
|
256 |
+
|
257 |
+
# Convert to grayscale if needed
|
258 |
+
if len(img.shape) == 3:
|
259 |
+
img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
|
260 |
+
else:
|
261 |
+
img_gray = img
|
262 |
+
|
263 |
+
print(f"\nProcessing: {file}")
|
264 |
+
|
265 |
+
try:
|
266 |
+
# Test with mm units
|
267 |
+
scaling_factor_mm, unit_mm = calculate_scaling_factor_with_units(
|
268 |
+
target_image=img_gray,
|
269 |
+
reference_obj_size=20.0, # 20mm reference object
|
270 |
+
reference_unit="mm",
|
271 |
+
output_unit="mm",
|
272 |
+
feature_detector="ORB",
|
273 |
+
debug=False,
|
274 |
+
roi_margin=90,
|
275 |
+
)
|
276 |
+
|
277 |
+
# Test with inch units
|
278 |
+
scaling_factor_inch, unit_inch = calculate_scaling_factor_with_units(
|
279 |
+
target_image=img_gray,
|
280 |
+
reference_obj_size=0.787, # ~20mm in inches
|
281 |
+
reference_unit="inches",
|
282 |
+
output_unit="inches",
|
283 |
+
feature_detector="ORB",
|
284 |
+
debug=False,
|
285 |
+
roi_margin=90,
|
286 |
+
)
|
287 |
+
|
288 |
+
print(f"MM scaling: {scaling_factor_mm:.6f} {unit_mm}")
|
289 |
+
print(f"Inch scaling: {scaling_factor_inch:.6f} {unit_inch}")
|
290 |
+
|
291 |
+
# Verify conversion consistency
|
292 |
+
converted_mm_to_inch = scaling_factor_mm / 25.4
|
293 |
+
print(f"Converted mm to inch: {converted_mm_to_inch:.6f}")
|
294 |
+
print(f"Difference: {abs(scaling_factor_inch - converted_mm_to_inch):.8f}")
|
295 |
+
|
296 |
+
except Exception as e:
|
297 |
+
print(f"Error processing {file}: {e}")
|
298 |
+
else:
|
299 |
+
print(f"Sample directory {sample_dir} not found")
|