本文介绍如何活用expand与Collapse单元来调整数据流,批量处理细分数据单元。expand相当于平铺操作,Collapse相当于合并操作。
利用normal单元,modelbox可以对数据单位作批量处理。
利用expand与Collapse单元,modelbox可以对数据的细分单位作批量处理。
什么意思呢?
举个例子,假如
那么,你可以部署一个推理单元,或者调用了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单元将展开的单元合并回原本形态的数据流。
数据流
Normal单元只会按批次处理Buffer:
Expand单元可以展开数据流,将数据流中的每个Buffer各自展开成一条数据流,展开时数据流的从属关系会被标记:
结合以上图片思考,对于文章开头的例子,我们可以组成 Expand->Normal->Collapse的流程图,并在Normal单元调用人脸识别模型,这样每张图片会被平铺为若干个人脸区域,而经过模型处理后的情绪识别结果会被合并到每张图片中,这样就能满足需求了。
参考modelbox项目源码下的路径modelbox\src\demo\emotion_detection\flowunit\expand_box
如下:
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()