通过python创建图像查看器并允许缩放、拖动和线条绘制的最佳方法

2024-05-01 21:31:31 发布

您现在位置:Python中文网/ 问答频道 /正文

使用python创建允许图像缩放和在其上绘制线条的应用程序的最佳方法是什么

我一直在使用pygame来实现这一点,通过在非常大的表面(大小约15000)上绘制图像,并使用pygame.transform.scale(),它允许我进行缩放。此外,将使用convert_alpha()创建一个用于线条绘制的额外曲面,并将其设置为0 alpha

这种方法的问题是,如果需要创建大尺寸的pygame.surface,则需要花费大量的时间和内存。此外,transform.scale()花费了很多时间。除直线外,还为图形类型创建了许多额外的曲面

那么,有没有更好的方法用pygame做同样的事情呢?还是另一种做同样事情的方式?多谢各位


Tags: 方法图像alpha应用程序convert时间绘制transform
1条回答
网友
1楼 · 发布于 2024-05-01 21:31:31

缩放图像可以通过复制“基础图像”的一部分,然后仅将较小的部分缩放到与窗口相同的大小来实现。这样,代码只保存基本图像、背景和子图像。您永远不会将整个图像缩放到缩放级别

在下面的示例代码中,我使用“平移框”的概念实现了这一点,平移框是定义缩放部分大小和位置的PyGame rectangleRect)。实际上,它是一个长方形的“放大镜”

代码的核心在该框的坐标和大小范围内复制背景,然后将其缩放为与窗口相同的大小,复制到“背景”图像:

window_size = ( WINDOW_WIDTH, WINDOW_HEIGHT )

zoom_image = pygame.Surface( ( pan_box.width, pan_box.height ) )  # new surface
zoom_image.blit( base_image, ( 0, 0 ), pan_box )                  # copy base image
pygame.transform.scale( zoom_image, window_size, background )     # scale into the background

window.blit( background, ( 0, 0 ) )
pygame.display.flip()

这是手术的关键。这样做效果很好,因为它只是在框所在的任何位置捕获原始图像的内容,并将其缩放以适应窗口。四处平移或缩放只是移动平移框或使其变大/变小的问题

然而,为了保持核心代码的简单性,示例中还有更多的代码主要用于将平移框保留在图像中。事实上,大多数示例都是简单的检查,以确保我们不会越界

图像复制和缩放是相对CPU昂贵的操作。因此,您将在代码中看到一些测试点,以确定是否需要执行某些操作。例如,如果平移框没有更改,我们不需要创建新的背景图像。我们也不需要制定新的zoom_image 如果平移框的大小没有更改。像这样的小检查可以大大提高代码的速度

zoomed panned doggo

参考代码:

import pygame
import sys

# Window size
WINDOW_WIDTH    = 300
WINDOW_HEIGHT   = 300
WINDOW_SURFACE  = pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE
image_filename  = None

PAN_BOX_WIDTH   = 64
PAN_BOX_HEIGHT  = 64
PAN_STEP        = 5

def errorExit( message, code=1 ):
    """ Write an error message to the console, then exit with an error code """
    sys.stderr.write( message + "\n" )
    sys.exit( code )

# The first argument is either the image filename
# or a " help" request for help

# Did we get any arguments?
if ( len( sys.argv ) == 1 ):
    errorExit( "Give an image Filename as an argument" )
else:
    # Get image filename as first argument
    for arg in sys.argv[1:]:
        if ( arg in [ '-h', ' help', '-?', '/?' ] ):
            errorExit( "Zooms an image, using arrow keys to pan\nGive an image Filename as an argument" )
    # Use the first argument as the image source
    image_filename = sys.argv[1] 
    sys.stdout.write( "Using [%s] as the image\n" % ( image_filename ) )


### PyGame initialisation
pygame.init()
window = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), WINDOW_SURFACE )
pygame.display.set_caption("Image Pan")

### Can we load the user's image OK?
try:
    base_image = pygame.image.load( image_filename ).convert()
except:
    errorExit( "Failed to open [%s]" % ( image_filename ) )

### Pan-position
background = pygame.Surface( ( WINDOW_WIDTH, WINDOW_HEIGHT ) )   # zoomed section is copied here
zoom_image = None
pan_box    = pygame.Rect( 0, 0, PAN_BOX_WIDTH, PAN_BOX_HEIGHT )  # curent pan "cursor position"
last_box   = pygame.Rect( 0, 0, 1, 1 )

### Main Loop
clock = pygame.time.Clock()
done = False
while not done:

    # Handle user-input
    for event in pygame.event.get():
        if ( event.type == pygame.QUIT ):
            done = True

    # Movement keys
    # Pan-box moves up/down/left/right, Zooms with + and -
    keys = pygame.key.get_pressed()
    if ( keys[pygame.K_UP] ):
        pan_box.y -= PAN_STEP
    if ( keys[pygame.K_DOWN] ):
         pan_box.y += PAN_STEP
    if ( keys[pygame.K_LEFT] ):
        pan_box.x -= PAN_STEP
    if ( keys[pygame.K_RIGHT] ):
        pan_box.x += PAN_STEP
    if ( keys[pygame.K_PLUS] or keys[pygame.K_EQUALS] ):
        pan_box.width  += PAN_STEP
        pan_box.height += PAN_STEP
        if ( pan_box.width > WINDOW_WIDTH ):  # Ensure size is sane
            pan_box.width = WINDOW_WIDTH
        if ( pan_box.height > WINDOW_HEIGHT ):
            pan_box.height = WINDOW_HEIGHT
    if ( keys[pygame.K_MINUS] ):
        pan_box.width  -= PAN_STEP
        pan_box.height -= PAN_STEP
        if ( pan_box.width < PAN_STEP ):  # Ensure size is sane
            pan_box.width = PAN_STEP
        if ( pan_box.height < PAN_STEP ):
            pan_box.height = PAN_STEP

    # Ensure the pan-box stays within image
    PAN_BOX_WIDTH  = min( PAN_BOX_WIDTH, base_image.get_width() )
    PAN_BOX_HEIGHT = min( PAN_BOX_HEIGHT, base_image.get_height() )
    if ( pan_box.x < 0 ):
        pan_box.x = 0 
    elif ( pan_box.x + pan_box.width >= base_image.get_width() ):
        pan_box.x = base_image.get_width() - pan_box.width - 1
    if ( pan_box.y < 0 ):
        pan_box.y = 0 
    elif ( pan_box.y + pan_box.height >= base_image.get_height() ):
        pan_box.y = base_image.get_height() - pan_box.height - 1

    # Re-do the zoom, but only if the pan box has changed since last time
    if ( pan_box != last_box ):
        # Create a new sub-image but only if the size changed
        # otherwise we can just re-use it
        if ( pan_box.width != last_box.width or pan_box.height != last_box.height ):
            zoom_image = pygame.Surface( ( pan_box.width, pan_box.height ) )  
        
        zoom_image.blit( base_image, ( 0, 0 ), pan_box )                  # copy base image
        window_size = ( WINDOW_WIDTH, WINDOW_HEIGHT )
        pygame.transform.scale( zoom_image, window_size, background )     # scale into thebackground
        last_box = pan_box.copy()                                         # copy current position

    window.blit( background, ( 0, 0 ) )
    pygame.display.flip()

    # Clamp FPS
    clock.tick_busy_loop(60)


pygame.quit()

相关问题 更多 >