Colouring an image Pygame - python

I'd like to create a game, similar to that of Geometry Dash. I have all the images for the cubes, but they are all grey and white - this is to allow the user to select the colours.
I have two variables, colour_1 and colour_2. colour_1 should be in the grey, and colour_2 should be in the white. If I say what the variables are, how would I modify the image to have the right colours?
The colours on the images are not all the same, the edges blend, so that the image is smoother. This may cause complications.

I found this on the website Fishstick proposed
Here's a working code based on it
img_surface = pygame.image.load("image.gif") # Load image on a surface
img_array = pygame.surfarray.array3d(img_surface) # Convert it into an 3D array
colored_img = numpy.array(img_array) # Array thing
colored_img[:, :, 0] = 255 # <-- Red
colored_img[:, :, 1] = 128 # <-- Green
colored_img[:, :, 2] = 0 # <-- Blue
img_surface = pygame.surfarray.make_surface(colored_img) # Convert it back to a surface
screen.blit(img_surface, (0, 0)) # Draw it on the screen
This change the color value of each pixel. If you set red to 255, it will add 255 to red for all pixels. But if you set 255 to all colors the image will be white.
Note that you need to install NumPy to use this and you can do so by doing this:
pip install numpy
Also you could try replacing the last two lines for
pygame.surfarray.blit_array(screen, colored_img)
Which should be faster but it didn't work for me so I converted the array into a surface then blitted it on the screen.
If that doesn't answer your question maybe these will:
Pygame - Recolor pixes of a certain color to another using SurfArray (Array slicing issue)
https://gamedev.stackexchange.com/questions/26550/how-can-a-pygame-image-be-colored
Pygame Surfarray Module Documentation
http://www.pygame.org/docs/ref/surfarray.html#pygame.surfarray.blit_array

Write classes and instantiate the objects with color code variables.
You can write a method that will draw the shape/image in combination with state-specific data.

Related

How to overlay outline Image with transparent background and filling?

I want to take one image, and overlay it as its outline only without background/filling. I have one image that is an outline in PNG format, that has had its background, as well as the contents within the outline removed, so that when opened, all is transparent except the outline, similar to this image:
However, when I open the image and try to overlay it in OpenCV, the background and area within the outline shows as all-white, showing the full rectangle of the image's dimensions and obscuring the background image.
However, what I want to do is the following, where only the outline is overlayed on the background image, like so:
Bonus points if you can help me with changing the color of the outline as well.
I don't want to deal with any blending with alphas, as I need the background to appear in full, and want the outline very clear.
In this special case, your image has some alpha channel you can use. Using Boolean array indexing, you can access all values 255 in the alpha channel. What's left to do, is setting up some region of interest (ROI) in the "background" image w.r.t. some position, and in that ROI, you again use Boolean array indexing to set all pixels to some color, i.e. red.
Here's some code:
import cv2
# Open overlay image, and its dimensions
overlay_img = cv2.imread('1W7HZ.png', cv2.IMREAD_UNCHANGED)
h, w = overlay_img.shape[:2]
# In this special case, take the alpha channel of the overlay image, and
# check for value 255; idx is a Boolean array
idx = overlay_img[:, :, 3] == 255
# Open image to work on
img = cv2.imread('path/to/your/image.jpg')
# Position for overlay image
top, left = (50, 50)
# Access region of interest with overlay image's dimensions at position
# img[top:top+h, left:left+w] and there, use Boolean array indexing
# to set the color to red (for example)
img[top:top+h, left:left+w, :][idx] = (0, 0, 255)
# Save image
cv2.imwrite('output.png', img)
That's the output for some random "background" image:
For the general case, i.e. without a proper alpha channel, you could threshold the overlay image to set up a proper mask for the Boolean array indexing.
----------------------------------------
System information
----------------------------------------
Platform: Windows-10-10.0.16299-SP0
Python: 3.8.5
OpenCV: 4.5.1
----------------------------------------

How to skew an image by moving its vertex?

