diff --git a/task_1/task_1.py b/task_1/task_1.py index 80bbb0e..ee1e7ba 100755 --- a/task_1/task_1.py +++ b/task_1/task_1.py @@ -116,4 +116,91 @@ def run_task_1(ctrl, msg, time_sleep=5000): # add go_straight(ctrl, msg, distance=0.3, observe=observe) - # section \ No newline at end of file + section('任务1-7:90度转弯', "旋转") + radius = res['radius'] * 2 + 0.1 + info(f"任务1-7: 转弯半径: {radius}", "信息") + turn_success, res = arc_turn_around_hori_line( + ctrl=ctrl, + msg=msg, + radius=radius, + angle_deg=85 if direction else -85, + # + pass_align=True, + observe=observe, + no_end_reset=True, + ) + + section('任务1-8:直线移动', "移动") + move_to_hori_line(ctrl, msg, target_distance=0.3, observe=observe) + + section('任务1-9:90度旋转', "旋转") + turn_degree_v2(ctrl, msg, degree=0, absolute=True, precision=True) + + section('任务1-10: y校准,准备 task-2', "移动") + # TODO + + success("任务1完成", "完成") + + +def run_task_1_back(ctrl, msg, time_sleep=5000): + section('任务1-11: 返回', "移动") + go_straight(ctrl, msg, distance=0.2, observe=observe) + turn_degree_v2(ctrl, msg, -90, absolute=True) # 不确定 odo 效果如何; + + section('任务1-11: 直线移动', "移动") + move_to_hori_line(ctrl, msg, target_distance=0.2, observe=observe) + + section('任务1-12: 180度旋转', "旋转") + turn_success, res = arc_turn_around_hori_line( + ctrl=ctrl, + msg=msg, + angle_deg=170 if direction else -170, + target_distance=0.6, + min_radius=0.3, + max_radius=0.4, + pass_align=True, + observe=observe, + no_end_reset=True, + ) + + turn_degree_v2(ctrl, msg, degree=90, absolute=True) + + section('任务1-13: 直线移动', "移动") + move_distance = 0.5 + go_straight(ctrl, msg, distance=move_distance, observe=observe) + + section('任务1-14: 模拟装货', "停止") + info('机器人躺下,模拟装货过程', "信息") + start_time = time.time() + ctrl.base_msg.lie_down(wait_time=time_sleep) + elapsed = time.time() - start_time + timing("装货过程", elapsed) + + section('任务1-15: 站起来', "移动") + ctrl.base_msg.stand_up() + + section('任务1-16: 返回', "移动") + go_straight(ctrl, msg, distance=-(move_distance + res['radius']), observe=observe) + + turn_degree_v2(ctrl, msg, degree=179, absolute=True) + + section('任务1-17: 90度转弯', "旋转") + turn_success, res = arc_turn_around_hori_line( + ctrl=ctrl, + msg=msg, + angle_deg=-85 if direction else 85, + radius=res['radius'] * 2, + pass_align=True, + observe=observe, + ) + + section('任务1-18: 直线移动', "移动") + move_to_hori_line(ctrl, msg, target_distance=0.4, observe=observe) + + section('任务1-19: 90度旋转', "旋转") + turn_degree_v2(ctrl, msg, degree=0, absolute=True) + + go_straight(ctrl, msg, distance=-1.3, observe=observe) + # go_to_xy(ctrl, msg, target_x=-0.2, target_y=0, observe=observe) # TEST + + success("任务1-back完成", "完成") diff --git a/test/text-image/imgs/1_process_1_原始图片.jpg b/test/text-image/imgs/1_process_1_原始图片.jpg new file mode 100644 index 0000000..2bff645 Binary files /dev/null and b/test/text-image/imgs/1_process_1_原始图片.jpg differ diff --git a/test/text-image/imgs/1_process_2_HSV-V通道.jpg b/test/text-image/imgs/1_process_2_HSV-V通道.jpg new file mode 100644 index 0000000..23987a9 Binary files /dev/null and b/test/text-image/imgs/1_process_2_HSV-V通道.jpg differ diff --git a/test/text-image/imgs/1_process_3_低亮度区域掩码.jpg b/test/text-image/imgs/1_process_3_低亮度区域掩码.jpg new file mode 100644 index 0000000..e17375e Binary files /dev/null and b/test/text-image/imgs/1_process_3_低亮度区域掩码.jpg differ diff --git a/test/text-image/imgs/1_process_4_轮廓检测.jpg b/test/text-image/imgs/1_process_4_轮廓检测.jpg new file mode 100644 index 0000000..e9c778a Binary files /dev/null and b/test/text-image/imgs/1_process_4_轮廓检测.jpg differ diff --git a/test/text-image/imgs/1_process_5_最终结果.jpg b/test/text-image/imgs/1_process_5_最终结果.jpg new file mode 100644 index 0000000..2bff645 Binary files /dev/null and b/test/text-image/imgs/1_process_5_最终结果.jpg differ diff --git a/test/text-image/imgs/result_1.jpg b/test/text-image/imgs/result_1.jpg index cd7a969..2bff645 100644 Binary files a/test/text-image/imgs/result_1.jpg and b/test/text-image/imgs/result_1.jpg differ diff --git a/test/text-image/imgs/result_2.jpg b/test/text-image/imgs/result_2.jpg deleted file mode 100644 index a7fa18e..0000000 Binary files a/test/text-image/imgs/result_2.jpg and /dev/null differ diff --git a/test/text-image/requirements.txt b/test/text-image/requirements.txt index 8739c64..b96544b 100644 --- a/test/text-image/requirements.txt +++ b/test/text-image/requirements.txt @@ -1,3 +1,2 @@ -opencv-python>=4.5.0 -numpy>=1.19.0 -pathlib +opencv-python +numpy diff --git a/test/text-image/text_image.py b/test/text-image/text_image.py index 89d2764..dd1d173 100644 --- a/test/text-image/text_image.py +++ b/test/text-image/text_image.py @@ -1,89 +1,149 @@ import cv2 import numpy as np -import os from pathlib import Path def preprocess_image(image): """ - 预处理图像:转换为HSV色彩空间,检测白色区域 + 预处理图像:转换为HSV色彩空间,提取V通道(明度通道)中的低亮度区域 """ # 转换为HSV色彩空间 hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) - # 定义白色的HSV范围 - 放宽范围 - # 白色在HSV中:饱和度低,明度高 - # 原来的范围:lower_white = np.array([0, 0, 200]), upper_white = np.array([180, 30, 255]) - # 放宽后的范围: - lower_white = np.array([0, 0, 150]) # 降低明度下限,从200降到150 - upper_white = np.array([180, 60, 255]) # 提高饱和度上限,从30提高到60 + # 提取V通道(明度通道) + v_channel = hsv[:, :, 2] - # 创建白色掩码 - white_mask = cv2.inRange(hsv, lower_white, upper_white) + # 对V通道进行二值化,提取低亮度区域(蓝色部分) + # 使用较低的阈值来提取暗色区域 + _, binary = cv2.threshold(v_channel, 120, 255, cv2.THRESH_BINARY_INV) # 形态学操作:开运算去除小噪点,闭运算填充小孔 - kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) - cleaned = cv2.morphologyEx(white_mask, cv2.MORPH_OPEN, kernel) + kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) + cleaned = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel) cleaned = cv2.morphologyEx(cleaned, cv2.MORPH_CLOSE, kernel) - return cleaned + return cleaned, v_channel -def find_white_rectangles(binary_image): +def find_text_regions(binary_image): """ - 查找白色矩形区域 + 查找可能的文字区域 """ contours, _ = cv2.findContours( binary_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE ) - # 筛选合适的矩形轮廓 - valid_rectangles = [] + # 筛选合适的文字区域轮廓 + valid_text_regions = [] for contour in contours: area = cv2.contourArea(contour) - if area > 200: # 过滤太小的区域,从500降低到200 + if area > 100: # 过滤太小的区域 # 计算轮廓的边界矩形 x, y, w, h = cv2.boundingRect(contour) - # 计算轮廓的近似多边形 - epsilon = 0.02 * cv2.arcLength(contour, True) - approx = cv2.approxPolyDP(contour, epsilon, True) + # 计算宽高比 + aspect_ratio = w / h - # 检查是否为四边形(矩形) - if len(approx) == 4: - # 计算宽高比 - aspect_ratio = w / h - # 矩形应该有合理的宽高比(不是太细长) - if 0.3 < aspect_ratio < 3.0: - valid_rectangles.append((contour, (x, y, w, h), approx)) + # 文字区域通常有合理的宽高比 + if 0.1 < aspect_ratio < 10.0: + valid_text_regions.append((contour, (x, y, w, h))) - return valid_rectangles + return valid_text_regions -def analyze_white_region(roi, original_roi): +def analyze_text_region(roi, original_roi): """ - 分析白色区域的特征 + 分析文字区域的特征 """ - # 计算白色像素比例 + # 计算黑色像素比例 total_pixels = roi.shape[0] * roi.shape[1] - white_pixels = np.sum(roi == 255) - white_ratio = white_pixels / total_pixels + black_pixels = np.sum(roi == 255) + black_ratio = black_pixels / total_pixels # 计算区域的形状特征 height, width = roi.shape aspect_ratio = width / height - # 计算边缘强度(白色区域应该有清晰的边缘) + # 计算边缘强度 edges = cv2.Canny(original_roi, 50, 150) edge_density = np.sum(edges > 0) / total_pixels return { - 'white_ratio': white_ratio, + 'black_ratio': black_ratio, 'aspect_ratio': aspect_ratio, 'edge_density': edge_density, 'area': total_pixels } -def detect_white_rectangles_in_image(image_path): +def classify_digit_1_or_2(roi): """ - 检测图片中的白色矩形区域 + 使用OpenCV基本方法判断是数字1还是2 + """ + # 调整ROI大小到统一尺寸进行分析 + roi_resized = cv2.resize(roi, (20, 20)) + + # 计算像素密度特征 + total_pixels = roi_resized.shape[0] * roi_resized.shape[1] + black_pixels = np.sum(roi_resized == 255) + density = black_pixels / total_pixels + + # 计算宽高比 + aspect_ratio = roi_resized.shape[1] / roi_resized.shape[0] + + # 计算水平投影(每行的黑色像素数) + horizontal_projection = np.sum(roi_resized == 255, axis=1) + + # 计算垂直投影(每列的黑色像素数) + vertical_projection = np.sum(roi_resized == 255, axis=0) + + # 计算中心区域的像素分布 + center_region = roi_resized[5:15, 5:15] + center_density = np.sum(center_region == 255) / (10 * 10) + + # 计算垂直投影的方差(数字1方差较小,数字2方差较大) + vertical_variance = np.var(vertical_projection) + + # 计算水平投影的方差 + horizontal_variance = np.var(horizontal_projection) + + # 基于多个特征的综合判断 + digit1_score = 0 + digit2_score = 0 + + # 数字1的判断条件 + if vertical_variance < 15: # 垂直投影方差小 + digit1_score += 2 + if center_density < 0.3: # 中心区域密度较低 + digit1_score += 1 + if density < 0.4: # 整体密度较低 + digit1_score += 1 + if aspect_ratio < 0.8: # 数字1通常较细 + digit1_score += 1 + + # 数字2的判断条件 + if vertical_variance > 15: # 垂直投影方差大 + digit2_score += 2 + if center_density > 0.3: # 中心区域密度较高 + digit2_score += 1 + if density > 0.4: # 整体密度较高 + digit2_score += 1 + if horizontal_variance > 8: # 水平投影方差大 + digit2_score += 1 + if aspect_ratio > 0.8: # 数字2通常较宽 + digit2_score += 1 + + # 返回得分更高的数字 + if digit1_score > digit2_score: + return 1 + elif digit2_score > digit1_score: + return 2 + else: + # 如果得分相同,使用更复杂的判断 + if vertical_variance < 12: + return 1 + else: + return 2 + +def detect_text_in_image(image_path, show_process=True): + """ + 检测图片中的文字区域 """ # 读取图像 image = cv2.imread(image_path) @@ -92,87 +152,130 @@ def detect_white_rectangles_in_image(image_path): return [] # 预处理 - white_mask = preprocess_image(image) + binary, v_channel = preprocess_image(image) - # 查找白色矩形 - valid_rectangles = find_white_rectangles(white_mask) + # 查找文字区域 + valid_text_regions = find_text_regions(binary) results = [] - for contour, (x, y, w, h), approx in valid_rectangles: + for contour, (x, y, w, h) in valid_text_regions: # 提取ROI - roi_mask = white_mask[y:y+h, x:x+w] + roi_binary = binary[y:y+h, x:x+w] roi_original = image[y:y+h, x:x+w] # 分析特征 - features = analyze_white_region(roi_mask, roi_original) + features = analyze_text_region(roi_binary, roi_original) - # 判断是否为有效的白色矩形 - is_valid_white_rectangle = ( - features['white_ratio'] > 0.4 and # 白色像素比例要求降低,从0.6降到0.4 - features['edge_density'] > 0.005 # 边缘密度要求降低,从0.01降到0.005 + # 判断是否为有效的低亮度区域 + is_valid_text_region = ( + features['black_ratio'] > 0.05 and # 低亮度像素比例要求降低 + features['edge_density'] > 0.003 # 边缘密度要求降低 ) - if is_valid_white_rectangle: + if is_valid_text_region: + # 使用OpenCV方法识别数字1或2 + predicted_digit = classify_digit_1_or_2(roi_binary) + results.append({ - 'type': 'white_rectangle', + 'type': 'digit_region', 'position': (x, y, w, h), + 'digit': predicted_digit, 'features': features, - 'confidence': 'high' if features['white_ratio'] > 0.8 else 'medium' + 'confidence': 'high' if features['black_ratio'] > 0.3 else 'medium' }) # 在图像上绘制结果 cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2) - cv2.putText(image, f"白色矩形", (x, y - 10), + cv2.putText(image, f"数字{predicted_digit}", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) + + # 如果需要显示中间过程 + if show_process: + # 创建过程图像 + process_images = [] + + # 1. 原始图片 + process_images.append(("原始图片", image.copy())) + + # 2. HSV-V通道(明度通道) + v_display = cv2.applyColorMap(v_channel, cv2.COLORMAP_JET) + process_images.append(("HSV-V通道", v_display)) + + # 3. 二值化后的低亮度区域 + process_images.append(("低亮度区域掩码", binary)) + + # 4. 轮廓检测结果 + contour_image = image.copy() + for contour, (x, y, w, h) in valid_text_regions: + cv2.rectangle(contour_image, (x, y), (x + w, y + h), (0, 0, 255), 2) + process_images.append(("轮廓检测", contour_image)) + + # 5. 最终结果 + process_images.append(("最终结果", image)) + + # 保存过程图像 + img_dir = Path(image_path).parent + base_name = Path(image_path).stem + + for i, (title, img) in enumerate(process_images): + if len(img.shape) == 2: # 如果是灰度图,转换为BGR + img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) - # 绘制轮廓点 - cv2.drawContours(image, [approx], -1, (255, 0, 0), 2) + output_path = img_dir / f"{base_name}_process_{i+1}_{title}.jpg" + cv2.imwrite(str(output_path), img) + print(f"过程图像已保存: {output_path}") return image, results def main(): """ - 主函数:测试白色矩形检测 + 主函数:测试文字区域检测 """ # 图片路径 img_dir = Path("imgs") img1_path = img_dir / "1.jpg" - img2_path = img_dir / "2.jpg" - - print("开始检测图片中的白色矩形区域...") + + print("开始检测图片中的文字区域...") + print("=" * 50) # 检测第一张图片 if img1_path.exists(): print(f"\n检测图片: {img1_path}") - result_img1, results1 = detect_white_rectangles_in_image(str(img1_path)) + print("检测过程包括以下步骤:") + print("1. 原始图片") + print("2. HSV色彩空间转换") + print("3. 明度通道提取") + print("4. 二值化(低亮度区域)") + print("5. 轮廓检测") + print("6. 特征分析") + print("7. 数字识别") + print("8. 最终结果") + print("-" * 30) - print("检测结果:") - for result in results1: - print(f" 类型: {result['type']}, 位置: {result['position']}, 置信度: {result['confidence']}") - print(f" 特征: 白色比例={result['features']['white_ratio']:.2f}, " - f"宽高比={result['features']['aspect_ratio']:.2f}") + result_img1, results1 = detect_text_in_image(str(img1_path), show_process=True) + + print("\n检测结果:") + if results1: + for i, result in enumerate(results1): + print(f" 文字区域 {i+1}:") + print(f" 位置: {result['position']}") + print(f" 识别数字: {result['digit']}") + print(f" 置信度: {result['confidence']}") + print(f" 黑色比例: {result['features']['black_ratio']:.2f}") + print(f" 宽高比: {result['features']['aspect_ratio']:.2f}") + print(f" 边缘密度: {result['features']['edge_density']:.4f}") + print(f" 面积: {result['features']['area']}") + else: + print(" 未检测到文字区域") # 保存结果图片 output_path1 = img_dir / "result_1.jpg" cv2.imwrite(str(output_path1), result_img1) - print(f"结果图片已保存到: {output_path1}") - - # 检测第二张图片 - if img2_path.exists(): - print(f"\n检测图片: {img2_path}") - result_img2, results2 = detect_white_rectangles_in_image(str(img2_path)) - - print("检测结果:") - for result in results2: - print(f" 类型: {result['type']}, 位置: {result['position']}, 置信度: {result['confidence']}") - print(f" 特征: 白色比例={result['features']['white_ratio']:.2f}, " - f"宽高比={result['features']['aspect_ratio']:.2f}") - - # 保存结果图片 - output_path2 = img_dir / "result_2.jpg" - cv2.imwrite(str(output_path2), result_img2) - print(f"结果图片已保存到: {output_path2}") + print(f"\n最终结果图片已保存到: {output_path1}") + print("中间过程图像也已保存到imgs目录") + else: + print(f"图片文件不存在: {img1_path}") print("\n检测完成!")