Im having problems with blitting images to rect objects in pygame. i have a background image blitted to my main pygame window, and also an image blitted to a rect object on the screen which moves. the problem i am having is the rect object is overlapping my background image when its moving around. i was looking to only be able to see the green helicopter shape and not the black outline around it. sorry if i havent explained this very well. will try to include all files im using.
Thanks for any help
import pygame as pg
import random as r
import time
pg.init()
MAX_X = 1190
MAX_Y = 590
MIN_X = 10
MIN_Y = 10
SIZE = 100
SPEED = 1
COLOR = (0,255,0)
move_amount = 0
wn = pg.display.set_mode((1200, 600))
BG_IMG = pg.image.load('bg.png').convert()
BG_IMG = pg.transform.scale(BG_IMG, (1200, 600))
class Wall (pg.Rect):
def __init__(self, posX, posY):
self.xcor = posX
self.ycor = posY
self.rect = None
class Heli (pg.Rect):
def __init__(self, posX, posY):
self.image = pg.image.load('art.png').convert()
self.rect = self.image.get_rect()
self.xcor = posX
self.ycor = posY
# top and bottom constant walls
TOP = pg.Rect(MIN_X, MIN_Y, MAX_X, 3)
BOTTOM = pg.Rect(MIN_X, MAX_Y, MAX_X, 3)
heli = Heli(MIN_X, MAX_Y //2)
# keep moving walls in a list
moving_walls = [Wall(MAX_X, r.randint((MIN_Y + 10), (MAX_Y - 10)))]
# main loop
while True:
# fill screen
wn.fill('black')
# editing objects to move
# blitting must happen before everything else
pg.draw.rect(wn,COLOR, heli.rect)
wn.blit(BG_IMG, (0,0))
wn.blit(heli.image, heli.rect)
heli.rect.y += move_amount
heli.rect.y += 1
# use a variable to control how much movement is happening
# movement happens continuosly
# if key down it oves if key up it doesnt
for wall in moving_walls :
wall.rect = pg.Rect(wall.xcor, wall.ycor, 3, SIZE)
pg.draw.rect(wn, COLOR, wall.rect)
wall.xcor -= SPEED
if wall.xcor < MIN_X + 10:
wall.xcor = MAX_X
wall.ycor = r.randint((MIN_Y), (MAX_Y - SIZE))
# drawing all objects back to the screen
pg.draw.rect(wn, COLOR, TOP)
pg.draw.rect(wn, COLOR, BOTTOM)
# update window
pg.display.update()
# event handling
for ev in pg.event.get():
if ev.type == pg.KEYDOWN:
if ev.key == pg.K_UP:
move_amount = -3
if ev.type == pg.KEYUP:
move_amount = 0
if ev.type == pg.QUIT:
pg.quit()
time.sleep(0.01)
You discard the transparency information of the image. You have to use convert_alpha instead of convert:
self.image = pg.image.load('art.png').convert()
self.image = pg.image.load('art.png').convert_alpha()
The pygame documentation notes that:
The returned Surface will contain the same color format, colorkey and alpha transparency as the file it came from. You will often want to call convert() with no arguments, to create a copy that will draw more quickly on the screen.
For alpha transparency, like in .png images, use the convert_alpha() method after loading so that the image has per pixel transparency.
See also How can I make an Image with a transparent Backround in Pygame?
Related
I'm trying to resize sprites to a 96x96 boundary while retaining the aspect ratio.
The following code:
im.getbbox()
Returns a tuple containing the bounds of the sprite (original backgrounds are transparent), and I am stuck on this next part - I want to take that part of the image and resize it as large as it can possibly go within a 96x96 boundary
Here's an example of some sprites from Pokemon:
Since some are 80x80, some are 64x64, and the largest are 96x96, I would like to effectively select the contents of the sprite with im.getbbox() and then enlarge it to fit on top of a 96px white background.
Could anyone please help? I'm not sure how to maximise it within the bounds
My current code is as follows:
x = 0
for dirname, dirs, files in os.walk(path):
for filename in files:
x+=1
path = dirname + "/" + filename
print(path)
im = Image.open(path)
fill_color = (255,255,255)
im = im.convert("RGBA")
if im.mode in ('RGBA', 'LA'):
background = Image.new(im.mode[:-1], im.size, fill_color)
background.paste(im, im.split()[-1])
im = background
imResize = ImageOps.fit(im, (96, 96), Image.BOX, 0, (0.5, 0.5))
imResize.save("dataset/images/" + str(x) + '.png', 'PNG')
It takes the image and pastes it on to a white background and sets the size as 96px. This is fine for the native 96px images but the aspect ratio of the smaller images is ruined. By being able to enlarge them to the maximum bounds of a 96px image then it should prevent this from happening
Thanks!
ok, made my own pics, here:
zY28f_64.png, zY28f_80.png , zY28f_96.png
ended up with code :
from PIL import Image, ImageOps
import os
path= 'images'
x = 0
for dirname, dirs, files in os.walk(path):
for filename in files:
x+=1
path = dirname + "/" + filename
print(path)
im = Image.open(path)
im.show()
fill_color = (255,255,255)
im = im.convert("RGBA")
if im.mode in ('RGBA', 'LA'):
background = Image.new(im.mode[:-1], im.size, fill_color)
# background.paste(im, im.split()[-1])
background.paste(im)
im = background
im_inv = ImageOps.invert(im) ### make white background black
imResize = ImageOps.fit(im.crop(im_inv.getbbox()), (96, 96), Image.BOX, 0, (0.5, 0.5)) # im.crop crop on box got from image with black background, Image.getbbox works only with black bacground returning biggest box containing non zero(non black ) pixels
imResize.show()
# imResize.save("dataset/images/" + str(x) + '.png', 'PNG')
output:
zY28f_64.png2.png , zY28f_80.png1.png , zY28f_96.png3.png
main changes here:
make white background black
im_inv = ImageOps.invert(im)
im.crop crop on box got from image with black background, Image.getbbox works only with black background returning biggest box containing non zero(non black ) pixels
imResize = ImageOps.fit(im.crop(im_inv.getbbox()), (96, 96), Image.BOX, 0, (0.5, 0.5))
Since ImageOps.fit(...) line return chopped images, not sure how it works, to keep the entire figures I used code from PIL Image.resize() not resizing the picture to get:
from PIL import Image, ImageOps
import os
import math
path= 'images'
x = 0
for dirname, dirs, files in os.walk(path):
for filename in files:
x+=1
path = dirname + "/" + filename
print(path)
im = Image.open(path)
fill_color = (255,255,255)
im = im.convert("RGBA")
if im.mode in ('RGBA', 'LA'):
background = Image.new(im.mode[:-1], im.size, fill_color)
# background.paste(im, im.split()[-1])
background.paste(im)
im = background
im_inv = ImageOps.invert(im)
imResize = im.crop(im_inv.getbbox())
width, height = imResize.size
print(width, height)
if height > width:
ratio = math.floor(height / width)
newheight = ratio * 96
print(imResize, ratio, newheight)
imResize = imResize.resize((96, newheight))
else:
ratio = math.floor(width /height )
newwidth = ratio * 96
print(imResize, ratio, newwidth)
imResize = imResize.resize((newwidth, 96))
imResize.show()
print(imResize.size)
imResize.save(path + str(x) + '.png', 'PNG')
output:
zY28f_64.png2.png , zY28f_80.png1.png , zY28f_96.png3.png
In the game our character jumps on platforms in order to move higher. My game resolution is 600x600. I have a picture that's going to be my background and has resolution of 600x1000. I'm trying to setup a background that progressively moves or changes as we go higher. So that when I get higher, the background shows not the bottom of the original picture (Y-axis 400-1000), but shows, for example, the middle part of the picture (Y-axis 200-800).
So far I haven't been able to figure out a way how to blit 600x600 image from 600x1000 image. It's like I want to cut the upper part of the original picture so that it fits. Could you help me with this?
I'm sorry if I didn't explain it properly. How do I blit a non-distorted, smaller-sized image from a bigger-sized image. The background I have in mind should look like from an android game called "Happy Jump".
Thank you for reading.
Have a good day.
enter image description here
import pygame
pygame.init()
run = True
surf = pygame.display.set_mode((600, 600))
img = pygame.image.load('image.jpg')
down = -400
center_of_screen = 300
maximum_limit_of_down = 0
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((40, 40))
self.image.fill((255, 0, 0))
self.rect = self.image.get_rect()
self.rect.center = (300, 570)
self.isJump = False
self.jumpCount = 10
def update(self):
global down
key = pygame.key.get_pressed()
if key[pygame.K_UP] and not self.isJump:
self.isJump = True
if self.isJump:
if self.rect.y > center_of_screen or (down >= maximum_limit_of_down):
if self.jumpCount >= 0:
self.rect.y = self.rect.y - (self.jumpCount ** 2) * 0.5
self.jumpCount -= 1
else:
self.jumpCount = 10
self.isJump = False
if down >= maximum_limit_of_down:
down = maximum_limit_of_down
else:
if self.jumpCount >= 0:
down = down + (self.jumpCount ** 2) * 0.5
self.jumpCount -= 1
else:
self.jumpCount = 10
self.isJump = False
player = Player()
all_sprite = pygame.sprite.Group()
all_sprite.add(player)
clock = pygame.time.Clock()
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
surf.fill((0, 0, 0))
surf.blit(img, (0, down))
all_sprite.update()
all_sprite.draw(surf)
pygame.display.update()
This will help you, sorry for the Image i used whatever i found
so I am trying to animate a dotted line moving down the screen... like the white dashed lines moving past you on a road. It was easy enough for me to draw the dotted line:
import pygame
GREY = (211, 211, 211)
WHITE = (255, 255, 255)
pygame.init()
screen = pygame.display.set_mode(800, 600)
pygame.display.set_caption("Line Test")
clock = pygame.time.Clock()
running = True
while running:
clock.tick(60)
for event in pygame.event,get():
if event.type == pygame.QUIT:
running = False
all_sprites.update()
screen.fill(GREY)
dash = 0
for i in range(30):
pygame.draw.line(screen, WHITE, [400, 1 + dash], [400, dash + 10], 6)
dash += 20
all_sprites.draw(screen)
pygame.display.flip()
pygame.quit()
But I am trying to animate that line in a Class so it appears it is constantly moving down. So far no luck, only being able to get a single dash moving down the screen with:
class Line(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((6, 10))
self.image.fill(WHITE)
self.rect = self.image.get_rect()
self.last_uodate = pygame.time.get_ticks()
self.rect.x = 400
dash = 0
for i in range(30):
self.rect.y = 0 + dash
dash += 20
self.speedy = 5
def update(self):
self.rect.y += self.speedy
if self.rect.y > 600:
now = pygame.time.get_ticks()
if now - self.last_update > 0:
self.last_update = now
self.rect.x = 400
self.rect.y = 0
self.speedy = 5
all_sprites = pygame.sprite.Group()
line = pygame.sprite.Group()
for i in range(30):
ln = Line()
all_sprites.add(ln)
line.add(ln)
Any suggestion or tell me where I've gone wrong? This only produces single dash going down screen and not full dotted line top to bottom constantly moving.
As this link was quiet and didn't get the feedback I was hoping for, I played around some more in Pygame and in the end, just used sprite images on road lines. I was able to use about 4 separate ones each in their own Class starting at different rect.y locations off the top of the screen and have the self.speedy the same for them all and then when reaching off the bottom of the screen, all restarting from -20 y position. This gave the seamless flow of it all being on continuous road line moving down the screen. If anyone wants the code I used I'd be happy to post.
I would still like if someone could tell me how to do it within one Class.
I'm trying to locate an object (here a PWB) on a picture.
First I do this by finding the largest contour. Then I want to rewrite solely this object into a new picture so that in the future I can work on smaller pictures.
The problem however is that when I rewrite this ROI, the picture gets of a lighter color than the original one.
CODE:
Original = cv2.imread(picture_location)
image = cv2.imread(mask_location)
img = cv2.medianBlur(image,29)
imgray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
dst = cv2.bitwise_and(Original, image)
roi = cv2.add(dst, Original)
ret,thresh = cv2.threshold(imgray,127,255,0)
im2, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
area = 0
max_x = 0
max_y = 0
min_x = Original.shape[1]
min_y = Original.shape[0]
for i in contours:
new_area = cv2.contourArea(i)
if new_area > area:
area = new_area
cnt = i
x,y,w,h = cv2.boundingRect(cnt)
min_x = min(x, min_x)
min_y = min(y, min_y)
max_x = max(x+w, max_x)
max_y = max(y+h, max_y)
roi = roi[min_y-10:max_y+10, min_x-10:max_x+10]
Original = cv2.rectangle(Original,(x-10,y-10),(x+w+10,y+h+10),(0,255,0),2)
#Writing down the images
cv2.imwrite('Pictures/PCB1/LocatedPCB.jpg', roi)
cv2.imwrite('Pictures/PCB1/LocatedPCBContour.jpg',Original)
Since I don't have 10 reputation yet I cannot post the pictures. I can however provide the links:
Original
Region of Interest
The main question is how do I get the software to write down the ROI in the exact same colour as the original picture?
I'm a elektromechanical engineer however, so I'm fairly new to this, remarks on the way I wrote my code would also be appreciated if possible.
The problem is that you first let roi = cv2.add(dst, Original)
and finally cut from the lighten picture in here:
roi = roi[min_y-10:max_y+10, min_x-10:max_x+10]
If you want to crop the original image, you should do:
roi = Original[min_y-10:max_y+10, min_x-10:max_x+10]
You can perhaps perform an edge detection after blurring your image.
How to select best parameters for Canny edge? SEE HERE
lower = 46
upper = 93
edged = cv2.Canny(img, lower, upper) #--- Perform canny edge on the blurred image
kernel = np.ones((5,5),np.uint8)
dilate = cv2.morphologyEx(edged, cv2.MORPH_DILATE, kernel, 3) #---Morphological dilation
_, contours , _= cv2.findContours(dilate, cv2.RETR_EXTERNAL, 1) #---Finds all parent contours, does not find child contours(i.e; does not consider contours within another contour)
max = 0
cc = 0
for i in range(len(contours)): #---For loop for finding contour with maximum area
if (cv2.contourArea(contours[i]) > max):
max = cv2.contourArea(contours[i])
cc = i
cv2.drawContours(img, contours[cc], -1, (0,255,0), 2) #---Draw contour having the maximum area
cv2.imshow(Contour of PCB.',img)
x,y,w,h = cv2.boundingRect(cnt[cc]) #---Calibrates a straight rectangle for the contour of max. area
crop_img = img1[y:y+h, x:x+w] #--- Cropping the ROI having the coordinates of the bounding rectangle
cv2.imshow('cropped PCB.jpg',crop_img)
I'm trying to code a program that can take text and animate it to bounce on a loop, like a ball bouncing to the floor. I used a similar piece of code I found a starting point as I'm still fairly new to Pygame (thank you Pete Shinners, whoever you are), but after updating the code and playing with it for a long time I still can't get it to blit to the screen correctly. The text starts above the rendered area and then gradually falls into view, but the top part of the text is cut off.
I've tried moving the blitted region around the window and resizing the rectangles and surface the program is using, but nothing seems to fix it.
import os, sys, math, pygame, pygame.font, pygame.image
from pygame.locals import *
def bounce():
# define constants
G = 0.98
FLOOR = 0
COEFFICIENT = 0.8
#define variables
ball = 500
direction = 'DOWN'
v = 0
count = 0
#create array to store data
array = [ball]
while True:
if count == 4:
return array
elif ball > FLOOR and direction == 'DOWN':
v += G
if (ball - v) >= FLOOR:
ball = ball - v
array.append(round(ball,2))
else:
ball = FLOOR
array.append(round(ball,2))
direction = 'UP'
v *= COEFFICIENT
count += 1
elif ball >= FLOOR and direction == 'UP':
v -= G
if (ball + v) >= FLOOR:
ball = ball + v
array.append(round(ball,2))
if v <= 0:
direction = 'DOWN'
else:
ball = FLOOR
array.append(ball)
direction = 'UP'
v *= COEFFICIENT
class textBouncy:
array = bounce()
def __init__(self, font, message, fontcolor, amount=10):
# Render the font message
self.base = font.render(message, 0, fontcolor)
# bounce amount (height)
self.amount = amount
#size = rect of maximum height/width of text
self.size = self.base.get_rect().inflate(0, amount).size
#normalise array to meet height restriction
self.array = [round(-x/(500/amount),2) for x in array]
def animate(self):
# create window surface s
s = pygame.Surface(self.size)
# height = max inflated height
height = self.size[1]
# define a step-sized rectangle in the location of the step
src = Rect(0, 0, self.base.get_width(), height)
# moves the message according to the array list.
dst = src.move(0, self.array[i])
if (i + 1) == len(self.array):
global i
i = 0
# blits the information onto the screen
s.blit(self.base, dst, src)
return s
entry_info = 'Bouncing ball text'
if __name__ == '__main__':
pygame.init()
#create text renderer
i = 0
array = bounce()
bigfont = pygame.font.Font(None, 60)
white = 255, 255, 255
renderer = textBouncy(bigfont, entry_info, white, 16)
text = renderer.animate()
#create a window the correct size
win = pygame.display.set_mode(text.get_size())
win.blit(text, (0, 10))
pygame.display.flip()
#run animation loop
finished = 0
while True:
pygame.time.delay(10)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
text = renderer.animate()
i += 1
win.blit(text, (0, 10)) # blits the finished product from animate
pygame.display.flip()
(Quote) "it all comes down to math really" Kay so you need to - the y axis when you want to make it go up and + the x axis to make it go side ways you could make it go up and down will moveing it horizontally and then when it reaches a point it will stop moving horizontally and just bonce up and down +ing it more every time
That was my 100$ which took me 5 mins to write
After revisiting this I managed to work this out - I needed to add everything I blitted down to compensate for the bounce up. So in the __init__function:
self.array = [round(-x/(500/amount),2)**+self.amount** for x in array]
Works perfectly now :)