I'm trying to find a way to transform an image by translating one of its vertexes.
I have already found various methods for transforming an image like rotation and scaling, but none of the methods involved skewing like so:
There is shearing, but it's not the same since it can move two or more of the image's vertex while I only want to move one.
What can I use that can perform such an operation?
I took your "cat-thing" and resized it to a nice size, added some perfectly vertical and horizontal white gridlines and added some extra canvas in red at the bottom to give myself room to transform it. That gave me this which is 400 pixels wide and 450 pixels tall:
I then used ImageMagick to do a "Bilinear Forward Transform" in Terminal. Basically you give it 4 pairs of points, the first pair is where the top-left corner is before the transform and then where it must move to. The next pair is where the top-right corner is originally followed by where it ends up. Then the bottom-right. Then the bottom-left. As you can see, 3 of the 4 pairs are unmoved - only the bottom-right corner moves. I also made the virtual pixel black so you can see where pixels were invented by the transform in black:
convert cat.png -matte -virtual-pixel black -interpolate Spline -distort BilinearForward '0,0 0,0 399,0 399,0 399,349 330,430 0,349 0,349' bilinear.png
I also did a "Perspective Transform" using the same transform coordinates:
convert cat.png -matte -virtual-pixel black -distort Perspective '0,0 0,0 399,0 399,0 399,349 330,430 0,349 0,349' perspective.png
Finally, to illustrate the difference, I made a flickering comparison between the 2 images so you can see the difference:
I am indebted to Anthony Thyssen for his excellent work here which I commend to you.
I understand you were looking for a Python solution and would point out that there is a Python binding to ImageMagick called Wand which you may like to use - here.
Note that I only used red and black to illustrate what is going on (atop the Stack Overflow white background) and where aspects of the result come from, you would obviously use white for both!
The perspective transformation is likely what you want, since it preserves straight lines at any angle. (The inverse bilinear only preserves horizontal and vertical straight lines).
Here is how to do it in ImageMagick, Python Wand (based upon ImageMagick) and Python OpenCV.
Input:
ImageMagick
(Note the +distort makes the output the needed size to hold the full result and is not restricted to the size of the input. Also the -virtual-pixel white sets color of the area outside the image pixels to white. The points are ordered clockwise from the top left in pairs as inx,iny outx,outy)
convert cat.png -virtual-pixel white +distort perspective \
"0,0 0,0 359,0 359,0 379,333 306,376 0,333 0,333" \
cat_perspective_im.png
Python Wand
(Note the best_fit=true makes the output the needed size to hold the full result and is not restricted to the size of the input.)
#!/bin/python3.7
from wand.image import Image
from wand.display import display
with Image(filename='cat.png') as img:
img.virtual_pixel = 'white'
img.distort('perspective', (0,0, 0,0, 359,0, 359,0, 379,333, 306,376, 0,333, 0,333), best_fit=True)
img.save(filename='cat_perspective_wand.png')
display(img)
Python OpenCV
#!/bin/python3.7
import cv2
import numpy as np
# Read source image.
img_src = cv2.imread('cat.png')
# Four corners of source image
# Coordinates are in x,y system with x horizontal to the right and y vertical downward
pts_src = np.float32([[0,0], [359,0], [379,333], [0,333]])
# Four corners of destination image.
pts_dst = np.float32([[0, 0], [359,0], [306,376], [0,333]])
# Get perspecive matrix if only 4 points
m = cv2.getPerspectiveTransform(pts_src,pts_dst)
# Warp source image to destination based on matrix
# size argument is width x height
# compute from max output coordinates
img_out = cv2.warpPerspective(img_src, m, (359+1,376+1), cv2.INTER_LINEAR, borderMode=cv2.BORDER_CONSTANT, borderValue=(255, 255, 255))
# Save output
cv2.imwrite('cat_perspective_opencv.png', img_out)
# Display result
cv2.imshow("Warped Source Image", img_out)
cv2.waitKey(0)
cv2.destroyAllWindows()

Make a colorbar for RGB data?

I got data that is made based on a 3 channels, which are red in as RGB, this means that the color bar would have to circle between all 3 colors to show all possible shades Is there a simple way of doing this?
Here is an example. Red is left enhanced, blue right enhanced and green centrally enhanced. (it is looking at spectral features.) This means that Red+Blue (= Purple) would be right and central enhanced and weak in left. etc.
I need a way to show that properly with a colorbar of sorts.
I'm not sure I understood what is your expected result. I'm providing a temporary answer anyway so that you can eventually point me to the right direction.
This is an example colorbar made with numpy arrays:
The code I used to generate it is the following:
import numpy as np
import cv2
# Initialize an empty array that matches opencv ranges for hsv images:
# hue (cylinder 180°) 0-179 (multiplied by 10 to "stretch" horizontally)
# saturation is fixed at 254
# value (0-254)
bar = np.ndarray([255,1800,3], dtype="uint8")
for x in range(1800):
for y in range(255):
bar[y,x,0] = int(x/10)
bar[y,x,1] = 254
bar[y,x,2] = y
#Convert to BGR (opencv standard instead of rgb)
bgr = cv2.cvtColor(bar, cv2.COLOR_HSV2BGR)
cv2.imshow('Colorbar', bgr)
cv2.waitKey()

