You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化Pygame中反色掩码代码,提升大尺寸掩码运行效率?

优化特定区域图像反色代码的性能问题

我编写了一个测试函数,可在特定区域和形状内实现图像反色,且支持调整掩码的位置与尺寸。但存在问题:掩码尺寸越大,其移动或放大时运行速度越慢。我尝试过缓存结果、使用numpy数组、检查已修改像素等方法,但均无效果,请问有什么解决方案可加速代码运行?

原代码

import pygame

fps = pygame.time.Clock()
pygame.init()
pygame.event.set_allowed([pygame.QUIT])

# 声明必要变量
image = pygame.image.load('...\\image.png')
org_mask_inversion = pygame.image.load('...\\mask_inversion.png')
ix, iy = 0, 0
size_x, size_y = 100, 100
current_ix, current_iy = ix, iy
current_size_x, current_size_y = size_x, size_y
screen = pygame.display.set_mode([960, 720])
mask_inversion = pygame.transform.scale(org_mask_inversion, (size_x, size_y))
image2 = image


# 反色函数
def inversion_of_color(r, g, b):
    return [255 - r, 255 - g, 255 - b]


# 区域反色函数
def inversion_of_surface(surface, sx, sy):
    # 创建与掩码尺寸相同的带透明通道的Surface
    inverted_surface = pygame.surface.Surface(mask_inversion.get_size(), pygame.SRCALPHA)

    # 遍历掩码的每个像素
    for x in range(mask_inversion.get_width()):
        for y in range(mask_inversion.get_height()):
            # 获取掩码像素颜色
            m_color = mask_inversion.get_at((x, y))
            # 判断是否为绿色像素
            if m_color.g == 255:
                # 检查是否超出屏幕范围
                if 0 <= sx + x <= screen.get_width() - 1 and 0 <= sy + y <= screen.get_height() - 1:
                    # 获取原图对应像素颜色
                    color = surface.get_at((x + sx, y + sy))
                    # 反色并设置到新Surface
                    inverted_color = inversion_of_color(color.r, color.g, color.b)
                    inverted_surface.set_at((x, y), inverted_color)

    return inverted_surface


running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    fps.tick(60)
    keys = pygame.key.get_pressed()
    pygame.display.set_caption(f'Inverted Image {round(fps.get_fps())} x:{ix} y:{iy}')

    # 移动掩码位置
    if keys[pygame.K_LEFT]:
        ix -= 6
    elif keys[pygame.K_RIGHT]:
        ix += 6
    if keys[pygame.K_UP]:
        iy -= 6
    elif keys[pygame.K_DOWN]:
        iy += 6

    # 调整掩码尺寸
    if keys[pygame.K_a]:
        size_x -= 6
        size_y -= 6
        mask_inversion = pygame.transform.scale(org_mask_inversion, (size_x, size_y))
    elif keys[pygame.K_d]:
        size_x += 6
        size_y += 6
        mask_inversion = pygame.transform.scale(org_mask_inversion, (size_x, size_y))

    # 当掩码位置或尺寸变化时重新生成反色图
    if current_ix != ix or current_iy != iy or current_size_x != size_y or current_size_x != size_x:
        image2 = inversion_of_surface(image, ix, iy)
        current_ix, current_iy = ix, iy
        current_size_x, current_size_y = size_x, size_y

    screen.blit(image, (0, 0))
    screen.blit(image2, (ix, iy))
    pygame.display.update()

pygame.quit()

掩码图片

绿色掩码图片


性能优化方案

1. 替换单像素操作:使用Pygame批量像素处理

原代码中get_at()和set_at()是单像素操作,循环遍历数千个像素时性能极差。改用pygame.PixelArray实现批量处理:

def inversion_of_surface(surface, sx, sy):
    mask_size = mask_inversion.get_size()
    inverted_surface = pygame.Surface(mask_size, pygame.SRCALPHA)
    
    # 创建像素数组,避免单像素操作
    surf_array = pygame.PixelArray(surface)
    mask_array = pygame.PixelArray(mask_inversion)
    inv_array = pygame.PixelArray(inverted_surface)
    
    # 计算有效区域,避免超出屏幕范围的无效计算
    min_x = max(0, -sx)
    max_x = min(mask_size[0], screen.get_width() - sx)
    min_y = max(0, -sy)
    max_y = min(mask_size[1], screen.get_height() - sy)
    
    # 遍历有效区域像素
    for x in range(min_x, max_x):
        for y in range(min_y, max_y):
            mask_pixel = mask_array[x, y]
            # 提取绿色通道值
            g = (mask_pixel >> 8) & 0xFF
            if g == 255:
                # 获取原图像素并反色
                orig_pixel = surf_array[sx + x, sy + y]
                r = (orig_pixel >> 16) & 0xFF
                g_orig = (orig_pixel >> 8) & 0xFF
                b = orig_pixel & 0xFF
                inv_pixel = ((255 - r) << 16) | ((255 - g_orig) << 8) | (255 - b)
                inv_array[x, y] = inv_pixel
    
    # 释放像素数组,避免内存泄漏
    del surf_array, mask_array, inv_array
    return inverted_surface

2. 利用Pygame混合模式实现快速反色

不需要手动遍历像素,直接用blit混合模式实现反色,再用掩码遮罩:

# 提前生成全白反色模板(仅需初始化一次)
inversion_template = pygame.Surface(image.get_size(), pygame.SRCALPHA)
inversion_template.fill((255,255,255))

def inversion_of_surface(surface, sx, sy):
    mask_size = mask_inversion.get_size()
    temp_surf = pygame.Surface(mask_size, pygame.SRCALPHA)
    
    # 计算原图中需要反色的有效区域
    source_rect = pygame.Rect(sx, sy, mask_size[0], mask_size[1])
    source_rect.clamp_ip(surface.get_rect())
    
    # 截取原图区域并反色(BLEND_RGB_SUB等价于255-原像素值)
    temp_surf.blit(surface, (-source_rect.x + sx, -source_rect.y + sy), source_rect)
    temp_surf.blit(inversion_template, (0,0), special_flags=pygame.BLEND_RGB_SUB)
    
    # 用掩码过滤,只保留绿色区域的反色
    temp_surf.blit(mask_inversion, (0,0), special_flags=pygame.BLEND_RGBA_MULT)
    
    return temp_surf

3. 预处理掩码为Alpha遮罩

提前将绿色掩码转换为Alpha通道遮罩,避免每次判断绿色像素:

# 初始化时预处理掩码
def preprocess_mask(mask_surface):
    alpha_mask = mask_surface.copy()
    alpha_array = pygame.PixelArray(alpha_mask)
    for x in range(alpha_mask.get_width()):
        for y in range(alpha_mask.get_height()):
            pixel = alpha_array[x,y]
            g = (pixel >> 8) & 0xFF
            # 绿色像素设为完全不透明,其他透明
            alpha = 255 if g ==255 else 0
            alpha_array[x,y] = (255 << 24) | (255 <<16) | (255 <<8) | alpha
    del alpha_array
    return alpha_mask

# 替换原掩码初始化逻辑
org_mask_inversion = pygame.image.load('...\\mask_inversion.png')
org_alpha_mask = preprocess_mask(org_mask_inversion)
mask_inversion = pygame.transform.scale(org_alpha_mask, (size_x, size_y))

4. 减少不必要的重计算

  • 仅当掩码尺寸变化时,重新生成掩码和反色Surface
  • 掩码位置变化时,直接偏移绘制位置,无需重新计算像素(尺寸不变时)

内容的提问来源于stack exchange,提问作者Ketoketo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 16:09:56