diff --git a/src/lane_identification/lane_identificantion.py b/src/lane_identification/lane_identificantion.py index ff81b1ee45..41685ff64c 100644 --- a/src/lane_identification/lane_identificantion.py +++ b/src/lane_identification/lane_identificantion.py @@ -6,10 +6,11 @@ from PIL import Image, ImageTk import threading import time -import json -from dataclasses import dataclass -from typing import Optional, Tuple, List, Dict, Any +from dataclasses import dataclass, field +from typing import Optional, Tuple, List, Dict, Any, Callable +from collections import deque +# ==================== 配置类 ==================== @dataclass class DetectionConfig: """检测配置参数""" @@ -28,927 +29,547 @@ class DetectionConfig: hough_min_length: int = 20 hough_max_gap: int = 50 - # 方向判断阈值 - width_ratio_threshold: float = 0.7 - center_deviation_threshold: float = 0.15 - # 形态学操作参数 morph_kernel_size: int = 5 blur_kernel_size: int = 5 - # 新算法参数 + # ROI参数 roi_top_ratio: float = 0.4 roi_bottom_ratio: float = 0.9 - lane_width_ratio: float = 0.3 - min_contour_area_ratio: float = 0.02 - # 路径预测参数 - prediction_steps: int = 5 # 预测步数 - prediction_distance: float = 0.7 # 预测距离(相对于图像高度) - -class EnhancedImageProcessor: - """增强图像处理器""" - - def __init__(self, config: DetectionConfig = None): - self.config = config or DetectionConfig() - - def resize_image(self, image: np.ndarray, max_size: Tuple[int, int] = (1200, 800)) -> np.ndarray: - """调整图像尺寸""" - h, w = image.shape[:2] - if w > max_size[0] or h > max_size[1]: - scale = min(max_size[0] / w, max_size[1] / h) - new_size = (int(w * scale), int(h * scale)) - return cv2.resize(image, new_size, interpolation=cv2.INTER_AREA) - return image - - def adaptive_enhancement(self, image: np.ndarray) -> np.ndarray: - """自适应图像增强""" - # 1. 直方图均衡化 - lab = cv2.cvtColor(image, cv2.COLOR_BGR2LAB) - l, a, b = cv2.split(lab) - clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8)) - l = clahe.apply(l) - lab = cv2.merge([l, a, b]) - enhanced = cv2.cvtColor(lab, cv2.COLOR_LAB2BGR) - - # 2. 自适应伽马校正 - def adaptive_gamma_correction(img, gamma=1.0): - inv_gamma = 1.0 / gamma - table = np.array([((i / 255.0) ** inv_gamma) * 255 for i in np.arange(0, 256)]).astype("uint8") - return cv2.LUT(img, table) - - # 根据图像亮度自适应调整gamma - gray = cv2.cvtColor(enhanced, cv2.COLOR_BGR2GRAY) - mean_brightness = np.mean(gray) - - if mean_brightness < 50: # 太暗 - enhanced = adaptive_gamma_correction(enhanced, 0.7) - elif mean_brightness > 200: # 太亮 - enhanced = adaptive_gamma_correction(enhanced, 1.3) - - return enhanced + # 方向判断阈值 + width_ratio_threshold: float = 0.7 + center_deviation_threshold: float = 0.15 - def remove_shadows_and_glare(self, image: np.ndarray) -> np.ndarray: - """去除阴影和反光""" - rgb_planes = cv2.split(image) - result_planes = [] - for plane in rgb_planes: - dilated_img = cv2.dilate(plane, np.ones((7,7), np.uint8)) - bg_img = cv2.medianBlur(dilated_img, 21) - diff_img = 255 - cv2.absdiff(plane, bg_img) - result_planes.append(diff_img) - return cv2.merge(result_planes) + # 路径预测参数 + prediction_steps: int = 5 + prediction_distance: float = 0.7 - def extract_roi(self, image: np.ndarray) -> np.ndarray: - """提取感兴趣区域""" - height, width = image.shape[:2] - - # 定义梯形ROI区域,更专注于道路区域 - top_width = int(width * 0.15) - bottom_width = int(width * 0.9) - top_height = int(height * self.config.roi_top_ratio) - bottom_height = int(height * self.config.roi_bottom_ratio) - - roi_vertices = np.array([[ - ((width - bottom_width) // 2, bottom_height), - ((width - top_width) // 2, top_height), - ((width + top_width) // 2, top_height), - ((width + bottom_width) // 2, bottom_height) - ]], dtype=np.int32) - - # 创建掩码 - mask = np.zeros_like(image) - cv2.fillPoly(mask, roi_vertices, (255, 255, 255)) - - # 应用掩码 - roi_image = cv2.bitwise_and(image, mask) - - return roi_image, roi_vertices + # 性能优化参数 + max_image_size: Tuple[int, int] = (1200, 800) + cache_size: int = 3 -class EnhancedRoadDetector: - """增强道路检测器""" +# ==================== 图像处理器 ==================== +class ImageProcessor: + """高效图像处理器""" - def __init__(self, config: DetectionConfig = None): - self.config = config or DetectionConfig() - self.image_processor = EnhancedImageProcessor(config) - self.last_processing_time = 0 - - def detect_road_region(self, image: np.ndarray) -> Dict[str, Any]: - """检测道路区域 - 改进的多方法融合""" - height, width = image.shape[:2] - min_area = height * width * self.config.min_contour_area_ratio + def __init__(self, config: DetectionConfig): + self.config = config + self._cache = {} # 简单的处理结果缓存 + def process_image(self, image_path: str) -> Optional[np.ndarray]: + """加载并预处理图像""" try: - # 1. 图像预处理 - enhanced = self.image_processor.adaptive_enhancement(image) - shadow_removed = self.image_processor.remove_shadows_and_glare(enhanced) - - # 2. 提取ROI - roi_image, roi_vertices = self.image_processor.extract_roi(shadow_removed) + # 读取图像 + image = cv2.imread(image_path) + if image is None: + return None - # 3. 多方法道路检测 - methods_results = [] + # 缓存键 + cache_key = f"{image_path}_{image.shape}" - # 方法1: HSV颜色空间分割 - hsv_result = self._detect_by_hsv(roi_image) - methods_results.append(hsv_result) + # 检查缓存 + if cache_key in self._cache: + return self._cache[cache_key] - # 方法2: 基于边缘检测 - edge_result = self._detect_by_edges(roi_image) - methods_results.append(edge_result) - - # 方法3: 基于灰度阈值 - gray_result = self._detect_by_gray(roi_image) - methods_results.append(gray_result) + # 调整图像尺寸 + processed = self._resize_image(image) - # 4. 融合结果 - fused_mask = self._fuse_masks(methods_results) + # 增强对比度 + processed = self._enhance_contrast(processed) - # 5. 形态学优化 - kernel = np.ones((self.config.morph_kernel_size, self.config.morph_kernel_size), np.uint8) - fused_mask = cv2.morphologyEx(fused_mask, cv2.MORPH_CLOSE, kernel) - fused_mask = cv2.morphologyEx(fused_mask, cv2.MORPH_OPEN, kernel) + # 去除阴影 + processed = self._remove_shadows(processed) - # 6. 提取轮廓 - contours, _ = cv2.findContours(fused_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + # 更新缓存 + if len(self._cache) >= self.config.cache_size: + self._cache.pop(next(iter(self._cache))) + self._cache[cache_key] = processed - valid_contours = [] - for contour in contours: - area = cv2.contourArea(contour) - if area > min_area: - valid_contours.append(contour) - - if valid_contours: - # 合并所有有效轮廓 - combined_contour = np.vstack(valid_contours) - - # 使用凸包简化轮廓 - hull = cv2.convexHull(combined_contour) - - # 使用多边形近似进一步简化 - epsilon = 0.005 * cv2.arcLength(hull, True) - simplified_hull = cv2.approxPolyDP(hull, epsilon, True) - - return { - 'mask': fused_mask, - 'contour': simplified_hull, - 'roi_vertices': roi_vertices, - 'method_scores': [r['confidence'] for r in methods_results] - } - - return {'mask': fused_mask, 'contour': None, 'roi_vertices': roi_vertices} + return processed except Exception as e: - print(f"道路区域检测失败: {str(e)}") - return {'mask': None, 'contour': None, 'roi_vertices': None} + print(f"图像处理错误: {e}") + return None - def _detect_by_hsv(self, image: np.ndarray) -> Dict[str, Any]: - """HSV颜色空间检测""" - hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) - - # 多种颜色范围(适应不同道路颜色) - color_ranges = [ - (np.array([0, 0, 50]), np.array([180, 50, 200])), # 深色道路 - (np.array([0, 0, 100]), np.array([180, 30, 220])), # 中等颜色 - (np.array([0, 0, 150]), np.array([180, 20, 240])) # 浅色道路 - ] + def _resize_image(self, image: np.ndarray) -> np.ndarray: + """智能调整图像尺寸""" + h, w = image.shape[:2] + max_w, max_h = self.config.max_image_size - masks = [] - for lower, upper in color_ranges: - mask = cv2.inRange(hsv, lower, upper) - masks.append(mask) + if w > max_w or h > max_h: + scale = min(max_w / w, max_h / h) + new_size = (int(w * scale), int(h * scale)) + return cv2.resize(image, new_size, interpolation=cv2.INTER_AREA) - # 合并所有掩码 - combined_mask = np.zeros_like(masks[0]) - for mask in masks: - combined_mask = cv2.bitwise_or(combined_mask, mask) + return image + + def _enhance_contrast(self, image: np.ndarray) -> np.ndarray: + """快速对比度增强""" + # 使用YUV颜色空间进行亮度调整 + yuv = cv2.cvtColor(image, cv2.COLOR_BGR2YUV) + y_channel = yuv[:, :, 0] - # 计算置信度(基于非零像素比例) - total_pixels = mask.shape[0] * mask.shape[1] - road_pixels = np.count_nonzero(combined_mask) - confidence = road_pixels / total_pixels + # 自适应直方图均衡化 + clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)) + yuv[:, :, 0] = clahe.apply(y_channel) - return {'mask': combined_mask, 'confidence': confidence} + return cv2.cvtColor(yuv, cv2.COLOR_YUV2BGR) - def _detect_by_edges(self, image: np.ndarray) -> Dict[str, Any]: - """基于边缘检测""" + def _remove_shadows(self, image: np.ndarray) -> np.ndarray: + """快速阴影去除""" + # 转换为灰度图 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) - # 自适应阈值 - thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, - cv2.THRESH_BINARY, 11, 2) + # 使用形态学操作移除阴影 + dilated = cv2.dilate(gray, np.ones((3, 3), np.uint8)) + blurred = cv2.medianBlur(dilated, 15) + diff = 255 - cv2.absdiff(gray, blurred) + normalized = cv2.normalize(diff, None, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX) - # Canny边缘检测 - edges = cv2.Canny(gray, self.config.canny_low, self.config.canny_high) + # 将处理后的灰度图转回BGR + return cv2.cvtColor(normalized, cv2.COLOR_GRAY2BGR) + +# ==================== 道路检测器 ==================== +class RoadDetector: + """高效道路检测器""" + + def __init__(self, config: DetectionConfig): + self.config = config - # 结合边缘和阈值 - combined = cv2.bitwise_or(thresh, edges) + def detect_road(self, image: np.ndarray) -> Dict[str, Any]: + """快速道路检测""" + height, width = image.shape[:2] - # 形态学操作连接边缘 - kernel = np.ones((3, 3), np.uint8) - combined = cv2.morphologyEx(combined, cv2.MORPH_CLOSE, kernel) + try: + # 1. 提取ROI + roi_vertices = self._get_roi_vertices(width, height) + roi_image = self._extract_roi(image, roi_vertices) + + # 2. 多方法道路检测 + road_mask = self._detect_road_mask(roi_image) + + # 3. 提取道路轮廓 + road_contour = self._extract_contour(road_mask) + + return { + 'roi_vertices': roi_vertices, + 'road_mask': road_mask, + 'road_contour': road_contour, + 'image_size': (width, height) + } + + except Exception as e: + print(f"道路检测错误: {e}") + return {'roi_vertices': None, 'road_mask': None, 'road_contour': None} + + def _get_roi_vertices(self, width: int, height: int) -> np.ndarray: + """计算ROI顶点""" + return np.array([[ + (width * 0.1, height * self.config.roi_bottom_ratio), + (width * 0.4, height * self.config.roi_top_ratio), + (width * 0.6, height * self.config.roi_top_ratio), + (width * 0.9, height * self.config.roi_bottom_ratio) + ]], dtype=np.int32) + + def _extract_roi(self, image: np.ndarray, roi_vertices: np.ndarray) -> np.ndarray: + """提取ROI区域""" + mask = np.zeros_like(image) + cv2.fillPoly(mask, roi_vertices, (255, 255, 255)) + return cv2.bitwise_and(image, mask) + + def _detect_road_mask(self, roi_image: np.ndarray) -> np.ndarray: + """检测道路掩码""" + # 转换为HSV颜色空间 + hsv = cv2.cvtColor(roi_image, cv2.COLOR_BGR2HSV) - # 填充闭合区域 - contours, _ = cv2.findContours(combined, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) - filled_mask = np.zeros_like(combined) - for contour in contours: - if cv2.contourArea(contour) > 100: - cv2.drawContours(filled_mask, [contour], -1, 255, -1) + # 创建道路掩码 + lower = np.array(self.config.hsv_lower) + upper = np.array(self.config.hsv_upper) + road_mask = cv2.inRange(hsv, lower, upper) - # 计算置信度 - total_pixels = filled_mask.shape[0] * filled_mask.shape[1] - road_pixels = np.count_nonzero(filled_mask) - confidence = road_pixels / total_pixels + # 形态学优化 + kernel = np.ones((self.config.morph_kernel_size, self.config.morph_kernel_size), np.uint8) + road_mask = cv2.morphologyEx(road_mask, cv2.MORPH_CLOSE, kernel) + road_mask = cv2.morphologyEx(road_mask, cv2.MORPH_OPEN, kernel) - return {'mask': filled_mask, 'confidence': confidence} + return road_mask - def _detect_by_gray(self, image: np.ndarray) -> Dict[str, Any]: - """基于灰度阈值""" - gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) - - # Otsu自动阈值 - _, otsu_thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) + def _extract_contour(self, road_mask: np.ndarray) -> Optional[np.ndarray]: + """提取道路轮廓""" + contours, _ = cv2.findContours(road_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) - # 均值偏移分割 - shifted = cv2.pyrMeanShiftFiltering(image, 21, 51) - gray_shifted = cv2.cvtColor(shifted, cv2.COLOR_BGR2GRAY) - _, shift_thresh = cv2.threshold(gray_shifted, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) + if not contours: + return None - # 结合两种方法 - combined = cv2.bitwise_and(otsu_thresh, shift_thresh) + # 找到最大轮廓 + largest_contour = max(contours, key=cv2.contourArea) - # 计算置信度 - total_pixels = combined.shape[0] * combined.shape[1] - road_pixels = np.count_nonzero(combined) - confidence = road_pixels / total_pixels + # 简化轮廓 + epsilon = 0.01 * cv2.arcLength(largest_contour, True) + simplified = cv2.approxPolyDP(largest_contour, epsilon, True) - return {'mask': combined, 'confidence': confidence} + return simplified + +# ==================== 车道线检测器 ==================== +class LaneDetector: + """高效车道线检测器""" - def _fuse_masks(self, method_results: List[Dict[str, Any]]) -> np.ndarray: - """融合多个方法的掩码""" - masks = [r['mask'] for r in method_results] - confidences = [r['confidence'] for r in method_results] - - if not masks: - return np.zeros((100, 100), dtype=np.uint8) - - # 加权融合 - total_confidence = sum(confidences) - if total_confidence > 0: - weights = [c / total_confidence for c in confidences] - - # 初始化融合掩码 - fused = np.zeros_like(masks[0], dtype=np.float32) - - for mask, weight in zip(masks, weights): - fused += mask.astype(np.float32) * weight - - # 转换为二值图像 - fused_binary = (fused > 127).astype(np.uint8) * 255 - - return fused_binary - else: - return masks[0] + def __init__(self, config: DetectionConfig): + self.config = config - def detect_lane_lines_enhanced(self, image: np.ndarray, roi_vertices: np.ndarray) -> Dict[str, Any]: - """增强车道线检测""" + def detect_lanes(self, image: np.ndarray, roi_vertices: np.ndarray) -> Dict[str, Any]: + """检测车道线""" height, width = image.shape[:2] try: - # 转换为灰度图 + # 1. 预处理图像 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) + blurred = cv2.GaussianBlur(gray, (self.config.blur_kernel_size, self.config.blur_kernel_size), 0) - # 自适应直方图均衡化 - clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)) - gray = clahe.apply(gray) - - # 高斯模糊 - blur = cv2.GaussianBlur(gray, (self.config.blur_kernel_size, - self.config.blur_kernel_size), 0) - - # 边缘检测 - 使用Sobel算子增强车道线 - sobelx = cv2.Sobel(blur, cv2.CV_64F, 1, 0, ksize=3) - sobely = cv2.Sobel(blur, cv2.CV_64F, 0, 1, ksize=3) - gradient_magnitude = np.sqrt(sobelx**2 + sobely**2) - gradient_magnitude = np.uint8(255 * gradient_magnitude / np.max(gradient_magnitude)) + # 2. 边缘检测 + edges = cv2.Canny(blurred, self.config.canny_low, self.config.canny_high) - # 自适应阈值 - _, binary = cv2.threshold(gradient_magnitude, 0, 255, - cv2.THRESH_BINARY + cv2.THRESH_OTSU) - - # 应用ROI掩码 - mask = np.zeros_like(binary) + # 3. 应用ROI + mask = np.zeros_like(edges) cv2.fillPoly(mask, roi_vertices, 255) - masked_edges = cv2.bitwise_and(binary, mask) + masked_edges = cv2.bitwise_and(edges, mask) - # 概率霍夫变换检测直线 + # 4. 霍夫变换检测直线 lines = cv2.HoughLinesP( masked_edges, - rho=2, - theta=np.pi/180, - threshold=50, - minLineLength=30, - maxLineGap=100 + self.config.hough_rho, + self.config.hough_theta, + self.config.hough_threshold, + minLineLength=self.config.hough_min_length, + maxLineGap=self.config.hough_max_gap ) if lines is None: - return {"left_lines": [], "right_lines": [], "center_line": None} - - # 改进的线段分类和过滤 - left_segments, right_segments = self._classify_lanes_enhanced(lines, width) + return self._empty_result() - # 拟合车道线 - left_lane = self._fit_lane_line(left_segments, height) - right_lane = self._fit_lane_line(right_segments, height) + # 5. 分类和处理车道线 + left_lines, right_lines = self._classify_lines(lines, width) - # 计算中心线 - center_line = self._calculate_center_line(left_lane, right_lane, height) \ - if left_lane and right_lane else None + # 6. 拟合车道线 + left_lane = self._fit_lane(left_lines, height) if left_lines else None + right_lane = self._fit_lane(right_lines, height) if right_lines else None - # 预测未来路径 - future_path = self._predict_future_path(left_lane, right_lane, center_line, height) \ - if left_lane and right_lane else None + # 7. 预测未来路径 + future_path = self._predict_path(left_lane, right_lane, height) if left_lane and right_lane else None return { - "left_lines": left_segments, - "right_lines": right_segments, - "left_lane": left_lane, - "right_lane": right_lane, - "center_line": center_line, - "future_path": future_path, - "num_lines": len(lines) + 'left_lines': left_lines, + 'right_lines': right_lines, + 'left_lane': left_lane, + 'right_lane': right_lane, + 'future_path': future_path, + 'num_lines': len(lines) } except Exception as e: - print(f"车道线检测失败: {str(e)}") - return {"left_lines": [], "right_lines": [], "center_line": None} + print(f"车道线检测错误: {e}") + return self._empty_result() + + def _empty_result(self) -> Dict[str, Any]: + """返回空结果""" + return { + 'left_lines': [], 'right_lines': [], + 'left_lane': None, 'right_lane': None, + 'future_path': None, 'num_lines': 0 + } - def _classify_lanes_enhanced(self, lines: np.ndarray, image_width: int) -> Tuple[List, List]: - """改进的车道线分类""" - left_segments = [] - right_segments = [] + def _classify_lines(self, lines: np.ndarray, image_width: int) -> Tuple[List, List]: + """分类左右车道线""" + left_lines = [] + right_lines = [] for line in lines: x1, y1, x2, y2 = line[0] - # 过滤水平线 - if abs(y2 - y1) < 10: - continue - - # 计算斜率和截距 - if x2 - x1 == 0: + # 跳过垂直线 + if x2 == x1: continue + # 计算斜率 slope = (y2 - y1) / (x2 - x1) - length = np.sqrt((x2 - x1)**2 + (y2 - y1)**2) - # 过滤不合理斜率的线段 - if abs(slope) < 0.2 or abs(slope) > 2.0: + # 过滤水平线 + if abs(slope) < 0.3: continue - # 根据斜率和位置分类 - if slope < 0: # 左车道线 - if x1 < image_width * 0.6 and x2 < image_width * 0.6: - left_segments.append({ - 'points': [(x1, y1), (x2, y2)], - 'slope': slope, - 'length': length, - 'midpoint': ((x1 + x2)//2, (y1 + y2)//2) - }) - else: # 右车道线 - if x1 > image_width * 0.4 and x2 > image_width * 0.4: - right_segments.append({ - 'points': [(x1, y1), (x2, y2)], - 'slope': slope, - 'length': length, - 'midpoint': ((x1 + x2)//2, (y1 + y2)//2) - }) - - # 过滤异常值(基于聚类) - left_segments = self._filter_outliers(left_segments) - right_segments = self._filter_outliers(right_segments) - - return left_segments, right_segments - - def _filter_outliers(self, segments: List) -> List: - """过滤异常线段(基于斜率和位置聚类)""" - if len(segments) < 2: - return segments - - # 提取斜率 - slopes = [seg['slope'] for seg in segments] - - # 使用标准差过滤 - mean_slope = np.mean(slopes) - std_slope = np.std(slopes) + # 分类左右车道线 + if slope < 0 and x1 < image_width * 0.6 and x2 < image_width * 0.6: + left_lines.append((x1, y1, x2, y2, slope)) + elif slope > 0 and x1 > image_width * 0.4 and x2 > image_width * 0.4: + right_lines.append((x1, y1, x2, y2, slope)) - filtered = [] - for seg in segments: - if abs(seg['slope'] - mean_slope) < 2 * std_slope: - filtered.append(seg) - - return filtered + return left_lines, right_lines - def _fit_lane_line(self, segments: List, image_height: int) -> Optional[Dict]: - """拟合车道线""" - if len(segments) < 2: + def _fit_lane(self, lines: List, height: int) -> Optional[Dict]: + """拟合单条车道线""" + if len(lines) < 2: return None # 收集所有点 - all_x = [] - all_y = [] - - for seg in segments: - for point in seg['points']: - all_x.append(point[0]) - all_y.append(point[1]) - - # 二次多项式拟合(适应弯曲车道) - try: - coeffs = np.polyfit(all_y, all_x, 2) # x = ay² + by + c - poly_func = np.poly1d(coeffs) - - # 生成拟合点 - y_top = int(image_height * 0.4) - y_bottom = image_height - - x_top = int(poly_func(y_top)) - x_bottom = int(poly_func(y_bottom)) - - return { - 'coefficients': coeffs, - 'points': [(x_bottom, y_bottom), (x_top, y_top)], - 'func': poly_func, - 'confidence': min(len(segments) / 10.0, 1.0) - } - except: - # 如果二次拟合失败,使用线性拟合 - coeffs = np.polyfit(all_y, all_x, 1) - poly_func = np.poly1d(coeffs) - - y_top = int(image_height * 0.4) - y_bottom = image_height - - x_top = int(poly_func(y_top)) - x_bottom = int(poly_func(y_bottom)) - - return { - 'coefficients': coeffs, - 'points': [(x_bottom, y_bottom), (x_top, y_top)], - 'func': poly_func, - 'confidence': min(len(segments) / 10.0, 0.8) # 线性拟合置信度较低 - } - - def _calculate_center_line(self, left_lane: Dict, right_lane: Dict, image_height: int) -> Dict: - """计算中心线""" - if not left_lane or not right_lane: - return None - - try: - # 获取左右车道线函数 - left_func = left_lane['func'] - right_func = right_lane['func'] - - # 计算中心线函数(平均) - def center_func(y): - return (left_func(y) + right_func(y)) / 2 - - # 生成中心线点 - y_top = int(image_height * 0.4) - y_bottom = image_height - - x_top = int(center_func(y_top)) - x_bottom = int(center_func(y_bottom)) - - return { - 'func': center_func, - 'points': [(x_bottom, y_bottom), (x_top, y_top)] - } - except: - return None + x_points, y_points = [], [] + for x1, y1, x2, y2, _ in lines: + x_points.extend([x1, x2]) + y_points.extend([y1, y2]) + + # 线性拟合 + coeffs = np.polyfit(y_points, x_points, 1) + poly_func = np.poly1d(coeffs) + + # 计算车道线端点 + y_bottom, y_top = height, int(height * self.config.roi_top_ratio) + x_bottom, x_top = int(poly_func(y_bottom)), int(poly_func(y_top)) + + return { + 'func': poly_func, + 'points': [(x_bottom, y_bottom), (x_top, y_top)], + 'confidence': min(len(lines) / 8.0, 1.0) + } - def _predict_future_path(self, left_lane: Dict, right_lane: Dict, center_line: Dict, image_height: int) -> Dict: + def _predict_path(self, left_lane: Dict, right_lane: Dict, height: int) -> Dict: """预测未来路径""" - if not left_lane or not right_lane: - return None + left_func = left_lane['func'] + right_func = right_lane['func'] + + # 计算中心线函数 + def center_func(y): + return (left_func(y) + right_func(y)) / 2 + + # 生成预测点 + current_y = height + target_y = int(height * (1 - self.config.prediction_distance)) + y_values = np.linspace(current_y, target_y, self.config.prediction_steps) + + # 计算路径点 + path_points = [] + for y in y_values: + x = center_func(y) + path_points.append((int(x), int(y))) + + return { + 'center_path': path_points, + 'prediction_distance': self.config.prediction_distance + } + +# ==================== 方向分析器 ==================== +class DirectionAnalyzer: + """智能方向分析器""" + + def __init__(self, config: DetectionConfig): + self.config = config + self.history = deque(maxlen=5) # 历史记录队列 + self.confidence_history = deque(maxlen=5) + + def analyze(self, road_contour: np.ndarray, lane_info: Dict[str, Any], + image_size: Tuple[int, int]) -> Dict[str, Any]: + """分析道路方向""" + width, height = image_size try: - # 获取车道线函数 - left_func = left_lane['func'] - right_func = right_lane['func'] - center_func = center_line['func'] if center_line else None + # 1. 基于轮廓分析 + contour_dir, contour_conf = self._analyze_contour(road_contour, width, height) - if not center_func: - # 如果没有中心线,计算一个 - def center_func(y): - return (left_func(y) + right_func(y)) / 2 + # 2. 基于车道线分析 + lane_dir, lane_conf = self._analyze_lanes(lane_info, width, height) - # 确定预测范围 - current_y = image_height # 当前位置(图像底部) - prediction_end_y = int(image_height * (1 - self.config.prediction_distance)) + # 3. 基于路径预测分析 + path_dir, path_conf = self._analyze_path(lane_info, width, height) - # 生成预测点 - y_values = np.linspace(current_y, prediction_end_y, self.config.prediction_steps) + # 4. 综合判断 + directions = [contour_dir, lane_dir, path_dir] + confidences = [contour_conf, lane_conf, path_conf] - # 预测中心线路径 - path_points = [] - for y in y_values: - x = center_func(y) - if 0 <= x < image_height * 2: # 合理的x范围 - path_points.append((int(x), int(y))) + # 过滤无效结果 + valid_indices = [i for i, conf in enumerate(confidences) + if conf > 0.3 and directions[i] != "未知"] - # 预测左右边界 - left_boundary = [] - right_boundary = [] + if not valid_indices: + return self._default_result() - for y in y_values: - left_x = left_func(y) - right_x = right_func(y) - - if 0 <= left_x < image_height * 2: - left_boundary.append((int(left_x), int(y))) - if 0 <= right_x < image_height * 2: - right_boundary.append((int(right_x), int(y))) + # 加权投票 + final_dir, final_conf = self._weighted_vote( + [directions[i] for i in valid_indices], + [confidences[i] for i in valid_indices] + ) + + # 5. 历史平滑 + smoothed_dir, smoothed_conf = self._smooth_with_history(final_dir, final_conf) return { - 'center_path': path_points, - 'left_boundary': left_boundary, - 'right_boundary': right_boundary, - 'prediction_distance': self.config.prediction_distance, - 'num_points': len(path_points) + 'direction': smoothed_dir, + 'confidence': smoothed_conf, + 'source': '综合判断', + 'details': { + 'contour': contour_dir, + 'lanes': lane_dir, + 'path': path_dir + } } except Exception as e: - print(f"路径预测失败: {str(e)}") - return None - -class EnhancedDirectionAnalyzer: - """增强方向分析器""" + print(f"方向分析错误: {e}") + return self._default_result() + + def _default_result(self) -> Dict[str, Any]: + """返回默认结果""" + return { + 'direction': '未知', + 'confidence': 0.0, + 'source': '错误', + 'details': {} + } - def __init__(self, config: DetectionConfig = None): - self.config = config or DetectionConfig() - self.direction_history = [] - self.confidence_history = [] - self.history_size = 5 - - def analyze_direction(self, road_info: Dict[str, Any], lane_info: Dict[str, Any], - image_size: Tuple[int, int]) -> Dict[str, Any]: - """综合分析道路方向 - 多特征融合""" - width, height = image_size - - # 1. 基于道路轮廓的方向分析 - contour_direction, contour_confidence = self._analyze_from_contour( - road_info.get('contour'), image_size - ) - - # 2. 基于车道线的方向分析 - lane_direction, lane_confidence = self._analyze_from_lanes(lane_info, image_size) + def _analyze_contour(self, contour: np.ndarray, width: int, height: int) -> Tuple[str, float]: + """基于轮廓分析方向""" + if contour is None or len(contour) < 3: + return "未知", 0.0 - # 3. 基于路径预测的方向分析 - path_direction, path_confidence = self._analyze_from_path(lane_info, image_size) + # 计算轮廓质心 + M = cv2.moments(contour) + if M["m00"] == 0: + return "未知", 0.0 - # 4. 综合判断 - directions = [contour_direction, lane_direction, path_direction] - confidences = [contour_confidence, lane_confidence, path_confidence] + cx = int(M["m10"] / M["m00"]) - # 加权投票 - final_direction, final_confidence = self._weighted_vote(directions, confidences) + # 判断方向 + center_x = width / 2 + deviation = (cx - center_x) / (width / 2) - # 5. 使用历史记录平滑 - if final_confidence > 0.5: - smoothed_direction, smoothed_confidence = self._smooth_with_history( - final_direction, final_confidence - ) - - return { - 'direction': smoothed_direction, - 'confidence': smoothed_confidence, - 'contour_direction': contour_direction, - 'lane_direction': lane_direction, - 'path_direction': path_direction, - 'raw_confidences': confidences - } + if abs(deviation) < self.config.center_deviation_threshold: + return "直行", 0.7 - abs(deviation) + elif deviation > 0: + return "右转", min(0.8, abs(deviation)) else: - return { - 'direction': final_direction, - 'confidence': final_confidence, - 'contour_direction': contour_direction, - 'lane_direction': lane_direction, - 'path_direction': path_direction, - 'raw_confidences': confidences - } + return "左转", min(0.8, abs(deviation)) - def _analyze_from_contour(self, contour: np.ndarray, image_size: Tuple[int, int]) -> Tuple[str, float]: - """基于道路轮廓分析方向""" - if contour is None or len(contour) < 3: - return "未知方向", 0.0 - - width, height = image_size - - try: - contour_points = contour.reshape(-1, 2) - - # 计算轮廓的几何特征 - M = cv2.moments(contour) - if M["m00"] == 0: - return "未知方向", 0.0 - - cx = int(M["m10"] / M["m00"]) - cy = int(M["m01"] / M["m00"]) - - # 1. 质心位置分析 - image_center_x = width / 2 - deviation_ratio = (cx - image_center_x) / (width / 2) - - if abs(deviation_ratio) < 0.1: - centroid_direction = "直行" - centroid_confidence = 0.7 - abs(deviation_ratio) - elif deviation_ratio > 0: - centroid_direction = "右转" - centroid_confidence = min(0.7, abs(deviation_ratio)) - else: - centroid_direction = "左转" - centroid_confidence = min(0.7, abs(deviation_ratio)) - - # 2. 轮廓形状分析 - shape_direction, shape_confidence = self._analyze_contour_shape( - contour_points, height, width - ) - - # 3. 融合结果 - if centroid_direction == shape_direction: - confidence = (centroid_confidence + shape_confidence) / 2 - return centroid_direction, confidence - else: - # 取置信度更高的结果 - if centroid_confidence > shape_confidence: - return centroid_direction, centroid_confidence * 0.8 - else: - return shape_direction, shape_confidence * 0.8 - - except Exception as e: - print(f"轮廓分析失败: {str(e)}") - return "未知方向", 0.0 - - def _analyze_contour_shape(self, contour_points: np.ndarray, height: int, width: int) -> Tuple[str, float]: - """分析轮廓形状""" - # 在不同高度分析轮廓宽度 - heights = [height * 0.3, height * 0.5, height * 0.7] - widths = [] - centers = [] - - for h in heights: - points_at_height = [p for p in contour_points if abs(p[1] - h) < 10] - if len(points_at_height) >= 2: - min_x = min(p[0] for p in points_at_height) - max_x = max(p[0] for p in points_at_height) - widths.append(max_x - min_x) - centers.append((min_x + max_x) / 2) - - if len(widths) >= 2: - # 分析宽度变化 - width_ratio = widths[0] / widths[-1] # 顶部宽度 / 底部宽度 - - if width_ratio < 0.6: # 明显变窄 - if centers[0] < width / 2: - return "左转", 0.8 - else: - return "右转", 0.8 - elif width_ratio > 1.4: # 明显变宽 - if centers[0] < width / 2: - return "右转", 0.7 - else: - return "左转", 0.7 - else: # 宽度变化不大 - # 分析中心线偏移 - center_deviation = centers[0] - width / 2 - if abs(center_deviation) < width * 0.1: - return "直行", 0.6 - elif center_deviation > 0: - return "右转", 0.7 - else: - return "左转", 0.7 - - return "未知方向", 0.0 - - def _analyze_from_lanes(self, lane_info: Dict[str, Any], image_size: Tuple[int, int]) -> Tuple[str, float]: + def _analyze_lanes(self, lane_info: Dict[str, Any], width: int, height: int) -> Tuple[str, float]: """基于车道线分析方向""" - width, height = image_size - if not lane_info.get('left_lane') or not lane_info.get('right_lane'): - return "未知方向", 0.0 + return "未知", 0.0 - left_lane = lane_info['left_lane'] - right_lane = lane_info['right_lane'] + left_func = lane_info['left_lane']['func'] + right_func = lane_info['right_lane']['func'] - try: - # 1. 计算车道中心线 - left_func = left_lane['func'] - right_func = right_lane['func'] - - def lane_center_func(y): - return (left_func(y) + right_func(y)) / 2 - - # 2. 分析顶部和底部的中心线位置 - y_top = int(height * 0.4) - y_bottom = height - - center_top = lane_center_func(y_top) - center_bottom = lane_center_func(y_bottom) - - # 3. 计算偏移和收敛角度 - image_center = width / 2 - - top_deviation = center_top - image_center - bottom_deviation = center_bottom - image_center - - # 偏移方向 - if abs(top_deviation) < width * 0.1 and abs(bottom_deviation) < width * 0.1: - direction = "直行" - confidence = max(0.7 - abs(top_deviation/image_center), 0.4) - elif top_deviation > 0: - direction = "右转" - confidence = min(0.8, abs(top_deviation/image_center)) - else: - direction = "左转" - confidence = min(0.8, abs(top_deviation/image_center)) - - # 4. 考虑车道宽度变化 - top_width = right_func(y_top) - left_func(y_top) - bottom_width = right_func(y_bottom) - left_func(y_bottom) - - width_ratio = top_width / bottom_width if bottom_width > 0 else 1 - - # 宽度变化对置信度的影响 - if direction == "直行" and width_ratio < 0.8: - confidence *= 0.8 # 可能误判 - elif direction in ["左转", "右转"] and width_ratio > 0.9: - confidence *= 0.8 # 可能误判 - - return direction, confidence - - except Exception as e: - print(f"车道线分析失败: {str(e)}") - return "未知方向", 0.0 + # 计算顶部中心点 + y_top = int(height * self.config.roi_top_ratio) + center_top = (left_func(y_top) + right_func(y_top)) / 2 + + # 判断方向 + center_x = width / 2 + deviation = (center_top - center_x) / (width / 2) + + if abs(deviation) < 0.1: + return "直行", 0.7 + elif deviation > 0: + return "右转", min(0.8, abs(deviation)) + else: + return "左转", min(0.8, abs(deviation)) - def _analyze_from_path(self, lane_info: Dict[str, Any], image_size: Tuple[int, int]) -> Tuple[str, float]: + def _analyze_path(self, lane_info: Dict[str, Any], width: int, height: int) -> Tuple[str, float]: """基于路径预测分析方向""" - width, height = image_size - if not lane_info.get('future_path'): - return "未知方向", 0.0 + return "未知", 0.0 - future_path = lane_info['future_path'] + path = lane_info['future_path']['center_path'] + if len(path) < 2: + return "未知", 0.0 - try: - if not future_path.get('center_path') or len(future_path['center_path']) < 2: - return "未知方向", 0.0 - - # 获取路径点 - path_points = future_path['center_path'] - - # 分析路径方向 - if len(path_points) >= 2: - # 计算起点和终点 - start_point = path_points[0] # 当前位置 - end_point = path_points[-1] # 预测终点 - - # 计算方向变化 - start_x, start_y = start_point - end_x, end_y = end_point - - # 计算水平偏移 - horizontal_shift = end_x - start_x - - # 根据偏移判断方向 - if abs(horizontal_shift) < width * 0.05: - return "直行", 0.7 - elif horizontal_shift > 0: - return "右转", min(0.8, abs(horizontal_shift) / (width * 0.2)) - else: - return "左转", min(0.8, abs(horizontal_shift) / (width * 0.2)) - - return "未知方向", 0.0 - - except Exception as e: - print(f"路径分析失败: {str(e)}") - return "未知方向", 0.0 - - def _weighted_vote(self, directions: List[str], confidences: List[float]) -> Tuple[str, float]: - """加权投票决定最终方向""" - valid_results = [] - for dir, conf in zip(directions, confidences): - if dir != "未知方向" and conf > 0.3: - valid_results.append((dir, conf)) + # 分析路径走向 + start_x, start_y = path[0] + end_x, end_y = path[-1] - if not valid_results: - return "未知方向", 0.0 + # 计算水平偏移 + horizontal_shift = end_x - start_x - # 统计每个方向的加权得分 + # 判断方向 + if abs(horizontal_shift) < width * 0.05: + return "直行", 0.6 + elif horizontal_shift > 0: + confidence = min(0.8, abs(horizontal_shift) / (width * 0.2)) + return "右转", confidence + else: + confidence = min(0.8, abs(horizontal_shift) / (width * 0.2)) + return "左转", confidence + + def _weighted_vote(self, directions: List[str], confidences: List[float]) -> Tuple[str, float]: + """加权投票""" scores = {} - for dir, conf in valid_results: - if dir not in scores: - scores[dir] = 0 - scores[dir] += conf - - # 选择得分最高的方向 - best_direction = max(scores.items(), key=lambda x: x[1])[0] - best_score = scores[best_direction] + for dir, conf in zip(directions, confidences): + scores[dir] = scores.get(dir, 0) + conf - # 归一化置信度 + best_dir = max(scores.items(), key=lambda x: x[1])[0] total_score = sum(scores.values()) - final_confidence = best_score / total_score if total_score > 0 else 0 + confidence = scores[best_dir] / total_score if total_score > 0 else 0 - return best_direction, final_confidence + return best_dir, confidence def _smooth_with_history(self, direction: str, confidence: float) -> Tuple[str, float]: - """使用历史记录平滑方向""" - self.direction_history.append(direction) + """历史平滑""" + self.history.append(direction) self.confidence_history.append(confidence) - if len(self.direction_history) > self.history_size: - self.direction_history.pop(0) - self.confidence_history.pop(0) - - # 当有足够历史记录时,进行平滑 - if len(self.direction_history) == self.history_size: - # 计算每个方向的出现频率和平均置信度 - direction_stats = {} - - for i, dir in enumerate(self.direction_history): - if dir not in direction_stats: - direction_stats[dir] = {'count': 0, 'total_confidence': 0} - direction_stats[dir]['count'] += 1 - direction_stats[dir]['total_confidence'] += self.confidence_history[i] - - # 选择最频繁且高置信度的方向 - best_dir = None - best_score = 0 - - for dir, stats in direction_stats.items(): - avg_confidence = stats['total_confidence'] / stats['count'] - score = stats['count'] * avg_confidence # 加权得分 - - if score > best_score: - best_score = score - best_dir = dir + if len(self.history) == self.history.maxlen: + # 统计最频繁的方向 + from collections import Counter + freq = Counter(self.history) + most_common = freq.most_common(1)[0] - if best_dir and best_score > 1.0: - smoothed_confidence = min(1.0, best_score / self.history_size) - return best_dir, smoothed_confidence + if most_common[1] >= 3: # 至少出现3次 + # 计算平均置信度 + indices = [i for i, d in enumerate(self.history) if d == most_common[0]] + avg_conf = sum(self.confidence_history[i] for i in indices) / len(indices) + return most_common[0], avg_conf return direction, confidence -class EnhancedVisualizationEngine: - """增强可视化引擎""" +# ==================== 可视化引擎 ==================== +class Visualizer: + """高效可视化引擎""" def __init__(self): self.colors = { - 'contour': (0, 255, 255), # 黄色 - 轮廓 - 'road_area': (0, 255, 0), # 绿色 - 道路区域 - 'left_lane': (255, 0, 0), # 蓝色 - 左车道线 - 'right_lane': (0, 0, 255), # 红色 - 右车道线 + 'contour': (0, 255, 255), # 黄色 - 轮廓 + 'road_area': (0, 255, 0), # 绿色 - 道路区域 + 'left_lane': (255, 0, 0), # 蓝色 - 左车道线 + 'right_lane': (0, 0, 255), # 红色 - 右车道线 'center_line': (255, 255, 0), # 青色 - 中心线 'future_path': (255, 0, 255), # 紫色 - 未来路径 - 'direction': (0, 0, 255), # 红色 - 方向指示 - 'roi': (0, 255, 255), # 黄色 - ROI区域 - 'text': (255, 255, 255), # 白色 - 文本 + 'direction': (0, 0, 255), # 红色 - 方向指示 + 'roi': (0, 255, 255), # 黄色 - ROI区域 + 'text': (255, 255, 255), # 白色 - 文本 + 'text_bg': (0, 0, 0, 180) # 黑色半透明背景 } - def draw_detection_results(self, image: np.ndarray, road_info: Dict[str, Any], - lane_info: Dict[str, Any], direction_info: Dict[str, Any]) -> np.ndarray: - """绘制增强的检测结果""" + def draw_results(self, image: np.ndarray, road_info: Dict[str, Any], + lane_info: Dict[str, Any], direction_info: Dict[str, Any]) -> np.ndarray: + """绘制检测结果""" result = image.copy() - height, width = result.shape[:2] - # 绘制ROI区域 - if road_info.get('roi_vertices') is not None: - cv2.polylines(result, [road_info['roi_vertices']], True, self.colors['roi'], 2) - - # 绘制道路轮廓和区域 - if road_info.get('contour') is not None: - self._draw_road_contour(result, road_info['contour']) - - # 绘制车道线 - self._draw_enhanced_lane_lines(result, lane_info) - - # 绘制未来路径 - if lane_info.get('future_path'): - self._draw_future_path(result, lane_info['future_path']) + # 绘制道路信息 + if road_info.get('road_contour') is not None: + self._draw_road(result, road_info) - # 添加增强的信息文本 - self._draw_enhanced_info_text(result, direction_info, height, width) + # 绘制车道线信息 + self._draw_lanes(result, lane_info) - # 绘制方向指示器 - direction = direction_info.get('direction', '未知方向') - confidence = direction_info.get('confidence', 0.0) - self._draw_enhanced_direction_indicator(result, direction, confidence, width, height) + # 绘制文本信息 + self._draw_info(result, direction_info) return result - def _draw_road_contour(self, image: np.ndarray, contour: np.ndarray): - """绘制道路轮廓""" - # 绘制轮廓线 + def _draw_road(self, image: np.ndarray, road_info: Dict[str, Any]): + """绘制道路信息""" + # 绘制ROI边界 + if road_info.get('roi_vertices') is not None: + cv2.polylines(image, [road_info['roi_vertices']], True, self.colors['roi'], 2) + + # 绘制道路轮廓 + contour = road_info['road_contour'] cv2.drawContours(image, [contour], -1, self.colors['contour'], 3) # 填充道路区域(半透明) @@ -956,451 +577,439 @@ def _draw_road_contour(self, image: np.ndarray, contour: np.ndarray): cv2.fillPoly(overlay, [contour], self.colors['road_area']) cv2.addWeighted(overlay, 0.15, image, 0.85, 0, image) - def _draw_enhanced_lane_lines(self, image: np.ndarray, lane_info: Dict[str, Any]): - """绘制增强的车道线""" - # 绘制原始线段 - for side in ['left_lines', 'right_lines']: - color = self.colors['left_lane'] if side == 'left_lines' else self.colors['right_lane'] - for segment in lane_info.get(side, []): - points = segment['points'] - cv2.line(image, tuple(points[0]), tuple(points[1]), color, 2) + def _draw_lanes(self, image: np.ndarray, lane_info: Dict[str, Any]): + """绘制车道线信息""" + # 绘制原始车道线段 + for side, color in [('left_lines', self.colors['left_lane']), + ('right_lines', self.colors['right_lane'])]: + for line in lane_info.get(side, []): + x1, y1, x2, y2, _ = line + cv2.line(image, (x1, y1), (x2, y2), color, 2) # 绘制拟合的车道线 - for side, color_key in [('left_lane', 'left_lane'), ('right_lane', 'right_lane')]: - if lane_info.get(side): - lane = lane_info[side] - points = lane.get('points', []) + for side, color in [('left_lane', self.colors['left_lane']), + ('right_lane', self.colors['right_lane'])]: + lane = lane_info.get(side) + if lane and 'points' in lane: + points = lane['points'] if len(points) == 2: - cv2.line(image, tuple(points[0]), tuple(points[1]), - self.colors[color_key], 4) - - # 绘制中心线 - if lane_info.get('center_line'): - center = lane_info['center_line'] - points = center.get('points', []) - if len(points) == 2: - cv2.line(image, tuple(points[0]), tuple(points[1]), - self.colors['center_line'], 3, cv2.LINE_AA) - - def _draw_future_path(self, image: np.ndarray, future_path: Dict[str, Any]): - """绘制未来路径""" - # 绘制中心路径 - center_path = future_path.get('center_path', []) - if len(center_path) >= 2: - # 将点转换为整数坐标 - points = np.array(center_path, np.int32) - - # 绘制路径线(使用抗锯齿) - for i in range(len(points) - 1): - cv2.line(image, tuple(points[i]), tuple(points[i + 1]), - self.colors['future_path'], 4, cv2.LINE_AA) - - # 绘制路径点 - for point in points: - cv2.circle(image, tuple(point), 4, self.colors['future_path'], -1) - - # 绘制路径边界(可选) - left_boundary = future_path.get('left_boundary', []) - right_boundary = future_path.get('right_boundary', []) - - if left_boundary and len(left_boundary) >= 2: - left_points = np.array(left_boundary, np.int32) - for i in range(len(left_points) - 1): - cv2.line(image, tuple(left_points[i]), tuple(left_points[i + 1]), - (255, 150, 255), 2, cv2.LINE_AA) - - if right_boundary and len(right_boundary) >= 2: - right_points = np.array(right_boundary, np.int32) - for i in range(len(right_points) - 1): - cv2.line(image, tuple(right_points[i]), tuple(right_points[i + 1]), - (255, 150, 255), 2, cv2.LINE_AA) + cv2.line(image, points[0], points[1], color, 4) + + # 绘制未来路径 + future_path = lane_info.get('future_path') + if future_path and 'center_path' in future_path: + path_points = future_path['center_path'] + if len(path_points) >= 2: + # 绘制路径线 + for i in range(len(path_points) - 1): + cv2.line(image, path_points[i], path_points[i + 1], + self.colors['future_path'], 3, cv2.LINE_AA) + + # 绘制路径点 + for point in path_points: + cv2.circle(image, point, 4, self.colors['future_path'], -1) - def _draw_enhanced_info_text(self, image: np.ndarray, direction_info: Dict[str, Any], height: int, width: int): - """绘制增强的信息文本""" - direction = direction_info.get('direction', '未知方向') + def _draw_info(self, image: np.ndarray, direction_info: Dict[str, Any]): + """绘制文本信息""" + height, width = image.shape[:2] + direction = direction_info.get('direction', '未知') confidence = direction_info.get('confidence', 0.0) - # 主方向信息 - direction_text = f"方向: {direction}" - confidence_text = f"置信度: {confidence:.2%}" - - # 路径预测信息 - prediction_text = "路径预测: 已启用" - - # 绘制主信息 - font_scale_large = 0.9 - font_scale_small = 0.6 + # 绘制半透明背景 + bg_height = 100 + overlay = image.copy() + cv2.rectangle(overlay, (0, 0), (width, bg_height), (0, 0, 0), -1) + cv2.addWeighted(overlay, 0.6, image, 0.4, 0, image) - cv2.putText(image, direction_text, (10, 30), - cv2.FONT_HERSHEY_SIMPLEX, font_scale_large, self.colors['text'], 2) + # 绘制方向文本 + direction_text = f"方向: {direction}" + confidence_text = f"置信度: {confidence:.1%}" # 根据置信度设置颜色 - conf_color = (0, 255, 0) if confidence > 0.7 else (0, 165, 255) if confidence > 0.5 else (0, 0, 255) - cv2.putText(image, confidence_text, (10, 65), - cv2.FONT_HERSHEY_SIMPLEX, font_scale_small, conf_color, 2) + if confidence > 0.7: + color = (0, 255, 0) # 绿色 + elif confidence > 0.5: + color = (0, 165, 255) # 橙色 + else: + color = (0, 0, 255) # 红色 - # 绘制路径预测信息 - cv2.putText(image, prediction_text, (10, 95), - cv2.FONT_HERSHEY_SIMPLEX, font_scale_small, (255, 255, 255), 1) - - def _draw_enhanced_direction_indicator(self, image: np.ndarray, direction: str, - confidence: float, width: int, height: int): - """绘制增强的方向指示器""" - center_x, center_y = width // 2, height // 2 - arrow_length = min(width, height) // 4 + # 绘制文本 + y_offset = 30 + cv2.putText(image, direction_text, (20, y_offset), + cv2.FONT_HERSHEY_SIMPLEX, 1.0, color, 2) - # 根据置信度调整箭头大小和颜色 - arrow_thickness = int(6 * confidence + 3) - alpha = int(255 * confidence) + cv2.putText(image, confidence_text, (20, y_offset + 40), + cv2.FONT_HERSHEY_SIMPLEX, 0.8, color, 2) + + # 绘制方向指示器 + self._draw_direction_indicator(image, direction, confidence, width, height) + + def _draw_direction_indicator(self, image: np.ndarray, direction: str, + confidence: float, width: int, height: int): + """绘制方向指示器""" + center_x, center_y = width // 2, height - 100 + arrow_length = 80 if direction == "左转": end_point = (center_x - arrow_length, center_y) - arrow_color = (0, 0, 255, alpha) # 红色,带透明度 + color = (0, 0, 255) # 红色 elif direction == "右转": end_point = (center_x + arrow_length, center_y) - arrow_color = (0, 0, 255, alpha) # 红色,带透明度 + color = (0, 0, 255) # 红色 else: # 直行 end_point = (center_x, center_y - arrow_length) - arrow_color = (0, 255, 0, alpha) # 绿色,带透明度 - - # 绘制箭头(使用叠加层实现透明度) - overlay = image.copy() - cv2.arrowedLine(overlay, (center_x, center_y), end_point, - arrow_color[:3], arrow_thickness, tipLength=0.3) + color = (0, 255, 0) # 绿色 - # 根据透明度混合 - cv2.addWeighted(overlay, alpha/255, image, 1 - alpha/255, 0, image) + # 根据置信度调整箭头大小 + thickness = int(6 * confidence + 3) + cv2.arrowedLine(image, (center_x, center_y), end_point, color, thickness, tipLength=0.3) -class EnhancedLaneDetectionApp: - """增强道路方向识别系统""" +# ==================== 主应用程序 ==================== +class LaneDetectionApp: + """主应用程序""" def __init__(self, root): self.root = root - self.root.title("智能道路方向识别系统 - 增强版") - self.root.geometry("1400x900") - self.root.minsize(1200, 800) + self._setup_window() - # 初始化增强组件 + # 初始化组件 self.config = DetectionConfig() - self.road_detector = EnhancedRoadDetector(self.config) - self.direction_analyzer = EnhancedDirectionAnalyzer(self.config) - self.visualization_engine = EnhancedVisualizationEngine() - self.image_processor = EnhancedImageProcessor(self.config) + self.image_processor = ImageProcessor(self.config) + self.road_detector = RoadDetector(self.config) + self.lane_detector = LaneDetector(self.config) + self.direction_analyzer = DirectionAnalyzer(self.config) + self.visualizer = Visualizer() # 状态变量 - self.current_image_path = None - self.original_image = None + self.current_image = None self.is_processing = False - # 创建UI - self._create_enhanced_ui() + # 创建界面 + self._create_ui() - print("增强版应用程序初始化完成") + print("道路方向识别系统已启动") + + def _setup_window(self): + """设置窗口""" + self.root.title("智能道路方向识别系统") + self.root.geometry("1200x700") + self.root.minsize(1000, 600) + self.root.configure(bg="#f0f0f0") - def _create_enhanced_ui(self): - """创建增强的用户界面""" + def _create_ui(self): + """创建用户界面""" # 主框架 - main_frame = ttk.Frame(self.root, padding="15") - main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) - - # 配置网格权重 - self.root.columnconfigure(0, weight=1) - self.root.rowconfigure(0, weight=1) - main_frame.columnconfigure(1, weight=1) - main_frame.rowconfigure(1, weight=1) - - # 创建控件 - self._create_enhanced_control_panel(main_frame) - self._create_image_display(main_frame) + main_frame = ttk.Frame(self.root, padding="10") + main_frame.grid(row=0, column=0, sticky="nsew") + + # 配置权重 + self.root.grid_rowconfigure(0, weight=1) + self.root.grid_columnconfigure(0, weight=1) + main_frame.grid_rowconfigure(1, weight=1) + main_frame.grid_columnconfigure(0, weight=1) + main_frame.grid_columnconfigure(1, weight=1) + + # 控制面板 + self._create_control_panel(main_frame) + + # 图像显示区域 + self._create_display_area(main_frame) + + # 状态栏 self._create_status_bar(main_frame) - def _create_enhanced_control_panel(self, parent): - """创建增强的控制面板""" + def _create_control_panel(self, parent): + """创建控制面板""" control_frame = ttk.LabelFrame(parent, text="控制面板", padding="10") - control_frame.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10)) + control_frame.grid(row=0, column=0, columnspan=2, sticky="ew", pady=(0, 10)) - # 文件操作按钮 - file_frame = ttk.Frame(control_frame) - file_frame.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10)) + # 按钮框架 + button_frame = ttk.Frame(control_frame) + button_frame.pack(fill="x", pady=(0, 10)) - ttk.Button(file_frame, text="选择图片", - command=self._select_image).pack(side=tk.LEFT, padx=(0, 10)) - ttk.Button(file_frame, text="重新检测", - command=self._redetect).pack(side=tk.LEFT) + # 按钮 + ttk.Button(button_frame, text="选择图片", + command=self._select_image, width=15).pack(side="left", padx=(0, 10)) + ttk.Button(button_frame, text="重新检测", + command=self._redetect, width=15).pack(side="left") # 文件路径显示 - self.file_path_var = tk.StringVar(value="未选择图片") - ttk.Label(file_frame, textvariable=self.file_path_var).pack(side=tk.LEFT, padx=(20, 0)) + self.file_label = ttk.Label(control_frame, text="未选择图片", + font=("Arial", 10), foreground="blue") + self.file_label.pack(anchor="w", pady=(0, 10)) + + # 参数调整 + self._create_parameter_controls(control_frame) + + # 结果显示 + self.result_label = ttk.Label(control_frame, text="等待检测...", + font=("Arial", 12, "bold"), foreground="blue") + self.result_label.pack(anchor="w", pady=(5, 0)) - # 算法参数调整 - param_frame = ttk.Frame(control_frame) - param_frame.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(10, 0)) + self.confidence_label = ttk.Label(control_frame, text="", + font=("Arial", 10), foreground="green") + self.confidence_label.pack(anchor="w") + + def _create_parameter_controls(self, parent): + """创建参数控制""" + param_frame = ttk.Frame(parent) + param_frame.pack(fill="x", pady=(0, 10)) # 敏感度调整 - ttk.Label(param_frame, text="检测敏感度:").grid(row=0, column=0, sticky=tk.W) + ttk.Label(param_frame, text="检测敏感度:").grid(row=0, column=0, sticky="w", padx=(0, 10)) self.sensitivity_var = tk.DoubleVar(value=0.5) - ttk.Scale(param_frame, from_=0.1, to=1.0, variable=self.sensitivity_var, - command=self._on_sensitivity_change, orient=tk.HORIZONTAL, length=200).grid(row=0, column=1, sticky=(tk.W, tk.E)) + sensitivity_scale = ttk.Scale(param_frame, from_=0.1, to=1.0, + variable=self.sensitivity_var, orient="horizontal", + command=self._on_parameter_change, length=200) + sensitivity_scale.grid(row=0, column=1, sticky="ew", padx=(0, 20)) - # 路径预测参数 - ttk.Label(param_frame, text="预测距离:").grid(row=1, column=0, sticky=tk.W, pady=(5, 0)) + # 预测距离调整 + ttk.Label(param_frame, text="预测距离:").grid(row=0, column=2, sticky="w", padx=(0, 10)) self.prediction_var = tk.DoubleVar(value=self.config.prediction_distance) - ttk.Scale(param_frame, from_=0.3, to=0.9, variable=self.prediction_var, - command=self._on_prediction_change, orient=tk.HORIZONTAL, length=200).grid(row=1, column=1, sticky=(tk.W, tk.E), pady=(5, 0)) - - # 结果显示 - self.result_var = tk.StringVar(value="等待检测...") - self.confidence_var = tk.StringVar(value="") - - result_frame = ttk.Frame(control_frame) - result_frame.grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(10, 0)) - - ttk.Label(result_frame, textvariable=self.result_var, - font=("Arial", 12, "bold"), foreground="blue").pack(side=tk.LEFT) - ttk.Label(result_frame, textvariable=self.confidence_var, - font=("Arial", 10), foreground="green").pack(side=tk.LEFT, padx=(20, 0)) + prediction_scale = ttk.Scale(param_frame, from_=0.3, to=0.9, + variable=self.prediction_var, orient="horizontal", + command=self._on_parameter_change, length=200) + prediction_scale.grid(row=0, column=3, sticky="ew") - def _create_image_display(self, parent): + def _create_display_area(self, parent): """创建图像显示区域""" display_frame = ttk.Frame(parent) - display_frame.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=10) - display_frame.columnconfigure(0, weight=1) - display_frame.columnconfigure(1, weight=1) - display_frame.rowconfigure(0, weight=1) + display_frame.grid(row=1, column=0, columnspan=2, sticky="nsew", pady=(0, 10)) + display_frame.grid_rowconfigure(0, weight=1) + display_frame.grid_columnconfigure(0, weight=1) + display_frame.grid_columnconfigure(1, weight=1) # 原图显示 - original_frame = ttk.LabelFrame(display_frame, text="原始图像", padding="10") - original_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(0, 10)) - original_frame.columnconfigure(0, weight=1) - original_frame.rowconfigure(0, weight=1) - - self.original_canvas = tk.Canvas( - original_frame, - width=600, - height=500, - bg="white", - highlightthickness=1, - highlightbackground="#cccccc" - ) - self.original_canvas.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) + original_frame = ttk.LabelFrame(display_frame, text="原始图像", padding="5") + original_frame.grid(row=0, column=0, sticky="nsew", padx=(0, 5)) + original_frame.grid_rowconfigure(0, weight=1) + original_frame.grid_columnconfigure(0, weight=1) + + self.original_canvas = tk.Canvas(original_frame, bg="white", highlightthickness=1) + self.original_canvas.grid(row=0, column=0, sticky="nsew") + self.original_canvas.create_text(300, 200, text="请选择道路图片", + font=("Arial", 14), fill="gray") # 结果图显示 - result_frame = ttk.LabelFrame(display_frame, text="检测结果(含路径预测)", padding="10") - result_frame.grid(row=0, column=1, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(10, 0)) - result_frame.columnconfigure(0, weight=1) - result_frame.rowconfigure(0, weight=1) - - self.result_canvas = tk.Canvas( - result_frame, - width=600, - height=500, - bg="white", - highlightthickness=1, - highlightbackground="#cccccc" - ) - self.result_canvas.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) - - # 初始提示文本 - self.original_canvas.create_text(300, 250, text="请选择道路图片", font=("Arial", 16), fill="gray") - self.result_canvas.create_text(300, 250, text="检测结果将显示在这里", font=("Arial", 16), fill="gray") + result_frame = ttk.LabelFrame(display_frame, text="检测结果", padding="5") + result_frame.grid(row=0, column=1, sticky="nsew", padx=(5, 0)) + result_frame.grid_rowconfigure(0, weight=1) + result_frame.grid_columnconfigure(0, weight=1) + + self.result_canvas = tk.Canvas(result_frame, bg="white", highlightthickness=1) + self.result_canvas.grid(row=0, column=0, sticky="nsew") + self.result_canvas.create_text(300, 200, text="检测结果将显示在这里", + font=("Arial", 14), fill="gray") def _create_status_bar(self, parent): """创建状态栏""" status_frame = ttk.Frame(parent) - status_frame.grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(10, 0)) + status_frame.grid(row=2, column=0, columnspan=2, sticky="ew") # 进度条 - self.progress = ttk.Progressbar(status_frame, mode='indeterminate') - self.progress.pack(side=tk.LEFT, fill=tk.X, expand=True) + self.progress = ttk.Progressbar(status_frame, mode='indeterminate', length=200) + self.progress.pack(side="left", fill="x", expand=True, padx=(0, 10)) # 状态文本 self.status_var = tk.StringVar(value="就绪") - status_label = ttk.Label(status_frame, textvariable=self.status_var, relief="sunken") - status_label.pack(side=tk.RIGHT, padx=(10, 0)) + status_label = ttk.Label(status_frame, textvariable=self.status_var, + relief="sunken", padding=(5, 2)) + status_label.pack(side="right") def _select_image(self): - """选择图片文件""" + """选择图片""" if self.is_processing: - messagebox.showwarning("警告", "正在处理中,请稍候...") + messagebox.showwarning("提示", "正在处理中,请稍候...") return file_types = [ - ("图像文件", "*.jpg *.jpeg *.png *.bmp *.tiff *.tif"), + ("图像文件", "*.jpg *.jpeg *.png *.bmp"), ("所有文件", "*.*") ] file_path = filedialog.askopenfilename(title="选择道路图片", filetypes=file_types) if file_path: - self.current_image_path = file_path - self.file_path_var.set(os.path.basename(file_path)) - self._load_and_display_original_image(file_path) + self.file_label.config(text=os.path.basename(file_path)) + self._load_image(file_path) self._start_detection() - def _load_and_display_original_image(self, file_path: str): - """加载并显示原始图像""" + def _load_image(self, file_path: str): + """加载图像""" try: - self.original_image = cv2.imread(file_path) - if self.original_image is None: - raise ValueError("无法读取图像文件") - - # 调整图像尺寸 - self.original_image = self.image_processor.resize_image(self.original_image, (1200, 800)) - - # 显示图像 - self._display_image_on_canvas(self.original_image, self.original_canvas, "原始图像") + self.current_image = self.image_processor.process_image(file_path) + if self.current_image is None: + raise ValueError("无法读取图像") + self._display_image(self.current_image, self.original_canvas) self.status_var.set("图片加载成功") - print(f"图片加载成功: {file_path}") except Exception as e: messagebox.showerror("错误", f"无法加载图片: {str(e)}") - print(f"图片加载失败: {str(e)}") + print(f"图片加载失败: {e}") - def _display_image_on_canvas(self, image: np.ndarray, canvas: tk.Canvas, title: str): + def _display_image(self, image: np.ndarray, canvas: tk.Canvas): """在Canvas上显示图像""" try: - # 清除Canvas canvas.delete("all") # 转换颜色空间 image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) pil_image = Image.fromarray(image_rgb) + # 获取Canvas尺寸 + canvas_width = canvas.winfo_width() or 400 + canvas_height = canvas.winfo_height() or 300 + + # 调整图像大小 + pil_image.thumbnail((canvas_width, canvas_height), Image.Resampling.LANCZOS) + # 转换为Tkinter格式 photo = ImageTk.PhotoImage(pil_image) - # 在Canvas上显示图像 - canvas.create_image(0, 0, anchor=tk.NW, image=photo) - canvas.image = photo # 保持引用 + # 计算居中位置 + x = (canvas_width - photo.width()) // 2 + y = (canvas_height - photo.height()) // 2 - # 更新滚动区域 - canvas.config(scrollregion=canvas.bbox(tk.ALL)) + # 显示图像 + canvas.create_image(x, y, anchor="nw", image=photo) + canvas.image = photo # 保持引用 except Exception as e: - print(f"图像显示失败: {str(e)}") - canvas.create_text(300, 250, text=f"{title}显示失败", font=("Arial", 16), fill="red") + print(f"图像显示失败: {e}") + canvas.create_text(150, 150, text="图像显示失败", fill="red") def _start_detection(self): """开始检测""" - if self.is_processing or self.original_image is None: + if self.is_processing or self.current_image is None: return self.is_processing = True self.progress.start() self.status_var.set("正在分析道路方向...") - self.result_var.set("检测中...") - self.confidence_var.set("") + self.result_label.config(text="检测中...", foreground="blue") + self.confidence_label.config(text="") # 在后台线程中执行检测 - thread = threading.Thread(target=self._enhanced_detection_thread) + thread = threading.Thread(target=self._detection_thread) thread.daemon = True thread.start() - def _enhanced_detection_thread(self): - """增强的检测线程""" + def _detection_thread(self): + """检测线程""" try: start_time = time.time() - # 1. 检测道路区域(多方法融合) - road_info = self.road_detector.detect_road_region(self.original_image) + # 1. 检测道路 + road_info = self.road_detector.detect_road(self.current_image) - # 2. 检测车道线(增强算法) - lane_info = self.road_detector.detect_lane_lines_enhanced( - self.original_image, road_info.get('roi_vertices', np.array([])) + # 2. 检测车道线 + lane_info = self.lane_detector.detect_lanes( + self.current_image, road_info.get('roi_vertices', np.array([])) ) - # 3. 分析方向(多特征融合) - direction_info = self.direction_analyzer.analyze_direction( - road_info, lane_info, - (self.original_image.shape[1], self.original_image.shape[0]) + # 3. 分析方向 + direction_info = self.direction_analyzer.analyze( + road_info.get('road_contour'), + lane_info, + road_info.get('image_size', self.current_image.shape[1::-1]) ) - processing_time = time.time() - start_time - # 4. 生成结果图像 - result_image = self.visualization_engine.draw_detection_results( - self.original_image, road_info, lane_info, direction_info + result_image = self.visualizer.draw_results( + self.current_image, road_info, lane_info, direction_info ) + processing_time = time.time() - start_time + # 在主线程中更新UI - self.root.after(0, self._update_enhanced_results, direction_info, result_image, processing_time) + self.root.after(0, self._update_results, direction_info, result_image, processing_time) except Exception as e: - print(f"检测过程出错: {str(e)}") - self.root.after(0, self._show_error, f"检测失败: {str(e)}") + print(f"检测过程出错: {e}") + self.root.after(0, self._show_error, str(e)) - def _update_enhanced_results(self, direction_info: Dict[str, Any], result_image: np.ndarray, processing_time: float): - """更新增强的检测结果""" + def _update_results(self, direction_info: Dict[str, Any], + result_image: np.ndarray, processing_time: float): + """更新结果""" self.is_processing = False self.progress.stop() # 显示结果图像 - self._display_image_on_canvas(result_image, self.result_canvas, "检测结果") + self._display_image(result_image, self.result_canvas) - # 更新结果文本 - direction = direction_info.get('direction', '未知方向') + # 更新文本信息 + direction = direction_info.get('direction', '未知') confidence = direction_info.get('confidence', 0.0) - self.result_var.set(f"检测结果: {direction}") - self.confidence_var.set(f" (置信度: {confidence:.2%})") + self.result_label.config(text=f"检测结果: {direction}") - self.status_var.set(f"分析完成 - 耗时: {processing_time:.2f}秒") + if confidence > 0: + confidence_text = f"置信度: {confidence:.1%} | 耗时: {processing_time:.2f}秒" + self.confidence_label.config(text=confidence_text) + + # 根据置信度设置颜色 + if confidence > 0.7: + color = "green" + elif confidence > 0.5: + color = "orange" + else: + color = "red" + + self.confidence_label.config(foreground=color) + + self.status_var.set("分析完成") - print(f"检测完成: {direction}, 置信度: {confidence:.2%}, 耗时: {processing_time:.2f}秒") + print(f"检测完成: {direction}, 置信度: {confidence:.1%}, 耗时: {processing_time:.2f}秒") def _show_error(self, error_msg: str): - """显示错误信息""" + """显示错误""" self.is_processing = False self.progress.stop() - messagebox.showerror("错误", error_msg) + + messagebox.showerror("错误", f"检测失败: {error_msg}") self.status_var.set("检测失败") - self.result_var.set("检测失败") - self.confidence_var.set("") - print(f"检测错误: {error_msg}") + self.result_label.config(text="检测失败", foreground="red") + self.confidence_label.config(text="") def _redetect(self): """重新检测""" - if self.current_image_path and not self.is_processing: + if self.current_image is not None and not self.is_processing: self._start_detection() - def _on_sensitivity_change(self, value): - """敏感度变化回调""" - sensitivity = float(value) - # 根据敏感度调整配置参数 + def _on_parameter_change(self, value): + """参数变化回调""" + # 更新配置 + sensitivity = self.sensitivity_var.get() + prediction = self.prediction_var.get() + self.config.width_ratio_threshold = 0.3 + sensitivity * 0.4 self.config.center_deviation_threshold = 0.1 + sensitivity * 0.1 - print(f"敏感度调整为: {sensitivity:.2f}") + self.config.prediction_distance = prediction - # 如果已有图像,自动重新检测 - if self.current_image_path and not self.is_processing: - self._start_detection() - - def _on_prediction_change(self, value): - """预测距离变化回调""" - prediction_distance = float(value) - self.config.prediction_distance = prediction_distance - print(f"预测距离调整为: {prediction_distance:.2f}") - - # 如果已有图像,自动重新检测 - if self.current_image_path and not self.is_processing: + print(f"参数更新 - 敏感度: {sensitivity:.2f}, 预测距离: {prediction:.2f}") + + # 自动重新检测 + if self.current_image and not self.is_processing: self._start_detection() - - def run(self): - """运行应用程序""" - try: - self.root.mainloop() - except Exception as e: - print(f"应用程序运行错误: {str(e)}") def main(): """主函数""" try: + # 创建主窗口 root = tk.Tk() - app = EnhancedLaneDetectionApp(root) - app.run() + + # 设置窗口图标和样式 + root.iconbitmap(default=None) # 可以设置图标文件路径 + + # 创建应用程序 + app = LaneDetectionApp(root) + + # 运行主循环 + root.mainloop() + except Exception as e: - print(f"应用程序启动失败: {str(e)}") + print(f"应用程序启动失败: {e}") messagebox.showerror("致命错误", f"应用程序启动失败: {str(e)}") if __name__ == "__main__":