Sfoglia il codice sorgente

居中框改为矩形

AnlaAnla 2 settimane fa
parent
commit
5bcd0fc4e9
3 ha cambiato i file con 318 aggiunte e 129 eliminazioni
  1. 221 120
      Test/test01.py
  2. 5 4
      app/services/defect_service.py
  3. 92 5
      app/utils/defect_inference/AnalyzeCenter.py

+ 221 - 120
Test/test01.py

@@ -1,121 +1,222 @@
-import torch
-import matplotlib.pyplot as plt
 import numpy as np
-
-plt.rcParams['font.family'] = 'SimHei'
-
-# --- 1. 准备非线性数据 ---
-# 我们来创建一个更复杂的数据模式,比如一条带有线性趋势的正弦曲线
-# 真实函数: y = sin(x) + 0.5*x + noise
-TRUE_FUNCTION = lambda x: 2*x + 3
-# TRUE_FUNCTION = lambda x: torch.sin(x) + 0.5 * x
-
-# 创建 100 个数据点
-X = torch.linspace(0, 16 * np.pi, 120).unsqueeze(1)
-y = TRUE_FUNCTION(X) + torch.randn(X.size()) * 0.4  # 加入一些噪声
-
-# 可视化我们创建的数据集
-plt.figure(figsize=(10, 5))
-plt.scatter(X.numpy(), y.numpy(), c='blue', label='原始数据点')
-plt.plot(X.numpy(), TRUE_FUNCTION(X).numpy(), 'g--', label='真实函数曲线')
-plt.title("非线性模拟数据")
-plt.xlabel("X")
-plt.ylabel("y")
-plt.legend()
-plt.grid(True)
-plt.show()
-
-# --- 2. 定义多项式模型和参数 ---
-# 我们的模型是 y_pred = a*x^3 + b*x^2 + c*x + d
-# 我们需要学习 4 个参数: a, b, c, d
-# 随机初始化它们,并设置 requires_grad=True
-a = torch.randn(1, requires_grad=True)
-b = torch.randn(1, requires_grad=True)
-c = torch.randn(1, requires_grad=True)
-d = torch.randn(1, requires_grad=True)
-
-print("--- 训练开始前 ---")
-print(f"随机初始化的参数: a={a.item():.3f}, b={b.item():.3f}, c={c.item():.3f}, d={d.item():.3f}")
-
-# --- 3. 定义损失函数和优化器 ---
-# 对于更复杂的模型,Adam 优化器通常表现更好
-# 学习率可以适当调高一点
-learning_rate = 0.01
-optimizer = torch.optim.Adam([a, b, c, d], lr=learning_rate)
-
-# 损失函数仍然使用均方误差
-loss_fn = torch.nn.MSELoss()
-
-# --- 4. 训练循环 ---
-# 增加训练周期数,因为模型更复杂,需要更多时间学习
-epochs = 18000
-all_losses = []
-
-plt.figure(figsize=(12, 6))
-plt.ion()
-
-for epoch in range(epochs):
-    # 4.1 前向传播 (Forward Pass) - 这是唯一需要大改的地方!
-    # 根据当前的 a, b, c, d 计算预测值
-    y_pred = a * X ** 3 + b * X ** 2 + c * X + d
-
-    # 4.2 计算损失
-    loss = loss_fn(y_pred, y)
-    all_losses.append(loss.item())
-
-    # 4.3 清空过往梯度
-    optimizer.zero_grad()
-
-    # 4.4 反向传播 - 核心步骤,但代码完全不变!
-    # PyTorch 自动处理复杂的求导链条
-    loss.backward()
-
-    # 4.5 更新参数 - 代码也完全不变!
-    optimizer.step()
-
-    # --- 可视化 ---
-    if (epoch + 1) % 100 == 0:
-        print(f'Epoch [{epoch + 1}/{epochs}], Loss: {loss.item():.4f}')
-
-        plt.clf()
-        plt.scatter(X.numpy(), y.numpy(), c='blue', s=15, label='原始数据')
-        plt.plot(X.numpy(), TRUE_FUNCTION(X).numpy(), 'g--', label='真实函数曲线')
-        # detach() 是为了切断梯度追踪,因为绘图不需要计算梯度
-        plt.plot(X.numpy(), y_pred.detach().numpy(), 'r-', label='当前拟合曲线')
-
-        plt.title(f"训练过程 - Epoch {epoch + 1}")
-        plt.xlabel("X")
-        plt.ylabel("y")
-        plt.ylim(y.min() - 1, y.max() + 1)  # 固定Y轴范围,防止图像跳动
-        plt.legend()
-        plt.grid(True)
-        plt.pause(0.1)
-
-plt.ioff()
-plt.show()
-
-# --- 5. 最终结果可视化 ---
-print("\n--- 训练完成 ---")
-print(f"学习到的参数: a={a.item():.3f}, b={b.item():.3f}, c={c.item():.3f}, d={d.item():.3f}")
-
-plt.figure(figsize=(10, 5))
-plt.scatter(X.numpy(), y.numpy(), c='blue', s=15, label='原始数据')
-plt.plot(X.numpy(), TRUE_FUNCTION(X).numpy(), 'g--', label='真实函数曲线')
-final_pred = a * X ** 3 + b * X ** 2 + c * X + d
-plt.plot(X.numpy(), final_pred.detach().numpy(), 'r-', label='最终拟合曲线')
-plt.title("最终拟合结果")
-plt.xlabel("X")
-plt.ylabel("y")
-plt.legend()
-plt.grid(True)
-plt.show()
-
-# 绘制损失函数下降曲线
-plt.figure(figsize=(10, 5))
-plt.plot(range(epochs), all_losses)
-plt.title("损失函数下降曲线")
-plt.xlabel("周期 (Epoch)")
-plt.ylabel("损失 (Loss)")
-plt.yscale('log')  # 使用对数坐标轴,可以更清晰地看到早期的快速下降和后期的缓慢优化
-plt.grid(True)
-plt.show()
+import cv2
+
+points = [
+                [
+                  350,
+                  329
+                ],
+                [
+                  347,
+                  332
+                ],
+                [
+                  304,
+                  332
+                ],
+                [
+                  301,
+                  335
+                ],
+                [
+                  301,
+                  2045
+                ],
+                [
+                  297,
+                  2049
+                ],
+                [
+                  297,
+                  2262
+                ],
+                [
+                  301,
+                  2266
+                ],
+                [
+                  301,
+                  2371
+                ],
+                [
+                  297,
+                  2374
+                ],
+                [
+                  297,
+                  3136
+                ],
+                [
+                  301,
+                  3140
+                ],
+                [
+                  301,
+                  3607
+                ],
+                [
+                  304,
+                  3611
+                ],
+                [
+                  1611,
+                  3611
+                ],
+                [
+                  1614,
+                  3607
+                ],
+                [
+                  1689,
+                  3607
+                ],
+                [
+                  1692,
+                  3611
+                ],
+                [
+                  2563,
+                  3611
+                ],
+                [
+                  2566,
+                  3614
+                ],
+                [
+                  2591,
+                  3614
+                ],
+                [
+                  2594,
+                  3611
+                ],
+                [
+                  2597,
+                  3611
+                ],
+                [
+                  2600,
+                  3607
+                ],
+                [
+                  2600,
+                  3592
+                ],
+                [
+                  2597,
+                  3589
+                ],
+                [
+                  2597,
+                  3462
+                ],
+                [
+                  2600,
+                  3459
+                ],
+                [
+                  2600,
+                  1760
+                ],
+                [
+                  2603,
+                  1757
+                ],
+                [
+                  2603,
+                  1674
+                ],
+                [
+                  2600,
+                  1670
+                ],
+                [
+                  2600,
+                  1593
+                ],
+                [
+                  2603,
+                  1590
+                ],
+                [
+                  2603,
+                  335
+                ],
+                [
+                  2597,
+                  335
+                ],
+                [
+                  2594,
+                  332
+                ],
+                [
+                  2572,
+                  332
+                ],
+                [
+                  2569,
+                  335
+                ],
+                [
+                  2553,
+                  335
+                ],
+                [
+                  2550,
+                  332
+                ],
+                [
+                  2144,
+                  332
+                ],
+                [
+                  2141,
+                  335
+                ],
+                [
+                  1872,
+                  335
+                ],
+                [
+                  1869,
+                  332
+                ],
+                [
+                  1841,
+                  332
+                ],
+                [
+                  1838,
+                  335
+                ],
+                [
+                  1751,
+                  335
+                ],
+                [
+                  1748,
+                  332
+                ],
+                [
+                  449,
+                  332
+                ],
+                [
+                  446,
+                  329
+                ]
+              ]
+points = np.array(points, dtype=np.int32)
+
+x, y, w, h = cv2.boundingRect(points)
+# 2. 构造 4 个顶点坐标
+result = np.array([
+    [x, y],             # 左上
+    [x + w, y],         # 右上
+    [x + w, y + h],     # 右下
+    [x, y + h]          # 左下
+], dtype=np.int32).tolist()
+
+print(f"Bounding Rect (x,y,w,h): {x}, {y}, {w}, {h}")
+print(f"Result (4 points):\n{result}")

