Daverj
Posts: 28
Joined: Tue Mar 06, 2012 2:23 am
Contact: Website

cycling between JPEGs

Sat Jul 21, 2012 5:46 am

Is there a simple method of throwing up JPEG images full screen using Python? Preferably a method that can change the images very fast. I'd like to do a simple script that can cycle through maybe 10 images per second as a GPIO pin is held down and then stop on one of them when the pin is released.

I've scanned through the forums and found a number of GPIO examples, but nothing about quickly throwing up JPEGs full screen.

timhoffman
Posts: 85
Joined: Sat Nov 05, 2011 11:31 pm

Re: cycling between JPEGs

Sat Jul 21, 2012 5:58 am

For something low level I would use PySDL., or PyGame.

timhoffman
Posts: 85
Joined: Sat Nov 05, 2011 11:31 pm

Re: cycling between JPEGs

Sat Jul 21, 2012 12:42 pm

Here is a really simple example (ok uses png rather than jpeg) and only does two images using pygame. This flips images once per second - see the clock.tick(1).
.
But you will get the idea.

Code: Select all


import pygame

pygame.init()
screen = pygame.display.set_mode((0, 0))
done = False
image1 = pygame.image.load("/usr/share/pixmaps/lxmusic.png")
image2 = pygame.image.load("/usr/share/pixmaps/lxterminal.png")

clock = pygame.time.Clock()

while not done:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
    screen.fill((255,255,255))
    screen.blit(image1,(0,0))
    clock.tick(1)
    pygame.display.flip()   
    screen.fill((255,255,255))
    screen.blit(image2,(0,0))
    clock.tick(1)
    pygame.display.flip()

