Как воспроизвести видео из стека изображений в массиве numpy - PullRequest
0 голосов
/ 05 августа 2020

Я пишу kivy GUI для некоторого анализа данных стека изображений (например, видео), которые я читаю как массив numpy. Мне нужно иметь возможность воспроизводить видео, то есть была бы кнопка воспроизведения, которая запускает от-l oop до go через все изображения стека и обновляет текстуру Image для каждого изображения из стека.

Я не могу понять, как сделать последнюю часть.

Я могу сгенерировать виджет Image и назначить ему текстуру из кадра np.array вот так (a - это np.array, содержащий стек изображений):

img = np.zeros((128, 128, 3))
img[:, :, 0] = a[0]   # I have to do it like this
img[:, :, 1] = a[0]   # b/c apparently it is not possible
img[:, :, 2] = a[0]   # to assign a grayscale image from a single 2D array
h, w, _ = img.shape
texture = Texture.create(size=(w, h))
texture.blit_buffer(img.flatten(), colorfmt='rgb', bufferfmt='ubyte')
imgFrame = Image(size=(w, h), texture=texture)

Этот фрагмент кода работает для создания виджета Image и отображения первого кадра a массив. Однако мне не удалось обновить текстуру Image, если она уже сгенерирована заранее.

Следующий код - это своего рода рабочий пример. Теперь я сначала создаю виджет Image, а потом назначаю текстуру. См. Комментарии в коде - обновление текстуры работает только без for-l oop, а также мне нужно переместить / изменить размер окна, чтобы вызвать перерисовку текстуры.

import numpy as np

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.graphics.texture import Texture
from kivy.uix.image import Image
from kivy.properties import ObjectProperty


Builder.load_string('''
<BoxyBox>:
  w_img: img1

  FileChooserListView:
    id: filechooser
    rootpath: '/path/to/binary/data/'
    on_submit: root.readInBin(self.selection[0])
  ImgFrame:
    id: img1
''')

class BoxyBox(BoxLayout):

  w_img = ObjectProperty(None)

  def readInBin(self, fileName):
    xRes      = 128
    yRes      = 128
    nFrames   = 10000
    precision = np.uint16

    # read in binary data:
    f = open(fileName, 'r')
    a = np.fromfile(f, dtype=precision).reshape(nFrames, yRes, xRes)
    f.close()

    img = np.zeros((128, 128, 3))

    for i in range(a.shape[0]):   # if i remove the for-loop, and just use i = 0
    # i = 0                       # then this block works
      img[:, :, 0] = a[i]         # but not with the loop
      img[:, :, 1] = a[i]
      img[:, :, 2] = a[i]

      img = np.flipud(np.uint8(255*img/np.max(img)))


      # any of the following works to update the texture ONCE, but not in the loop:
      # also, it does not work 100% as intended -- it only redraws after moving the window

      # option 1:
      self.w_img.texture.blit_buffer(img.flatten(), colorfmt='rgb', bufferfmt='ubyte')

      # option 2:
      #self.w_img.cbuffer = img.flatten()
      #self.w_img.populate_texture(self.w_img.texture)

    return a


class ImgFrame(Image):

  # these two functions are copied from
  # https://kivy.org/doc/stable/api-kivy.graphics.texture.html#reloading-the-texture
  def __init__(self, **kwargs):
    super(ImgFrame, self).__init__(**kwargs)
    self.texture = Texture.create(size=(128, 128), colorfmt='rgb', bufferfmt='ubyte')
    self.texture.add_reload_observer(self.populate_texture)

    # and load the data now.
    self.cbuffer = np.zeros((128, 128, 3), dtype = np.int8).flatten()
    self.populate_texture(self.texture)

  def populate_texture(self, texture):
    texture.blit_buffer(self.cbuffer)

class TextureTestApp(App):
  def build(self):
    return BoxyBox()

if __name__ == '__main__':
  TextureTestApp().run()

( Очевидно, программа не будет работать в конечном итоге так, но в целях тестирования я хотел начать воспроизведение видео сразу после загрузки его из FileChooser.)

Я не уверен насчет add_reload_observer метод и документация kivy не объясняют его должным образом. Я также нашел ask_update (), но не смог использовать его в качестве намерения.

Может быть, независимо от моего примера, мой вопрос: Как воспроизвести видео из стека изображений в массиве numpy? , или, если я правильно понял, что мне нужно рисовать кадры через текстуру, тогда: Как обновить и перерисовать текстуру виджета изнутри функции?

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

Я понял, что проблема действительно связана с for-l oop. Как объяснено здесь и здесь , l oop блокирует основной поток. Тогда решение должно заключаться в использовании часов kivy или потоковой передачи, чтобы позаботиться об обновлении текстуры (или любого другого свойства kivy) изнутри l oop.

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

Я добавил текстуру как tex = ObjectProperty(None) в виджет root, инициализировал ее как Kivy Texture в своей функции __init__ и назначил текстуру Child- Image как texture: root.tex в кв.

Использование часов для планирования функции обновления в принципе работает, но также я вижу обновляющуюся текстуру только при перемещении окна. Так что мне все еще не хватает команды для запуска перерисовки текстуры .

...