Как ускорить построение графиков rgb в реальном времени на python

#python #performance #matplotlib #animation

Вопрос:

Меня попросили закодировать сумасшедшую идею. Мы хотим иметь экран, на котором будут отображаться все возможные изображения, которые он может создать, используя формат RGB и каждый доступный пиксель. Конечно, мы понимаем, что для выполнения такого кода потребуются миллионы лет (даже больше), но все же это то, что мы хотим сделать. У меня есть кое-что, что работает довольно хорошо, единственная проблема в том, что построение изображения происходит очень медленно…

В настоящее время я использую matplotlib, я готов попробовать что-то еще для ускорения, но должен признать, что я не знаком с другими инструментами, и мне часто трудно следовать документации (например, с глумпи).

Вот мой код:

 import numpy as np
import matplotlib.pyplot as plt
import time
import itertools
from PyQt5 import QtWidgets 
plt.ion()
import ctypes

# Getting the screensize
user32 = ctypes.windll.user32
screensize = user32.GetSystemMetrics(0), user32.GetSystemMetrics(1)


fig = plt.figure()

#To remove toolbar from figure
try:
     win = fig.canvas.manager.window
except AttributeError:
     win = fig.canvas.window()          
toolbar = win.findChild(QtWidgets.QToolBar) #same
toolbar.setVisible(False)                   #same
fig.canvas.manager.full_screen_toggle()     #To display the result in fullscreen

# To remove white space between axis and figure
ax = fig.add_axes([0,0,1,1]) #position: left, bottom, width, height
ax.set_axis_off()
pixel_x, pixel_y = screensize[::-1]

initial_image = np.zeros((pixel_x,pixel_y,3)).astype(np.uint8)
image = ax.imshow(initial_image, animated = True)
ch = ax.get_children()
fig.canvas.draw()

# itertools allow me to iterate over all possible image pretty efficiently
for x in itertools.product(np.arange(0,256,steps,dtype = "uint8"),repeat = pixel_y*pixel_x*3):

    x = np.fromiter(x,dtype = np.uint8) # I read that np.fromiter is quicker than np.array
    x = x.reshape(pixel_x,pixel_y,3)    # Putting everything back in an image appropriate 
                                        # format
   
    ch[-2].set_data(x)       # I try not to redraw the entire figure by only changing this
    ax.draw_artist(ch[-2])   # I redraw the artist
    fig.canvas.blit(fig.bbox)# Read online that this was quicker
    fig.canvas.flush_events()# same...



    j =1
    if j % 10 == 0:
        print(f'{j/(time.time() - t):.2f} iterations per seconde')
 

Итак, я обнаружил, что итерации itertools.product выполняются относительно быстро : эта часть может выполняться со скоростью 40-45 итераций в секунду. Преобразование вывода itertools в массив numpy с использованием np.fromiter numpy значительно замедляет процесс : он составляет примерно 25 итераций в секунду. Изменение формы результата в формате, соответствующем изображению, похоже, ничего не замедляет. Наконец, когда я включаю все построение графика set_data методом и все остальное, оно замедляется до 5-6 взаимодействий в секунду, что действительно медленно…

Если у вас есть какое-либо представление о том, как я мог бы сделать этот процесс более эффективным, пожалуйста, дайте мне знать! Это будет действительно оценено по достоинству. Спасибо

Комментарии:

1. Вы пробовали заглянуть в OpenCV? Он использует массивы numpy для представления изображений, так что это может быть довольно быстро

2. Ты думал о том, как это глупо? При скорости 60 кадров в секунду потребуется 3 дня, чтобы просмотреть все возможные цвета для ОДНОГО ПИКСЕЛЯ. В чем смысл?

3. Я так и сделал, но, насколько я понимаю, OpenCV больше подходит для обработки изображений, чем для чего-либо еще. В каждом примере, который я нашел, они фактически используют matplotlib для отображения результата.

