高炉智变:12期实战带你玩转工业AI落地~系列文章06:双目视觉+三维重建:让料面形状“看得见“

🎯 高炉智变06|双目视觉+三维重建:让料面形状"看得见"

📅 本文目录


一、前言:为什么要看料面? 👁️

1.1 料面的重要性

高炉装料是高炉炼铁的第一道工序,料面形状直接影响煤气分布和冶炼效果:

📊 料面形状与冶炼效果:

理想料面形状:
      ___________
     /            \
    /     ⭐ 中心   \      ← 布料均匀,煤气上升稳定
   /                 \
  /_______🔥边缘_______\   ← 焦炭多,透气性好
  
理想状态:
✅ 煤气流分布合理
✅ 透气性良好
✅ 焦比最优
✅ 产量最高

1.2 传统料面检测方法

📋 传统料面检测方法对比:

┌──────────┬────────┬────────────────┐
│   方法   │  精度  │      缺点       │
├──────────┼────────┼────────────────┤
│ 人工观察 │  ⭐    │ 主观性强,不精确 │
│ 机械探尺 │  ⭐⭐   │ 只能测单点,有限│
│ 雷达探测 │  ⭐⭐⭐ │ 数据不直观     │
│ 红外测温 │  ⭐⭐   │ 只能测温度     │
└──────────┴────────┴────────────────┘

1.3 双目视觉的优势

💡 双目视觉就像人的两只眼睛,通过视差计算深度,实现料面的"立体感知"!

✨ 双目视觉检测优势:

✅ 三维感知: 获取料面完整三维形状
✅ 全局覆盖: 同时观测整个料面
✅ 非接触: 不影响生产过程
✅ 实时性: 毫秒级响应
✅ 智能化: AI自动分析

二、双目视觉基础原理 🧠

2.1 人眼立体视觉原理

👁️ 人眼立体视觉:

左眼看到的图像     右眼看到的图像
    ┌───┐             ┌───┐
    │ A │             │ A │
    │ B │   融合后     │ B │
    │ C │     →       │   │  ← 感觉C更近
    │   │             │ C │
    └───┘             └───┘
    
    距离近的物体,视差大
    距离远的物体,视差小

2.2 双目视觉模型

📐 双目视觉几何模型:

           物理世界中的点 P
                 │
                 │ (X, Y, Z)
                 │
    ┌────────────┼────────────┐
    │            │            │
    │            │            │
  相机1中心 O_L   │   相机2中心 O_R
    │            │            │
    │            B            │
    │     ┌─────┴─────┐       │ 
    │     │  图像平面  │       │
    │     │           │       │
    │     │   像素    │        │
    │     │  (u_L)   │       │
    │     └───────────┘      │
    │                        │
    └───────────────────────┘
            基线距离 b

视差 d = |u_L - u_R|
深度 Z = f * b / d

其中:
- f: 焦距
- b: 基线距离
- d: 视差

2.3 核心公式推导

"""
双目视觉深度计算原理:

            P(X, Y, Z)
             /│
            / │
           /  │ Z (深度)
          /   │
         /    │
        /     │
   O_L /──────│────── O_R
      │       │
      │       │
      b       │
      │       │
      │       X
      │
      
      视差: d = x_L - x_R
      
      由相似三角形:
      (Z - f) / Z = (b - d) / b
      
      解得:
      Z = f * b / d
      
      所以深度Z与视差d成反比!
"""

def calculate_depth(focal_length, baseline, disparity):
    """
    根据视差计算深度
    
    Args:
        focal_length: 焦距(像素)
        baseline: 基线距离(mm)
        disparity: 视差(像素)
        
    Returns:
        depth: 深度(mm)
    """
    if disparity < 1e-6:
        return float('inf')  # 无效视差
    
    depth = focal_length * baseline / disparity
    return depth

三、相机标定技术 🔧

3.1 为什么需要标定?

