Skip to content Skip to footer

物流中的人工智能:利用计算机视觉优化集装箱填充率

image-20231228231803530

物流中最明显的低效率之一是空置空间问题。作为全球贸易命脉的集装箱,经常部分装满,浪费宝贵的空间和资源。这种低效率转化为运营成本的增加以及对企业和环境可持续性的损害。

更高的运输成本
承运人根据集装箱尺寸而不是集装箱装载的货物数量收取费用。这意味着即使是部分装满的容器也与完全包装的容器成本相同。从这个角度来看,根据 Statista(2018-2023 年)的报告,AP Moller — Maersk 在 Covid-19 大流行期间的运费显着上涨。因此,运输部分装满的集装箱基本上归结为为空置空间而不是有价值的货物付费,从而影响您的投资回报。

增加整个供应链
的碳足迹 将相同的货物拆分到一个集装箱中意味着运输方式增加一倍。

货物损坏
增加 随着空间的增加,货物的包装不那么紧密。这使得箱子、托盘和货物在运输过程中可以更自由地移动,特别是由于振动和突然停止。

为了在集装箱密封和运输之前帮助从根源上识别这一点,开发了一种集装箱填充率分析仪,该分析仪使用计算机视觉和人工智能 (AI) 来了解装载到容器中的每层托盘的填充率。集装箱的填充率是货物占用可用空间的百分比。

在人工智能的帮助下使用计算机视觉,可以消除一个人判断每张图像填充率的手动任务,并将注意力集中在解决实际问题上。

方法

有许多方法可以应对这一挑战。可以使用单次检测器 (SSD) 或您只看一次 (YOLO) 模型来检测托盘,然后从那里计算填充率。Arcgic 在其文档页面上详细介绍了 SSD 的工作原理。

然而,我们的想法是针对这个特定的用例测试元细分任何事物模型(SAM)。在 Meta AI 博客中,Meta 分享了一个演示游乐场,并概述了 SAM 的功能。与为此特定任务训练模型相比,这种方法当然不是特定于领域的,但广义模型已经走了很长一段路,值得测试这种任务的可行性。

SAM 用途广泛,具有 2 种检测方法,一种是自动掩码生成,它将分割图像上的所有内容,另一种是基于提示的,其中图像上的坐标指导分割。Meta 在这里分享了一篇关于如何构建 SAM 的非常详细的帖子。

SAM 自动模板生成

# Initialize Segement Anything and pass in the image for auto mask generation
mask_generator = SamAutomaticMaskGenerator(sam)
masks = mask_generator.generate(input_layer_img)

这种方法效果很好,只需 2 行 Python 代码即可轻松设置,并且无需任何说明即可在图像中对所有内容进行分割。

image-20231228231835317

异物分割

然而,在确定奇数尺寸的托盘或异物是否是层的一部分时,挑战就来了。在上图中,安全气囊、一些填充包装纸和纸板被分割开来,看起来像一个托盘。

image-20231228231901994

多重分割

有时,由于肩带或松散的包装纸,会单独分段,如上所示。

基于提示的分段

基于提示的分割需要提示来指导 SAM 了解焦点区域的位置和方式。针对自动掩码生成方法进行测试,基于提示的分割方法对于本项目更可行。

下面是程序执行流程的伪代码和代码片段。

# Read the input image
input_layer_img: np.ndarray = cv2.imread(img_fp)

# Downscale image for performance
input_layer_img = downscale(input_layer_img)

# First, find all the labels in the image
# The label position can help prompt SAM to generate segments better
label_points: list[list[int, int]] = pallet_label_detector(input_layer_img)

# Send the labels position to SAM and get a segment mask
segmented_mask: np.ndarray = prompt_segment(label_points, input_layer_img)

# Draw on the original image with values from the mask
segment_color = np.random.random(3) * 100

segmented_img = input_layer_img.copy()
segmented_img[segmented_mask] = segment_color
mask = cv2.inRange(segmented_img, segment_color - 10, segment_color + 10)

# Based on the segmented image, find the fill rate
fill_rate: float = fill_rate_calculation(label_points, mask, segmented_img)

在这种情况下,托盘上每个标签的坐标可以传递到 SAM 中进行分段。可以使用计算机视觉技术完成标签提取,例如定义感兴趣区域、颜色过滤和轮廓。此过程是特定于业务域的,但通常,大多数标签都接近白色。

检测标签的更准确方法是扫描串行运输集装箱代码(SSCC) 条形码,但是,图像质量不足以检测条形码。

