2

I am working on a animation environment in python using pygame. The user draw's each frame, and then using ffmpeg the animation is saved as an .avi movie. I would like to implement a feature, but am not sure how.. frame ghosting. Like display the previous frame while you draw the current.

I tried creating a surface called ghost that copies the current frame when the next-frame key is pressed. Then draws it with an alpha level of 10, but this didn't work out correctly.

I am not sure what to do, here is the source code for anyone that thinks they have an idea:

#Anim8

import pygame,subprocess,shutil
from os import makedirs
from pygame.locals import *
from random import randrange
pygame.init()
screen=pygame.display.set_mode((740,580))
draw=pygame.Surface((740,540))
draw.fill((200,200,200))
bcol=(200,200,200)
gui=pygame.Surface((740,40))
gui.fill((50,50,50))
size=2
color=(0,0,0)
screen.fill((200,200,200))
prevcol=0
newcol=0
f=0
msg=''
framerate=60
try:
    makedirs('anim')
except:
    pass
def DrawColors(x,y):
    pygame.draw.rect(gui, (255,0,0), (x+3,y+3,15,15),0)
    pygame.draw.rect(gui, (0,0,0), (x+3,y+21,15,15),0)
    pygame.draw.rect(gui, (0,255,0), (x+21,y+3,15,15),0)
    pygame.draw.rect(gui, (200,200,200), (x+21,y+21,15,15),0)
    pygame.draw.rect(gui, (0,0,255), (x+39,y+3,15,15),0)
while True:
    pygame.display.set_caption('Anim8 - Sam Tubb - '+'Frame: '+str(f)+' '+str(msg))
    mse=pygame.mouse.get_pos()
    screen.blit(gui, (0,0))
    DrawColors(0,0)
    screen.blit(draw,(0,40))
    key=pygame.key.get_pressed()
    if key[K_1]:
        framerate=10
        msg='Frame Rate set to 10'
    if key[K_2]:
        framerate=20
        msg='Frame Rate set to 20'
    if key[K_3]:
        framerate=30
        msg='Frame Rate set to 30'
    if key[K_4]:
        framerate=40
        msg='Frame Rate set to 40'
    if key[K_5]:
        framerate=50
        msg='Frame Rate set to 50'
    if key[K_6]:
        framerate=60
        msg='Frame Rate set to 60'
    if key[K_7]:
        framerate=70
        msg='Frame Rate set to 70'
    if key[K_8]:
        framerate=80
        msg='Frame Rate set to 80'
    if key[K_9]:
        framerate=90
        msg='Frame Rate set to 90'
    if key[K_0]:
        framerate=100
        msg='Frame Rate set to 100'

    if key[K_a]:
        pygame.image.save(draw, 'anim/frame'+str(f)+'.png')
        f+=1
    for e in pygame.event.get():
        if e.type==QUIT:
            shutil.rmtree('anim')
            exit()
        if e.type==KEYDOWN:
            if e.key==K_s:
                msg='Added Frame!'
                pygame.image.save(draw, 'anim/frame'+str(f)+'.png')
                f+=1
            if e.key==K_c:
                draw.fill(bcol)
            if e.key==K_r:
                name='anim'+str(randrange(0,999))+str(randrange(0,999))+'.avi'
                msg='Rendering: '+name
                pygame.display.set_caption('Anim8 - Sam Tubb - '+'Frame: '+str(f)+' '+str(msg))
                subprocess.call('ffmpeg -f image2 -s 640x480 -i anim/frame%01d.png -r '+str(framerate)+' '+name,shell=True)
                msg='Done!'
            if e.key==K_p:
                subprocess.call('ffplay '+name,shell=True)
        if e.type==MOUSEBUTTONDOWN:
            if e.button==1:
                try:
                    prevcol=color
                    newcol=gui.get_at(mse)
                    if newcol==(50,50,50):
                        newcol=prevcol
                    color=newcol
                except:
                    pass
            if e.button==3:
                try:
                    prevcol=bcol
                    newcol=gui.get_at(mse)
                    if newcol==(50,50,50):
                        newcol=prevcol
                    draw.fill(newcol)
                    bcol=newcol
                except:
                    pass
            if e.button==4:
                size+=1
                if size>7:
                    size=7
            if e.button==5:
                size-=1
                if size==0:
                    size=1 
        if e.type == pygame.MOUSEMOTION:
            lineEnd = pygame.mouse.get_pos()
            lineEnd = (lineEnd[0],lineEnd[1]-40)
            if pygame.mouse.get_pressed() == (1, 0, 0):
                    pygame.draw.line(draw, color, lineStart, lineEnd, size)
            lineStart = lineEnd

    pygame.display.flip()