📐 相机标定的必要性:

相机实际存在以下畸变:
1. 径向畸变: 桶形畸变、枕形畸变
2. 切向畸变: 镜头与传感器不平行
3. 内参: 焦距、主点
4. 外参: 相机位置、姿态

标定目的:
✅ 消除镜头畸变
✅ 获取相机内参
✅ 获取相机外参
✅ 建立世界坐标系

3.2 张正友标定法

import numpy as np
import cv2


class CameraCalibrator:
    """
    张正友标定法相机标定
    
    使用棋盘格标定板进行标定
    """
    
    def __init__(self, chessboard_size=(9, 6), square_size=25):
        """
        Args:
            chessboard_size: 棋盘格内角点数 (列, 行)
            square_size: 每个棋盘格的大小(mm)
        """
        self.chessboard_size = chessboard_size
        self.square_size = square_size
        
        # 存储标定板角点
        self.object_points = []  # 世界坐标系中的点
        self.image_points = []   # 图像中的点
        
        # 准备世界坐标系的点
        self._prepare_object_points()
        
    def _prepare_object_points(self):
        """准备世界坐标系中的点"""
        objp = np.zeros((self.chessboard_size[0] * self.chessboard_size[1], 3), np.float32)
        objp[:, :2] = np.mgrid[0:self.chessboard_size[0], 
                                 0:self.chessboard_size[1]].T.reshape(-1, 2)
        objp *= self.square_size
        self.object_points_template = objp
        
    def find_corners(self, image):
        """
        在图像中找棋盘格角点
        
        Args:
            image: 输入图像
            
        Returns:
            corners: 角点坐标
            found: 是否找到
        """
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        
        # 找棋盘格角点
        found, corners = cv2.findChessboardCorners(gray, self.chessboard_size, None)
        
        if found:
            # 亚像素精确化
            criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
            corners = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
            
        return corners, found
    
    def add_calibration_image(self, image):
        """添加标定图像"""
        corners, found = self.find_corners(image)
        
        if found:
            self.image_points.append(corners)
            self.object_points.append(self.object_points_template)
            return True
        return False
    
    def calibrate(self, image_size):
        """
        执行相机标定
        
        Args:
            image_size: 图像尺寸 (width, height)
            
        Returns:
            camera_matrix: 内参矩阵
            dist_coeffs: 畸变系数
            rvecs: 旋转向量
            tvecs: 平移向量
        """
        ret, camera_matrix, dist_coeffs, rvecs, tvecs = cv2.calibrateCamera(
            self.object_points, 
            self.image_points, 
            image_size, 
            None, 
            None
        )
        
        self.camera_matrix = camera_matrix
        self.dist_coeffs = dist_coeffs
        self.rvecs = rvecs
        self.tvecs = tvecs
        
        return camera_matrix, dist_coeffs, rvecs, tvecs
    
    def undistort(self, image):
        """校正图像畸变"""
        return cv2.undistort(
            image, 
            self.camera_matrix, 
            self.dist_coeffs
        )
    
    def get_extrinsic_params(self, idx):
        """获取指定图像的外参"""
        rvec = self.rvecs[idx]
        tvec = self.tvecs[idx]
        
        # 旋转向量转旋转矩阵
        R, _ = cv2.Rodrigues(rvec)
        
        return R, tvec
    
    def print_results(self):
        """打印标定结果"""
        print("\n📷 相机标定结果:")
        print("="*50)
        
        print("\n📐 内参矩阵:")
        print(self.camera_matrix)
        
        print("\n📐 畸变系数:")
        print(self.dist_coeffs)
        
        print("\n📏 重投影误差:")
        total_error = 0
        for i in range(len(self.object_points)):
            imgpoints2, _ = cv2.projectPoints(
                self.object_points[i], 
                self.rvecs[i], 
                self.tvecs[i],
                self.camera_matrix, 
                self.dist_coeffs
            )
            error = cv2.norm(self.image_points[i], imgpoints2, cv2.NORM_L2) / len(imgpoints2)
            total_error += error
        mean_error = total_error / len(self.object_points)
        print(f"平均误差: {mean_error:.4f} 像素")

