OAKChina 新品:OAK-D SR PoE结合ToF实现箱体检测
3D+AI解决方案提供商
手动测量箱体、缺陷、大小等操作可能是一项繁琐并且劳累而机械的任务,但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)
我们以 全新产品:OAK-D-SR-POE(几周后将会首次发布!)为基础开发了此应用,因为它借助 ToF 传感器实现了最佳近距离感知。它还具有边缘 AI 推理功能,是此应用的完美候选者。
ToF传感器的深度误差在室内<1%,在室外<2%。
OAK - D SR PoE
一样,附上源码:
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中国 丨 3D+AI 解决方案提供商
如果你初次遇见OAK,请看这个视频:BV1u84y1p73d
或者来这里系统查看一下:https://www.oakchina.cn/intro/
shopping直通车在这里:https://www.oakchina.cn/shop/
私信我发送1,邀请加入OAK官方群,每周开源项目、免费CV模型、优惠活动及时掌握,还可以咨询技术、项目应用等等。😉
```
OAK中国 | 追踪AI技术和产品新动态
公众号 | OAK视觉人工智能开发
戳「+关注」获取最新资讯↗↗
如果喜欢,请一键三连吧~比心❤️
```