Oh, and on another note, just if anyone was curious, here is what the output looks like.. I made a little new year's animation:

Animation Test

Sam Tubb
  • 945
  • 3
  • 19
  • 40
  • Unrelated, but you declare `-r` after your input, so this becomes an output option. Default `-r` for input is 25. So `ffmpeg` will drop or duplicate frames to reach your desired output frame rate. You can move `-r` to the input, or have separate `-r` for input and output if you need the read the input at a certain frame rate and are restricted to a certain output frame rate. – llogan Dec 31 '13 at 20:38
  • Your method sounds perfect, have you tried seeing if it was a minor error like a typo or blitting at the wrong time? – Cosine Dec 31 '13 at 21:23
  • The problem was that I couldn't clear the ghost surface in between adding frames, so I scrapped it, would you like me to re-write it so you can take a look at it? – Sam Tubb Dec 31 '13 at 22:06

1 Answers1

0

Load a set of images into a list, set the set_colorkey with the background color of the images and set an alpha channel with set_alpha:

images = [image1, image2, image3]
for image in images:
    image.set_colorkey("white")
    image.set_alpha(10)

Blend (blit) an image multiple times in the window in successive frames. This creates a fade-in effect. Repeat this with one image after the other:

window.fill("white")
count = 0
run = True
while run:
    clock.tick(20)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False 

    index = count // 26
    count += 1
    if index < len(images):
        if count % 26 == 25:
            images[index].set_alpha(255)
        window.blit(images[index], (0, 0))
    pygame.display.flip()

Minimal example

import pygame, math

pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()

image1 = pygame.Surface((400, 400))
image1.fill("white")
pygame.draw.circle(image1, "black", (200, 100), 50, 5)
image2 = pygame.Surface((400, 400))
image2.fill("white")
pygame.draw.line(image2, "black", (200, 150), (200, 230), 5)
pygame.draw.line(image2, "black", (200, 180), (120, 140), 5)
pygame.draw.line(image2, "black", (200, 180), (280, 140), 5)
pygame.draw.line(image2, "black", (200, 230), (170, 300), 5)
pygame.draw.line(image2, "black", (200, 230), (230, 300), 5)
image3 = pygame.Surface((400, 400))
image3.fill("white")
pygame.draw.circle(image3, "black", (180, 85), 10, 5)
pygame.draw.circle(image3, "black", (220, 85), 10, 5)
pygame.draw.line(image3, "black", (200, 95), (200, 115), 5)
pygame.draw.arc(image3, "black", (180, 100, 40, 30), math.pi, 0, 5)

images = [image1, image2, image3]
for image in images:
    image.set_colorkey("white")
    image.set_alpha(10)

window.fill("white")
count = 0
run = True
while run:
    clock.tick(20)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False 

    index = count // 26
    count += 1
    if index < len(images):
        if count % 26 == 25:
            images[index].set_alpha(255)
        window.blit(images[index], (0, 0))
    pygame.display.flip()

pygame.quit()
exit()
Rabbid76
  • 202,892
  • 27
  • 131
  • 174