from maix import image, display, app, time, camera, nn
import cv2
import numpy as np
# 轮廓面积限制变量
MIN_CONTOUR_AREA = 100 # 最小轮廓面积
MAX_CONTOUR_AREA = 25000 # 最大轮廓面积
# 膨胀操作参数
DILATE_KERNEL_SIZE = 3 # 膨胀核大小
DILATE_ITERATIONS = 2 # 膨胀迭代次数
# 全局变量存储检测结果
detected_shapes = [] # 存储形状信息: [{'name': str, 'center': (x,y), 'bbox': (x,y,w,h), 'area': int}]
detected_digits = [] # 存储数字信息: [{'text': str, 'center': (x,y), 'bbox': (x,y,w,h)}]
frame_mode = 0 # 0: 检测形状, 1: 检测数字
# 初始化OCR模型
model = "/root/models/pp_ocr.mud"
ocr = nn.PP_OCR(model)
# 加载字体
image.load_font("ppocr", "/maixapp/share/font/ppocr_keys_v1.ttf", size=20)
image.set_default_font("ppocr")
# 初始化显示和相机
disp = display.Display()
cam = camera.Camera(ocr.input_width(), ocr.input_height(), ocr.input_format())
print("整合检测程序已启动...")
print("检测模式: 交替进行形状检测和数字识别")
frame_count = 0
while not app.need_exit():
frame_count += 1
# 读取图像
img = cam.read()
img_for_ocr = img
# 获取图像尺寸
height, width = img.height(), img.width()
center_x, center_y = width // 2, height // 2
if frame_mode == 0:
# 形状检测帧
img_cv = image.image2cv(img, ensure_bgr=False, copy=False)
detected_shapes = []
# 获取图像尺寸
height_cv, width_cv = img_cv.shape[:2]
# 转换为灰度图
gray = cv2.cvtColor(img_cv, cv2.COLOR_BGR2GRAY)
# 高斯模糊去噪
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
# 边缘检测
edges = cv2.Canny(blurred, 50, 150)
# # 膨胀操作 - 连接断开的轮廓
# kernel = np.ones((DILATE_KERNEL_SIZE, DILATE_KERNEL_SIZE), np.uint8)
# dilated = cv2.dilate(edges, kernel, iterations=DILATE_ITERATIONS)
# 查找轮廓 - 只检测外轮廓
contours, hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 按轮廓面积从大到小排序
contours = sorted(contours, key=cv2.contourArea, reverse=True)
# 遍历所有轮廓
for contour in contours:
# 计算轮廓面积
area = cv2.contourArea(contour)
# 跳过太小的轮廓
if area < MIN_CONTOUR_AREA:
continue
# 检测形状
try:
perimeter = cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, 0.02 * perimeter, True)
area = cv2.contourArea(contour)
if area < MIN_CONTOUR_AREA or area > MAX_CONTOUR_AREA:
shape_name = "Small"
else:
vertices = len(approx)
if vertices == 3:
shape_name = "three-side"
elif vertices == 4:
x, y, w, h = cv2.boundingRect(approx)
aspect_ratio = float(w) / h
if 0.9 <= aspect_ratio <= 1.1:
shape_name = "four-side-square"
else:
shape_name = "four-side"
elif vertices == 5:
shape_name = "five-side"
elif vertices == 6:
shape_name = "six-side"
elif vertices == 7:
shape_name = "seven-side"
elif vertices >= 8:
shape_name = "circle"
else:
shape_name = "unknown"
except Exception as e:
print(f"形状检测异常: {e}")
shape_name = "unknown"
# 跳过小图形
if shape_name == "Small" or shape_name == "unknown":
continue
# 计算轮廓中心点和边界框
M = cv2.moments(contour)
if M["m00"] != 0:
cx = int(M["m10"] / M["m00"])
cy = int(M["m01"] / M["m00"])
# 计算边界框
x, y, w, h = cv2.boundingRect(contour)
# 存储形状信息
shape_info = {
'name': shape_name,
'center': (cx, cy),
'bbox': (x, y, w, h),
'area': int(area),
'contour': contour,
'digits': [] # 用于存储该形状内的数字
}
detected_shapes.append(shape_info)
# 如果有之前检测到的数字,进行匹配
if detected_digits:
# 匹配数字到形状
for shape in detected_shapes:
shape['digits'] = [] # 清空之前的数字
# 处理矩形形状(包括正方形)、三角形和圆形
if ("four-side" not in shape['name'] and
"three-side" not in shape['name'] and
shape['name'] != "circle"):
continue
shape_x, shape_y, shape_w, shape_h = shape['bbox']
# 检查每个数字是否在当前形状内
for digit in detected_digits:
digit_cx, digit_cy = digit['center']
# 判断数字中心是否在形状边界框内
if (shape_x <= digit_cx <= shape_x + shape_w and
shape_y <= digit_cy <= shape_y + shape_h):
# 更精确的检查:使用点在多边形内的算法
result = cv2.pointPolygonTest(shape['contour'], (digit_cx, digit_cy), False)
if result >= 0: # 点在轮廓内或轮廓上
shape['digits'].append(digit)
# 打印检测结果
for shape in detected_shapes:
cx, cy = shape['center']
area = shape['area']
if shape['digits']:
digit_texts = [digit['text'] for digit in shape['digits']]
digit_str = ','.join(digit_texts)
print(f"{shape['name']} ({cx},{cy}) 轮廓面积: {area} 内含数字: [{digit_str}]")
else:
print(f"{shape['name']} ({cx},{cy}) 轮廓面积: {area} 内含数字: []")
else:
# 数字检测帧 - 使用掩膜去除轮廓后进行OCR识别
detected_digits = []
# OCR检测
objs = ocr.detect(img_for_ocr)
for obj in objs:
# 获取检测到的文字和位置
text = obj.char_str()
box = obj.box
# 计算中心点
cx = int((box.x1 + box.x3) / 2)
cy = int((box.y1 + box.y3) / 2)
# 计算边界框
bbox_x = min(box.x1, box.x2, box.x3, box.x4)
bbox_y = min(box.y1, box.y2, box.y3, box.y4)
bbox_w = max(box.x1, box.x2, box.x3, box.x4) - bbox_x
bbox_h = max(box.y1, box.y2, box.y3, box.y4) - bbox_y
# 存储数字信息
digit_info = {
'text': text,
'center': (cx, cy),
'bbox': (int(bbox_x), int(bbox_y), int(bbox_w), int(bbox_h)),
'box_points': [box.x1, box.y1, box.x2, box.y2, box.x3, box.y3, box.x4, box.y4]
}
detected_digits.append(digit_info)
# 如果有之前检测到的形状,进行匹配
if detected_shapes:
# 匹配数字到形状
for shape in detected_shapes:
shape['digits'] = [] # 清空之前的数字
# 处理矩形形状(包括正方形)、三角形和圆形
if ("four-side" not in shape['name'] and
"three-side" not in shape['name'] and
shape['name'] != "circle"):
continue
shape_x, shape_y, shape_w, shape_h = shape['bbox']
# 检查每个数字是否在当前形状内
for digit in detected_digits:
digit_cx, digit_cy = digit['center']
# 判断数字中心是否在形状边界框内
if (shape_x <= digit_cx <= shape_x + shape_w and
shape_y <= digit_cy <= shape_y + shape_h):
# 更精确的检查:使用点在多边形内的算法
result = cv2.pointPolygonTest(shape['contour'], (digit_cx, digit_cy), False)
if result >= 0: # 点在轮廓内或轮廓上
shape['digits'].append(digit)
# 打印检测结果
for shape in detected_shapes:
cx, cy = shape['center']
area = shape['area']
if shape['digits']:
digit_texts = [digit['text'] for digit in shape['digits']]
digit_str = ','.join(digit_texts)
print(f"{shape['name']} ({cx},{cy}) 轮廓面积: {area} 内含数字: [{digit_str}]")
else:
print(f"{shape['name']} ({cx},{cy}) 轮廓面积: {area} 内含数字: []")
# 创建显示图像
img_cv = image.image2cv(img, ensure_bgr=False, copy=False)
img_display = img_cv.copy()
# 绘制图像中心点
cv2.line(img_display, (center_x - 10, center_y), (center_x + 10, center_y), (255, 0, 0), 2)
cv2.line(img_display, (center_x, center_y - 10), (center_x, center_y + 10), (255, 0, 0), 2)
cv2.circle(img_display, (center_x, center_y), 3, (255, 0, 0), -1)
# 绘制形状检测结果
for shape in detected_shapes:
# 绘制轮廓
cv2.drawContours(img_display, [shape['contour']], -1, (0, 255, 0), 2)
cx, cy = shape['center']
# 绘制形状中心点
cv2.circle(img_display, (cx, cy), 3, (0, 0, 255), -1)
# 绘制形状名称
cv2.putText(img_display, shape['name'], (cx - 20, cy - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1)
# 绘制该形状内的数字
if shape['digits']:
digit_texts = [digit['text'] for digit in shape['digits']]
digit_str = ','.join(digit_texts)
cv2.putText(img_display, f"Digits: {digit_str}", (cx - 20, cy + 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 0), 1)
# 绘制数字检测结果
for digit in detected_digits:
# 绘制数字边界框
bbox_x, bbox_y, bbox_w, bbox_h = digit['bbox']
cv2.rectangle(img_display, (bbox_x, bbox_y),
(bbox_x + bbox_w, bbox_y + bbox_h), (255, 0, 255), 1)
# 绘制数字文本
cx, cy = digit['center']
cv2.putText(img_display, digit['text'], (cx - 10, cy - 5),
cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 0, 255), 1)
# 显示状态信息
y_offset = 20
mode_text = "Shape Detection" if frame_mode == 0 else "Digit Detection"
cv2.putText(img_display, f"Frame: {frame_count} - Mode: {mode_text}",
(10, y_offset), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1)
y_offset += 15
cv2.putText(img_display, f"Shapes: {len(detected_shapes)}, Digits: {len(detected_digits)}",
(10, y_offset), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1)
# 转换回maix格式并显示
img_show = image.cv2image(img_display, bgr=True, copy=False)
disp.show(img_show)
# 切换检测模式
frame_mode = 1 - frame_mode
最新发布