3.3 双目标定

class StereoCalibrator:
    """
    双目相机标定
    """
    
    def __init__(self, left_calibrator, right_calibrator):
        self.left_calibrator = left_calibrator
        self.right_calibrator = right_calibrator
        
    def calibrate_stereo(self, left_images, right_images, image_size):
        """
        执行双目标定
        
        Returns:
            R: 旋转矩阵
            T: 平移向量
            E: 本质矩阵
            F: 基础矩阵
        """
        # 确保左右图像数量一致
        assert len(left_images) == len(right_images), "左右图像数量不一致"
        
        # 分别标定左右相机
        cameraMatrix1, distCoef1, _, _ = self.left_calibrator.calibrate(image_size)
        cameraMatrix2, distCoef2, _, _ = self.right_calibrator.calibrate(image_size)
        
        # 双目标定
        criteria = (cv2.TERM_CRITERIA_MAX_ITER + cv2.TERM_CRITERIA_EPS, 100, 1e-5)
        
        ret, cameraMatrix1, distCoef1, cameraMatrix2, distCoef2, R, T, E, F = \
            cv2.stereoCalibrate(
                self.left_calibrator.object_points,
                self.left_calibrator.image_points,
                self.right_calibrator.image_points,
                cameraMatrix1, distCoef1,
                cameraMatrix2, distCoef2,
                image_size,
                flags=cv2.CALIB_FIX_INTRINSIC,
                criteria=criteria
            )
        
        self.R = R  # 旋转矩阵
        self.T = T  # 平移向量
        self.E = E  # 本质矩阵
        self.F = F  # 基础矩阵
        self.cameraMatrix1 = cameraMatrix1
        self.cameraMatrix2 = cameraMatrix2
        
        return R, T, E, F
    
    def stereo_rectify(self, image_size):
        """
        双目校正
        
        使左右图像对齐到同一平面上
        """
        rectify_scale = 1
        
        R1, R2, P1, P2, Q, _, _ = cv2.stereoRectify(
            self.cameraMatrix1, self.left_calibrator.dist_coeffs,
            self.cameraMatrix2, self.right_calibrator.dist_coeffs,
            image_size,
            self.R, self.T,
            alpha=rectify_scale
        )
        
        # 生成校正映射
        map1x, map1y = cv2.initUndistortRectifyMap(
            self.cameraMatrix1, 
            self.left_calibrator.dist_coeffs,
            R1, P1, 
            image_size, 
            cv2.CV_32FC1
        )
        
        map2x, map2y = cv2.initUndistortRectifyMap(
            self.cameraMatrix2, 
            self.right_calibrator.dist_coeffs,
            R2, P2, 
            image_size, 
            cv2.CV_32FC1
        )
        
        self.map1x, self.map1y = map1x, map1y
        self.map2x, self.map2y = map2x, map2y
        self.Q = Q
        
        return map1x, map1y, map2x, map2y, Q

四、立体匹配算法 🔍

4.1 立体匹配原理

📐 立体匹配流程:

1. 预处理: 灰度化、归一化
         ↓
2. 代价计算: 计算每个像素的匹配代价
         ↓
3. 代价聚合: 聚合邻域像素的代价
         ↓
4. 视差计算: 求解最优视差
         ↓
5. 视差细化: 亚像素精化
         ↓
6. 后处理: 过滤无效视差

4.2 BM算法(块匹配)

