赞
踩
手动测量箱体、缺陷、大小等操作可能是一项繁琐并且劳累而机械的任务,但OAK中国本次将提供了更好的解决方案:3D+AI视觉处理箱体的识别和检测,使用了即将发布的全新3D+AI相机:OAK-D-SR PoE,利用了前沿的视觉处理技术结合AI快速准确地确定箱子的尺寸。本文将对这款应用程序的工作原理及其背后的技术做一个简单的介绍。
6.28新品箱体检测演示
以下是分布说明:
- 框检测:AI 识别框。使用 Roboflow 训练自定义对象检测 NN 模型。
Model & API (roboflow.com)- 数据捕获:ToF 深度传感器与设备上的彩色流对齐,并提供准确的 RGB-D 流。应用程序将根据(填充的)边界框裁剪深度图,并提供仅框的点云,从而获得最佳效果。
- 分析:使用 Open3D 库Open3D – A Modern Library for 3D Data Processing和自定义算法,我们首先估计框的顶面,然后使用 OpenCV 提取顶面的最小面积(以获得宽度和长度)。一旦我们有了框的顶面和地面,我们也可以计算框的高度。我们开发了 [BoxEstimator]类,它封装了此应用程序的数据处理。
- 可视化:Depthai-viewer(基于 Rerun)用于清晰、直观地表示结果;带有框检测的彩色帧(以及尺寸)、深度图和稀疏/密集点云。
事实上,有很多行业都可以从此类解决方案中受益,这里我们列举一些用户的实际案例:
电子商务/零售:测量运输箱以优化包装尺寸并降低成本。
制造:确保产品尺寸符合规格,包装缺陷、无需人工检查,全程自动化24小时连轴运转。
物流:实现全称自动化分拣、分类,以视觉AI的方案完成全程自动化工作,加速库仓包裹分类速度。
我们仍在努力改进,但视频显示每个尺寸的误差小于 5 毫米,以下是 GT 尺寸供参考:
箱子尺寸为 26.5 x 30 厘米,高 20 厘米
箱子尺寸为 18.5 x 28 厘米,高 18 厘米
箱子尺寸为 11 x 20 厘米,高 7.5 厘米
我们以 全新产品:OAK-D-SR-POE(几周后将会首次发布!)为基础开发了此应用,因为它借助 ToF 传感器实现了最佳近距离感知。它还具有边缘 AI 推理功能,是此应用的完美候选者。
ToF传感器的深度误差在室内<1%,在室外<2%。
from depthai_sdk import OakCamera from depthai_sdk.classes.packets import DetectionPacket, PointcloudPacket from depthai_sdk.classes.box_estimator import BoxEstimator import depthai_viewer as viewer import cv2 import subprocess import select import sys FPS = 10.0 box = BoxEstimator(median_window=10) with OakCamera() as oak: try: subprocess.Popen([sys.executable, "-m", "depthai_viewer", "--viewer-mode"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) except subprocess.TimeoutExpired: pass viewer.init("Depthai Viewer") viewer.connect() color = oak.create_camera('cam_c', resolution='800p', fps=20) model_config = { 'source': 'roboflow', # Specify that we are downloading the model from Roboflow 'model':'cardboard-box-u35qd/1', 'key':'dDOP8nC4A9rZzWUTG8ia' # Fake API key, replace with your own! } nn = oak.create_nn(model_config, color) nn.config_nn(conf_threshold=0.85) tof = oak.create_tof(fps=20) tof.set_align_to(color, output_size=(640, 400)) tof.configure_tof(phaseShuffleTemporalFilter=True, phaseUnwrappingLevel=2, phaseUnwrapErrorThreshold=100) pointcloud = oak.create_pointcloud(tof) q = oak.queue([ pointcloud.out.main.set_name('pcl'), tof.out.main.set_name('tof'), nn.out.main.set_name('nn'), ]).configure_syncing(enable_sync=True, threshold_ms=500//FPS).get_queue() # oak.show_graph() oak.start() viewer.log_rigid3(f"Right", child_from_parent=([0, 0, 0], [0, 0, 0, 0]), xyz="RDF", timeless=True) viewer.log_rigid3(f"Cropped", child_from_parent=([0, 0, 0], [1, 0, 0, 0]), xyz="RDF", timeless=True) def draw_mesh(): pos,ind,norm = box.get_plane_mesh(size=500) viewer.log_mesh("Right/Plane", pos, indices=ind, normals=norm, albedo_factor=[0.5,1,0], timeless=True) if box.is_calibrated(): draw_mesh() else: print("Calibrate first, write 'c' in terminal when most of the view is flat floor!!") while oak.running(): packets = q.get() nn: DetectionPacket = packets["nn"] cvFrame = nn.frame[..., ::-1] # BGR to RGB depth = packets["tof"].frame pcl_packet: PointcloudPacket = packets["pcl"] points = pcl_packet.points # Convert 800P into 400P into 256000x3 colors_640 = cv2.pyrDown(cvFrame).reshape(-1, 3) viewer.log_points("Right/PointCloud", points.reshape(-1, 3), colors=colors_640) # Depth map visualize viewer.log_depth_image("depth/frame", depth, meter=1e3) viewer.log_image("video/color", cvFrame) if box.is_calibrated(): if 0 == len(nn.detections): continue # No boxes found # Currently supports only 1 detection (box) at a time det = nn.detections[0] # Get the bounding box of the detection (relative to full frame size) # Add 10% padding on all sides box_bb = nn.bbox.get_relative_bbox(det.bbox) padded_box_bb = box_bb.add_padding(0.1) points_roi = pcl_packet.crop_points(padded_box_bb).reshape(-1, 3) dimensions, corners = box.process_points(points_roi) if corners is None: continue viewer.log_points("Cropped/Box_PCL", box.box_pcl) viewer.log_points("Cropped/Plane_PCL", box.plane_pcl, colors=(0.2,1.0,0.6)) # viewer.log_points("Cropped/TopSide_PCL", box.top_side_pcl, colors=(1,0.3,0.6)) viewer.log_points(f"Cropped/Box_Corners", corners, radii=8, colors=(1.0,0,0.0)) corners = box.inverse_corner_points() viewer.log_points(f"Right/Box_Corners", corners, radii=8, colors=(1.0,0,0.0)) viewer.log_line_segments(f"Right/Box_Edges", box.get_3d_lines(corners), stroke_width=4, color=(1.0,0,0.0)) l,w,h = dimensions label = f"{det.label_str} ({det.confidence:.2f})\n{l/10:.1f} x {w/10:.1f}\nH: {h/10:.1f} cm" viewer.log_rect('video/bbs', box_bb.to_tuple(cvFrame.shape), label=label, rect_format=viewer.RectFormat.XYXY) viewer.log_rect('depth/bbs', padded_box_bb.to_tuple(depth.shape), label="Padded BoundingBox", rect_format=viewer.RectFormat.XYXY) key = oak.poll() ready, _, _ = select.select([sys.stdin], [], [], 0.001) # Terminal input if ready: key = sys.stdin.readline().strip() if key == 'c': if box.calibrate(points): print(f"Calibrated Plane: {box.ground_plane_eq}") draw_mesh()
如果你初次遇见OAK,请看这个视频:BV1u84y1p73d
或者来这里系统查看一下:https://www.oakchina.cn/intro/
shopping直通车在这里:https://www.oakchina.cn/shop/
私信我发送1,邀请加入OAK官方群,每周开源项目、免费CV模型、优惠活动及时掌握,还可以咨询技术、项目应用等等。声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/黑客灵魂/article/detail/840838
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。