Массив numpy отображается неправильно с pyglet

У меня проблемы с отображением массива numpy с помощью pyglet. Я нашел очень похожую тему (как отобразить массив numpy с помощью pyglet ?), который я использовал. Я хочу отобразить массив в оттенках серого, но pyglet отображает его в цветах, см. изображение: https://i.stack.imgur.com/pL6Yr.jpg

def create(self, X,Y):

    IMG = random((X,Y)) * 255
    self.IMG = dstack((IMG,IMG,IMG))

    return self.IMG

def image(self):

    self.img_data = self.create(X,Y).data.__str__()
    self.image = pyglet.image.ImageData(X,Y, 'RGB', self.img_data, pitch = -X*3)

    return self.image

Если вместо этого я сохраняю и загружаю массив, он работает (но ужасно медленнее):

def image(self):

    self.im_save=scipy.misc.toimage(self.create(X,Y),cmin=0, cmax=255)
    self.im_save.save('outfile.png')
    self.image = pyglet.image.load('outfile.png')

    return self.image

И я получаю то, что хотел:

i.stack.imgur.com/FCY1v.jpg

Я не могу найти ошибку в первом примере кода :(

ИЗМЕНИТЬ:

Большое спасибо за ваши ответы. С подсказкой от Bago я заставил код работать :) И действительно, предложение nfirvine разумно, так как я хочу отображать матрицу только в оттенках серого.

def create(self, X,Y):

        self.IMG = (random((X,Y)) * 255).astype('uint8')

        return self.IMG


def image(self):

        self.img_data = self.create(X,Y).data.__str__()
        self.image = pyglet.image.ImageData(X,Y, 'L', self.img_data)

        return self.image

person T-Lo    schedule 27.01.2012    source источник
comment
В методе create X и Y являются локальными переменными, но в изображении они являются глобальными, которые не определены в вашем листинге.   -  person nfirvine    schedule 28.01.2012
comment
Ваши методы не должны устанавливать переменную экземпляра, а затем возвращать ее. Если он что-то возвращает, это не должна быть переменная экземпляра, так как эта переменная в любом случае доступна через self.   -  person nfirvine    schedule 28.01.2012


Ответы (4)


Я думаю, что pyglet ожидает uint8, вы пробовали?

IMG = ( random((X,Y)) * 255 ).astype('uint8')
person Bi Rico    schedule 27.01.2012

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

Я могу подтвердить, что ранее принятый ответ НЕ ВЕРЕН.

Это кажется правильным, потому что вы используете изображения в оттенках серого. Но если бы вы использовали цветное изображение (например, RGBA) и обнулили бы каналы GBA, вы бы это обнаружили, потому что в вашей текстуре по-прежнему проявлялись бы зеленый и синий цвета.

Используя __str__(), вы фактически отправляете мусор, а не значения, которые вам действительно нужны.

Я буду использовать свой код, чтобы продемонстрировать это.

import numpy
import pyglet
from pyglet.gl import *

# the size of our texture
dimensions = (16, 16)

# we need RGBA textures
# which has 4 channels
format_size = 4
bytes_per_channel = 1

# populate our array with some random data
data = numpy.random.random_integers(
    low = 0,
    high = 1,
    size = (dimensions[ 0 ] * dimensions[ 1 ], format_size)
    )

# convert any 1's to 255
data *= 255
        
# set the GB channels (from RGBA) to 0
data[ :, 1:-1 ] = 0
        
# ensure alpha is always 255
data[ :, 3 ] = 255

# we need to flatten the array
data.shape = -1

Используя ответ выше, вы должны сделать следующее

НЕ ДЕЛАЙТЕ ЭТОГО!

tex_data = data.astype('uint8').__str__()

Если вы попробуете код, вы получите все цвета, а не только красный!

Сделайте это вместо этого!

Правильный способ - преобразовать в ctype GLubites.

# convert to GLubytes
tex_data = (GLubyte * data.size)( *data.astype('uint8') )

Затем вы можете передать это в свою текстуру.

# create an image
# pitch is 'texture width * number of channels per element * per channel size in bytes'
return pyglet.image.ImageData(
    dimensions[ 0 ],
    dimensions[ 1 ],
    "RGBA",
    tex_data,
    pitch = dimensions[ 1 ] * format_size * bytes_per_channel
    )
