当前位置: 首页 > article >正文

【计算机视觉】工业表计读数(4)--基于关键点检测的读数识别

随着工业自动化和智能制造的发展,对设备状态实时监控和数据采集提出了更高要求。本文提出了一种基于YOLO的工业表计读数识别方法,通过首先利用YOLO进行表计目标检测,提取出单独的表计图像,然后分别对表针和刻度进行关键点检测,再结合OCR技术识别刻度数字,最后通过计算指针与刻度之间的夹角实现读数判定。该方法既兼顾了目标检测的高效性,又通过精细化关键点定位和字符识别解决了传统方法在复杂工业环境中准确率不足的问题。

1.整体调用

results = model(image_path, conf=0.60, save=False)
frame = cv2.imread(image_path)
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
for idx, result in enumerate(results):
    boxes = result.boxes.cpu().numpy()
    for i, box in enumerate(boxes.data):
        l, t, r, b = box[:4].astype(np.int32)
        conf, id = box[4:]
        id = int(id)
        target_image = frame[t:b, l:r]
        target_image_path = os.path.join(output_dir, f"target_{idx+1}_{i+1}.jpg")
        print("############正在进行{}图片识别############".format(target_image_path))
        jpeg_quality = 100
        cv2.imwrite(target_image_path, target_image, [int(cv2.IMWRITE_JPEG_QUALITY), jpeg_quality])
        final_result, final_img = predict(target_image_path)
        cv2.rectangle(frame_rgb, (l, t), (r, b), (0, 0, 255), 2)
        final_result_str = f"{final_result:.2f}" if final_result is not None else "N/A"
        cv2.putText(frame_rgb, f"{model.names[id]} {conf * 100:.1f} %  current value:{final_result_str}", (l, t - 10), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
  1. 模型预测与图像读取
    使用模型对指定图片进行预测(置信度阈值设为 0.60),并读取原始图像,将 BGR 格式转换为 RGB 以便后续处理。
  2. 遍历预测结果
    对每个预测结果,转换其检测到的目标边界框为 NumPy 数组,然后遍历每个边界框。
  3. 提取目标框并保存目标图片
    根据边界框坐标(左、上、右、下)裁剪出目标区域,构造保存路径,并以指定 JPEG 质量保存裁剪后的目标图片,同时打印当前处理信息。
  4. 目标图片预测与绘制结果
    对裁剪后的目标图片调用 predict 函数获取数值预测结果;在原图上绘制目标框,并在目标框上方显示文本信息,文本包括目标类别、置信度(百分比形式)以及预测的数值(若无则显示 “N/A”)。

2.指针&刻度信息获取

def run(image_path, save_path="./run/main_1/", save_json_result=False):
    image_name, _ = str(image_path.split("/")[-1]).split(".")
    save_path = os.path.join(save_path, image_name)
    if save_json_result:
        os.makedirs(save_path, exist_ok=True)
        os.makedirs(os.path.join(save_path, "ocr"), exist_ok=True)
        os.makedirs(os.path.join(save_path, "un_ocr"), exist_ok=True)
        
    with torch.no_grad():
        task_1_results = task_1_model(source=[image_path], save=False, imgsz=640, iou=0)
        task_2_results = task_2_model(source=[image_path], save=False, imgsz=640, iou=0)
        
    frame = cv2.imread(image_path)
    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    
    # 处理数标检测结果
    for result in task_2_results:
        boxes = result.boxes.cpu().numpy()
        number_result = {}
        delete_list = []
        for index, box in enumerate(boxes.data):
            l, t, r, b = box[:4].astype(np.int32)
            conf, class_id = box[4:]
            frame_ocr = frame[t:b, l:r]
            ocr_txt, ocr_score = ocr_img(frame_ocr)
            if ocr_txt is None:
                delete_list.append(index)
                cv2.imwrite(f"{save_path}/un_ocr/{index}.jpg", frame_ocr)
                cv2.rectangle(frame, (l, t), (r, b), (0, 255, 255), 3)
                continue
            if save_json_result:
                cv2.imwrite(f"{save_path}/ocr/{index}_ocr_{ocr_txt}.jpg", frame_ocr)
            number_result[index] = {
                "index": index,
                "ocr_txt": ocr_txt,
                "ocr_score": ocr_score,
                "xy": (int(l), int(t), int(r), int(b))
            }
            if save_json_result:
                cv2.rectangle(frame, (l, t), (r, b), (0, 0, 255), 3)
                cv2.putText(frame, f"{ocr_txt} {ocr_score * 100:.1f}% {index}",
                            (l, t - 10), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 3)
                
        keypoints = result.keypoints.cpu().numpy()
        for index, keypoint in enumerate(keypoints.data):
            if index in delete_list:
                continue
            for i in range(len(keypoint)):
                x, y, _ = keypoint[i]
                x, y = int(x), int(y)
                cv2.circle(frame, (x, y), 10, (0, 255, 0), -1)
                cv2.putText(frame, f"{index}", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
                number_result[index]['keypoint'] = (x, y)
            if len(keypoint) >= 2:
                x0, y0, _ = keypoint[0]
                x1, y1, _ = keypoint[1]
                cv2.line(frame, (int(x0), int(y0)), (int(x1), int(y1)), (255, 0, 255), 4)
        print(number_result)
        
    # 处理指针检测结果
    for result in task_1_results:
        print("task_1_result=={}".format(result))
        pointer_result = {}
        boxes = result.boxes.cpu().numpy()
        if len(boxes) == 0:
            return None
        index, box = max(enumerate(boxes.data), key=lambda x: x[1][4])
        l, t, r, b = box[:4].astype(np.int32)
        conf, id = box[4:]
        id = int(id)
        cv2.rectangle(frame, (l, t), (r, b), (255, 0, 0), 3)
        cv2.putText(frame, f"{task_1_objs_labels[id]} {conf * 100:.1f}%",
                    (l, t - 10), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
        
        keypoint_list = result.keypoints.cpu().numpy().data[index]
        if len(keypoint_list) < 2:
            return None
        # 调整头部和中心点顺序
        center_x, center_y, _ = keypoint_list[1]
        head_x, head_y, _ = keypoint_list[0]
        cv2.circle(frame, (int(center_x), int(center_y)), 10, (0, 255, 0), -1)
        cv2.circle(frame, (int(head_x), int(head_y)), 10, (0, 255, 0), -1)
        pointer_result['center'] = (int(center_x), int(center_y))
        pointer_result['head'] = (int(head_x), int(head_y))
        if len(keypoint_list) >= 2:
            x0, y0, _ = keypoint_list[0]
            x1, y1, _ = keypoint_list[1]
            cv2.line(frame, (int(x0), int(y0)), (int(x1), int(y1)), (255, 0, 255), 4)
        print(pointer_result)
        
    if save_json_result:
        cv2.imwrite(os.path.join(save_path, image_name + ".jpg"), frame)
        
    json_result = {
        "number_result": number_result,
        "pointer_result": pointer_result,
        "image_path": image_path
    }
    frame = None
    if save_json_result:
        with open(os.path.join(save_path, image_name + ".json"), 'w') as file:
            json.dump(json_result, file, indent=4)
    return json_result
  1. 初始化及文件夹创建
    从输入路径中提取图片名称,并构建保存结果的路径;若需要保存 JSON 结果,则创建对应的子文件夹(ocr 与 un_ocr)。
  2. 批量推理
    在无梯度环境下分别对图片进行指针检测(task_1_model)和数标检测(task_2_model)。
  3. 图像读取与颜色转换
    读取原始图片,并将 BGR 格式转换为 RGB。
  4. 数标检测处理
    • 遍历数标检测结果,对每个检测框进行处理:
      • 裁剪出框内区域,并通过 OCR 函数识别数字。
      • 若 OCR 失败,则保存裁剪图至 un_ocr 目录,并在图像上绘制标记。
      • 若 OCR 成功,则保存识别结果(及图片,若启用保存标志),记录数字及关键点信息,并绘制检测框与文本。
    • 遍历关键点,对有效检测框绘制圆点、文本和连线。
  5. 指针检测处理
    • 选取置信度最高的检测框,绘制对应的矩形框和类别文本。
    • 提取关键点信息,并调整顺序(头部与中心),在图上绘制对应的圆点和连线,同时记录检测结果。
  6. 结果保存与返回
    若启用保存标志,则将带有标注的图像保存,并将检测结果以 JSON 格式写入文件;最后返回 JSON 结果。

3.基于夹角的读数识别

3.1 OCR 结果匹配修复

代码片段
    # ----- OCR结果匹配修复 -----
    print("找出最接近OCR结果的正确的列表")
    # 定义多个标准刻度组,不同组可能对应不同的仪表盘样式
    common_ocr_degrees = [
        {"1.6": 45, "1.2": -22.5, "0.8": -90, "0.4": -157.5, "0": -225},
        {"1": 45, "0.8": -9, "0.6": -63, "0.4": -117, "0.2": -171, "0": -225},
        {"0.6": 45, "0.5": 0, "0.4": -45, "0.3": -90, "0.2": -135, "0.1": -180, "0": -225},
        {"2.5": 45, "2": -9, "1.5": -63, "1": -117, "0.5": -171, "0": -225},
        {"4": 45, "3": -22.5, "2": -90, "1": -157.5, "0": -225},
        {"16": 45, "12": -22.5, "8": -90, "4": -157.5, "0": -225},
        {"25": 45, "20": -9, "15": -63, "10": -117, "5": -171, "0": -225},
        {"0.1": 45, "0.08": -9, "0.06": -63, "0.04": -117, "0.02": -171, "0": -225},
        {"250": 45, "200": -9, "150": -63, "100": -117, "50": -171, "0": -225},
    ]
    # 每个标准组中指定的首尾刻度
    common_ocr_degree_first_end = [
        ("1.6", "0"), ("1", "0"), ("0.6", "0"), ("2.5", "0"),
        ("4", "0"), ("16", "0"), ("25", "0"), ("0.1", "0"),
        ("250", "0")
    ]
    final_right_index = None     # 保存最终匹配到的标准组索引
    ocr_txt_list_list = []       # 存放每个组中匹配到的OCR文本列表
    max_nums = 0                 # 当前匹配到数字最多的标准组对应的数字个数

    # 遍历每一组标准刻度,检测 OCR 识别结果中有多少数字与标准组一致
    for index, common_ocr_degree in enumerate(common_ocr_degrees):
        nums = 0               # 当前组匹配到的数字计数
        ocr_txt_list = []      # 当前组匹配到的 OCR 文本列表
        for key, value in number_result.items():
            # 在原图上绘制每个 OCR 检测框,颜色为黄色
            l, t, r, b = value["xy"]
            cv2.rectangle(frame, (l, t), (r, b), (0, 255, 255), 3)
            try:
                float_ocr_txt = float(value['ocr_txt'])
            except Exception as e:
                continue  # 若 OCR 文本无法转换为浮点数,则跳过该条记录
            # 如果 OCR 数值与当前标准组中某个刻度匹配,则计数累加
            for common_ocr in common_ocr_degree.keys():
                if float_ocr_txt == float(common_ocr):
                    nums += 1
                    ocr_txt_list.append(common_ocr)
                    # 强制修正 OCR 文本,确保其格式与标准刻度一致
                    value["ocr_txt"] = common_ocr
        # 将每组匹配的 OCR 文本列表保存下来
        ocr_txt_list_list.append(ocr_txt_list)
        # 如果匹配数量大于等于 4,则直接选定当前标准组
        if nums >= 4:
            final_right_index = index
            break
        # 否则记录匹配最多的标准组
        if nums > max_nums:
            max_nums = nums
            final_right_index = index

    # 若没有找到任何匹配,则保存图片并返回空结果
    if final_right_index is None:
        error_info = "没有找到匹配的标准列表"
        if if_plt:
            axs[0].imshow(frame)
            axs[0].text(0.5, -0.15, f'{error_info}', ha='center', va='bottom',
                        fontproperties=FontProperties(size=14), transform=axs[0].transAxes)
        fig.savefig(os.path.join(save_path, image_name), dpi=200)
        return None, None

    # 取出最终选定的 OCR 文本列表,并输出匹配情况
    ocr_txt_list = ocr_txt_list_list[final_right_index]
    print("正确匹配到的刻度为{0}".format(ocr_txt_list))
    print(f"匹配到的标准列表:{common_ocr_degrees[final_right_index]}")
解析
  • 标准刻度组定义
    定义了多个字典,每个字典(即一个标准组)描述了 OCR 数字与对应角度的关系,适用于不同类型的表盘。
    同时,为每个组预定义了首尾刻度(first、end),便于后续补充缺失的关键点。

  • 匹配流程
    遍历每个标准组,对每个 OCR 识别出的数字进行比较:

    • 在原图上用黄色矩形标出每个检测区域;
    • 尝试将 OCR 文本转换为浮点数,若失败则跳过;
    • 若 OCR 数字与当前标准组中的某个刻度完全一致,则计数并记录该数字,同时更新 OCR 文本格式。
  • 选择标准组
    如果某一组匹配数量达到或超过 4 个,则认为该组为正确标准组;否则选取匹配数量最多的一组。
    若没有匹配到任何标准组,则返回错误。

3.2 首尾刻度修正

代码片段
    # 获取当前选定标准组对应的首尾刻度
    end_txt, first_txt = common_ocr_degree_first_end[final_right_index]
    # 若末尾刻度缺失,则在图像上根据平均半径 r,假定在中心右下45°处添加该刻度点
    if end_txt not in ocr_txt_list:
        x = int(cent_x + r * math.cos(math.radians(45)))
        y = int(cent_y + r * math.sin(math.radians(45)))
        cv2.circle(frame, (x, y), 10, (255, 255, 0), -1)  # 用黄色标记新添加的点
        add_dict = {"index": 99, "ocr_txt": end_txt, "keypoint": (x, y)}
        number_result[99] = add_dict

    # 若首刻度缺失,则在图像上根据平均半径 r,假定在中心左上(-225°)处添加该刻度点
    if first_txt not in ocr_txt_list:
        x = int(cent_x + r * math.cos(math.radians(-225)))
        y = int(cent_y + r * math.sin(math.radians(-225)))
        cv2.circle(frame, (x, y), 10, (255, 255, 0), -1)
        add_dict = {"index": 100, "ocr_txt": first_txt, "keypoint": (x, y)}
        number_result[100] = add_dict
解析
  • 功能说明
    为确保 OCR 检测结果覆盖整个刻度区域,如果缺少首尾刻度(即最小值或最大值),则根据当前中心点与平均半径补充虚拟的关键点。

  • 修正逻辑

    • 若末尾刻度(end_txt)未出现在 OCR 匹配列表中,假定该刻度在中心点以 45°(右下方)方向上,并绘制圆点标记后添加到 number_result 字典中。
    • 同理,若首刻度(first_txt)缺失,则假定其在中心点以 -225°(左上方)方向上,并执行同样操作。
  • 注意事项
    这里采用的是固定角度假设,依赖于预设的表盘结构,确保后续排序和角度计算的连续性。

3.3 透视变换

代码片段
    # ----- 透视变换 -----
    # 若 OCR 检测结果数量大于等于4,则允许透视变换,否则保留原始图像坐标
    if tran:
        new_point = {}  # 保存每个刻度点透视变换后应处于的位置
        old_point = {}  # 保存原始的刻度点位置
        # 从选定的标准组获取每个 OCR 数值对应的理想角度
        right_change_dict = common_ocr_degrees[final_right_index]
        for index, (key, value) in enumerate(number_result.items()):
            # 根据标准角度计算目标位置(新坐标)
            degree = right_change_dict[value['ocr_txt']]
            new_x = int(cent_x + r * math.cos(math.radians(degree)))
            new_y = int(cent_y + r * math.sin(math.radians(degree)))
            number_result[key]['new_keypoint'] = (new_x, new_y)
            # 分别保存原始位置和新位置,键值为 OCR 数字
            a, b = value['keypoint']
            old_point[value['ocr_txt']] = [a, b]
            new_point[value['ocr_txt']] = [new_x, new_y]
            # 在原图上用蓝色圆点标出新计算的目标位置
            cv2.circle(frame, (new_x, new_y), 10, (255, 0, 0), -1)

        # 随机从匹配到的 OCR 文本中选择 4 个作为透视转换的对应点,并按顺序排序
        choose_list = random.sample(ocr_txt_list, 4)
        choose_list = sorted(choose_list)
        print(f"随机选择了{choose_list}进行透视转换")
        src_points = np.array([old_point[choose_list[0]], old_point[choose_list[1]],
                               old_point[choose_list[2]], old_point[choose_list[3]]], dtype=np.float32)
        dst_points = np.array([new_point[choose_list[0]], new_point[choose_list[1]],
                               new_point[choose_list[2]], new_point[choose_list[3]]], dtype=np.float32)

        # 根据对应点计算透视变换矩阵 M,并对原图进行透视变换
        M = cv2.getPerspectiveTransform(src_points, dst_points)
        frame_trans = cv2.warpPerspective(frame, M, (frame.shape[1], frame.shape[0]))

        # 定义辅助函数,将坐标通过透视矩阵 M 转换至新图像坐标系
        def cvt_pos(pos, cvt_mat_t):
            u = pos[0]
            v = pos[1]
            x_ = (cvt_mat_t[0][0] * u + cvt_mat_t[0][1] * v + cvt_mat_t[0][2]) / (
                cvt_mat_t[2][0] * u + cvt_mat_t[2][1] * v + cvt_mat_t[2][2])
            y_ = (cvt_mat_t[1][0] * u + cvt_mat_t[1][1] * v + cvt_mat_t[1][2]) / (
                cvt_mat_t[2][0] * u + cvt_mat_t[2][1] * v + cvt_mat_t[2][2])
            return int(x_), int(y_)

        # 将指针的中心和头部坐标转换至透视变换后的坐标系
        new_center_x, new_center_y = cvt_pos(pointer_result['center'], M)
        new_head_x, new_head_y = cvt_pos(pointer_result['head'], M)
        # 计算指针的当前角度
        r = math.atan2(new_head_y - new_center_y, new_head_x - new_center_x)
        d = math.degrees(r)
        d_360 = ((d + 360) % 360)
        # 对所有刻度点,转换原始关键点坐标,并计算相对于中心的角度(包括标准化至0-360°)
        for index, (key, value) in enumerate(number_result.items()):
            trans_x, trans_y = cvt_pos(value['keypoint'], M)
            r1 = math.atan2(trans_y - new_center_y, trans_x - new_center_x)
            d1 = math.degrees(r1)
            d1_360 = ((d1 + 360) % 360)
            value['trans_keypoint'] = (trans_x, trans_y)
            value['degree'] = d1
            degree_list.append(d1)
            degree_360_list.append(d1_360)
    else:
        # 若不进行透视变换,则直接使用原始图像中的关键点坐标及角度
        new_center_x, new_center_y = pointer_result['center']
        new_head_x, new_head_y = pointer_result['head']
        r = math.atan2(new_head_y - new_center_x, new_head_x - new_center_x)
        d = math.degrees(r)
        d_360 = ((d + 360) % 360)
        for index, (key, value) in enumerate(number_result.items()):
            trans_x, trans_y = value['keypoint']
            r1 = math.atan2(trans_y - new_center_y, trans_x - new_center_x)
            d1 = math.degrees(r1)
            d1_360 = ((d1 + 360) % 360)
            value['trans_keypoint'] = (trans_x, trans_y)
            value['degree'] = d1
            degree_list.append(d1)
            degree_360_list.append(d1_360)
        frame_trans = frame
解析
  • 目的
    根据选定的标准组,计算每个刻度点的理想位置,并利用这组对应点构造透视变换矩阵,以校正因拍摄角度等引起的畸变。

  • 步骤说明

    1. 计算目标位置
      对于 number_result 中的每个数字,根据标准组中的角度(存于 right_change_dict)计算新坐标: n e w _ x = cent_x + r ⋅ cos ⁡ ( θ ) , new_y = cent_y + r ⋅ sin ⁡ ( θ ) {new\_x} = \text{cent\_x} + r \cdot \cos(\theta), \quad \text{new\_y} = \text{cent\_y} + r \cdot \sin(\theta) new_x=cent_x+rcos(θ),new_y=cent_y+rsin(θ)同时保存原始坐标和新坐标,便于后续匹配。
    2. 随机选择对应点
      从匹配的 OCR 文本中随机选择 4 个点构成对应点对(src/dst),确保计算透视矩阵的稳定性。
    3. 计算透视矩阵与转换
      使用 OpenCV 的 getPerspectiveTransformwarpPerspective 得到透视变换后的图像。
      辅助函数 cvt_pos 用于将任意点通过矩阵 M 转换至新图像坐标系。
    4. 转换所有关键点
      将指针关键点以及每个数字的原始关键点转换到新坐标系,并计算其角度(归一化到0-360°)。
  • 注意事项
    若 OCR 数量不足时,不进行透视变换,直接在原图基础上计算。

3.4 指针&关键点匹配

代码片段
    # ----- 寻找指针所在的两个关键点之间 -----
    choose_index, bet_degree_n, bet_degree_p = None, None, None
    # 遍历转换后所有刻度点的角度(0-360°)
    for index, value in enumerate(degree_360_list):
        # 避免越界,最后一个点没有后继
        if index + 1 == len(degree_360_list):
            break
        # 情况一:当前点角度小于下一个点角度(可能跨越0°)
        if degree_360_list[index] < degree_360_list[index + 1]:
            # 判断指针角度 d_360 是否位于跨越区间(包括0°两侧)
            if 0 <= d_360 <= degree_360_list[index] or degree_360_list[index + 1] <= d_360 <= 360:
                # 计算当前区间总跨度(考虑跨越360°)
                bet_degree_n = 360 - degree_360_list[index + 1] + degree_360_list[index]
                # 根据指针位置计算其在该区间内的偏移量
                if d_360 <= degree_360_list[index]:
                    bet_degree_p = 360 - degree_360_list[index + 1] + d_360
                else:
                    bet_degree_p = d_360 - degree_360_list[index + 1]
                choose_index = index
                break
        # 情况二:当区间内角度递减且指针角度位于两点之间
        if degree_360_list[index + 1] <= d_360 <= degree_360_list[index]:
            bet_degree_n = degree_360_list[index] - degree_360_list[index + 1]
            bet_degree_p = d_360 - degree_360_list[index + 1]
            choose_index = index
            break
    print("choose_index=={}".format(choose_index))
    # 若未能找到合适的区间,则直接判定读数为 0,并保存图片
    if choose_index is None:
        axs[0].imshow(frame)
        fig.savefig(os.path.join(save_path, image_name), dpi=200)
        axs[1].imshow(frame_trans)
        axs[1].text(0.5, -0.15, f'最终读数: 0', ha='center', va='bottom',
                    fontproperties=FontProperties(size=14),
                    transform=axs[1].transAxes)
        fig.savefig(os.path.join(save_path, image_name), dpi=200)
        plt.close(fig)
        return 0

    print("选择了区间:", choose_index + 1)
    # 在透视变换后的图像上绘制该区间两关键点与中心点的连线
    a1, b1 = number_result[choose_index]["trans_keypoint"]
    a2, b2 = number_result[choose_index + 1]["trans_keypoint"]
    cv2.line(frame_trans, (int(new_center_x), int(new_center_y)),
             (int(a1), int(b1)), (255, 0, 255), 5)
    cv2.line(frame_trans, (int(new_center_x), int(new_center_y)),
             (int(a2), int(b2)), (255, 0, 255), 5)
解析
  • 功能说明
    遍历所有转换后刻度点的角度,判断指针角度(d_360)落在两个相邻刻度点之间,并计算指针相对于该区间的偏移比例。
  • 区间判断逻辑
    • 情况一(跨越0°)
      若当前点角度小于后一个点角度,可能存在跨越 0° 的情况,此时判断指针角度是否处于区间外部(从较大角度到 360° 和 0°到较小角度),并计算区间总跨度与指针偏移量。
    • 情况二(普通区间)
      当两个连续刻度点角度递减时,若指针角度在这两个点之间,则直接计算区间跨度及指针相对偏移量。
  • 结果应用
    选择合适的区间后,在透视变换图中用紫色连线标出中心点与该区间两关键点,便于后续读数计算。
  • 异常处理
    若无法确定合适区间,则直接认为仪表读数为 0,并将图片保存,结束函数。

3.5 读数计算

代码片段
    # ----- 读数计算 -----
    # 取出区间内两个关键点对应的 OCR 数值,分别作为起始与结束读数
    end_ocr_txt = float(number_result[choose_index]["ocr_txt"])
    start_ocr_txt = float(number_result[choose_index + 1]["ocr_txt"])
    print(f'指标刻度位于之间{start_ocr_txt}{end_ocr_txt}之间')
    # 计算最终读数:
    # 公式:最终读数 = 起始读数 + (两个刻度差值) * (指针在区间内的相对偏移比例)
    final_result = start_ocr_txt + ((end_ocr_txt - start_ocr_txt) * (bet_degree_p / bet_degree_n))
    print("最终读数", final_result)
解析
  • 功能说明
    利用已确定的区间以及指针在该区间中的相对位置计算仪表盘的最终读数。
  • 计算原理
    • 取区间内两个连续刻度的 OCR 数值,假设它们分别代表起始值和结束值;
    • 根据前一步骤中计算出的指针在该区间的角度偏移比例(bet_degree_p / bet_degree_n),按比例线性插值计算最终读数。
    • 该公式假定数值在两个刻度之间均匀分布。
  • 结果输出
    打印最终读数,并在后续步骤中将该结果在图像中显示,便于视觉验证。

在这里插入图片描述


http://www.kler.cn/a/599876.html

相关文章:

  • C#实现自己的Json解析器(LALR(1)+miniDFA)
  • IO模型种类
  • http代理的工作原理与功能应用
  • C++ 重构隐马尔可夫模型:从 Python 性能困境到高效运行的突破实录
  • Ubuntu版免翻墙搭建BatteryHistorian
  • 《Python机器学习基础教程》第2讲:监督学习与分类算法
  • 健康养生:铺就活力生活之路
  • 人工智能革命:技术演进图谱与人类文明重构路径
  • Android集成Facebook登录与分享的常见问题及解决方案
  • UE4-UE5虚幻引擎,前置学习一--Console日志输出经常崩溃,有什么好的解决办法
  • linux下基本命令和扩展命令(安装和登录命令、文件处理命令、系统管理相关命令、网络操作命令、系统安全相关命令、其他命令)欢迎补充噢
  • Netlify 的深度解析及使用指南
  • 使用 OpenCV 拼接进行图像处理对比:以形态学操作为例
  • 【机器学习】什么是决策树?
  • HTML图像标签的详细介绍
  • win32汇编环境,网络编程入门之十一
  • COBOL语言的微服务
  • 2025-03-24 学习记录--C/C++-PTA 习题7-4 求矩阵各行元素之和
  • MAC terminal
  • tcping 命令的使用,ping IP 和端口