【问题标题】:Modify alpha of Surface pixels directly in pygame直接在pygame中修改Surface像素的alpha
【发布时间】:2021-12-23 22:21:47
【问题描述】:

我在 PyGame 中有一个 Surface。我想直接修改像素的 alpha 值。我尝试过使用各种访问 alpha 值的方法,但它们似乎不起作用。

from screeninfo import get_monitors
import pygame, os, numpy.random, pygame.surfarray

pygame.init()
FPS = 60
CLOCK = pygame.time.Clock()
monitor_info = get_monitors()
x = 0
y = 0
width = monitor_info[0].width
height = monitor_info[0].height
if len(monitor_info) > 1:
    if monitor_info[0].x < 0:
        x = 0
    else:
        x = monitor_info[0].width
    width = monitor_info[1].width
    height = monitor_info[1].height

os.environ['SDL_VIDEO_WINDOW_POS'] = "{},0".format(x)
pygame.display.init()
pygame.mouse.set_visible(False)
screen_info = pygame.display.Info()
screen_size = width, height
base_screen = pygame.display.set_mode(screen_size, pygame.NOFRAME)
base_screen.fill([100, 100, 100])
board_size = (int(min(width, height)*0.75), int(min(width, height)*0.75))
surface = pygame.Surface(board_size, pygame.SRCALPHA)
surface.fill([255, 255, 255, 255])
base_screen.blit(surface, (0,0))
pygame.display.flip()
pixels = numpy.random.uniform(low=0, high=255, size=(board_size[0], board_size[1], 3))
transparency = numpy.random.uniform(low=0, high=255, size=board_size).astype('uint8')
while True:
    events = pygame.event.get()
    ms = CLOCK.tick(FPS)
    print('\r             {}                  '.format(ms), end='')
    # pygame.surfarray.blit_array(surface, pixels)
    aa = pygame.surfarray.pixels_alpha(surface)
    aa = numpy.random.uniform(low=0, high=255, size=board_size).astype('uint8')
    del aa
    # for i in range(board_size[0]):
    #     for j in range(board_size[1]):
    #         a = surface.get_at((i,j))
    #         a[3] = 0
    #         surface.set_at((i,j), a)
    base_screen.blit(surface, (0,0))
    pygame.display.flip()

我已经在循环中尝试了这两种方法(pixels_array 和 get_at/set_at),但都不起作用——图像只是保持白色(如果我将初始 alpha 设置为 0,它会保持透明)。有谁知道如何为 Surface 设置每个像素的 alpha 值?

【问题讨论】:

  • 只是为了确保您理解,当您说aa = ...pixels_alphaaa = numpy.random.uniform 时,您确实明白第二个语句不会影响表面,对吧?这会获取一组 alpha 值,然后将其丢弃并将名称重新分配给 numpy 中的随机数。
  • 您注释掉的for 循环看起来很正确。那没有用吗? get_buffer 也可以使用,如果您可以进行像素寻址。
  • 看起来已经有了答案,但就我自己而言:根据文档,pixels_alpha 调用创建了一个参考:pygame.org/docs/ref/…。我的第二行是否只是重新分配变量的引用而不是修改引用?
  • 对。请记住,名称只是对对象的引用。当您说aa = ... 时,它会在aa 中存储对新对象的新引用。旧参考已发布。

标签: python pygame alpha-transparency


【解决方案1】:

我发现了你的问题!!看不到 alpha 的原因是:

a) 你首先将surface alpha 设置为255surface.fill([255, 255, 255, 255])

b) 我相信 aa = pygame.surfarray.pixels_alpha(surface) aa = numpy.random.uniform(low=0, high=255, size=board_size).astype('uint8') 不工作,但是 pygame.surfarray.blit_array(surface, pixels) 工作(产生颜色)但我不认为他们有任何实际的 Alpha。

c) 你需要填充 base_screen 并 THEN 让你的表面出现。如此常见的错误,但这是主要问题。

最后,Tim Robert 对 for 循环的评论,肯定会让你获得你的 alpha!

这是重新编写的工作(没有screeninfo,因为我目前没有那个库):

import pygame, os, numpy.random, pygame.surfarray
from random import randint

pygame.init()
FPS = 60
CLOCK = pygame.time.Clock()
x = 50
y = 50
width = 500
height = 500

