|
@@ -17,7 +17,7 @@ class ClassifyEdgeCorner:
|
|
|
"""
|
|
"""
|
|
|
self.PIXEL_RESOLUTION_UM = PIXEL_RESOLUTION_UM
|
|
self.PIXEL_RESOLUTION_UM = PIXEL_RESOLUTION_UM
|
|
|
self.CORNER_SIZE_MM = PIXEL_CENTER_UM
|
|
self.CORNER_SIZE_MM = PIXEL_CENTER_UM
|
|
|
- self.EDGE_SIZE_MM = EDGE_SIZE_MM # 新增:边的宽度
|
|
|
|
|
|
|
+ self.EDGE_SIZE_MM = EDGE_SIZE_MM
|
|
|
|
|
|
|
|
def get_rotated_rect_data(self, outer_box_data):
|
|
def get_rotated_rect_data(self, outer_box_data):
|
|
|
"""
|
|
"""
|
|
@@ -42,7 +42,6 @@ class ClassifyEdgeCorner:
|
|
|
def create_corner_regions(self, outer_box_corners, corner_size_px):
|
|
def create_corner_regions(self, outer_box_corners, corner_size_px):
|
|
|
"""
|
|
"""
|
|
|
根据外框的角点和指定的尺寸(像素),创建四个角区的多边形。
|
|
根据外框的角点和指定的尺寸(像素),创建四个角区的多边形。
|
|
|
- (逻辑保持不变)
|
|
|
|
|
"""
|
|
"""
|
|
|
corner_regions = []
|
|
corner_regions = []
|
|
|
num_corners = len(outer_box_corners)
|
|
num_corners = len(outer_box_corners)
|
|
@@ -58,7 +57,6 @@ class ClassifyEdgeCorner:
|
|
|
norm1 = np.linalg.norm(vec1)
|
|
norm1 = np.linalg.norm(vec1)
|
|
|
norm2 = np.linalg.norm(vec2)
|
|
norm2 = np.linalg.norm(vec2)
|
|
|
|
|
|
|
|
- # 防止除以0的保护
|
|
|
|
|
if norm1 == 0 or norm2 == 0:
|
|
if norm1 == 0 or norm2 == 0:
|
|
|
unit_vec1 = np.array([0, 0])
|
|
unit_vec1 = np.array([0, 0])
|
|
|
unit_vec2 = np.array([0, 0])
|
|
unit_vec2 = np.array([0, 0])
|
|
@@ -71,6 +69,7 @@ class ClassifyEdgeCorner:
|
|
|
q2 = p_current + corner_size_px * unit_vec2
|
|
q2 = p_current + corner_size_px * unit_vec2
|
|
|
q3 = q1 + corner_size_px * unit_vec2
|
|
q3 = q1 + corner_size_px * unit_vec2
|
|
|
|
|
|
|
|
|
|
+ # 确保生成的点是 float32,用于相交计算
|
|
|
corner_poly = np.array([q0, q1, q3, q2], dtype=np.float32)
|
|
corner_poly = np.array([q0, q1, q3, q2], dtype=np.float32)
|
|
|
corner_regions.append(corner_poly)
|
|
corner_regions.append(corner_poly)
|
|
|
|
|
|
|
@@ -86,7 +85,6 @@ class ClassifyEdgeCorner:
|
|
|
inner_w = w - 2 * edge_size_px
|
|
inner_w = w - 2 * edge_size_px
|
|
|
inner_h = h - 2 * edge_size_px
|
|
inner_h = h - 2 * edge_size_px
|
|
|
|
|
|
|
|
- # 如果边设置得太宽,导致内框消失,则返回 None 或极小的框
|
|
|
|
|
if inner_w <= 0 or inner_h <= 0:
|
|
if inner_w <= 0 or inner_h <= 0:
|
|
|
logger.warning("警告: EDGE_SIZE_MM 设置过大,超过了卡牌尺寸,面(Face)区域将不存在。")
|
|
logger.warning("警告: EDGE_SIZE_MM 设置过大,超过了卡牌尺寸,面(Face)区域将不存在。")
|
|
|
return None
|
|
return None
|
|
@@ -96,15 +94,34 @@ class ClassifyEdgeCorner:
|
|
|
|
|
|
|
|
# 获取内框的四个角点
|
|
# 获取内框的四个角点
|
|
|
inner_box = cv2.boxPoints(inner_rect)
|
|
inner_box = cv2.boxPoints(inner_rect)
|
|
|
- inner_box = np.int32(inner_box) # 转换为整数点以便于 polygon test
|
|
|
|
|
|
|
+ # 修改:为了支持 intersectConvexConvex,这里转换为 float32 而不是 int32
|
|
|
|
|
+ inner_box = np.array(inner_box, dtype=np.float32)
|
|
|
return inner_box
|
|
return inner_box
|
|
|
|
|
|
|
|
|
|
+ def check_intersection(self, poly1, poly2):
|
|
|
|
|
+ """
|
|
|
|
|
+ 检测两个凸多边形是否相交(重合面积 > 0)
|
|
|
|
|
+ poly1, poly2: np.float32 类型的多边形点集
|
|
|
|
|
+ """
|
|
|
|
|
+ if poly1 is None or poly2 is None:
|
|
|
|
|
+ return False
|
|
|
|
|
+
|
|
|
|
|
+ # intersectConvexConvex 返回 (intersection_area, intersection_polygon)
|
|
|
|
|
+ # 我们只需要面积即可。如果面积 > 0 (考虑到浮点误差,可以用 > 0.01) 即视为相交
|
|
|
|
|
+ try:
|
|
|
|
|
+ intersection_area, _ = cv2.intersectConvexConvex(poly1, poly2)
|
|
|
|
|
+ return intersection_area > 0.1 # 设置一个微小的阈值防止浮点噪声
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ # 极少数情况下如果多边形退化可能报错,视为不相交
|
|
|
|
|
+ logger.debug(f"Intersection check failed: {e}")
|
|
|
|
|
+ return False
|
|
|
|
|
+
|
|
|
def classify_defects_location(self, defect_data, outer_box_data):
|
|
def classify_defects_location(self, defect_data, outer_box_data):
|
|
|
"""
|
|
"""
|
|
|
- 主函数:分类为 corner, face, edge
|
|
|
|
|
|
|
+ 主函数:分类为 corner, face, edge (基于重合逻辑)
|
|
|
|
|
+ 优先级:Face (只要碰到面就算面) > Corner (没碰到面但碰到角) > Edge (其余)
|
|
|
"""
|
|
"""
|
|
|
if not defect_data or 'defects' not in defect_data or not defect_data['defects']:
|
|
if not defect_data or 'defects' not in defect_data or not defect_data['defects']:
|
|
|
- # logger.warn("无缺陷数据")
|
|
|
|
|
return defect_data
|
|
return defect_data
|
|
|
|
|
|
|
|
# 1. 单位转换
|
|
# 1. 单位转换
|
|
@@ -117,15 +134,13 @@ class ClassifyEdgeCorner:
|
|
|
|
|
|
|
|
# 2. 获取外框几何信息
|
|
# 2. 获取外框几何信息
|
|
|
try:
|
|
try:
|
|
|
- # 获取旋转矩形完整数据 ((x,y), (w,h), angle)
|
|
|
|
|
rect_data = self.get_rotated_rect_data(outer_box_data)
|
|
rect_data = self.get_rotated_rect_data(outer_box_data)
|
|
|
- # 获取外框的四个角点 (用于角区计算)
|
|
|
|
|
outer_box_corners = cv2.boxPoints(rect_data)
|
|
outer_box_corners = cv2.boxPoints(rect_data)
|
|
|
except ValueError as e:
|
|
except ValueError as e:
|
|
|
logger.error(f"外框数据错误: {e}")
|
|
logger.error(f"外框数据错误: {e}")
|
|
|
return None
|
|
return None
|
|
|
|
|
|
|
|
- # 3. 定义区域
|
|
|
|
|
|
|
+ # 3. 定义区域 (多边形)
|
|
|
# A. 定义四个角区
|
|
# A. 定义四个角区
|
|
|
corner_regions = self.create_corner_regions(outer_box_corners, corner_size_px)
|
|
corner_regions = self.create_corner_regions(outer_box_corners, corner_size_px)
|
|
|
|
|
|
|
@@ -138,25 +153,27 @@ class ClassifyEdgeCorner:
|
|
|
if 'min_rect' not in defect or not defect['min_rect']:
|
|
if 'min_rect' not in defect or not defect['min_rect']:
|
|
|
continue
|
|
continue
|
|
|
|
|
|
|
|
- # 获取缺陷中心点
|
|
|
|
|
- center_point = tuple(defect['min_rect'][0])
|
|
|
|
|
|
|
+ # --- 步骤变更:获取缺陷的多边形轮廓 ---
|
|
|
|
|
+ # defect['min_rect'] 结构通常为 ((x,y), (w,h), angle)
|
|
|
|
|
+ defect_rect = defect['min_rect']
|
|
|
|
|
+ defect_box = cv2.boxPoints(defect_rect)
|
|
|
|
|
+ defect_poly = np.array(defect_box, dtype=np.float32)
|
|
|
|
|
|
|
|
# --- 判定逻辑优先级 ---
|
|
# --- 判定逻辑优先级 ---
|
|
|
- # 优先级 1: 面 (Face)
|
|
|
|
|
- # 如果点在内缩矩形内部,且不是角,那就是面
|
|
|
|
|
|
|
+
|
|
|
|
|
+ # 优先级 1: 面 (Face) - 只要碰到 Face 区域就算 Face
|
|
|
is_face = False
|
|
is_face = False
|
|
|
if face_region is not None:
|
|
if face_region is not None:
|
|
|
- if cv2.pointPolygonTest(face_region, center_point, False) >= 0:
|
|
|
|
|
|
|
+ if self.check_intersection(face_region, defect_poly):
|
|
|
is_face = True
|
|
is_face = True
|
|
|
|
|
|
|
|
if is_face:
|
|
if is_face:
|
|
|
defect['defect_type'] = 'face'
|
|
defect['defect_type'] = 'face'
|
|
|
else:
|
|
else:
|
|
|
- # 优先级 2: 角 (Corner)
|
|
|
|
|
|
|
+ # 优先级 2: 角 (Corner) - 没碰到 Face,但碰到了 Corner 区域
|
|
|
is_corner = False
|
|
is_corner = False
|
|
|
for region in corner_regions:
|
|
for region in corner_regions:
|
|
|
- # pointPolygonTest: >0 内部, =0 边界, <0 外部
|
|
|
|
|
- if cv2.pointPolygonTest(region, center_point, False) >= 0:
|
|
|
|
|
|
|
+ if self.check_intersection(region, defect_poly):
|
|
|
is_corner = True
|
|
is_corner = True
|
|
|
break
|
|
break
|
|
|
|
|
|
|
@@ -164,8 +181,7 @@ class ClassifyEdgeCorner:
|
|
|
defect['defect_type'] = 'corner'
|
|
defect['defect_type'] = 'corner'
|
|
|
else:
|
|
else:
|
|
|
# 优先级 3: 边 (Edge)
|
|
# 优先级 3: 边 (Edge)
|
|
|
- # 既不在角区,也不在面区(内框)里,但还在处理范围内,那就是边
|
|
|
|
|
- # (注:这里默认缺陷是在外框内的,如果是外框外的噪声,通常也会被算作 Edge 或需要额外过滤)
|
|
|
|
|
|
|
+ # 既没碰到面,也没碰到角,说明完全处于边区域内(或者在外框外)
|
|
|
defect['defect_type'] = 'edge'
|
|
defect['defect_type'] = 'edge'
|
|
|
|
|
|
|
|
processed_count += 1
|
|
processed_count += 1
|