4. @TimRoberts Да, я знаю, что это глупо в данном контексте. Но я вполне мог бы захотеть сделать что-то очень похожее в ближайшем будущем (отображать изображение RGB в режиме реального времени с высокой частотой кадров в секунду), и я чувствую, что это должно быть выполнимо для достижения лучшего результата.

5. Это безумие! Предполагая, что шаги оптимистично равны 128, а экран всего 400×300 (маленький), количество итераций составит около 10**83177. Это больше, чем количество атомов в наблюдаемой Вселенной! Число на самом деле настолько велико, что его даже нельзя напечатать в этом комментарии. Предполагая, что вы можете использовать машину в миллиард миллиардов миллиардов миллиардов миллиардов раз быстрее, чем все суперкомпьютеры в мире, используемые вместе, простое повторение всех возможных решений заняло бы гораздо больше времени, чем возраст Вселенной… Какой смысл делать такое, кроме как тратить энергию впустую?

Ответ №1:

  1. используйте matplotlib с осторожностью
  2. Если кто-то использует matplotlib для визуализации, прекратите чтение
  3. Для показа видео используйте OpenCV. Для отображения изображения используйте opencv/Pillow, если вы не хотите показывать что-либо дополнительное рядом с изображением, например оси
  4. Потратьте больше времени на размышления о том, что вы делаете, потому что то, что вы делаете, действительно не стоит того, чтобы делать.

Реализация OPENCV (333-500 кадров в секунду)

 import numpy as np
import time
import itertools

import cv2


steps = 1
pixel_x, pixel_y = [3, 3]
initial_image = np.zeros((pixel_x, pixel_y, 3)).astype(np.uint8)

import time

for x in itertools.product(
    np.arange(0, 256, steps, dtype="uint8"), repeat=pixel_y * pixel_x * 3
):
    s = time.time()
    x = np.fromiter(x, dtype=np.uint8)
    x = x.reshape(pixel_x, pixel_y, 3)
    cv2.imshow("Frame", x)

    if cv2.waitKey(1) amp; 0xFF == ord("q"):

        break
    print("FPS: ", 1 / (time.time() - s))
cv2.destroyAllWindows()
 

Комментарии:

1. Если кто-то использует matplotlib для визуализации, прекратите читать -это отличный совет. Matplotlib делает много вещей хорошо, но он также делает много вещей плохо, которые хорошо выполняются в других пакетах.

2. @Мартин, спасибо тебе! Я знаю, что этого не стоит делать. Меня попросили закодировать это для художественной инсталляции, где они хотят показать, как такое сумасшедшее число на самом деле может быть представлено простым кодом. Мне было в основном любопытно, как эффективно отображать RGB-изображение.

3. @Пьеро-Батист, в этом есть смысл. Тогда, если позволите, я дам вам совет… Вам действительно не нужно превышать ~60 кадров в секунду в качестве ограничения для его глаз. Все, что выше этого FPS, не будет иметь никакого значения(более того, ограничение при фиксированном более низком FPS сделает FPS стабильным). Поэтому вместо повышения производительности я предлагаю уменьшить цветовую гамму и, возможно, изобрести лучший визуальный алгоритм для вашей цели. Это чертовски скучно, так как кажется, что один пиксель просто мигает вечно

4. @Martin Если вы когда-либо использовали дисплей с частотой 144 Гц по сравнению с дисплеем с частотой 60 Гц, есть очень заметная разница.

5. @AKX По совершенно другим причинам. 1) проверьте частоту кадров в секунду, 2) Вы можете поэкспериментировать самостоятельно -> создайте мигающий светодиод и продолжайте уменьшать период, вы увидите эффект сглаживания около 40 кадров в секунду, когда светодиод перестанет «мигать». 3) Переменный ток имеет частоту 50 Гц, но вы не видите, как мигают домашние и уличные фонари… 4) Фильмы даже ниже 60 кадров в секунду и около 25 кадров в секунду… работает просто отлично…