class StereoMatcherBM:
    """
    块匹配立体匹配算法 (BM)
    
    简单高效,适合实时应用
    """
    
    def __init__(self):
        # 创建BM匹配器
        self.matcher = cv2.StereoBM_create(
            numDisparities=64,  # 视差搜索范围(必须是16的倍数)
            blockSize=15        # 匹配块大小(奇数)
        )
        
    def compute(self, left_image, right_image):
        """
        计算视差图
        
        Args:
            left_image: 左图像
            right_image: 右图像
            
        Returns:
            disparity: 视差图
        """
        # 转灰度
        if len(left_image.shape) == 3:
            left_gray = cv2.cvtColor(left_image, cv2.COLOR_BGR2GRAY)
            right_gray = cv2.cvtColor(right_image, cv2.COLOR_BGR2GRAY)
        else:
            left_gray = left_image
            right_gray = right_image
            
        # 计算视差
        disparity = self.matcher.compute(left_gray, right_gray)
        
        return disparity
    
    def set_params(self, num_disparities=64, block_size=15):
        """设置参数"""
        self.matcher = cv2.StereoBM_create(
            numDisparities=num_disparities,
            blockSize=block_size
        )

4.3 SGBM算法(半全局块匹配)

class StereoMatcherSGBM:
    """
    半全局块匹配算法 (SGBM)
    
    精度更高,适合高精度测量
    """
    
    def __init__(self):
        # 创建SGBM匹配器
        self.matcher = cv2.StereoSGBM_create(
            minDisparity=0,
            numDisparities=128,
            blockSize=3,
            P1=8 * 3 * 3**2,   # 惩罚系数1
            P2=32 * 3 * 3**2,  # 惩罚系数2
            disp12MaxDiff=1,
            uniquenessRatio=10,
            speckleWindowSize=100,
            speckleRange=32
        )
        
    def compute(self, left_image, right_image):
        """计算视差图"""
        if len(left_image.shape) == 3:
            left_gray = cv2.cvtColor(left_image, cv2.COLOR_BGR2GRAY)
            right_gray = cv2.cvtColor(right_image, cv2.COLOR_BGR2GRAY)
        else:
            left_gray = left_image
            right_gray = right_image
            
        disparity = self.matcher.compute(left_gray, right_gray)
        
        return disparity
    
    def set_params(self, **kwargs):
        """设置参数"""
        self.matcher = cv2.StereoSGBM_create(**kwargs)

4.4 AD-Census算法

class ADCensusStereo:
    """
    AD-Census立体匹配算法
    
    基于自适应窗口的代价聚合,精度高
    """
    
    def __init__(self, max_disparity=128):
        self.max_disparity = max_disparity
        
    def compute(self, left_image, right_image):
        """计算视差图"""
        # 转灰度
        if len(left_image.shape) == 3:
            left_gray = cv2.cvtColor(left_image, cv2.COLOR_BGR2GRAY)
            right_gray = cv2.cvtColor(right_image, cv2.COLOR_BGR2GRAY)
        else:
            left_gray = left_image
            right_gray = right_image
            
        # 转换为float
        left = left_gray.astype(np.float32)
        right = right_gray.astype(np.float32)
        
        h, w = left.shape
        disparity = np.zeros((h, w), np.float32)
        
        # 简化的AD-Census实现
        # 实际工程中应使用完整版AD-Census
        for y in range(7, h - 7):
            for x in range(7, w - 7):
                best_cost = float('inf')
                best_disp = 0
                
                for d in range(0, min(self.max_disparity, x - 7)):
                    # AD代价
                    cost = abs(left[y, x] - right[y, x - d])
                    
                    # Census变换代价
                    left_census = self._census_transform(left[y-7:y+8, x-7:x+8], x, 7)
                    right_census = self._census_transform(right[y-7:y+8, x-7-d:x+8-d], x-d, 7)
                    cost += self._hamming_distance(left_census, right_census) * 2
                    
                    if cost < best_cost:
                        best_cost = cost
                        best_disp = d
                        
                disparity[y, x] = best_disp
                
        return disparity
    
    def _census_transform(self, patch, center_x, offset):
        """Census变换"""
        center_val = patch[offset, offset]
        census = 0
        
        for i in range(patch.shape[0]):
            for j in range(patch.shape[1]):
                if i != offset or j != offset:
                    census = (census << 1) | (1 if patch[i, j] < center_val else 0)
                    
        return census
    
    def _hamming_distance(self, a, b):
        """汉明距离"""
        xor = a ^ b
        return bin(xor).count('1')

