modelbox入门教程(三) modelbox讲解 expand与Collapse单元

简介

本文介绍如何活用expand与Collapse单元来调整数据流,批量处理细分数据单元。expand相当于平铺操作,Collapse相当于合并操作。

背景介绍

利用normal单元,modelbox可以对数据单位作批量处理。
利用expand与Collapse单元,modelbox可以对数据的细分单位作批量处理。
什么意思呢?
举个例子,假如

  • 输入是一批图片,设一张图片为I,则一批图片为 [ I , I , I , . . . ] [I, I, I, ...] [I,I,I,...]
  • 设模型为 M 1 M_1 M1(比如resnet),可以并行处理一批图片,得到推理结果 [ Y , Y , . . . ] [Y, Y, ...] [Y,Y,...]

那么,你可以部署一个推理单元,或者调用了M的功能单元 U M U_M UM,然后令其处理数据流,如公式所示 ( I , I , I ) − > U M − > ( Y , Y , Y ) (I, I, I)->U_M->(Y, Y, Y) (I,I,I)>UM>(Y,Y,Y)

在上面的例子中,数据单位是一张图片。假如数字单位是一批不定长的图片呢?

我们假设这样的情景:

我们希望识别并标注每张图片中所有人脸的情绪,这个过程需要并行按批次处理。为此,我们要将每张图片中的若干个人脸区域都识别出来,裁剪缩放成一个个图片,再输入给人脸情绪识别模型,那么每个数据单位都包含若干个缩放后的图片人脸区域。模型会预测每一个人脸区域的表情情绪,处理流程会之后在图片的人脸区域上标记人脸情绪。

此时,设人脸情绪识别模型为 M 2 M_2 M2,输入就会类似于 ( [ I , I ] , [ I , I , I , I ] , [ I , I , I ] ) ([I, I], [I, I, I, I], [I, I, I]) ([I,I],[I,I,I,I],[I,I,I]),3个数据单元分别是2、4、3张图片。我们的模型是 M 2 M_2 M2,只能处理一批图片。

这样,直接输入给模型 M 2 M_2 M2就不合适了,因为 M 2 M_2 M2无法处理图片组的批次,只能处理图片的批次。因此可以利用expand把那些人脸区域单位平铺,输入给M处理,再利用Collapse单元将展开的单元合并回原本形态的数据流。

介绍Expand和Collapse单元

数据流

Normal单元只会按批次处理Buffer:

  1. 输入是两个数据流,蓝色和绿色是两条数据流。 第一条数据流包含3个Buffer,第二条数据流包含4个Buffer。
  2. 经过Normal单元时,根据设置的batch_size=2,数据被两两一组处理,其中(B1,B2)是第一批处理的,(B3,B4)是第二批处理的。
  3. 批次处理后,输出的数据流形态不变,数据会存储在Buffer中。
    modelbox入门教程(三) modelbox讲解 expand与Collapse单元_第1张图片

Expand单元可以展开数据流,将数据流中的每个Buffer各自展开成一条数据流,展开时数据流的从属关系会被标记:

  1. 输入是一个数据流,有3个Buffer。
  2. 数据经过Expand单元,3个Buffer依次处理,每个Buffer都会展开成一个数据流。
  3. 展开后, 三个Buffer分别被展开成了有4/3/5个Buffer的数据流,共计3条数据流。三条数据流与原本三个Buffer的从属关系会被标记,这样当后面经过Collapse操作时,能够被重新合并。
    modelbox入门教程(三) modelbox讲解 expand与Collapse单元_第2张图片

合并回一条数据流
modelbox入门教程(三) modelbox讲解 expand与Collapse单元_第3张图片

结合以上图片思考,对于文章开头的例子,我们可以组成 Expand->Normal->Collapse的流程图,并在Normal单元调用人脸识别模型,这样每张图片会被平铺为若干个人脸区域,而经过模型处理后的情绪识别结果会被合并到每张图片中,这样就能满足需求了。

代码参考

参考modelbox项目源码下的路径modelbox\src\demo\emotion_detection\flowunit\expand_box

如下:

  1. 输入in_data_list是一个BufferList,每个Buffer代表一张图片。
  2. 每个Buffer内含有多个bbox,经过该expand单元后,每个Buffer被展开为若干个bbox,输入到后面的单元。
  3. 等后面的单元批处理完成后,经过Collapse单元,bbox的数据会重新被整理回图片流。
import _flowunit as modelbox
import numpy as np

class ExpandBox(modelbox.FlowUnit):
    def __init__(self):
        super().__init__()

    def open(self, config):
        return modelbox.Status.StatusCode.STATUS_SUCCESS

    def process(self, data_context):
        in_data_list = data_context.input("in_data")
        out_image_list = data_context.output("roi_image")

        for in_buffer in in_data_list:
            width = in_buffer.get("width")
            height = in_buffer.get("height")
            channel = in_buffer.get("channel")

            img = np.array(in_buffer.as_object(), dtype=np.uint8)
            img = img.reshape(height, width, channel)

            bboxes = in_buffer.get("bboxes")
            bboxes = np.array(bboxes).reshape(-1, 4)
            for box in bboxes:
                img_roi = img[box[1]:box[3], box[0]:box[2]]
                img_roi = img_roi[:, :, ::-1]

                img_roi = img_roi.flatten()
                add_buffer = modelbox.Buffer(self.get_bind_device(), img_roi)
                add_buffer.copy_meta(in_buffer)
                add_buffer.set("pix_fmt", "rgb")
                add_buffer.set("width", int(box[2] - box[0]))
                add_buffer.set("height", int(box[3] - box[1]))
                add_buffer.set("width_stride", int(box[2] - box[0]))
                add_buffer.set("height_stride", int(box[3] - box[1]))
                out_image_list.push_back(add_buffer)
        return modelbox.Status.StatusCode.STATUS_SUCCESS

    def close(self):
        return modelbox.Status()
    
    def data_pre(self, data_context):
        return modelbox.Status()

    def data_post(self, data_context):
        return modelbox.Status()

你可能感兴趣的:(python,开发语言)