os.environ['SDL_VIDEO_WINDOW_POS'] = "{},0".format(x)
#pygame.display.init(), don't actually need this
pygame.mouse.set_visible(False)
screen_info = pygame.display.Info()
screen_size = width, height
base_screen = pygame.display.set_mode(screen_size, pygame.NOFRAME)
base_screen.fill([100, 100, 100])
board_size = (int(min(width, height)*0.75), int(min(width, height)*0.75))
surface = pygame.Surface(board_size, pygame.SRCALPHA)
surface.fill([255, 0, 0]) ###could also be surface.fill([255,0,0,255]) to set the whole surface's alpha straight up if you didn't want to change each pixel later

while True:
    #just so you can quit this program
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            raise SystemExit()
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_q:
                pygame.quit()
                raise SystemExit()
                
    ms = CLOCK.tick(FPS)

    for i in range(board_size[0]):
         for j in range(board_size[1]):
             a = surface.get_at((i,j))
             a[3] = randint(0, 128)#OR SET TO REQUIRED ALPHA VALUE
             surface.set_at((i,j), a)

    ##################################################
    base_screen.fill([100, 100, 100]) #NEED TO DO THIS
    ##################################################

    base_screen.blit(surface, (0,0))
    pygame.display.flip()

(顺便说一句,我使用红色作为第二个表面颜色,因为我认为它会更突出)

编辑

正如 cmets 中所说的 Eternal Ambiguity,这里是更快的版本,much 代替 for 循环:

aa = pygame.surfarray.pixels_alpha(surface)
aa[:] = numpy.random.uniform(low=0, high=255, size=board_size).astype('uint8')
del aa

【讨论】:

  • 谢谢!我最终使用了一个修改版本,在该版本中我保留了 pygame.surfarray.pixels_alpha 的用法并将 numpy 随机网格分配给第二个变量。然后我可以循环并分配给像素数组,而无需运行 randint 数百万次。
  • 只是为了提高意识 - 我可以通过使用 a[:] = b[:] 就地修改来使其更快,其中 a 是像素数组,b 是 numpy 随机数组。
  • @EternalAmbiguity 不错!!!我假设这样? for i in range(board_size[0]): pygame.surfarray.pixels_alpha(surface)[i] = numpy.random.uniform(low=0, high=255, size=board_size).astype('uint8')[i] 。还是更像:i +=1 if i &gt;= board_size[0]: i = -1 pygame.surfarray.pixels_alpha(surface)[i] = numpy.random.uniform(low=0, high=255, size=board_size).astype('uint8')[i] 。我只是想知道,因为我不使用 numpy :)
  • 实际上要简单得多 - 只需 aa = pygame.surfarray.pixels_alpha(self.image) 然后是 aa[:] = numpy.random.uniform(low=0, high=255, size=self.board_size).astype('uint8') 最后是 del aa。使用[:],我将aa 的值修改为--不需要循环。
  • @EternalAmbiguity 啊,[:] 制作了一个副本并将其传递给 Numpy 数组,整洁。我在这个问题上学到了很多东西,所以非常感谢你的朋友,真的很感激:)
【解决方案2】:

pygame.Surface.blit() 不会将目标表面中的像素替换为源表面。它混合(混合)表面的像素。实际上,您每帧都在将新表面与旧的先前表面混合。这意味着表面区域在几毫秒内呈现出均匀的颜色。您必须在每一帧中清除屏幕:

while True:
    # [...]

    base_screen.fill((0, 0, 0))
    base_screen.blit(surface, (0,0))
    pygame.display.flip()

小例子:

import pygame

pygame.init()

width, height = 200, 200
base_screen = pygame.display.set_mode((width, height))
clock = pygame.time.Clock()

board_size = (int(min(width, height)*0.75), int(min(width, height)*0.75))
surface = pygame.Surface(board_size, pygame.SRCALPHA)
surface.fill([255, 0, 0])

for x in range(board_size[0]):
    for y in range(board_size[1]):
        a = surface.get_at((x, y))
        a[3] = round(y / board_size[1] * 255)
        surface.set_at((x, y), a)

run = True
while run:
    clock.tick(100)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
                
    base_screen.fill((100, 100, 100))
    base_screen.blit(surface, surface.get_rect(center = base_screen.get_rect().center))
    pygame.display.flip()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-15
    • 2015-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-17
    • 1970-01-01
    • 2012-12-20
    相关资源
    最近更新 更多