Ускорение функции скриншота - Python
Мне нужно, чтобы функция скриншота была максимально быстрой, и теперь каждый вызов функции занимает около 0,2 с.
Это функция:
def get_screenshot(self, width, height):
image = self.screen_capture.grab(self.monitor)
image = Image.frombuffer('RGB', image.size, image.bgra, 'raw', 'BGRX')
image = image.resize((int(width), int(height)), Image.BICUBIC) # Resize to the size of 0.8 from original picture
image = np.array(image)
image = np.swapaxes(image, 0, 1)
# This code below supposed to replace each black color ([0,0,0]) to the color of [0,0,1]
# r1,g1,b1 = [0,0,0] and r2,g2,b2 = [0,0,1]
red, green, blue = image[:, :, 0], image[:, :, 1], image[:, :, 2]
mask = (red == r1) & (green == g1) & (blue == b1)
image[:, :, :3][mask] = [r2, g2, b2]
return image
Вы заметили какие-либо изменения, которые я могу сделать, чтобы сделать функцию быстрее?
Изменить: некоторые детали, которые я забыл упомянуть:
Мои размеры экрана 1920*1080
Эта функция является частью проекта прямой трансляции, над которым я сейчас работаю. Решение, предложенное Карло ниже, в этом случае не подходит, поскольку удаленный компьютер не будет синхронизирован с экраном нашего компьютера.
2 ответа
Поскольку ваш код неполон, я могу только догадываться, что может помочь, поэтому вот несколько мыслей...
Я начал с изображения 1200x1200, потому что я не знаю, насколько велик ваш, и уменьшил его в 0,8 раза до 960x960 из-за комментария в вашем коде.
Мои идеи по его ускорению основаны либо на использовании другого метода интерполяции, либо на использовании OpenCV, который является высоко оптимизированным кодом SIMD. Либо, либо и то, и другое может быть уместным, но, поскольку я не знаю, как выглядят ваши изображения, только вы можете сказать.
Итак, начнем с PIL resize()
и разные методы интерполяции:
# Open image with PIL
i = Image.open('start.png').convert('RGB')
In [91]: %timeit s = i.resize((960,960), Image.BICUBIC)
16.2 ms ± 28 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [92]: %timeit s = i.resize((960,960), Image.BILINEAR)
10.9 ms ± 87.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [93]: %timeit s = i.resize((960,960), Image.NEAREST)
440 µs ± 10.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Таким образом, BILINEAR в 1,5 раза быстрее, чем BICUBIC, и настоящий победитель здесь - БЛИЖАЙШИЙ в 32 раза быстрее.
Теперь, преобразовав массив Numpy (как вы делаете в любом случае) и используя высокооптимизированный код OpenCV SIMD для изменения размера:
# Now make into Numpy array for OpenCV methods
n = np.array(i)
In [100]: %timeit s = cv2.resize(n, (960,960), interpolation = cv2.INTER_CUBIC)
806 µs ± 9.81 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [101]: %timeit s = cv2.resize(n, (960,960), interpolation = cv2.INTER_LINEAR)
3.69 ms ± 29 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [102]: %timeit s = cv2.resize(n, (960,960), interpolation = cv2.INTER_AREA)
12.3 ms ± 136 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [103]: %timeit s = cv2.resize(n, (960,960), interpolation = cv2.INTER_NEAREST)
692 µs ± 448 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
И победитель здесь выглядит как INTER_CUBIC, который в 20 раз быстрее, чем PIL resize()
,
Пожалуйста, попробуйте их все и посмотрите, что работает для вас! Просто удалите магию Питона %timeit
в начале строки и запустить то, что осталось.
Это всего лишь пример того, что я имею в виду. Если это решит проблему, дайте мне знать.
Вы можете создать две разные темы. Один, который делает снимок экрана, другой, который детализирует экран позже. Оба добавляют результат в список. Это улучшит скорость функции get_screenshot. Но для его разработки вам понадобится время, необходимое для выполнения функции.
import threading
#import your stuff
class your_class(object):
def __init__(self):
self.images = list()
self.elaborated_images = list()
threading.Thread(name="Take_Screen", target=self.get_screenshot, args=(width, height))
threading.Thread(name="Elaborate_Screen", target=self.elaborate_screenshot)
def get_screenshot(self, width, height):
while True:
images.append(self.screen_capture.grab(self.monitor))
def elaborate_screenshot(self):
while True:
image = self.images[0]
image = Image.frombuffer('RGB', image.size, image.bgra, 'raw', 'BGRX')
image = image.resize((int(width), int(height)), Image.BICUBIC) # Resize to the size of 0.8 from original picture
image = np.array(image)
image = np.swapaxes(image, 0, 1)
# This code below supposed to replace each black color ([0,0,0]) to the color of [0,0,1]
# r1,g1,b1 = [0,0,0] and r2,g2,b2 = [0,0,1]
red, green, blue = image[:, :, 0], image[:, :, 1], image[:, :, 2]
mask = (red == r1) & (green == g1) & (blue == b1)
image[:, :, :3][mask] = [r2, g2, b2]
del self.images[0]
self.elaborated_images.append(image)
your_class()
Поскольку у меня нет вашего полного кода, я не могу построить его как можно лучше.