Функция hist
в matplotlib.pyplot работает очень медленно, что, по-видимому, связано с выбранной мной структурой. Я создал в Tkinter переднюю панель, которая запускает цикл управления камерой. Чтобы обеспечить оперативность цикла управления, я создал класс ImageProcessor
, который собирает, обрабатывает и отображает изображения в cv2. Объект ImageProcessor
выполняется в своем собственном потоке. Это работает до тех пор, пока я не пытаюсь построить гистограмму изображения.
Поскольку Tkinter не является потокобезопасным, я использую Agg в качестве бэкэнда и рисую нарисованный холст pyplot.figure
с помощью cv2. Расчет гистограммы изображения с помощью pyplot.hist
занимает более 20 секунд. Самостоятельный расчет гистограммы занимает всего 0,5 секунды.
Как это проявляется? Нужно ли запускать Matplotlib из основного потока или этого достаточно, если с ним взаимодействует только один поток (как в моем случае)? Или в моем коде есть еще одно недоразумение?
import threading
import time
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from timeit import default_timer as timer
from datetime import timedelta
import queue
class ImageProcessor(threading.Thread):
def __init__(self):
matplotlib.use('Agg')
threading.Thread.__init__(self)
# initialize plot for histograms
self.hist_fig = plt.figure()
self.loop = True
self.continuous_acquisition_var = False
self.a = None
def run(self):
while self.loop:
self.a = np.random.uniform(low=0, high=16384, size=12320768).reshape((4096, 3008))
self.hist_fig.clf() # clear histogram plot
start = timer()
plt.hist(self.a.flatten(), bins=256, range=(0.0, 16384), fc='r', ec='r')
end = timer()
print(timedelta(seconds=end - start))
def stop(self):
self.loop = False
def ctl_loop(command):
ctl_loop_var = True
img_proc = ImageProcessor()
img_proc.daemon = True
img_proc.start()
while ctl_loop_var: # main loop
while not command.empty():
q_element = command.get()
task = q_element[0]
data = q_element[1]
func = getattr(img_proc, task)
func(data)
if task == "stop":
ctl_loop_var = False
if __name__ == '__main__':
cmd_queue = queue.Queue()
ctl = threading.Thread(target=ctl_loop, args=(cmd_queue, ))
ctl.daemon = True
ctl.start()
time.sleep(40)
cmd_queue.put(('stop', ''))
🤔 А знаете ли вы, что...
С Python можно создавать веб-скраперы для извлечения данных из веб-сайтов.
Решение простое и не имеет ничего общего с plt.hist
. Просто добавьте строку time.sleep(0.01)
в основной цикл. Причина в том, что многопоточность — это не то же самое, что многопроцессорность. Все потоки используют один и тот же процесс (ЦП), то есть одновременно может выполняться только один поток. В вашем случае основной поток (цикл while ctl_loop_var
) как можно быстрее проверяет, истинно ли ctl_loop_var
, не позволяя другому потоку что-либо делать. Поэтому убедитесь, что вы не создаете ненужную нагрузку на процессор. Это относится и к многопроцессорной обработке, хотя влияние может быть менее заметным.
def ctl_loop(command):
ctl_loop_var = True
img_proc = ImageProcessor()
img_proc.daemon = True
img_proc.start()
while ctl_loop_var: # main loop
while not command.empty():
q_element = command.get()
task = q_element[0]
data = q_element[1]
if task == "stop":
img_proc.stop()
ctl_loop_var = False
else:
func = getattr(img_proc, task)
func(data)
time.sleep(.01) # give the other thread time to process
Кроме того, код также исправляет две ошибки в исходном коде:
ImageProcessor.stop
принимает только один аргументImageProcessor.stop
не останавливал поток должным образом, когда основной поток перегружен.Я также заметил, что ваша реализация с plt.hist(..., bins=256, range=(0.0, 16384))
примерно в 7 раз быстрее, чем с plt.hist(..., bins=list_of_bins)
! Не меняйте его ;).