[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Re: [pygame] pygame.pixelcopy.array_to_surface(Dest, Src) not working



On 24 December 2016 at 12:08, Ian Mallett <ian@xxxxxxxxxxxxxx> wrote:

> Now that I'm reading more carefully, it looks like you're creating a
> palettalized RGB surface (8-bit indices to 24-bit color).
> Offhand, I also would expect referencing the indices into a 2D array to work.
> I'm tempted to conclude a possible bug, or at least an insufficiently helpful exception.

Now I have found out what the real issue is, and it is funny,
I remember now that I new something about it, but just forgot it.
So the thing is that surfarray, pixelarray are designed not compliant
with numpy axes order, namely pixelcopy.array_to_surface()
expects the numpy array in axes order [width, height] or [x,y]
whereas numpy itself and actually many imaging libraries uses [y,x],
e.g. openCV reads an image as numpy array in [height, width] format.
So to make this method work I must transpose the source array first,
e.g. this works:

def put_arr2(Dest, Src):                   
    pygame.pixelcopy.array_to_surface(Dest, Src.T)

The problem is however, transpose adds overhead, ca. 10-15% performance hit,
it is hard to meause, but it is noticable, and if used in a highly dynamical app,
this is not good to transpose all arrays in each loop cycle.
On the other hand the "trick"  with get_buffer(), how you call it,
works directly without transposing, this means that this method
is numpy-compliant, and it is good so.
Now I realise it is not a bug, but a feature, but I would say this is frustrating
feature. Why it was made so, probably they want to choose [x,y] since it is
more traditional notation. Anyway, it comes out not so good. 

Good thing is that get_buffer() trick works, but it is hardly discoverable, I have
found it by rule of thumb and what is worse, this causes confusion
by learning the library and it is quite common question by users
"how I show up an array in pygame". Or "why the array shows up rotated?"

I mean, I am ready to deal with different axes order in code, but I
dont want to make reduntant transpose operations in a rendering loop.
I could rewrite all numpy logic in [x,y] notation but would it be the
right way? I am not impying this is correct or not, I think here
one should look into what typically a GPU oriented frontends do
and how they map arrays into what is shown on the screen.
Also since get_buffer() trick works then comes the frustration,
how actually both methods work internally?

> A few questions:
> ... merely update a surface repeatedly?

Yes exactly. I have whole apps written in numpy and some of them are highly interactive.

> --Are you particularly tied to pygame.pixelcopy?

No I just want a convenient method, which makes an array "blittable", is numpy-compliant
and is most effective. Assuming that the programmer knows what he is doing.

> --Are you tied to palettal color?

Yes in many cases. I need also palettal rendering for higher
depths. Ideally graphical frontend should support also 16 bit and 32 bit
palettal rendering. And ideally the final colorisation should happen on GPU
namely I send an integer array to video system and video system
must resolve the values to the end format and ideally
this should be done in most effective way.
But this is not what Pygame can do.

So currently for higher depths I use numpy to convert integers
to composite colors in each loop cycle. So Pygame provides only 8 bit
paletted, but actually for many applications it is good enough.
Even if I'll write commercial arcade game, it would not suffer
much from being 8 bit color. Most of the the time it is not
about smooth gradients, it is about action and fun.

> The copy buffer trick you're using looks a little suspect to me.

But since there is no other method which is comliant with numpy order,
and as I see now, it works as fast as pixelcopy.
Look what I mean, if I use get_buffer() and then I simply copy a
memory chunk from one place to another(?), it works and the result
shows up correctly. So one numpy *row* is mapped to one image *row*
on the screen.

> In particular, if the internal layouts differ at all (for example, if the NumPy
> array is contiguous, and the SDL surface has row padding, both of which are likely)
> then the copy will fail (fatally crash) in some cases. I don't know whether PyGame
> is smart enough to anticipate that. Odd width/height surfaces,
> and especially truecolor surfaces, would be a reassuring test.

Yes there are a lot of alignment nuances here. As as understand
GPU surface must be memory aligned, so odd shapes must be padded
into aligned rectangels. Especially 24 bit formats makes it
even harder to convert.


Mikhail

On 24 December 2016 at 12:08, Ian Mallett <ian@xxxxxxxxxxxxxx> wrote:
On Fri, Dec 23, 2016 at 4:45 PM, Mikhail V <mikhailwas@xxxxxxxxx> wrote:
Could you elaborate please?
​Now that I'm reading more carefully, it looks like you're creating a palettalized RGB surface (8-bit indices to 24-bit color). Offhand, I also would expect referencing the indices into a 2D array to work. I'm tempted to conclude a possible bug, or at least an insufficiently helpful exception.

A few questions:
--Do you need to create a surface from data, or merely update a surface repeatedly?
--Are you particularly tied to pygame.pixelcopy? I've found the surfarray module works best of the three (pixelcopy, surfarray, PixelArray), and you can use any for either task.
--Are you tied to palettal color? These are slower, and more relevantly, less common and therefore more likely to be less robust. Their main use today is for accelerating palette rotations. Their higher memory usage is usually irrelevant for typical surface sizes.

The copy buffer trick you're using looks a little suspect to me. In particular, if the internal layouts differ at all (for example, if the NumPy array is contiguous, and the SDL surface has row padding, both of which are likely) then the copy will fail (fatally crash) in some cases. I don't know whether PyGame is smart enough to anticipate that. Odd width/height surfaces, and especially truecolor surfaces, would be a reassuring test.

Ian​