person Rebs    schedule 14.08.2012
comment
Вы используете Cython или что-то в этом роде? Я не могу получить что-либо похожее на синтаксис (GLubyte * data.size)( *data.astype('uint8') ) для запуска в Python 2.7. - person Chriszuma; 07.12.2012
comment
Я продолжаю получать эту ошибку, и я понятия не имею, что это значит: TypeError: only length-1 arrays can be converted to Python scalars - person Chriszuma; 07.12.2012
comment
Вы должны убедиться, что у вас есть все команды GL, импортированные из pyglet.gl import *, тогда у вас будут типы и функции GL. Я добавил импорт в свой пост. - person Rebs; 13.12.2012
comment
Лично я перестал использовать модуль изображений Pyglet. Я нахожу это сложным и чрезмерно абстрактным. Я реализовал свой собственный модуль текстуры здесь: github.com/adamlwgriffiths/ PyGLy/blob/master/pygly/texture.py, а пример можно найти здесь: github.com/adamlwgriffiths/PyGLy/blob/master/pygly/examples/ - person Rebs; 13.12.2012
comment
Как продолжение. PyOpenGL позволяет напрямую отправлять массивы numpy большинству функций. Проблема с использованием Ctypes заключается в том, что вы не можете смешивать типы значений в данных, т.е. int16, float32 и т. д. Преобразование в ctypes обеспечивает один тип. Функции PyOpenGL с поддержкой numpy могут правильно обрабатывать эти случаи. - person Rebs; 27.02.2013

Я играл с этим, чтобы получить динамическое представление массива numpy. Ответ @Rebs сработал, но стал неэффективным, когда я хотел обновлять изображение в каждом кадре. После профилирования я обнаружил, что приведение значений ctypes было шагом, ограничивающим скорость, и его можно было бы ускорить, вместо этого используя метод from_buffer объекта типа ctype для разделения базовых битов в памяти между массивом numpy и массивом GLubyte.

Вот класс, который будет отображать массив 2d numpy и изображение pyglet, используя для этого цветовые карты matplotlib. Если у вас есть пустой массив, создайте вокруг него оболочку ArrayView, а затем обновите и разбейте его в методе окна on_draw:

my_arr = np.random.random((nx, ny))
arr_img = ArrayImage(my_arr)

@window.event
def on_draw():
    arr_img.update()
    arr_img.image.blit(x, y)

Полная реализация класса:

import numpy as np
import matplotlib.cm as cmaps
from matplotlib.colors import Normalize
import pyglet
import pyglet.gl

class ArrayImage:
    """Dynamic pyglet image of a 2d numpy array using matplotlib colormaps."""
    def __init__(self, array, cmap=cmaps.viridis, norm=None, rescale=True):
        self.array = array
        self.cmap = cmap
        if norm is None:
            norm = Normalize()
        self.norm = norm
        self.rescale = rescale

        self._array_normed = np.zeros(array.shape+(4,), dtype=np.uint8)
        # this line below was the bottleneck...
        # we have removed it by setting the _tex_data array to share the buffer
        # of the normalised data _array_normed
        # self._tex_data = (pyglet.gl.GLubyte * self._array_normed_data.size)( *self._array_normed_data )
        self._tex_data = (pyglet.gl.GLubyte * self._array_normed.size).from_buffer(self._array_normed)
        self._update_array()

        format_size = 4
        bytes_per_channel = 1
        self.pitch = array.shape[1] * format_size * bytes_per_channel
        self.image = pyglet.image.ImageData(array.shape[0], array.shape[1], "RGBA", self._tex_data)
        self._update_image()

    def set_array(self, data):
        self.array = data
        self.update()

    def _update_array(self):
        if self.rescale:
            self.norm.autoscale(self.array)
        self._array_normed[:] = self.cmap(self.norm(self.array), bytes=True)
        # don't need the below any more as _tex_data points to _array_normed memory
        # self._tex_data[:] = self._array_normed

    def _update_image(self):
        self.image.set_data("RGBA", self.pitch, self._tex_data)

    def update(self):
        self._update_array()
        self._update_image()

person James P    schedule 29.01.2019
comment
Что послужило фактором ускорения? 2 раза? - person Daniel Scott; 18.08.2020

Согласно документации Pyglet на pyglet.image, если вы хотите оттенки серого, вы должны использовать код формата 'L', а не 'RGB', так как у вас есть только один канал.

person nfirvine    schedule 27.01.2012