Easy Pipeline,一种轻量级的Python Pipeline库
嗯,很久没有写博客了,最近的工作都是偏开发性质的,以至于没有时间对自己感兴趣的领域进行探索,感觉个人的成长停滞了一些。如何在枯燥的工作中,提取出有助于自己成长的养分,对于每个人来说都是不小的考验。
这次,带来的是之前编写的一下挺简单的库,用来简化流水线作业的小框架。
Github: https://github.com/miaoerduo/easy-pipeline 欢迎Star和提交MR。
起因是这样的,组内有一个需求,需要挖掘视频中的检测难样本,这样可以极大地减少标注的量,从而降低成本。难样本挖掘的策略,简单来说就是如果视频的前几帧和后几帧都能检测到目标,而就只有当前帧没有检测到,就说明当前帧很可能存在漏检(没有检测本到该检测到的目标);反之,如果前后都没有检测到目标,而当前帧检测到了,那就很可能是误检(检测到不是目标的东西)。

初步的方案是这样的,我们先把视频抽帧,直接用FFMpeg就可以方便的完成。然后调用现在的检测器,进行逐帧的检测,把检测结果存下来。最后写个脚本,分析检测的结果,然后输出可能有问题的帧,然后这些帧就会进行送标(发给标注员进行标注)。最终我们就只需要标注一些比较hard的样本就行了。
但是这样会带来很多的问题,最显著的两个:1. 需要保存大量的中间结果(图片帧);2. 必须依次完成每一步之后,才能得到最终的结果。
这时候,相比大家都知道了该如何去解决。对的,我们应该用流水线作业的方式去进行。

首先我们可以将每部分任务并行的去处理。抽帧之后的结果送入队列;之后检测模块从队列取帧,检测之后将结果送入下一个队列;最后一个队列得到检测结果,再做最终的分析。相比于之前的方式,这样可以尽量的减少中间的结果。
实现该方案,只需要使用最简单的生产者消费者队列即可以完成。所以说,相信大家都十分了解了。对于上面的逻辑,我们需要的队列的数目和我们的模块数是正相关的。如果单纯的进行实现的话,实在的太麻烦了,给队列命名都要我们绞尽脑汁了。所以,为了更优雅的编写代码,这里就推出本文标题中的Easy Pipeline框架。
首先,我们举个最简单的例子来说明该框架的工作模式。输入一个数字的序列,按要求对他们进行加减乘除的操作(这里的每个操作,其实可以等价于前面的抽帧或是检测的更复杂的操作 ),并且支持每个操作的进程数。
from easy_pipeline import SimplePipeline, PipelineItem, Task, StopTask, EmptyTask
import multiprocessing as mp
# define our Task
class NumTask(Task):
    def __init__(self, x):
        super(NumTask, self).__init__()
        self.val = x
# init function, here we use closure to get different function
def get_init_fn(x):
    def init():
        return x
    return init
# operations
def plus(res, task):
    return NumTask(task.val + res)
def mul(res, task):
    return NumTask(task.val * res)
def minus(res, task):
    return NumTask(task.val - res)
def div(res, task):
    return NumTask(task.val / res)
if __name__ == '__main__':
    # job queue
    manager = mp.Manager()
    job_queue = manager.Queue(1000)
    # define pipeline and start
    # x = ((x + 1) * 2 - 3)/ 5
    pipeline_items = [
        PipelineItem(plus, get_init_fn(1), 1, 10),      # plus 1
        PipelineItem(mul, get_init_fn(2), 2, 10),       # mul 2
        PipelineItem(minus, get_init_fn(3), 3, 10),     # minus 3
        PipelineItem(div, get_init_fn(5.), 4, 10),      # div 5
    ]
    pipeline = SimplePipeline(pipeline_items, job_queue)
    pipeline.start()
    result_queue = pipeline.get_result_queue()
    # Feed jobs anytime (before StopTask)
    for i in range(10):
        job_queue.put(NumTask(i))
    # get partial output
    print('Get Output Start')
    for i in range(5):
        result = result_queue.get()
        if isinstance(result, StopTask):
            print("get stop task")
            break
        if isinstance(result, EmptyTask):
            continue
        print(result.val)
    print('Get Output End')
    # Feed jobs anytime (before StopTask)
    for i in range(10, 20):
        job_queue.put(NumTask(i))
    # Stop pipeline, means no more job will be added then.
    # Every process will exit when it has done all current jobs in job_queue
    pipeline.stop()
    # get all output
    print('Get Output Start')
    while True:
        result = result_queue.get()
        if isinstance(result, StopTask):
            print("Output Queue Empty")
            break
        if isinstance(result, EmptyTask):
            continue
        print(result.val)
    print('Get Output End')
下面,我们来简单的说明一下工作逻辑。
- 首先,我们需要定义自己的任务Task。只需要继承Task这个类即可,内部可以存放自己喜欢的任何数据。这里只是为了计算,所以就只存放了一个数字。
 - 定义我们的初始化函数和工作函数。初始化函数的作用是给每个进程初始化一些资源,如果不需要也可以不要。这里的初始化函数就是返回了一个值,表示操作数。工作函数是最重要的函数,他会处理接收到的Task,处理并返回新的Task(新的Task可以理解为处理的结果)。工作函数有两个输入,一个是资源,即初始化函数的返回值,另一个就是Task本身。
 - 构建Pipeline。每个工作模块都只需要用PipelineItem这个对象进行封装即可。器参数分别是:工作函数、初始化函数、进程数、结果队列的长度(-1表示不限长度)。结果队列的长度,通常设置为较大的值即可。因为不能的模块的处理速度可能不同,因此很容易出现结果堆积的现象,如果不支持队列长度,会导致内存的大量的占用。最后将PipelineItem的数组和输入的对垒传给SimplePipeline对象即可构建完我们的整套Pipeline程序了!
 - 启动Pipeline程序,并输入数据。
 - 得到结果!完事了,优秀。
 
