简体   繁体   English

如何在pyglet中使用原始像素数据来改变图像大小

[英]How to use raw pixel data in pyglet to change image size

I need to be able to change the width or height of an image before loading it into a sprite.在将图像加载到精灵之前,我需要能够更改图像的宽度或高度。 I currently am using numpy to get either the center row or column and keep inserting it back into the image until it is the correct size.我目前正在使用 numpy 来获取中心行或列,并继续将其插入图像中,直到大小正确。 I do not know if this code even works yet, because I have also been having trouble loading the raw_image back into a sprite and displaying it.我不知道这段代码是否还有效,因为我也无法将 raw_image 加载回精灵并显示它。

def set_image_size(img, x, y):
    raw_img = img.get_image_data()
    format = 'RGBA'
    pitch = raw_img.width * len(format)
    rgba = np.array(list(img.get_image_data().get_data(format, pitch))).reshape(-1, raw_img.width, len(format))
    
    mid_y = rgba[round(raw_img.height/2),:] # This is needed to stretch along Y
    while rgba.shape[0] < y:
        rgba = np.insert(rgba, round(raw_img.height/2), mid_y, 0)
    mid_x = rgba[:,round(raw_img.width/2)] # This is needed to stretch along X
    while rgba.shape[1] < x:
        rgba = np.insert(rgba, round(raw_img.width/2), mid_x, 1)
    
    raw_img.set_data(format, pitch, ''.join(map(chr, rgba.tostring())))

    return raw_img

When I try blitting this onto an abstract image and blitting that to the screen, I get a weird striped red version of my image.当我尝试将其粘贴到抽象图像上并将其粘贴到屏幕上时,我得到了一个奇怪的红色条纹版本的图像。 I don't need to worry too much about quality, I just want to "stretch/tile" only the inside of the image so that the edges don't get warped.我不需要太担心质量,我只想“拉伸/平铺”图像的内部,这样边缘就不会变形。 Also, my images are very small, only 15 by 15 pixels or so.此外,我的图像非常小,只有 15 x 15 像素左右。

How can I fix either of these issues?我该如何解决这些问题?

EDIT:编辑:

When I use sprite.scale, it gets stretched in weird ways:当我使用 sprite.scale 时,它会以奇怪的方式拉伸:

self.sprite = pyglet.sprite.Sprite(self.image, self.x, self.y, batch=batch)
self.sprite.scale_x = self.width / 16

Original Sprite:原始雪碧: 原始雪碧

Stretched (with sprite.scale):拉伸(使用 sprite.scale):

在此处输入图像描述

This isn't as much of a problem for this sprite, because I can use OpenGL Quads to draw it, but I need to start using more complex sprites that need to be scaled in the above way.这对于这个精灵来说不是什么大问题,因为我可以使用 OpenGL Quads 来绘制它,但是我需要开始使用需要以上述方式缩放的更复杂的精灵。

So the problem lies with the scale of the image resolution you're originally working with, being 16px wide and 7px high.所以问题在于您最初使用的图像分辨率的比例,即 16 16px宽和7px高。 Up-scaling an image by default won't do any clever neighbor interpolation and "fill in the blanks" in the way you expect it to.默认情况下,放大图像不会像您期望的那样做任何巧妙的邻域插值和“填充空白”。

To solve this, you can tell OpenGL that each new pixel in your up-scaled image should take "inspiration" from its neighbors.为了解决这个问题,你可以告诉 OpenGL,你的放大图像中的每个新像素都应该从它的邻居那里获得“灵感”。 You do this by adding to your render loop the following each time you render the sprite:每次渲染精灵时,您可以通过在渲染循环中添加以下内容来做到这一点:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) 
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)

It's important that this is called before every render of the sprites you need to add the filter to.在您需要添加过滤器的精灵的每次渲染之前调用它是很重要的。 Not sure what the technical jargon is for this, but it has to be setup each render sequence, because the next time you loop over your render function it gets reset.不确定技术术语是什么,但必须设置每个渲染序列,因为下次循环渲染 function 时,它会重置。 There's a fancy word for this I'm sure of it.我敢肯定,这有一个花哨的词。

Here's a working example:这是一个工作示例:

from pyglet import *
from pyglet.gl import *

key = pyglet.window.key

class main(pyglet.window.Window):
    def __init__ (self, width=800, height=600, fps=False, *args, **kwargs):
        super(main, self).__init__(width, height, *args, **kwargs)
        self.x, self.y = 0, 0
        glEnable(GL_TEXTURE_2D) # Strictly speaking not needed, I think this is default or won't affect this test image in any significant way.

        self.keys = {}

        self.mouse_x = 0
        self.mouse_y = 0

        # The image itself is 800x400 originally
        # Positioned at x=0 and y=50 will place it at the bottom essentially
        self.sprite = pyglet.sprite.Sprite(pyglet.image.load('test.png'), 50, 50, batch=None)
        self.sprite.scale = 20 # Scale the height, not the width

        self.alive = 1

    def on_draw(self):
        self.render()

    def on_close(self):
        self.alive = 0

    def on_mouse_motion(self, x, y, dx, dy):
        self.mouse_x = x

    def on_key_release(self, symbol, modifiers):
        try:
            del self.keys[symbol]
        except:
            pass

    def on_key_press(self, symbol, modifiers):
        if symbol == key.ESCAPE: # [ESC]
            self.alive = 0

        self.keys[symbol] = True

    def render(self):
        self.clear()

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) 
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
        self.sprite.draw()

        self.flip()

    def run(self):
        while self.alive == 1:
            self.render()

            # -----------> This is key <----------
            # This is what replaces pyglet.app.run()
            # but is required for the GUI to not freeze
            #
            event = self.dispatch_events()

if __name__ == '__main__':
    x = main()
    x.run()

And here's a test-image you can use:这是您可以使用的测试图像: 在此处输入图像描述 (<-- Small white square, not zoomed in for effect) (<-- 小白方块,未放大效果)

And here's the result:结果如下:

在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM