#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:
- используйте matplotlib с осторожностью
- Если кто-то использует matplotlib для визуализации, прекратите чтение
- Для показа видео используйте OpenCV. Для отображения изображения используйте opencv/Pillow, если вы не хотите показывать что-либо дополнительное рядом с изображением, например оси
- Потратьте больше времени на размышления о том, что вы делаете, потому что то, что вы делаете, действительно не стоит того, чтобы делать.
Реализация 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 кадров в секунду… работает просто отлично…