五、三维重建技术 📊

5.1 视差图转深度图

def disparity_to_depth(disparity, focal_length, baseline):
    """
    将视差图转换为深度图
    
    Args:
        disparity: 视差图
        focal_length: 焦距
        baseline: 基线距离
        
    Returns:
        depth: 深度图
    """
    # 避免除零
    disparity_safe = np.maximum(disparity, 1e-6)
    
    # 计算深度
    depth = (focal_length * baseline) / disparity_safe
    
    # 标记无效区域
    depth[disparity <= 0] = 0
    
    return depth


def depth_to_point_cloud(depth, camera_matrix):
    """
    深度图转点云
    
    Args:
        depth: 深度图
        camera_matrix: 相机内参
        
    Returns:
        points: 点云数据 (N x 3)
    """
    h, w = depth.shape
    
    # 相机内参
    fx = camera_matrix[0, 0]
    fy = camera_matrix[1, 1]
    cx = camera_matrix[0, 2]
    cy = camera_matrix[1, 2]
    
    # 生成像素坐标
    u, v = np.meshgrid(np.arange(w), np.arange(h))
    
    # 转换为相机坐标系
    x = (u - cx) * depth / fx
    y = (v - cy) * depth / fy
    z = depth
    
    # 过滤无效点
    valid = depth > 0
    points = np.stack([x[valid], y[valid], z[valid]], axis=1)
    
    return points

5.2 料面三维重建