上面这是一个最简单的例子,可以比较直观的感受到这个框架的便捷之处。完全屏蔽掉对队列,并发等的操作。
在我推荐给同事之后,确实一定程度地减小他的工作量,但同时,他也向我反馈了一些问题:这个框架在某些地方有些比较灵活的设计,应该给出足够多的实例,才能方便实用。关于该框架的设计思路和实例,将会在下一篇博客中进行详细介绍。
最后,欢迎大家Star和提交MR。愿与你们一同进步。
Easy Pipeline,一种轻量级的Python Pipeline库的更多相关文章
- python开发_json_一种轻量级的数据交换格式
		
以下是我做的对于python中json模块的demo 运行效果: Python 3.3.2 (v3.3.2:d047928ae3f6, May 16 2013, 00:03:43) [MSC v.16 ...
 - JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式
		
JSON JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式. 它基于JavaScript(Standard ECMA-262 3rd Edition - D ...
 - 140种Python标准库、第三方库和外部工具
		
导读:Python数据工具箱涵盖从数据源到数据可视化的完整流程中涉及到的常用库.函数和外部工具.其中既有Python内置函数和标准库,又有第三方库和工具. 这些库可用于文件读写.网络抓取和解析.数据连 ...
 - 茴香豆的“茴”有四种写法,Python的格式化字符串也有
		
茴香豆的"茴"有四种写法,Python的格式化字符串也有 茴香豆的"茴"有四种写法,Python的格式化字符串也有 被低估的断言 多一个逗号,少一点糟心事 上下 ...
 - JavaScript 一种轻量级的编程语言
		
JavaScript 一种轻量级的编程语言 作为一名计算机应用专业的学生,大一上学期开始接触了网页设计和制作,刚开始时感觉做网页很不错,简单地写几行代码就能做出效果来,当时感觉很兴奋,渐渐的喜欢上它 ...
 - 【转载】pygame安装与两种版本的Python兼容问题
		
在开始学习游戏编程之前,我们先来安装下pygame和python3.2.5 参考园友: http://www.cnblogs.com/hongten/p/hongten_pygame_install. ...
 - 两种方法实现Python二分查找算法
		
两种方法实现Python二分查找算法 一. ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 arr=[1,3,6,9,10,20,30] def findnumber( ...
 - JSON --- 一种轻量级的数据交换格式
		
目录 1. 语法 2. 解析与序列化 JSON.stringify( jsData[, filter, indent] ) JSON.parse( jsonData[, reduction]) JSO ...
 - python第六天  函数  python标准库实例大全
		
今天学习第一模块的最后一课课程--函数: python的第一个函数: 1 def func1(): 2 print('第一个函数') 3 return 0 4 func1() 1 同时返回多种类型时, ...
 
随机推荐
- Oracle EBS 贷项通知单核销
			
SELECT cm.trx_number ,fnd_flex_ext.get_segs('SQLGL', 'GL#', gcc.chart_of_accounts_id, ad.code_combin ...
 - 数据库导入.bacpac 文件创建新实例
			
先连接好数据库,然后打开左侧 对象资源管理器,选择数据库 右键单击 ---> 选择导入数据层应用程序 根据提示向导一步步走就行了 部分导入失败以及处理方案 异常1 : 在数据库master中拒 ...
 - VMware 导出镜像文件供 Virtual Box 使用
			
1. 问题描述 Windows 系统安装的 VMware 里的安装配置好的虚拟机需要拷贝到 MAC 的 Virtual Box 中. 需要将 VMware 中的虚拟机导出为镜像文件供 Virtual ...
 - windows注册表
			
如何导入与导出注册表 进入注册表编辑器,选择“文件”“导出”命令,打开“导出注册表文件”对话框.选择保存位置并为其取名,单击保存即可完成注册表的备份. 打开注册表编辑器,选择“文件”“导入”命令,打开 ...
 - CIDR概述及其地址块计算
			
CIDR概述 英文:Classless Inter-Domain Routing,中文是:无分类域间路由选择.一般叫做无分类编址. 设计目的:解决路由表项目过多过大的问题. 表示法:{<网络前缀 ...
 - iOS资源大全中文版
			
我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列的资源整理.awesome-ios 就是 vsouza 发起维护的 iOS 资源列表,内容包括:框架.组件.测试.App ...
 - ArcGIS pro2.3中添加天地图底图
			
应用背景: 很多时候,我们需要使用网络上的遥感影像或者百度地图.天地图等在线地图做一些矢量化工作或者其他. 笔者见过很多人都是把百度地图截图,然后把图片导如Arcmap或者Arcgis pro中,然后 ...
 - Terminal Service 终端链接
			
2008 64位前有这项服务,之后就与远程管理合并了 如果要设置他的连接数可以去 桌面 --> 管理工具 --> 远程桌面服务 最大数设置成1个好了
 - BeanDefinition及其实现类
			
[转自 http://blog.csdn.net/u011179993 ] 目录(?)[+] 一. BeanDefinition及其实现类 BeanDefinition接口 这个接口描述bea ...
 - 博客系统实战——SprintBoot 集成Thymeleaf 实现用户增删查改(含源码)
			
近来在学习SprintBoot +Thymeleaf +Maven搭建自己的博客系统,故在学习过程中在此记录一下,也希望能给广大正在学习SprintBoot和Thymeleaf的朋友们一个参考. 以下 ...