I'm trying to create a french flag without using most of python's built in functions but for some reason my loop won't work... can anyone tell me why?
from PIL import Image
def french():
flag= Image.new('RGB', (300, 200))
pixels=flag.getdata()
pixs= list(pixels)
r=0
w=100
b=200
while True:
for x in range(r,(r+100)):
pixs[x]= (255,0,0)
flag.putdata(pixs)
r=+300
for x in range(w,(w+100)):
pixs[x]= (255,255,255)
flag.putdata(pixs)
w=+300
for x in range(b,(b+100)):
pixs[x]=(0,0,255)
flag.putdata(pixs)
b=+300
return (r>60000 or w>60000 or b>60000)
flag.save('frenchflag.png')
french()
Instead of
# on first pass through the loop, exit the function and return False
return (r>60000 or w>60000 or b>60000)
I think you mean
# when we are at the end of the pixel buffer, leave the loop
if r >= 60000:
break
Pixel-poking is a horribly inefficient way to create an image; use PIL.ImageDraw instead:
from PIL import Image, ImageDraw
RED = (255, 0, 0)
WHITE = (255, 255, 255)
BLUE = ( 0, 0, 255)
def french(fname, width=300, height=200):
flag = Image.new("RGB", (width, height))
p, q = width//3, 2*width//3 # bar edge coordinates
draw = ImageDraw.Draw(flag)
draw.rectangle((0, 0, p, height), RED)
draw.rectangle((p, 0, q, height), WHITE)
draw.rectangle((q, 0, width, height), BLUE)
flag.save(fname)
french("frenchflag.png")
which produces
Related
This question already has answers here:
PIL and pygame.image
(2 answers)
Closed 1 year ago.
I am trying to make a pygame project which involves some images. In these images some are very similar and there is just a change of colour. So I came up with idea that why not to use only one image and change its corresponding colour using Python from this article.
from PIL import Image
import pygame as pg
img = Image.open("Assets/image.png")
img = img.convert("RGBA")
d = img.getdata()
new_image = []
for item in d:
if item[:3] == (0,0,0):
new_image.append((255,255,255,0))
if item[:3] == (23,186,255):
new_image.append((255,38,49,item[3]))
else:
new_image.append(item)
img.putdata(new_image)
img.save("a.png","PNG")
But in the last two lines of the above code its saving the image and I don't want that!
I want to use it in the Pygame code for displaying and then when the program exits the image is gone. So how can I use a list of RGBA values new _image to display an image in Pygame.
Any help would be appreciated.
Use pygame.image.frombuffer() to create a pygame.Surface. However you have to convert the list to a byte array. Use the ctypes module to make a byte array from a list:
flat_list = [e for c in new_image for e in c]
bute_array = (ctypes.c_ubyte * len(flat_list))(*flat_list)
surf = pg.image.frombuffer(bute_array, img.size, img.mode).convert_alpha()
See also PIL and pygame.image.
Notice that there is a bug in your algorithm. You need to use elif instead of if in the middle case:
if item[:3] == (0,0,0):
new_image.append((255,255,255,0))
#if item[:3] == (23,186,255):
elif item[:3] == (23,186,255): # <---
new_image.append((255,38,49,item[3]))
else:
new_image.append(item)
Note: if you want to change the background to a white color, you need to make the color opaque:
new_image.append((255,255,255,0))
new_image.append((255, 255, 255, 255))
Minimal example:
The left image is the test image and the right image is the result:
from PIL import Image
import pygame as pg
import ctypes
img = Image.open("Assets/image.png")
img = img.convert("RGBA")
d = img.getdata()
new_image = []
for item in d:
if item[:3] == (0, 0, 0):
new_image.append((255, 255, 255, 0))
#new_image.append((255, 255, 255, 255))
elif item[:3] == (23, 186, 255):
new_image.append((255, 38, 49, item[3]))
else:
new_image.append(item)
pg.init()
window = pg.display.set_mode(img.size)
flat_list = [e for c in new_image for e in c]
bute_array = (ctypes.c_ubyte * len(flat_list))(*flat_list)
surf = pg.image.frombuffer(bute_array, img.size, img.mode).convert_alpha()
run = True
while run:
for event in pg.event.get():
if event.type == pg.QUIT:
run = False
window.fill(0)
window.blit(surf, (0, 0))
pg.display.flip()
pg.quit()
Side note:
You don't need to load the image using PLI you can access the pixels of a pygame.Surface directly. There are different options:
with the methods get_at / set_at
with pygame.PixelArray object
with the pygame.surfarray module
I may be completely missing the point of your question, but as I understand it, you want to load an image and change some colours. I would do it like this:
#!/usr/bin/env python3
from PIL import Image
import pygame as pg
import numpy as np
# Open image and ensure RGBA mode, not palette image
img = Image.open("image.png").convert('RGBA')
# Define some colours for readability
black = [0,0,0]
white = [255,255,255]
red = [255,0,0]
blue = [0,0,255]
# Make image into Numpy array for vectorised processing
na = np.array(img)
# Make re-usable mask of black pixels then change them to white
mBlack = np.all(na[...,:3] == black, axis=-1)
na[mBlack,:3] = white
# Make re-usable mask of red pixels then change them to blue
mRed = np.all(na[...,:3] == red, axis=-1)
na[mRed,:3] = blue
pg.init()
window = pg.display.set_mode(img.size)
surf = pg.image.frombuffer(na.tobytes(), img.size, img.mode)
run = True
while run:
for event in pg.event.get():
if event.type == pg.QUIT:
run = False
window.fill(0)
window.blit(surf, (0, 0))
pg.display.flip()
pg.quit()
Which makes this image:
display like this:
So you can see the power of the masks I made, you can do things like this:
# Make any pixels that were either red or black become magenta
na[mRed|mBlack,:3] = [255,0,255]
Or:
# Make all pixels that were not black into cyan
na[~mBlack,:3] = [0,255,255]
i wanted to draw a circle in pygame that is empty, i mean you should see behind surface through it.
i wrote a code that find all the colored pixels in a surface and empty their coordinates in the other surface.
def draw_empty(surf1, surf2, pos):
"""makes surf2 pixels empty on surf1"""
pixobj2 = pygame.PixelArray(surf2)
empty = []
for x in range(len(pixobj2)):
for y in range(len(pixobj2[x])):
if pixobj2[x][y]!=0:
empty.append((x,y))
del pixobj2
pixobj1 = pygame.PixelArray(surf1)
for pix in empty:
pixobj1[pix[0]+pos[0]][pix[1]+pos[1]] = 0
del pixobj1
window = pygame.display.set_mode((600, 600))
white_surf = pygame.surface.Surface((600, 600))
white_surf.fill((255, 255, 255))
circle_surf = pygame.surface.Surface((50, 50))
pygame.draw.circle(circle_surf, (1, 1, 1), circle_surf.get_rect().center, 25)
pic = pygame.image.load('pic.png')
pic = pygame.transform.scale(pic, (600, 600))
done = False
while not done:
for event in pygame.event.get():
if event.type==pygame.QUIT:
pygame.quit()
sys.exit()
draw_empty(white_surf, circle_surf, (200, 200))
window.blit(pic, (0, 0))
window.blit(white_surf, (0, 0))
pygame.display.update()
pygame.time.Clock().tick(60)
i expected to see background picture through that circle but the circle i just black, is this even possible to do that? can somebody help me with this?
P.S.
by empty, i mean like a hole in my front surface
Just use the draw() function:
pygame.draw.circle(Surface, color, pos, radius, width=0)
width represents the size of the circumference curve. When it is 0, the circle is solid. If you set it to 1, the edge will be only one pixel wide (an empty circle).
edit: to draw a circle with a sprite, you can use this code†:
import pygame
import pygame.gfxdraw
IMG = pygame.Surface((30, 30), pygame.SRCALPHA)
pygame.gfxdraw.aacircle(IMG, 15, 15, 14, (0, 255, 0))
pygame.gfxdraw.filled_circle(IMG, 15, 15, 14, (0, 255, 0))
class img(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = IMG
self.rect = self.image.get_rect(center=(150, 200))
†Credit for code goes to dunker_wanderer
You can draw the shape the same way you would if it had a color or surface image, and then just set the fill to a transparent color using:
empty = (255,255,255,0)
surface.fill(empty)
You will have to figure out how to add this to your code without breaking it, or rewrite it but, first you will need to make a set with three numbers
white = (255, 255, 255)
next use the function
pygame.draw.rect(canvas_name, set_name, (100, 100, 100, 100))
last use
pygame.display.flip()
#Initializes pygame import pygame, random, sys, time, math pygame.init()
#Creates screen screen = pygame.display.set_mode((400, 350)) pygame.display.set_caption("Game")
#Initializes screen running = True while running: pass
#Colors screen white = (255, 255, 255) screen.fill(white)
#Draws rectangle num1 = random.randrange(0, 255) num2 = random.randrange(0, 255) num3 = random.randrange(0, 255)
color = (num1, num2, num3) pygame.draw.rect(screen, color, pygame.Rect(100, 100, 60, 60), )
pygame.display.flip()
#Made by and only by the kid genius Nathan
I'm working on a game using Python and Pygame. I created a sprite sheet for one of the enemies and got my code for it working. The problem is that the image appears to have a black background even though it is a transparent image. The code for it is this:
enemySheet = pygame.image.load("resources/Alien.png").convert_alpha()
transColor = (255,255,255)
cells = []
for n in range(3):
width, height=(36,32)
rect = pygame.Rect(n * width, 0, width, height)
image = pygame.Surface(rect.size).convert_alpha()
image.blit(enemySheet, (0,0), rect)
cells.append(image)
enemyImg = cells[0]
enemyImg.set_colorkey(transColor)
enemy = enemyImg.get_rect()
enemy.center = (216,216)
I have already tried a few things but nothing has worked. Any ideas are welcome.
New surfaces are filled with black by default. If you want to make it transparent you can either add a fourth number to the transColor (the alpha value) and then fill the image,
transColor = (255,255,255,0)
# In the for loop.
image = pygame.Surface(rect.size).convert_alpha()
image.fill(transColor)
or just pass the pygame.SRCALPHA flag:
image = pygame.Surface(rect.size, pygame.SRCALPHA)
A nicer solution would be to use pygame.Surface.subsurface to cut the sheet:
for n in range(3):
width, height = (36, 32)
rect = pygame.Rect(n * width, 0, width, height)
image = enemySheet.subsurface(rect)
cells.append(image)
The code below draws a black background, a white box and text. However it seems that the drawing order is not determined and I sometimes have the text or the rectangle covered by the background rectangle. I'm trying to understand what the appropriate way to manage this is?
import sdl2.ext
import sdl2.sdlttf
from sdl2 import SDL_Color, SDL_Init
WHITE = SDL_Color(255, 255, 255)
class Entity(sdl2.ext.Entity):
def __init__(self, world, sprite, posx=0, posy=0):
self.sprite = sprite
self.sprite.position = posx, posy
sdl2.ext.init()
window = sdl2.ext.Window("PySDL2", size=(320, 240))
window.show()
world = sdl2.ext.World()
texture_renderer = sdl2.ext.Renderer(window)
spriterenderer = sdl2.ext.TextureSpriteRenderSystem(texture_renderer)
factory = sdl2.ext.SpriteFactory(sdl2.ext.TEXTURE, renderer=texture_renderer)
world.add_system(spriterenderer)
sdl2.sdlttf.TTF_Init()
font = sdl2.sdlttf.TTF_OpenFont('resources/Helvetica.dfont',32)
text_surface = sdl2.sdlttf.TTF_RenderText_Blended(font, 'test', WHITE).contents
sdl2.sdlttf.TTF_CloseFont(font)
bg = factory.from_color(sdl2.ext.Color(0, 0, 0), size = (320,240))
Entity(world, bg, 0, 0)
c = factory.from_color(sdl2.ext.Color(255, 255, 255), size = (50,50))
Entity(world, c, 100, 100)
text_sprite = factory.from_surface(text_surface)
text_entity = Entity(world, text_sprite, 50, 50)
def run():
running = True
while running:
events = sdl2.ext.get_events()
for event in events:
if event.type == sdl2.SDL_QUIT:
running = False
break
world.process()
run()
sdl2.ext.quit()
The default implementation of the TextureSpriteRenderSystem uses the depth attribute of Sprite object to determine the drawing order. If all sprites feature the same depth, the drawing order of them is undetermined, which results in your unwanted behaviour.
A Sprite with a depth value of 0 will be drawn below (or prior to) a Sprite with a higher depth value, e.g. 10. The default depth for each Sprite being created is 0. You could set your background's and rectangle's depth to a negative value in order to enforce the drawing order:
bg = factory.from_color(sdl2.ext.Color(0, 0, 0), size = (320,240))
bg.depth = -99 # always below everything
Entity(world, bg, 0, 0)
c = factory.from_color(sdl2.ext.Color(255, 255, 255), size = (50,50))
c.depth = -1
Entity(world, c, 100, 100)
You can read more about it in the PySDL2 sprite documentation.
After manipulating some code found on the net, (http://www.davidhampgonsalves.com/2011/05/OpenCV-Python-Color-Based-Object-Tracking) I am attempting to track coloured objects. While running the code, I have changed the input from a camera to an AVI file. When the code runs, there is no video and the code at line 40,
moments = cv.Moments(thresholded_img, 0)
returns a TypeError on argument: 'ܤ'. (It's basically a string of symbols I don't understand). Any help would be appreciated and I will postt he source code below in full.
Many Thanks
John
import cv
color_tracker_window = "Color Tracker"
class ColorTracker:
def __init__(self):
cv.NamedWindow( color_tracker_window, 1 )
f = 'video.avi'
self.capture = cv.CaptureFromFile(f)
def run(self):
while True:
img = cv.QueryFrame( self.capture )
#blur the source image to reduce color noise
cv.Smooth(img, img, cv.CV_BLUR, 3);
#convert the image to hsv(Hue, Saturation, Value) so its
#easier to determine the color to track(hue)
hsv_img = cv.CreateImage(cv.GetSize(img), 8, 3)
cv.CvtColor(img, hsv_img, cv.CV_BGR2HSV)
#limit all pixels that don't match our criteria, in this case we are
#looking for purple but if you want you can adjust the first value in
#both turples which is the hue range(120,140). OpenCV uses 0-180 as
#a hue range for the HSV color model
thresholded_img = cv.CreateImage(cv.GetSize(hsv_img), 8, 1)
cv.InRangeS(hsv_img, (120, 80, 80), (140, 255, 255), thresholded_img)
#determine the objects moments and check that the area is large
#enough to be our object
moments = cv.Moments(thresholded_img, 0)
area = cv.GetCentralMoment(moments, 0, 0)
data = []
#there can be noise in the video so ignore objects with small areas
if(area > 100000):
#determine the x and y coordinates of the center of the object
#we are tracking by dividing the 1, 0 and 0, 1 moments by the area
x = cv.GetSpatialMoment(moments, 1, 0)/area
y = cv.GetSpatialMoment(moments, 0, 1)/area
print 'x: ' + str(x) + ' y: ' + str(y) + ' area: ' + str(area)
data.append(x, y, area)
#create an overlay to mark the center of the tracked object
overlay = cv.CreateImage(cv.GetSize(img), 8, 3)
cv.Circle(overlay, (x, y), 2, (255, 255, 255), 20)
cv.Add(img, overlay, img)
#add the thresholded image back to the img so we can see what was
#left after it was applied
cv.Merge(thresholded_img, None, None, None, img)
#display the image
cv.ShowImage(color_tracker_window, img)
if cv.WaitKey(10) == 27:
break
if __name__=="__main__":
color_tracker = ColorTracker()
color_tracker.run()
You have to change the following:
moments = cv.Moments(thresholded_img, 0)
moments = cv.Moments(cv.GetMat(thresholded_img,1), 0)
and
cv.Circle(overlay, (x, y), 2, (255, 255, 255), 20)
cv.Circle(img, (int(x), int(y)), 2, (255, 255, 255), 20)
(Source: http://blog.goo.ne.jp/roboz80/e/16ea5be9a9eaf370046035be841b4bfd)