the set_mode((0,0) sets screen size to maximum. There are also some additional flags like pygame.FULLSCREEN that you might need. Docs here http://www.pygame.org/docs/ref/display. ... y.set_mode

Some good intro tutorials on using pygame can be found here. http://nerdparadise.com/tech/python/pyg ... ics/part1/

Cheers

Daverj
Posts: 28
Joined: Tue Mar 06, 2012 2:23 am
Contact: Website

Re: cycling between JPEGs

Mon Jul 23, 2012 1:18 am

Thanks for that. I tried out pygame, but I don't think that's going to be fast enough.

I tried the example above but using 10 JPEG images. I removed the background fills and the clock delays and just used 10 blits followed by display.flips, since the images are all the same size (current test was with 1500x1000 images on a 1920x1080p60 HDMI display).

The fastest I could get it to cycle was about 1/2 second per image. I'm hoping to be able to flip images at maybe 1/2 or 1/4 the video frame rate (maybe 15 - 30 images per second). At least for short bursts. I want it to cycle through as a fast flicker of images.

I did try it using the pygame.FULLSCREEN and pygame.FULLSCREEN | pygame.HWSURFACE flags, but they didn't speed it up.

I don't know if it's Python or pygame that is the limiting factor here. I was hoping to get something simple working with python, but perhaps I have to switch over to doing it in C in order to go faster?

timhoffman
Posts: 85
Joined: Sat Nov 05, 2011 11:31 pm

Re: cycling between JPEGs

Mon Jul 23, 2012 4:01 am

Its probably not python as such that is the cause of the slow performance. Remember the X server isn't accelerated. Maybe someone working on Pi3D can comment here.

You may have better luck using an opengl pipline, and loading a texture onto a flat surface.
You can do this in pygame as well.

Also if aren't already you might want to pre-scale your images so they don't have to be scaled each time you draw the image.

Cheers

Tim

Daverj
Posts: 28
Joined: Tue Mar 06, 2012 2:23 am
Contact: Website

Re: cycling between JPEGs

Mon Jul 23, 2012 5:30 am

As far as I know there's no scaling going on. My test images are 1500x1000 and the screen mode is 1920x1080. I see black at the bottom and a larger black on the right of the images, so they don't fill the screen. The final images will be larger and fill the screen.

I did try running the program directly from the shell without x server running. It was a bit faster, averaging 0.37 seconds per image instead of 0.46 seconds.

timhoffman
Posts: 85
Joined: Sat Nov 05, 2011 11:31 pm

Re: cycling between JPEGs

Mon Jul 23, 2012 6:25 am

On thing to check is how much memory you process is using.
You may find you are starting to page. Remember a jpeg will be uncompressed and assuming its 24bits deep, a single image 1500 x 1000 * 24bits is around 4.2 MB. 10 will use ~ 42MB. If the process of getting the image to the display makes copies then you could be up for 80MB of memory just for images. If the image depth in memory is 32bit (say an alpha plain etc then even more.)

Not saying that is where the performance problem is, but it's worth making sure it isn't a factor. Try just flipping 2 images at the rate you want and that will confirm if memory is having an impact.

Cheers

Tim

Daverj
Posts: 28
Joined: Tue Mar 06, 2012 2:23 am
Contact: Website

Re: cycling between JPEGs

Mon Jul 23, 2012 5:53 pm

I tried it with 2 images. Same speed.

I tried moving the images from the USB stick to the Pi home folder on the SD card. Same speed. And the SD card is a class 10 Sandisk Ultra, so I doubt that's slowing anything down.

I tried adding pygame.HWSURFACE to the set_mode command and that speeds it up slightly (0.34 seconds per image instead of 0.37 seconds) but with that the new image doesn't suddenly appear, you see a line travel down the screen as the new image is drawn over the old one. There is no delay once it reaches the bottom. It immediately goes to the top and starts drawing in the next one. So it is clearly the speed of redrawing the screen that is the speed problem.

I tried adding pygame.DOUBLEBUF to the set_mode command to hide that scrolling, but for some reason when I do that no images appear at all. The screen stays black.

Daverj
Posts: 28
Joined: Tue Mar 06, 2012 2:23 am
Contact: Website

Re: cycling between JPEGs

Mon Jul 23, 2012 7:06 pm

SUCCESS!!

I created a set of 10 surfaces and blited the 10 images into them. Then in my main loop I blited the surfaces to the display.

I'm now getting about 12.5 images per second (0.08 sec per image)

Your comment earlier about RAM does make me wonder what will happen when I go with the full sized images, and try to add even more images (I'd like to try this with maybe 60 images). But at least this is working at the current stage at a very respectable flicker rate.

I also haven't added any code to sample a pin at the GPIO, which was also part of my plan for controlling this. And that will no doubt slow things down.

Here's what I did, in case somebody else is trying to do something similar:

Code: Select all

import pygame

pygame.init()
screen = pygame.display.set_mode((0,0),pygame.FULLSCREEN)
done = False
image1 = pygame.image.load("./images2/painting1.jpg")
image2 = pygame.image.load("./images2/painting2.jpg")
image3 = pygame.image.load("./images2/painting3.jpg")
image4 = pygame.image.load("./images2/painting4.jpg")
image5 = pygame.image.load("./images2/painting5.jpg")
image6 = pygame.image.load("./images2/painting6.jpg")
image7 = pygame.image.load("./images2/painting7.jpg")
image8 = pygame.image.load("./images2/painting8.jpg")
image9 = pygame.image.load("./images2/painting9.jpg")
image10 = pygame.image.load("./images2/painting10.jpg")

back1 = pygame.Surface(screen.get_size())
back1 = back1.convert()
back1.blit(image1,(0,0))
back2 = pygame.Surface(screen.get_size())
back2 = back2.convert()
back2.blit(image2,(0,0))
back3 = pygame.Surface(screen.get_size())
back3 = back3.convert()
back3.blit(image3,(0,0))
back4 = pygame.Surface(screen.get_size())
back4 = back4.convert()
back4.blit(image4,(0,0))
back5 = pygame.Surface(screen.get_size())
back5 = back5.convert()
back5.blit(image5,(0,0))
back6 = pygame.Surface(screen.get_size())
back6 = back6.convert()
back6.blit(image6,(0,0))
back7 = pygame.Surface(screen.get_size())
back7 = back7.convert()
back7.blit(image7,(0,0))
back8 = pygame.Surface(screen.get_size())
back8 = back8.convert()
back8.blit(image8,(0,0))
back9 = pygame.Surface(screen.get_size())
back9 = back9.convert()
back9.blit(image9,(0,0))
back10 = pygame.Surface(screen.get_size())
back10 = back10.convert()
back10.blit(image10,(0,0))


while not done:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                done = True
    screen.blit(back1,(0,0))
    pygame.display.flip()
    screen.blit(back2,(0,0))
    pygame.display.flip()
    screen.blit(back3,(0,0))
    pygame.display.flip()
    screen.blit(back4,(0,0))
    pygame.display.flip()
    screen.blit(back5,(0,0))
    pygame.display.flip()
    screen.blit(back6,(0,0))
    pygame.display.flip()
    screen.blit(back7,(0,0))
    pygame.display.flip()
    screen.blit(back8,(0,0))
    pygame.display.flip()
    screen.blit(back9,(0,0))
    pygame.display.flip()
    screen.blit(back10,(0,0))
    pygame.display.flip()
    
pygame.quit()

Return to “Python”