I am trying to get a Raspberry Pi 3B+ to show live streams from 3 cameras, using a screen to get multiple instances of omxplayer to run on the positions I want them. This works exactly as expected.
The problem now is, that I want a static image in the last space on the screen. For that I wanted to use pygame, so I later could have more fun in that area, and maybe not only have an image.
The problem now is how the image is positioned:
https://imgur.com/mUQ38vV (the image is the same size as the video feeds, and I had expected it to be in the bottom right of the monitor, with a thin black line towards the feed above and to the right of it)
I expected the white square to be directly under the top left video feed, but there are large black borders.
The Python code I use for showing the image is as follows:
import time
import pygame
transform_x = 958 #648 #how wide to scale the jpg when replaying
transfrom_y = 539 #how high to scale the jpg when replaying
offset_x = 0 #how far off to left corner to display photos
offset_y = 540 #how far off to left corner to display photos
try:
pygame.init()
info = pygame.display.Info() # You have to call this before pygame.display.set_mode()
screen_width,screen_height = info.current_w,info.current_h
window_width,window_height = screen_width,screen_height
screen = pygame.display.set_mode((window_width, window_height))
#screen = pygame.display.set_mode((0,0))
pygame.mouse.set_visible(False) #hide the mouse cursor
filename = "image.png"
img=pygame.image.load(filename)
#img = pygame.transform.scale(img,(transform_x,transfrom_y))
screen.blit(img,(offset_x,offset_y))
pygame.display.flip() # update the display
time.sleep(30) # pause
finally:
pygame.quit()
I tried with pygame.FULLSCREEN and other modes, but none of them would go all the way to the edge of the monitor.
To show the picture I used this command:
sudo screen -dmS pic sh -c 'python pic.py'
Can anyone help me figure out what I am doing wrong here?
So I got it fixed...
I had to comment out disable_overscan=1 in /boot/config.txt ...
I didn't think of this, because omxplayer was at the positions I expected it to be..
Related
I am very new to python and I am working on a project using pygame and cv2
My first python file code has an output image that shows up in a pygame window, but I have another python file that detects objects and it gets an input video like this:
cap = cv2.VideoCapture("video.mp4")
and then it reads the video and shows the detected objects in the video by surrounding boxes around each object.
I want the cap = cv2.VideoCapture() to get the live video from the pygame window and maybe show the boxes of object detection in a second window or the same pygame window (not sure right now what I want, but maybe showing it in the same pygame window would be better)
I am not sure if this is enough information to help me since I am not very familiar with pygame or cv2, please let me know.
In pygame you are actually drawing on a Surface object. You can get the pygame.Surface object associated with the display with pygame.display.get_surface():
screen = pygame.display.get_surface()
Use pygame.surfarray.pixels3d to create a new 3D array that directly references the pixel values in the Surface. This is equivalent to a cv2 matrix respectively NumPy array when you transpose the x and y axis and transform from RGB to BGR:
capture = pygame.surfarray.pixels3d(screen)
capture = capture.transpose([1, 0, 2])
capture_bgr = cv2.cvtColor(capture, cv2.COLOR_RGB2BGR)
Do this once per frame after updating the display:
def capture_frame():
screen = pygame.display.get_surface()
capture = pygame.surfarray.pixels3d(screen)
capture = capture.transpose([1, 0, 2])
capture_bgr = cv2.cvtColor(capture, cv2.COLOR_RGB2BGR)
return capture_bgr
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# draw
# [...]
pygame.display.flip()
frame_img = capture_frame()
# [...]
I'm trying to make a chess opening trainer with Pygame. Graphically, it's incredibly simple; it works like a slideshow, I am only blitting a single image to the screen at a time which updates when I press the return key. I am using chess.svg to generate an SVG of the current game state. Not being able to use SVGs natively with pygame means that I have been converting the SVG to a PNG.
Viewing the PNG on my computer, it's very clear.
However when pygame displays the image, it's very blurry.
Ideally I would like pygame to display the SVG so I don't have to worry about the resolution at all. All I found on pygame and SVGs was this link from 12 years ago. In 2018, an answer was posted stating that the original methods no longer worked. However, now, in 2021 the methods given in 2018 no longer work either; I tried, and one comment also confirms this.
If using SVGs is not possible, that's fine, provided I can get pygame to display the PNG at a greater definition than it currently does.
Even if I make screen = pygame.display.set_mode((width, height)) fullscreen, it's still blurry. I can't seem to find anything on that, so I'm not sure that's the issue here.
Here's a simplified version of my code to demonstrate the graphical part:
import pygame
from pygame.locals import *
import chess
import cairosvg
import chess.svg
import chess.pgn
##Setup Pygame:
pygame.init()
width, height = 640, 640
screen = pygame.display.set_mode((width, height))
## MAIN ##
board = chess.Board()
#Step 1: Create image
boardsvg = chess.svg.board(board=board)
f = open("image.SVG", "w")
f.write(boardsvg)
f.close()
#scale
cairosvg.svg2png(url="image.svg", write_to="image_scaled.png", scale=3.0)
#Step2: Blit the image
image = pygame.image.load('image_scaled.png')
image = pygame.transform.scale(image, (640, 640))
screen.blit(image,(0,0))
pygame.display.flip()
Does anyone one know what I need to try?
Cheers.
I found that using smoothscale like so image = pygame.transform.smoothscale(image, (width, height)) helped a little, but it still wasn't perfect. In the end I decided to rewrite it in javascript using cm-chessboard.
chess.svg.board has a size keyword argument:
size – The size of the image in pixels (e.g., 400 for a 400 by 400 board), or None (the default) for no size limit.
e.g.
boardsvg = chess.svg.board(board=board, size=1024)
Setting this to your output size should fix the blurryness.
In 2021 you can use Pygame 2 + SDL2, which loads SVG images directly, no need of cairo or any other lib:
import io
import pygame
from pygame.locals import *
import chess
import chess.svg
##Setup Pygame:
pygame.init()
width, height = 640, 640
screen = pygame.display.set_mode((width, height))
## MAIN ##
board = chess.Board()
#Step 1: Create image
boardsvg = io.BytesIO(chess.svg.board(board=board).encode())
#Step 2: Blit the image
image = pygame.image.load(boardsvg)
image = pygame.transform.scale(image, (640, 640))
screen.blit(image,(0,0))
pygame.display.flip()
I am creating a painter software and want the user to be able to save their image after they have finished creating it, so I tried pygame.image.save(pygame.display.get_surface(), "/home/user/screenshot.png"). I made a quick drawing and pressed the key I set which would save the image. I look at the image, and it's only saved the blank display surface, not the pygame.draw.rect()s of the actual drawing. I looked at the following links: How to capture pygame screen? https://gamedev.stackexchange.com/questions/118372/how-can-i-take-a-screenshot-of-a-certain-part-of-the-screen-in-pygame Can python get the screen shot of a specific window? and much more. How would I take a screenshot of the entire display screen along with the drawing? This is my mainloop:
running = True
while running:
updateWindow() # Updates window
clearWindow() # Clears window
checkEvents() # Checks events
redrawItems() # Redraws your drawing
pygame.event.pump()
pygame.display.quit()
pygame.quit()
Try pygame.Surface.copy on the display surface. See docs here.
So if display is your screen, then:
screencopy = display.copy()
should get you a copy of the display image in screencopy. Remember because of double buffering it will give you a copy of what you would see on the screen if you did an display.update() right then, which might be different that what is showing on the screen if you have done things that have not yet been pushed to the screen yet by an update().
You can do this using pygame.image.save(Surface, filename), which you can read more about
here
Below is a simple function that will save a portion of the display as an image.
def Capture(display,name,pos,size): # (pygame Surface, String, tuple, tuple)
image = pygame.Surface(size) # Create image surface
image.blit(display,(0,0),(pos,size)) # Blit portion of the display to the image
pygame.image.save(image,name) # Save the image to the disk**
What this function does is created a pygame surface named image. Then the area (pos,size) is blitted to image at its origin. Finally, pygame.image.save(Surface, filename) will be called and save image to the disk.
For example, if we want to save a 100x100 image named "Capture.png" at the pos 50x50 on the display, name would equal "Capture.png", pos would equal (50,50), and size would equal (100,100), and the function call would look as such:
Capture(display,"Capture.png",(50,50),(100,100))
I've written a small script to display album art on USB display (on Raspberry Pi) by writing to the framebuffer with pygame. The script is working perfectly and the album art is displayed on the screen for 3 seconds:
def set_image(image):
""" Set the USB display image using pygame (320px x 240px) """
pygame.display.init()
size = (pygame.display.Info().current_w, pygame.display.Info().current_h)
black = 0, 0, 0
screen = pygame.display.set_mode(size)
pygame.mouse.set_visible(False)
pygame_image = pygame.image.fromstring(image.tobytes(), image.size, image.mode)
pygame_image_rect = pygame_image.get_rect()
screen.fill(black)
screen.blit(pygame_image, (40, 0))
pygame.font.init()
pygame.display.update()
time.sleep(3)
The problem is that when the script finishes, pygame (correctly) clears the framebuffer and my image disappears. Is there any way to tell it to leave the contents of the framebuffer when quitting?
Are you calling pygame.quit?
Generally:
- don't run window server (run from console)
- use flip to flip to a different surface
- don't call pygame.quit when exiting.
It is hard to guarantee what happens after that, but flip should flip the screen buffer to a different part of memory than the shell uses, so it won't get overwritten, and if you don't call pygame.quit, it should leave it in the (bad) state. Although there are scenarios this is useful.
I'm making a fighting game in pygame but whenever I load a background (940x680 PNG) the program starts to lag really badly, here is my code:
#import section
import os
import pygame
import time
from pygame.locals import *
#end of import section
#initiazing pygame
pygame.init()
print "Loaded Pygame 100%"
#lengths
width, height = 940,680
#opening window
screen = pygame.display.set_mode((width, height))
#position
background = [0,0]
#loading images
back = pygame.image.load("resources/image/Back.png")
print "Loaded graphics 100%"
while 1:
#clear to reload
screen.fill(0)
#drawing background
screen.blit(back, background )
#update
pygame.display.flip()
This may be a newbie question but I just started so yeah...
In pygame you need to use this code you wrote.
#clear to reload
screen.fill(0)
#drawing background
screen.blit(back, background )
#update
pygame.display.flip()
If you add a player to the screen and then move him. You will notice that unless you redraw the background, you will see the character more than once.
Pygame doesn't just let you add an image to the screen and move it around, you must redraw the background to get rid of a previous frame.
Another thing you should add to your code is a wait.
clock = pygame.time.Clock()
FPS = 30
while 1:
clock.tick(FPS)
This will set the frame rate. I'd say you would generally want this at 30 - 60 frames, depending on the game / hardware.
Your current program is probably redrawing the frame a several hundred times, so this will definately add lag.
When I checked this on my pc Ubuntu 12.04 without a background. i.e. A Black screen.
My frame rate was between 1000 - 2000 FPS.