我有一个程序,可以处理一些标记的实时视频。
它分为:
这在我的PC上运行得很好,但是它也需要在Raspberry Pi上运行,因此始终只使用一个内核就不会削减它。
这就是为什么我要引入流水线。 在大学的计算机体系结构课程中,我了解了硬件流水线技术,因此我想知道是否有可能在python中实现类似的东西:
所以不要做 导入->转换->处理->跟踪->绘制-> ...
我想这样做:
class BlockScrollView: UIScrollView {
var canScroll = true
override func setContentOffset(_ contentOffset: CGPoint, animated: Bool) {
if canScroll {
super.setContentOffset(contentOffset, animated: animated)
}
}
}
以便每个“时钟周期”都准备好图像,而不仅仅是每5个。
所以我当时正在考虑为此使用python的Multiprocessing库,但是我没有经验,但是有一些简单的测试程序,所以不确定是否最适合此用例的是Queue,Pool,Manager,...。 / p>
这可以通过mpipe来完成,这是一个很酷的python流水线工具套件。 [http://vmlaker.github.io/mpipe/][1]
-1----2----3----4-----5----...
Imp--Imp--Imp--Imp---Imp---...
-----Conv-Conv-Conv--Conv--...
----------Pro--Pro---Pro---...
---------------Track-Track-...
---------------------Draw--...
正如@r_e所建议的那样,我在一开始就读取了多个图像,并用它填充了一个管道。现在,在计算的每个步骤中,都会启动多个工作进程,以便每个人都可以在单独的图像上工作。
由于除了图像之外还需要传递一些其他信息,因此我只返回图像和其他信息,并在下一阶段再次将其解压缩。
此刻,我不得不禁用跟踪,以至于无法将其与旧版本进行比较。 Atm有点慢(跟踪不会提高速度,因为我不需要每隔30帧就检测一次物体)。但是如果我能使它工作,请给我一个更新。
答案 0 :(得分:1)
因此,在r_e的帮助下,我找到了一个名为mpipe的简洁工具包,可用于通过python进行流水线操作。
在进行测试时,我发现导入和显示图像比转换,处理和绘制UI快得多,所以我仅使用3级管道。
它非常易于使用:
data have;
do precinct = 1 to 10;
do date = '01jan2018'd to '31dec2018'd;
do seq = 1 to 20*ranuni(123);
length crime $10 location $8;
crime = scan('theft,assault,robbery,dnd', ceil(4*ranuni(123)));
location = scan ('public,private', ceil(2*ranuni(123)));
crime_dt = dhms(date,0,0,floor('24:00't*ranuni(123)));
output;
end;
end;
end;
drop date;
format crime_dt datetime19.;
run;
* shorter graphs for SO answer;
ods graphics / height=300px;
proc sgplot data=have;
title "VBAR all crimes combined by location";
vbar crime_dt
/ group=location
groupdisplay=cluster
;
format crime_dt dtmonyy7.;
run;
proc sgpanel data=have;
title "VBAR crime * location";
panelby crime;
vbar crime_dt
/ group=location
groupdisplay=cluster
;
format crime_dt dtmonyy7.;
run;
proc summary data=have noprint;
class crime_dt crime location;
format crime_dt dtmonyy7.;
output out=freqs;
run;
proc sgplot data=freqs;
title "SERIES all crimes,summary _FREQ_ * location";
where _type_ = 5;
series x=crime_dt y=_freq_ / group=location;
xaxis type=discrete;
run;
proc sgpanel data=freqs;
title "SERIES all crimes,summary _FREQ_ * crime * location";
where _type_ = 7;
panelby crime;
series x=crime_dt y=_freq_ / group=location;
rowaxis min=0;
colaxis type=discrete;
run;
答案 1 :(得分:0)
由于我没有50个声誉,因此无法对此发表评论。我对它也没有经验,但是一点点搜索使我进入了以下网站,该网站讨论了使用Multiprocessing库进行实时和视频处理的问题。希望对您有所帮助。
1)读取帧;将它们放在输入队列中,并为每个队列添加相应的帧号:
# Check input queue is not full
if not input_q.full():
# Read frame and store in input queue
ret, frame = vs.read()
if ret:
input_q.put((int(vs.get(cv2.CAP_PROP_POS_FRAMES)),frame))
2)从输入队列中取出帧,并以其对应的帧号放到输出中:
while True:
frame = input_q.get()
frame_rgb = cv2.cvtColor(frame[1], cv2.COLOR_BGR2RGB)
output_q.put((frame[0], detect_objects(frame_rgb, sess, detection_graph)))
3)如果输出队列不为空,则在输出队列中恢复已处理的帧并进给优先级队列
# Check output queue is not empty
if not output_q.empty():
# Recover treated frame in output queue and feed priority queue
output_pq.put(output_q.get())
4)绘制帧,直到输出队列为空
# Check output priority queue is not empty
if not output_pq.empty():
prior, output_frame = output_pq.get()
if prior > countWriteFrame:
output_pq.put((prior, output_frame))
else:
countWriteFrame = countWriteFrame + 1
# Draw something with your frame
5)最后,要停止,请检查输入队列是否为空。如果是,请休息。
if((not ret) & input_q.empty() &
output_q.empty() & output_pq.empty()):
break
可以找到链接HERE
答案 2 :(得分:0)
我对此做了一点尝试。它很大程度上取决于您的图表,并使用5级管道和多处理。从结尾处开始阅读:
def main():
...
...
#!/usr/bin/env python3
import logging
import numpy as np
from time import sleep
from multiprocessing import Process, Queue
class Stage1(Process):
"""Acquire frames as fast as possible and send to next stage"""
def __init__(self, oqueue):
super().__init__()
# Pick up parameters and store in class variables
self.oqueue = oqueue # output queue
def run(self,):
# Turn on logging
logging.basicConfig(level=logging.DEBUG,
format='%(created).6f [%(levelname)s] Stage1 %(message)s',
filename='log-stage1.txt', filemode='w')
logging.info('started')
# Generate frames and send down pipeline
for f in range(NFRAMES):
logging.debug('Generating frame %d',f)
# Generate frame of random stuff
frame = np.random.randint(0,256,(480,640,3), dtype=np.uint8)
logging.debug('Forwarding frame %d',f)
self.oqueue.put(frame)
class Stage2(Process):
"""Read frames from previous stage as fast as possible, process and send to next stage"""
def __init__(self, iqueue, oqueue):
super().__init__()
# Pick up parameters and store in class variables
self.iqueue = iqueue # input queue
self.oqueue = oqueue # output queue
def run(self,):
# Turn on logging
logging.basicConfig(level=logging.DEBUG,
format='%(created).6f [%(levelname)s] Stage2 %(message)s',
filename='log-stage2.txt', filemode='w')
logging.info('started')
for f in range(NFRAMES):
# Wait for next frame
frame = self.iqueue.get()
logging.debug('Received frame %d', f)
# Process frame ...
logging.debug('Forwarding frame %d', f)
self.oqueue.put(frame)
class Stage3(Process):
"""Read frames from previous stage as fast as possible, process and send to next stage"""
def __init__(self, iqueue, oqueue):
super().__init__()
# Pick up parameters and store in class variables
self.iqueue = iqueue # input queue
self.oqueue = oqueue # output queue
def run(self,):
# Turn on logging
logging.basicConfig(level=logging.DEBUG,
format='%(created).6f [%(levelname)s] Stage3 %(message)s',
filename='log-stage3.txt', filemode='w')
logging.info('started')
for f in range(NFRAMES):
# Wait for next frame
frame = self.iqueue.get()
logging.debug('Received frame %d', f)
# Process frame ...
logging.debug('Forwarding frame %d', f)
self.oqueue.put(frame)
class Stage4(Process):
"""Read frames from previous stage as fast as possible, process and send to next stage"""
def __init__(self, iqueue, oqueue):
super().__init__()
# Pick up parameters and store in class variables
self.iqueue = iqueue # input queue
self.oqueue = oqueue # output queue
def run(self,):
# Turn on logging
logging.basicConfig(level=logging.DEBUG,
format='%(created).6f [%(levelname)s] Stage4 %(message)s',
filename='log-stage4.txt', filemode='w')
logging.info('started')
for f in range(NFRAMES):
# Wait for next frame
frame = self.iqueue.get()
logging.debug('Received frame %d', f)
# Process frame ...
logging.debug('Forwarding frame %d', f)
self.oqueue.put(frame)
class Stage5(Process):
"""Read frames from previous stage as fast as possible, and display"""
def __init__(self, iqueue):
super().__init__()
# Pick up parameters and store in class variables
self.iqueue = iqueue # input queue
def run(self,):
# Turn on logging
logging.basicConfig(level=logging.DEBUG,
format='%(created).6f [%(levelname)s] Stage5 %(message)s',
filename='log-stage5.txt', filemode='w')
logging.info('started')
for f in range(NFRAMES):
# Wait for next frame
frame = self.iqueue.get()
logging.debug('Displaying frame %d', f)
# Display frame ...
def main():
# Create Queues to send data between pipeline stages
q1_2 = Queue(5) # queue between stages 1 and 2
q2_3 = Queue(5) # queue between stages 2 and 3
q3_4 = Queue(5) # queue between stages 3 and 4
q4_5 = Queue(5) # queue between stages 4 and 5
# Create Processes for stages of pipeline
stages = []
stages.append(Stage1(q1_2))
stages.append(Stage2(q1_2,q2_3))
stages.append(Stage3(q2_3,q3_4))
stages.append(Stage4(q3_4,q4_5))
stages.append(Stage5(q4_5))
# Start the stages
for stage in stages:
stage.start()
# Wait for stages to finish
for stage in stages:
stage.join()
if __name__ == "__main__":
NFRAMES = 1000
main()
目前,它只是生成一帧随机噪声,并将其向下传递到管道中。它将每个进程记录到一个单独的文件中,由于filemode='w'
,该文件将为该程序的每次新运行覆盖。您可以看到这样的单个日志:
-rw-r--r-- 1 mark staff 1097820 26 Jun 17:07 log-stage1.txt
-rw-r--r-- 1 mark staff 1077820 26 Jun 17:07 log-stage2.txt
-rw-r--r-- 1 mark staff 1077820 26 Jun 17:07 log-stage3.txt
-rw-r--r-- 1 mark staff 1077820 26 Jun 17:07 log-stage4.txt
-rw-r--r-- 1 mark staff 548930 26 Jun 17:07 log-stage5.txt
然后您可以查看每个进程接收和发送每个帧的时间:
more log-stage1.txt
1561565618.603456 [INFO] Stage1 started
1561565618.604812 [DEBUG] Stage1 Generating frame 0
1561565618.623938 [DEBUG] Stage1 Forwarding frame 0
1561565618.625659 [DEBUG] Stage1 Generating frame 1
1561565618.647139 [DEBUG] Stage1 Forwarding frame 1
1561565618.648173 [DEBUG] Stage1 Generating frame 2
1561565618.687316 [DEBUG] Stage1 Forwarding frame 2
或者在各个阶段跟踪说“框架1”:
pi@pi3:~ $ grep "frame 1$" log*
log-stage1.txt:1561565618.625659 [DEBUG] Stage1 Generating frame 1
log-stage1.txt:1561565618.647139 [DEBUG] Stage1 Forwarding frame 1
log-stage2.txt:1561565618.671272 [DEBUG] Stage2 Received frame 1
log-stage2.txt:1561565618.672272 [DEBUG] Stage2 Forwarding frame 1
log-stage3.txt:1561565618.713618 [DEBUG] Stage3 Received frame 1
log-stage3.txt:1561565618.715468 [DEBUG] Stage3 Forwarding frame 1
log-stage4.txt:1561565618.746488 [DEBUG] Stage4 Received frame 1
log-stage4.txt:1561565618.747617 [DEBUG] Stage4 Forwarding frame 1
log-stage5.txt:1561565618.790802 [DEBUG] Stage5 Displaying frame 1
或按时间顺序将所有日志组合在一起:
sort -g log*
1561565618.603456 [INFO] Stage1 started
1561565618.604812 [DEBUG] Stage1 Generating frame 0
1561565618.607765 [INFO] Stage2 started
1561565618.612311 [INFO] Stage3 started
1561565618.618425 [INFO] Stage4 started
1561565618.618785 [INFO] Stage5 started
1561565618.623938 [DEBUG] Stage1 Forwarding frame 0
1561565618.625659 [DEBUG] Stage1 Generating frame 1
1561565618.640585 [DEBUG] Stage2 Received frame 0
1561565618.642438 [DEBUG] Stage2 Forwarding frame 0