class BurdenSurfaceReconstructor:
    """
    高炉料面三维重建器
    """
    
    def __init__(self, camera_matrix, baseline, calibration_params):
        self.camera_matrix = camera_matrix
        self.baseline = baseline
        self.calibration = calibration_params
        
        # 立体匹配器
        self.matcher = StereoMatcherSGBM()
        
    def reconstruct(self, left_image, right_image):
        """
        重建料面三维形状
        
        Args:
            left_image: 左相机图像
            right_image: 右相机图像
            
        Returns:
            surface: 料面三维数据
        """
        # 1. 图像校正
        left_rect = cv2.remap(
            left_image, 
            self.calibration['map1x'], 
            self.calibration['map1y'],
            cv2.INTER_LINEAR
        )
        right_rect = cv2.remap(
            right_image,
            self.calibration['map2x'],
            self.calibration['map2y'],
            cv2.INTER_LINEAR
        )
        
        # 2. 计算视差
        disparity = self.matcher.compute(left_rect, right_rect)
        
        # 3. 视差后处理
        disparity = self._post_process_disparity(disparity)
        
        # 4. 视差转深度
        depth = disparity_to_depth(
            disparity.astype(np.float32),
            self.camera_matrix[0, 0],
            self.baseline
        )
        
        # 5. 生成点云
        points = depth_to_point_cloud(depth, self.camera_matrix)
        
        # 6. 料面提取
        surface = self._extract_surface(points)
        
        return {
            'disparity': disparity,
            'depth': depth,
            'points': points,
            'surface': surface
        }
    
    def _post_process_disparity(self, disparity):
        """视差后处理"""
        #  speckle滤波
        speckle_window = 200
        speckle_range = 32
        disparity = disparity.astype(np.int16)
        cv2.filterSpeckles(disparity, 0, speckle_window, speckle_range)
        
        # 中值滤波
        disparity = cv2.medianBlur(disparity, 5)
        
        # 转换为float
        disparity = disparity.astype(np.float32) / 16.0
        
        return disparity
    
    def _extract_surface(self, points):
        """
        提取料面
        
        料面是料面上所有点的集合
        """
        if len(points) == 0:
            return None
            
        # 计算料面高度分布
        z_values = points[:, 2]
        
        # 过滤掉非料面区域(根据Z值范围)
        surface_z_min = np.percentile(z_values, 5)
        surface_z_max = np.percentile(z_values, 95)
        
        surface_mask = (z_values >= surface_z_min) & (z_values <= surface_z_max)
        surface_points = points[surface_mask]
        
        return {
            'points': surface_points,
            'height_min': surface_z_min,
            'height_max': surface_z_max,
            'center_height': np.mean(surface_points[:, 2]) if len(surface_points) > 0 else 0
        }
    
    def analyze_surface_shape(self, surface):
        """
        分析料面形状
        
        Returns:
            analysis: 形状分析结果
        """
        if surface is None or len(surface['points']) == 0:
            return None
            
        points = surface['points']
        
        # 计算中心高度
        center_height = np.mean(points[:, 2])
        
        # 计算高度标准差
        height_std = np.std(points[:, 2])
        
        # 计算环形分布
        radial_profile = self._compute_radial_profile(points, center_height)
        
        return {
            'center_height': center_height,
            'height_variance': height_std,
            'radial_profile': radial_profile,
            'total_points': len(points),
            'coverage': len(points) / (np.pi * (1.0 ** 2))  # 假设半径1米
        }
    
    def _compute_radial_profile(self, points, center_height):
        """计算径向高度分布"""
        # 计算每个点到中心的距离
        distances = np.sqrt(points[:, 0]**2 + points[:, 1]**2)
        
        # 分桶统计
        bins = np.linspace(0, 1.0, 11)  # 0-1米,分10个桶
        radial_profile = []
        
        for i in range(len(bins) - 1):
            mask = (distances >= bins[i]) & (distances < bins[i+1])
            if np.sum(mask) > 0:
                avg_height = np.mean(points[mask, 2])
                radial_profile.append({
                    'radius_min': bins[i],
                    'radius_max': bins[i+1],
                    'avg_height': avg_height
                })
            else:
                radial_profile.append({
                    'radius_min': bins[i],
                    'radius_max': bins[i+1],
                    'avg_height': 0
                })
                
        return radial_profile

六、实战代码实现 💻

6.1 完整双目系统

import cv2
import numpy as np
import time