Fill in a hollow shape using Python and pillow (PIL)

I am trying to write a method that will fill in a given shape so that it becomes solid black.
Example:
This octagon which initially is only an outline, will turn into a solid black octagon, however this should work with any shape as long as all edges are closed.
Octagon
def img_filled(im_1, im_2):
img_fill_neg = ImageChops.subtract(im_1, im_2)
img_fill = ImageOps.invert(img_fill_neg)
img_fill.show()
I have read the docs 10x over and have found several other ways to manipulate the image, however I can not find an example to fill in a pre-existing shape within the image. I see that using floodfill() is an option, although I'm not sure how to grab the shape I want to fill.
Note: I do not have access to any other image processing libraries for this task.
There are several ways of doing this. You could do as I do here, and fill all the areas outside the outline with magenta, then make everything that is not magenta into black, and then revert all artificially magenta-coloured pixels to white.
I have interspersed intermediate images in the code, but you can just grab all the bits of code and collect them together in order to have a working lump of code.
#!/usr/bin/env python3
from PIL import Image, ImageDraw
import numpy as np
# Open the image
im = Image.open('octagon.png').convert('RGB')
# Make all background (exterior to octagon) pixels magenta (255,0,255)
ImageDraw.floodfill(im,xy=(0,0),value=(255,0,255),thresh=200)
# DEBUG
im.save('intermediate.png')
# Make everything not magenta black
n = np.array(im)
n[(n[:, :, 0:3] != [255,0,255]).any(2)] = [0,0,0]
# Revert all artifically filled magenta pixels to white
n[(n[:, :, 0:3] == [255,0,255]).all(2)] = [255,255,255]
Image.fromarray(n).save('result.png')
Or, you could fill all the background with magenta, then locate a white pixel and flood-fill with black using that white pixel as a seed. The method you choose depends on the expected colours of your images, and the degree to which you wish to preserve anti-aliasing and so on.

Python Imaging Library- Merging one type of pixel into another image

Basically, I have two images. One is comprised of white and black pixels, the black pixels making up a word, and the other image that I'm trying to paste the black pixels on top of. I've pasted the code below, however I'm aware that there's an issue with the "if pixels [x,y] == (0, 0, 0):' being a tuple and not an indice, however I'm uncertain of how to get it to look for black pixels with other means.
So essentially I need to find, and remember the positions of, the black pixels so that I can paste them onto the first image. Any help is very much appreciated!
image_one = Image.open (image_one)
image_two = Image.open (image_two)
pixels = list(image_two.getdata())
for y in xrange(image_two.size[1]):
for x in xrange(image_two.size[0]):
if pixels[x,y] == (0, 0, 0):
pixels = black_pixels
black_pixels.append()
image = Image.open (image_one);
image_one.putdata(pixels)
image.save(image_one+ "_X.bmp")
del image_one, image_two;
You're almost there. I am not too familiar with the PIL class, but instead of calling the getdata method, let's use getpixel directly on the image object, and directly set the results into the output image. That eliminates the need to store the set of pixels to overwrite. However, there may be cases beyond what you've listed here where such an approach would be necessary. I created a random image and then set various pixels to black. For this test I used a different condition - if the R channel of the image is greater than 50. You can comment that out and use the other test, which tests for tuple (R,G,B) == (0,0,0) which will work fine.
imagea = PIL.Image.open('temp.png')
imageb = PIL.Image.open('temp.png')
for y in xrange(imagea.size[1]):
for x in xrange(imagea.size[0]):
currentPixel = imagea.getpixel((x,y))
if currentPixel[0] > 50:
#if currentPixel ==(0,0,0):
#this is a black pixel, you can directly modify image 2 now
imageb.putpixel((x,y),(0,0,0))
imageb.save('outputfile.png')
An alternative way to do this is just to multiply the two images together. Any pixel that's black in the binary image will be black in the result (multiply by zero) and any pixel that's white in the binary image will be unchanged from the other image in the result (multiply by one).
PIL can do this,
from PIL import Image, ImageChops
image_one = Image.open("image_one.bmp")
image_two = Image.open("image_two.bmp")
out = ImageChops.multiply(image_one, image_two)
out.save("output.bmp")

Categories

Resources