lower_val = np.array([150, 150, 150], dtype=np.uint8)
upper_val = np.array([255, 255, 255], dtype=np.uint8)

# preparing the mask to overlay
mask = cv2.inRange(layer_img, lower_val, upper_val)

# find contours
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]

new_mask = np.ones(img.shape[:2], dtype="uint8") * 255
prompt_points = []

for c in contours:
    x, y, w, h = cv2.boundingRect(c)

    # only select points in our region of interest
    if is_outside_roi(layer_img, x, y):
        continue

    if w * h < 1000:
        continue

    cv2.rectangle(new_mask, (x, y), (x + w, y + h), (0, 0, 255), -1)

    # We calculate the center of the label to be passed for prompting
    prompt_points.append([int(x + (w / 2)), int(y + (h / 2))])

res_final = cv2.bitwise_and(layer_img, layer_img, mask=cv2.bitwise_not(new_mask))
cv2.imshow("Labels only", res_final)

如上面的 Python 代码所示,将 150 到 255 之间的滤色器应用于输入图像,并从输入图像中提取蒙版。

image-20231228231942687

res_final所选标签的输出(作者图片)

提示标签位置将产生 SAM 更注重域的结果。尽管提取的标签大小不准确,但估计值足以让提示对必要的标签进行细分。

# prompt_points contains the coordinates of the labels
# [ [x, y], [x, y]...]
input_point_nd = np.array(prompt_points, dtype=np.int32)

# As all the prompt points are labels, we are giving them a category of 1
input_label = np.ones(len(prompt_points), dtype=np.int32)

predictor.set_image(segment_img)
masks, scores, _ = predictor.predict(
    point_coords=input_point_nd,
    point_labels=input_label,
    multimask_output=False,
)

image-20231228232002434

另一张图片的SAM输出(图片由作者提供)

分段输出如上图所示。使用一种简单的方法来计算容器的边界,如红框所示。图像稍后转换为黑白以进行填充率计算。

image-20231228232024633

fill_rate_used输出(图片由作者提供)

# Sum of white pixels
total_white = np.sum(fill_rate_used[tallest:ch, cx: cw] == 255)

# Sum of black pixels
total_black = np.sum(fill_rate_used[tallest:ch, cx: cw] == 0)

# Percentage of white
fill_rate = round(total_white / (total_white + total_black), 2)

估计的填充率将是占用的彩色空间与未占用空间(容器边界中的黑色像素)的比较。可以应用很少的形态学操作,例如扩张来填充盒子之间的间隙。

结果

image-20231228232048026

示例结果

使用基于个人环境的当前测试用例,结果接近现实。这大大减少了分析每个集装箱填充率的手动工作量,并且对填充率百分比的判断更加一致。由于会检测到标签,因此会考虑异形托盘,并且由于标签坐标的提示,可以减少不需要的分割。

有了集装箱中每一层装载的结果,公司现在能够分析部分装载的原因,并确定运营或规划过程中是否存在差距。在操作上,在装运前密封集装箱的决定也可以使用填充率指标作为一个因素。

通过跟踪一段时间内的结果,可以建立一个可见的趋势,以可视化加载过程中是否有任何改进。

局限性

托盘层

image-20231228232117526

分层检测

其中一个限制是,如果颜色匹配得太近,后面的托盘偶尔会与前面的托盘分开。这会导致填充率的错误计算,因为隔间实际上被认为是空的。为了克服这些限制,使用基于提示的分割可能并不理想,而是将自动掩码生成和标签检测相结合。

异物

image-20231228232141217

安全气囊误检

另一个挑战是安全气囊的分段。在某些情况下,安全气囊会用托盘伪装,导致分段被分组。

image-20231228232205696

最近箱检测

克服这种限制的一种选择是尽可能画一个框,删除奇形怪状的分段。然而,这再次给奇形怪状的托盘带来了另一个挑战,想想不可折叠椅子的托盘。

关闭

通过使用计算机视觉,公司中的团队和同事可以做出数据驱动的决策,而无需手动分析单个图像。

这个项目可以通过多种方式进行扩展。其中一些包括:

  • 装载卡车甚至小型货车(最后一英里交付)
  • 通过视频进行实时估算/装货结束分析
  • 将填充率转化为货币价值和潜在的立方米 (m3) 损失
  • 根据填充率阈值计算货物损坏的概率

确保良好输出的最大贡献者是具有一致且标准化的输入图像或流。这将大大提高集装箱高度估计和托盘放置检测。最佳方法是检测SSCC条形码并使用条形码位置来提示分割,但是,这将以更昂贵的相机为代价。