Как использовать многопроцессорность Python для подготовки изображений для pygame

Я делаю приложение для слайд-шоу с эффектом панорамирования и масштабирования. Я использую пигейм.

Таким образом, основной дисплей работает со скоростью 30 кадров в секунду в реальном времени, и я не хочу, чтобы он зависал, когда ему нужно загрузить новое изображение — это занимает более 1/30 секунды.

Поэтому я хотел использовать какой-то параллельный процесс для подготовки изображений и подачи в основной процесс этих объектов, которые являются экземплярами класса.

Я пробовал использовать threading и многопроцессорность. Многопоточность «работает», но все еще дергается (я виню python) - все это тормозит, когда поток занят! Таким образом, код работал, но он не соответствовал цели обеспечения непрерывного плавного отображения.

Но многопроцессорный segfaults (pygame parachute), как только я вызываю метод на полученном подготовленном образе из основного процесса. Я пробовал связь по каналам и очередям - оба приводят к одной и той же проблеме. Метод работает до тех пор, пока не вызовет

sized = pygame.transform.scale(self.image, newsize )

Тогда есть segfault. Этот класс не имеет никаких зависимостей от основного процесса.

Pygame просто не любит многопроцессорность? Есть ли другой способ, который совместим? Есть ли способ «хороших» вторичных потоков, которые могут остановить выполнение метода потоков?

Любая помощь очень ценится. Рад опубликовать больше кода, просто спросите в комментариях, но не хотел выгружать здесь большой список, если в этом нет необходимости.

Заранее спасибо!

РЕДАКТИРОВАТЬ

Это так коротко, как я мог сделать это. В конструкторе внизу необходимо указать три пути к файлам jpeg.

#!/usr/bin/env python2
import pygame
import sys
import time
import re
import os
import pickle
from random import randrange, shuffle
from multiprocessing import Process, Pipe
import Queue


class Img:
    """The image objects I need to pass around"""
    def __init__(self, filename=None):
        image = pygame.image.load(filename).convert()
        self.image = pygame.transform.scale(image, (640,480))

    def getSurface(self):
        """Get a surface, blit our image onto it in right place."""
        surface = pygame.Surface((640,480))
        # xxx this next command fails
        sized = pygame.transform.scale(self.image, (640,480))
        surface.blit(sized, (0,0))
        return surface

class Floaty:
    """demo"""
    def __init__(self, fileList):
        self.fileList = fileList
        pygame.init()
        self.screen = pygame.display.set_mode((640,480))

        # open the first image to get it going
        self.img = Img(self.fileList.pop())

        # Set up parallel process for opening images
        self.parent_conn, child_conn = Pipe()
        self.feeder = Process(target=asyncPrep, args=(child_conn,))
        self.feeder.start()

    def draw(self):
        """draw the image"""
        # collect image ready-prepared by other process
        if self.parent_conn.poll():
            self.img = self.parent_conn.recv()
            print ("received ", self.img)

        # request new image
        self.parent_conn.send(self.fileList.pop())

        self.screen.blit(self.img.getSurface(), (0, 0))
        pygame.display.flip()

def asyncPrep(conn):
    """load up the files"""

    while True:
        if conn.poll(1):
            filename = conn.recv()
            print ("doing ", filename)
            img = Img(filename)
            conn.send(img)


if __name__ == '__main__':
    fileList = ['/path/to/a.jpg', 'path/to/b.jpg', 'path/to/c.jpg']
    f = Floaty(fileList)
    clock = pygame.time.Clock()
    while 1:
        f.draw()
        clock.tick(4);

Когда я запускаю это (Python 2.7.6), я получаю:

('doing ', '/path/to/a.jpg')
('received ', <__main__.Img instance at 0x7f2dbde2ce60>)
('doing ', '/path/to/b.jpg')
Fatal Python error: (pygame parachute) Segmentation Fault
zsh: abort (core dumped)

person artfulrobot    schedule 02.03.2015    source источник
comment
Короткий компилируемый пример поможет (если это возможно). В любом случае: как вы отправили изображение, можете ли вы как-то сериализовать его, а затем отправить? Вероятно, это проблема с памятью (потоки разделяют память, а многопроцессорность - нет).   -  person isset    schedule 03.03.2015
comment
@isset, спасибо, пожалуйста, посмотрите отредактированный вопрос. Я также пытался травить вещь, но получаю сообщение об ошибке «Поверхность не может быть травлена».   -  person artfulrobot    schedule 03.03.2015
comment
Эй, я наткнулся на очень похожее на вас решение, когда вы загружаете изображение в строковый буфер в другом процессе, а затем отправляете этот буфер в свой основной процесс, где находится ваш контекст openGL. Я думаю, именно поэтому у вас раньше возникал сбой, вы пытались получить доступ к контексту openGL извне основного процесса. Вы говорите, что это решение работает для вас, но я все еще получаю небольшое заикание при вызове pygame.image.frombuffer() в основном процессе, так как это блокирующий вызов для копирования буфера в видеопамять. Любопытно, видели ли вы заикание и т. д.? Что-то особенное, что я упускаю? Спасибо   -  person Ben P    schedule 04.03.2015
comment
Итак, что я сделал, так это использовал параллельный процесс, чтобы открыть изображение и уменьшить его до максимального размера, необходимого родительскому процессу. Таким образом, мой буфер относительно мал и быстро загружается.   -  person artfulrobot    schedule 04.03.2015


Ответы (1)


Я решил эту проблему с помощью многопроцессорной обработки, заставив класс Img загрузить изображение в строковый буфер, сохраненный в свойстве, а затем добавив метод postReceive, который загружает его в поверхность, хранящуюся в свойстве изображения.

Метод postReceive вызывается родительским процессом после получения объекта Img от дочернего процесса.

Поэтому объект, созданный дочерним элементом, не привязан ни к чему pygame-y.

    self.imageBuffer = pygame.image.tostring(
      pygame.transform.scale(image, (640,480)),
      'RGB')

Затем новый метод в Img просто:

def: postReceive(self):
    self.image = pygame.image.frombuffer( self.imagebuffer, 'RGB' )

Добавьте сюда вызов:

    # collect image ready-prepared by other process
    if self.parent_conn.poll():
        self.img = self.parent_conn.recv().postReceive()
        print ("received ", self.img)
person artfulrobot    schedule 03.03.2015