+ 5 - 4
app/services/defect_service.py

@@ -3,7 +3,8 @@ import numpy as np
 from ..core.model_loader import get_predictor
 from app.utils.defect_inference.CardDefectAggregator import CardDefectAggregator
 from app.utils.defect_inference.arean_anylize_draw import DefectProcessor, DrawingParams
-from app.utils.defect_inference.AnalyzeCenter import analyze_centering_rotated, formate_center_data
+from app.utils.defect_inference.AnalyzeCenter import (
+    analyze_centering_rotated,analyze_centering_rect ,formate_center_data)
 from app.utils.defect_inference.DrawCenterInfo import draw_boxes_and_center_info
 from app.utils.defect_inference.ClassifyEdgeCorner import ClassifyEdgeCorner
 from app.utils.json_data_formate import formate_face_data, formate_add_edit_type
@@ -202,9 +203,9 @@ class DefectInferenceService:
         outer_result = center_json['box_result']['outer_box']
 
         if inference_type == "center":
-            inner_points = inner_result['shapes'][0]['points']
-            outer_points = outer_result['shapes'][0]['points']
-            center_result, inner_rect_box, outer_rect_box = analyze_centering_rotated(inner_points, outer_points)
+            inner_rect = inner_result['shapes'][0]['rect_box']
+            outer_rect = outer_result['shapes'][0]['rect_box']
+            center_result, inner_rect_box, outer_rect_box = analyze_centering_rect(inner_rect, outer_rect)
 
             center_result = formate_center_data(center_result,
                                                 inner_result, outer_result,

+ 92 - 5
app/utils/defect_inference/AnalyzeCenter.py

@@ -104,11 +104,25 @@ def analyze_centering_rotated(inner_points: Union[str, List], outer_points: Unio
     outer_rect = cv2.minAreaRect(outer_contour)
 
     # 获取矩形的4个角点, 并转化为坐标
-    outer_box_corners = cv2.boxPoints(outer_rect)
-    outer_box_corners_int = np.intp(outer_box_corners).tolist()
-
-    inner_box_corners = cv2.boxPoints(inner_rect)
-    inner_box_corners_int = np.intp(inner_box_corners).tolist()
+    # outer_box_corners = cv2.boxPoints(outer_rect)
+    # outer_box_corners_int = np.intp(outer_box_corners).tolist()
+    #
+    # inner_box_corners = cv2.boxPoints(inner_rect)
+    # inner_box_corners_int = np.intp(inner_box_corners).tolist()
+    def get_rect(contour):
+        # 获取矩形框
+        x, y, w, h = cv2.boundingRect(contour)
+        # 2. 构造 4 个顶点坐标
+        box_corners_int = np.array([
+            [x, y],  # 左上
+            [x + w, y],  # 右上
+            [x + w, y + h],  # 右下
+            [x, y + h]  # 左下
+        ], dtype=np.int32).tolist()
+        return box_corners_int
+
+    inner_box_corners_int = get_rect(inner_contour)
+    outer_box_corners_int = get_rect(outer_contour)
 
     # --- 1. 标准化矩形尺寸,确保宽度总是长边 ---
     def standardize_rect(rect):
@@ -194,6 +208,79 @@ def analyze_centering_rotated(inner_points: Union[str, List], outer_points: Unio
             angle_diff), inner_box_corners_int, outer_box_corners_int
 
 
+def analyze_centering_rect(inner_points: Union[str, List], outer_points: Union[str, List]):
+    """
+    使用横平竖直的矩形 (Axis-Aligned Bounding Box) 进行居中分析。
+    适用于前端已经修正过的矩形数据。
+    """
+    if isinstance(inner_points, str):
+        inner_points = get_points_from_file(inner_points)
+    if isinstance(outer_points, str):
+        outer_points = get_points_from_file(outer_points)
+
+    # 转换为 numpy 数组
+    inner_contour = np.array(inner_points, dtype=np.int32)
+    outer_contour = np.array(outer_points, dtype=np.int32)
+
+    # --- 1. 获取横平竖直的矩形参数 (x, y, w, h) ---
+    # 即使传入的是4个点,用 boundingRect 也能确保取出准确的边界
+    ix, iy, iw, ih = cv2.boundingRect(inner_contour)
+    ox, oy, ow, oh = cv2.boundingRect(outer_contour)
+
+    # 构造标准的4点格式返回 (保持和 analyze_centering_rotated 输出一致)
+    def get_box_corners(x, y, w, h):
+        return [[x, y], [x + w, y], [x + w, y + h], [x, y + h]]
+
+    inner_box_corners_int = get_box_corners(ix, iy, iw, ih)
+    outer_box_corners_int = get_box_corners(ox, oy, ow, oh)
+
+    print("\n--- 基于修正矩形(横平竖直)的分析 ---")
+    print(f"内框: x={ix}, y={iy}, w={iw}, h={ih}")
+    print(f"外框: x={ox}, y={oy}, w={ow}, h={oh}")
+
+    # --- 2. 计算边距 (Margins) ---
+    # 图像坐标系:x向右增加,y向下增加
+
+    # 左边距:内框左边 - 外框左边
+    margin_left = ix - ox
+    # 右边距:外框右边(x+w) - 内框右边(x+w)
+    margin_right = (ox + ow) - (ix + iw)
+
+    # 上边距:内框上边 - 外框上边
+    margin_top = iy - oy
+    # 下边距:外框下边(y+h) - 内框下边(y+h)
+    margin_bottom = (oy + oh) - (iy + ih)
+
+    # --- 3. 计算百分比 ---
+    total_horizontal_margin = margin_left + margin_right
+    total_vertical_margin = margin_top + margin_bottom
+
+    left_percent = 50.0
+    right_percent = 50.0
+    top_percent = 50.0
+    bottom_percent = 50.0
+
+    if total_horizontal_margin > 0:
+        left_percent = (margin_left / total_horizontal_margin) * 100
+        right_percent = (margin_right / total_horizontal_margin) * 100
+        print(f"水平边距分布: 左 {left_percent:.1f}% | 右 {right_percent:.1f}%")
+
+    if total_vertical_margin > 0:
+        top_percent = (margin_top / total_vertical_margin) * 100
+        bottom_percent = (margin_bottom / total_vertical_margin) * 100
+        print(f"垂直边距分布: 上 {top_percent:.1f}% | 下 {bottom_percent:.1f}%")
+
+    # --- 4. 角度差异 ---
+    # 因为已经是横平竖直的矩形,角度差默认为 0
+    angle_diff = 0.0
+
+    # 返回格式保持与原函数 analyze_centering_rotated 一致:
+    # ((左%, 右%), (上%, 下%), 角度差), 内框点集, 外框点集
+    return ((left_percent, right_percent),
+            (top_percent, bottom_percent),
+            angle_diff), inner_box_corners_int, outer_box_corners_int
+
+
 def formate_center_data(center_result,
                         inner_data: dict, outer_data: dict,
                         inner_rect_points: List, outer_rect_points: List):