Как преобразовать этот массив (100, 100) numpy в оттенок серого в pygame?

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

Используя функцию matplotlib imshow, я получаю следующий патч. python-generated Gabor patch displayed with matplotlib's imshow

Пока цвет отличается, я подозреваю, что это связано с тем, как matplotlib отображает числовые значения. По сути, это изображение представляет собой 2D, 100-на-100 пиксельный массив, содержащий значения от -1.0 до 1.0 (включительно). Если кто-то хочет попробовать манипулировать рассматриваемым массивом, я сохранил его как объект рассола здесь.

Мой вопрос следующий:. Как передать этот массив на поверхность pygame, гарантируя выполнение следующих условий?

  • Раскраска преобразуется в оттенки серого (c.f.: последнее изображение в первой ссылке)
  • Решение должно использовать версию pygame 1.9.1release. По какой-то необъяснимой причине я не могу найти способ установить 1.9.2 на мою ОС (Ubuntu 13.04). Кажется, что нет PPA, и pygame, очевидно, не относится к PIP.

Спасибо вам большое заблаговременно, и, пожалуйста, дайте мне знать, если я могу предоставить дополнительную информацию!

Edit

Что касается решения @Veedrac (что очень похоже на мое собственное), вот как выглядит мой патч при использовании цветовой шкалы оттенков серого в matplotlib imshow. Это то, что мне хотелось:

from matplotlib.pyplot import *
import matplotlib.cm as cm

figure()
imshow(g, cm=cm.Greys_r)
show()

enter image description here

Ответ 1

import numpy
import pickle
import pygame

surface = pygame.Surface((100, 100))

Получить пиксели, конвертировать в RGBA. Использование Джо Кингтона напоминает, что данные варьируются от -1 до 1:

base = (pickle.load(open("g.pickle"))+1)/2 * 255
base = base[..., numpy.newaxis].repeat(4, -1).astype("uint8")

Скопируйте данные через

numpy_surface = numpy.frombuffer(surface.get_buffer())
numpy_surface[...] = numpy.frombuffer(base)
del numpy_surface

Показывать с помощью:

screen = pygame.display.set_mode((100, 100))
screen.blit(surface, (0, 0))
pygame.display.flip()

и вы получите

enter image description here


И упрощен, еще раз благодаря вводу Джо Кингтона, используя make_surface:

import numpy
import pickle
import pygame

base = (pickle.load(open("g.pickle"))+1) * 128
base = base[..., None].repeat(3, -1).astype("uint8")

surface = pygame.surfarray.make_surface(base)

screen = pygame.display.set_mode((100, 100))
screen.blit(surface, (0, 0))
pygame.display.flip()

base[..., None] обычно пишется base[..., numpy.newaxis], но, видя, что это единственный экземпляр numpy, я просто "расширил константу", чтобы не нуждался в numpy. Однако это не сработало, так как код ломается, если вы не импортируете numpy с помощью IndexError: bytes to write exceed buffer size. Спасибо, numpy.

... означает "всю всю ось до этой точки", поэтому вы можете заменить [3:2], [:, 3:2] и [:, :, :, 3:2] на [..., 3:2]. Фактически, ... был введен в Python именно по этой причине.

None, или numpy.newaxis, срезает новую ось (duh). Это, например, преобразует [a, b, c] в [[a], [b], [c]]. Это необходимо, потому что мы тогда repeat вдоль этой новой оси.

В принципе, глядя на одну строку, мы имеем

114, 202, 143, ...

и мы хотим

[114, 114, 114], [202, 202, 202], [143, 143, 143], ...

поэтому наш [..., None] довел нас до

[114], [202], [143], ...

и мы просто repeat 3 раз на оси -1. Ось -1 - это, конечно, последняя ось, которая является numpy.newaxis.