class StereoVisionSystem:
    """
    双目视觉系统
    
    完整的高炉料面检测系统
    """
    
    def __init__(self, config):
        self.config = config
        
        # 相机参数
        self.camera_matrix = np.array(config['camera_matrix'])
        self.baseline = config['baseline']
        
        # 初始化组件
        self.matcher = StereoMatcherSGBM()
        self.calibrator = None
        
    def load_calibration(self, calib_file):
        """加载标定参数"""
        data = np.load(calib_file, allow_pickle=True).item()
        self.camera_matrix = data['camera_matrix']
        self.baseline = data['baseline']
        self.calibrator = data
        
    def capture_stereo_pair(self, left_cam, right_cam):
        """采集双目图像对"""
        ret_left, left_frame = left_cam.read()
        ret_right, right_frame = right_cam.read()
        
        if ret_left and ret_right:
            return left_frame, right_frame
        return None, None
    
    def process_frame(self, left_image, right_image):
        """
        处理双目图像对
        
        Returns:
            results: 处理结果
        """
        # 1. 立体校正
        left_rect = self._rectify(left_image, 'left')
        right_rect = self._rectify(right_image, 'right')
        
        # 2. 计算视差
        disparity = self.matcher.compute(left_rect, right_rect)
        
        # 3. 计算深度
        depth = disparity_to_depth(
            disparity.astype(np.float32),
            self.camera_matrix[0, 0],
            self.baseline
        )
        
        # 4. 生成点云
        points = depth_to_point_cloud(depth, self.camera_matrix)
        
        # 5. 可视化
        vis = self._visualize(left_image, disparity, depth)
        
        return {
            'disparity': disparity,
            'depth': depth,
            'points': points,
            'visualization': vis
        }
    
    def _rectify(self, image, side='left'):
        """校正图像"""
        if self.calibrator is None:
            return image
            
        if side == 'left':
            return cv2.remap(
                image,
                self.calibrator['map1x'],
                self.calibrator['map1y'],
                cv2.INTER_LINEAR
            )
        else:
            return cv2.remap(
                image,
                self.calibrator['map2x'],
                self.calibrator['map2y'],
                cv2.INTER_LINEAR
            )
    
    def _visualize(self, left_image, disparity, depth):
        """可视化"""
        # 视差图着色
        disp_vis = cv2.applyColorMap(
            cv2.convertScaleAbs(disparity, alpha=255/128),
            cv2.COLORMAP_JET
        )
        
        # 深度图着色
        depth_vis = cv2.applyColorMap(
            cv2.convertScaleAbs(depth, alpha=255/5000),
            cv2.COLORMAP_JET
        )
        
        # 拼接
        vis = np.hstack([left_image, disp_vis, depth_vis])
        
        return vis
    
    def run_realtime(self, left_cam, right_cam):
        """实时运行"""
        print("📷 启动双目视觉系统...")
        
        while True:
            left_image, right_image = self.capture_stereo_pair(left_cam, right_cam)
            
            if left_image is not None:
                results = self.process_frame(left_image, right_image)
                
                # 显示
                cv2.imshow('Stereo Vision', results['visualization'])
                
                # 打印统计
                avg_depth = np.mean(results['depth'][results['depth'] > 0])
                print(f"\r深度: {avg_depth:.1f}mm", end='')
                
            # 按q退出
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break
                
        cv2.destroyAllWindows()


# 使用示例
def main():
    # 配置
    config = {
        'camera_matrix': [
            [1000, 0, 640],
            [0, 1000, 360],
            [0, 0, 1]
        ],
        'baseline': 300,  # mm
        'calibration_file': 'stereo_calib.npz'
    }
    
    # 创建系统
    system = StereoVisionSystem(config)
    
    # 加载标定参数
    system.load_calibration('stereo_calib.npz')
    
    # 打开相机
    left_cam = cv2.VideoCapture(0)
    right_cam = cv2.VideoCapture(1)
    
    # 实时运行
    system.run_realtime(left_cam, right_cam)
    
    # 释放资源
    left_cam.release()
    right_cam.release()


if __name__ == '__main__':
    main()

七、总结与预告 🎯

7.1 本期要点

📝 本期知识点总结:

✅ 理解了双目视觉原理
✅ 掌握了相机标定技术
✅ 学会了立体匹配算法(BM/SGBM)
✅ 实现了三维重建技术
✅ 完成了料面检测系统

🎯 核心收获:
   双目视觉让高炉料面"看得见、摸得着"!

7.2 下期预告

print("""
📢 下期预告:

第7期 | 深度学习异常检测:火眼金睛守护高炉安全

预告内容:
├── 👁️ CNN异常检测原理
├── 🔥 风口挂渣/烧穿检测
├── ⚠️ 实时预警系统
└── 💻 完整代码实现

敬请期待!🔔🔔🔔
""")

🔥 关注我,第一时间获取下一期精彩内容!

标签: #双目视觉 #三维重建 #立体匹配 #相机标定 #高炉炼铁 #点云

相关文章:

👍 如果觉得有帮助,请点赞、收藏、转发!
版权归作者所有,未经许可请勿抄袭,套用,商用(或其它具有利益性行为)
🔔 关注专栏,不错过后续精彩内容!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

段一凡-华北理工大学

感谢鼓励,继续努力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值