Merge branch 'main-v2' of ssh://120.27.199.238:222/Havoc420mac/mi-task into main-v2
@ -116,4 +116,91 @@ def run_task_1(ctrl, msg, time_sleep=5000):
|
||||
# add
|
||||
go_straight(ctrl, msg, distance=0.3, observe=observe)
|
||||
|
||||
# section
|
||||
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完成", "完成")
|
||||
|
BIN
test/text-image/imgs/1_process_1_原始图片.jpg
Normal file
After Width: | Height: | Size: 164 KiB |
BIN
test/text-image/imgs/1_process_2_HSV-V通道.jpg
Normal file
After Width: | Height: | Size: 241 KiB |
BIN
test/text-image/imgs/1_process_3_低亮度区域掩码.jpg
Normal file
After Width: | Height: | Size: 78 KiB |
BIN
test/text-image/imgs/1_process_4_轮廓检测.jpg
Normal file
After Width: | Height: | Size: 171 KiB |
BIN
test/text-image/imgs/1_process_5_最终结果.jpg
Normal file
After Width: | Height: | Size: 164 KiB |
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 164 KiB |
Before Width: | Height: | Size: 180 KiB |
@ -1,3 +1,2 @@
|
||||
opencv-python>=4.5.0
|
||||
numpy>=1.19.0
|
||||
pathlib
|
||||
opencv-python
|
||||
numpy
|
||||
|
@ -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检测完成!")
|
||||
|
||||
|