I am trying to make a basic Pokemon replica in Python using PyGame. I have followed a few tutorials concerning this library in the past. So, for this project, I will have a basic tile structure, either grass or water. The player is not allowed to move onto a water tile but can move freely on the grass tile. The sprite I obtained via the tutorial is of dimensions 64 x 64 and it occupies the entire space of the tile, so there are no transparent pixels around the boundaries.
However, for my purposes, I need to use my own sprites (64 x 64) which I got from a sprite sheet. However, the sprites, in this case, have the top half of the image as just transparent, unused pixels. The same applies to the quarters on each side of the sprite. So, I need to batch resize all those sprites by multiplying the visible pixel size by 2 along both axes. IE, just increase the sprite size by scale factor 2. I could easily do that, but it would still be ineffective since the new sprite size would be 128 x 128 and the ratio of unused pixels stays the same.
My question:
How can I resize the sprites by scale factor 2 and yet retain the original dimensions of 64 x 64 by just disposing of the transparent pixels which would be outside of the 64 x 64 frame?
I need a method which I could use for all 9600 sprites I have, preferably in Python, even though I wouldn't mind a solution with some other software.
I tried looking for solutions on the web but I don't know how to properly phrase this question.
Create an empty pygame.Surface object with transparent alpha and a size of 32x32:
new_image = pygame.Surface((32, 32), pygame.SRCALPHA)
blit the desired region (32, 16, 32, 32) form the original 64x64 Sprite (original_image) on to the new surface:
new_image.blit(original_image, (0, 0), (32, 16, 32, 32))
Scale the new Surface object by pygame.transform.smoothscale
new_image = pygame.transform.smoothscale(new_image, (64, 64))
If these are images you can easily do this using PIL.
from PIL import Image
Ignore this bit which just makes a 64x64 test image
from scipy import misc
img = misc.face()
img=Image.fromarray(np.uint8(img))
img=img.resize((64,64))
This should show a cute little raccoon
Next we can resize by a scale factor of two as you describe (NOTE: I'm not taking advantage of the fact that this is a square image just so this is more generalizable. You could simplify this a bit with imsz=img.size[0] )
imsz=img.size
# resize by factor of 2
img=img.resize((imsz[0]*2,imsz[1]*2))
Which should give a 2x scaled image
Now we just crop it
# box – The crop rectangle, as a (left, upper, right, lower)-tuple
crpimg=img.crop(box=(int(0.5*imsz[0]),int(0.5*imsz[1]),int(1.5*imsz[0]),int(1.5*imsz[1])))
Which gives us a 64x64 image:
#Rabbid76's answer of copying/blit'ing a section of the original and then enlarging/scaling it is a good option and probably the way I would go.
However, if you have already resized them to the larger size and just want a cropped down version of an existing image/surface, you can use subsurface(). That will give you a surface that is really like a virtual surface that provides a window into a subsection of the original surface. It references the same pixels as the original surface, so if one is changed it is visible in the other as well. That does not seem an issue for what you are doing though.
Related
The situation I am in now is that I have a lot of pictures with different sizes, some are 769x864 pixels others are 2000x3123 pixels and some are even 3500x4000 pixels.
I want to resize all the pictures to a certain height and width or at least close to the given height and width using openCV.
I know the cv2.resize() function but the problem is how I would define a fixed dimension, for example 800x900, without it changing the original pictures' shape.
Does anyone have an idea how this can be done?
That is possible with padding.
If your target size is 800*900, that means the images must have the same ratio (8/9) to be resized without shape changing.
For example if the original image has this size: 2400 * 2600, you should pad the columns with 100 pixels (e.g. add 100 zero pixels to each row border) to make the image 2400*2700. Then you can resize the result image to 800*900 (with 1/3 scale factor) without any change in the shapes of the image.
If I understood you correctly you want to scale down your picture.
You can do it like that:
cv2.resize(src, dsize[, dst[, fx[, fy[, interpolation]]]])
you can find more details and demo code over here:
https://www.tutorialkart.com/opencv/python/opencv-python-resize-image/
I'm very new to Python and am exploring it's use to allow users to build custom images. The idea is that the client would select a few options and the image would be created on the server then downloaded (or used for other things on the server side).
The image is composed of many images, most of which are small icon type of images that are irregular shapes and have transparency. All layers are .png files.
I've tried using Pillow but it seems the image needs to be the same size as the overall image to properly use the transparency of the top layers.
Here's what I've tried so far:
from PIL import Image
background = Image.open("Background.png")
foreground = Image.open("Trim.png")
fire = Image.open("Type_Fire_Large.png")
background = Image.alpha_composite(background, foreground)
background.paste(fire, (150, 150))
background.show()
The image looks like this:
Background.png is the shaded "noise" and Trim.png is the grey diagonal lines. The best part: Trim.png has the center transparent and is able to show Background.png in the middle. But it's also the same size as the image.
The problem is Fire; notice how theres that black border (and odd fuchsia dot). The documentation states that the overlay image needs to be the same size. But it seems like a common scenario where someone would want to place a smaller icon with transparency on top of another image and compose them into one image.
I'm not attached to any particular library, I'm wide open to ideas and alternatives. The only thing I'm trying to do is keep it simple, so creating an entire game engine or the like to produce an image would probably be too much.
To just paste one png on top of another, respecting transparency, try
background.paste(fire, (x,y), fire.convert("RGBA"))
First I'd say Johannes Holmberg already answered your main concern: The missing transparency.
But I can hopefully explain what it's about with that odd fuchsia dot:
A transparent color image is usually stored as RGBA (RGB for Red, Green, Blue and A for Alpha). Here Alpha defines the transparency, from no transparency to full transparency.
Overlaying the images the correct way we see the GIMP Logo but the color stripe is almost invisible - because it's (almost) transparent.
But - as every pixel could be possibly visible - every pixel does still have a color. Even those pixels with 100% transparency. Thus, if we do not take Alpha into consideration we see each pixel color without transparency. This way we might have disturbing colors that are usually not disturbing at all - as long as they are fully transparent.
I am having quite a lot of trouble thinking of how to make a four camera bird's eye view like that seen in luxury cars. Here is the original that I will be using as an example for this question...
Right now, I have made it so the image is skewed using .getPerspectiveTransform but that is just for one image.
I obviously need four and am clueless on how to stitch those images together. I am also clueless if this is how the images are supposed to look like. Here is the code I currently have:
import cv2 as cv
import numpy as np
img1 = cv.imread("testBird.jpg", cv.IMREAD_COLOR)
image = np.zeros((700, 700, 3), np.uint8)
src = np.array([[0,200],[480,200],[480,360],[0,360]],np.float32)
dst = np.array([[0,0],[480,0],[300,360],[180,360]],np.float32)
M = cv.getPerspectiveTransform(src, dst)
warp = cv.warpPerspective(img1.copy(), M, (480, 360))
cv.imshow('transform', warp)
cv.waitKey(0)
cv.destroyAllWindows()
and here is the end image that I would roughly like to have (A friend put together using Photoshop)...
To implement the transform, you need to refer to the getPerspectiveTransform function. It takes:
src: Coordinates of quadrangle vertices in the source image.
dst: Coordinates of the corresponding quadrangle vertices in the destination image.
I think that it's not an easy problem to define "src" and "dst". It needs some computations based on real-world data and cannot be solved by itself, just by having a look at the pictures.
So for me, the key idea is make a plan of the desired scene (what it must look like). It should use real data such as:
the distance between cameras
the angle of view of the cameras
the size of the rectangle between the cameras (the gray and white grid)
Then you can find a good value for the distance E-F depending on the size of the "viewport of your fictive bird's view camera". After that, your job is nearly done.
The dst parameter is simply a scaled version of the rectangle I J L K (for the upper camera). Depending on the size in pixel of the output image.
The src parameter should be a rectangle in your photograph. Its width will fill the entire picture. The height must be computed from the E-F wanted distance.
They are two ways to compute the height of the red rectangle. Either you place "markers" on the real scene (or you try to detect some) to automatically find a horizontal line. Or, you can try to compute it as a complex function of the elevation angle of your camera (but I want to advise you, I think it seems quite complicated).
Here's how I would have solved that problem. I hope it helped :)
I've been attempting to overlay two images in python to match coordinates, the top left and bottom right corners have the same coordinates and their aspects are almost identical bar a few pixels. Although they are different resolutions.
Using PIL I have been able to overlay the images, though after overlaying them the image output is square but the resolution is that of the background image, the foreground image is also re-sized incorrectly (As far as I can see). I must be doing something wrong.
import Image
from PIL import Image
#load images
background = Image.open('ndvi.png')
foreground = Image.open('out.png')
#resizing
foreground.thumbnail((643,597),Image.ANTIALIAS)
#overlay
background.paste(foreground, (0, 0), foreground)
#save
background.save("overlay.png")
#display
background.show()
When dropping the images into something horrible like powerpoint the image aspects are almost identical. I've included an example image, the image on the left is my by hand overlay and the image on the right is the output from python. The background at some point in the code is squashed vertically, also affecting the overlay. I'd like to be able to do this in python and make it correctly look like the left hand image.
A solution upfront.
Background image
width/height/ratio: 300 / 375 / 0.800
Foreground image
width/height/ratio: 400 / 464 / 0.862
Overlay
from PIL import Image
imbg = Image.open("bg.png")
imfg = Image.open("fg.png")
imbg_width, imbg_height = imbg.size
imfg_resized = imfg.resize((imbg_width, imbg_height), Image.LANCZOS)
imbg.paste(imfg_resized, None, imfg_resized)
imbg.save("overlay.png")
Discussion
The most important information you have given in your question were:
the aspect ratios of your foreground and background images are not equal, but similar
the top left and bottom right corners of both images need to be aligned in the end.
The conclusion from these points is: the aspect ratio of one of the images has to change. This can be achieved with the resize() method (not with thumbnail(), as explained below). To summarize, the goal simply is:
Resize the image with larger dimensions (foreground image) to the exact dimensions of the smaller background image. That is, do not necessarily maintain the aspect ratio of the foreground image.
That is what the code above is doing.
Two comments on your approach:
First of all, I recommend using the newest release of Pillow (Pillow is the continuation project of PIL, it is API-compatible). In the 2.7 release they have largely improved the image re-scaling quality. The documentation can be found at http://pillow.readthedocs.org/en/latest/reference.
Then, you obviously need to take control of how the aspect ratio of both images evolves throughout your program. thumbnail(), for instance, does not alter the aspect ratio of the image, even if your size tuple does not have the same aspect ratio as the original image. Quote from the thumbnail() docs:
This method modifies the image to contain a thumbnail version of
itself, no larger than the given size. This method calculates an
appropriate thumbnail size to preserve the aspect of the image
So, I am not sure where you were going exactly with your (643,597) tuple and if you are possibly relying on the thumbnail to have this exact size afterwards.
I have got some surfaces in Pygame with a transparent background. They're all the same size. But there's a different sized circle drawn on each of them, so the circle doesn't exactly fit the image.
Here are some example images (I took a screenshot in Photoshop so you can clearly see the transparency and the size of the images):
Now I want to remove the transparent border around the image so the circle exactly fits into the image. I don't want the surface to be circle shaped, I don't think that's possible, but I want that the surface doesn't have blank columns on the left and right and that it doesn't have any blank rows on the top and the bottom. The wanted results:
The circle on the surfaces changes size every frame so I have to recalculate the new surfaces every frame.
I already Googled it, but I haven't found anything for Pygame surfaces yet. I also tried making my own function but it looks ugly and much worse: the framerate drops from 50 (if I don't call the function) to 30 fps (if I do call the function). I tested it a little bit and I found out that smaller circles take longer to process than bigger circles. How can I do this, but faster. If you want I can show the function I made.
The surface object has a method called get_bounding_rect which is where we will start. The function returns the smallest rect possible which contains all of the non-transparent pixels on the surface.
pixel_rect = image.get_bounding_rect()
With the size of this rect, we can create a new surface:
trimmed_surface = pygame.Surface(pixel_rect.size)
Now blit the portion of image contained within pixel_rect onto trimmed_surface:
trimmed_surface.blit(image, (0,0), pixel_rect)
At this point, trimmed_surface should be a surface the same size as pixel_rect, with the unwanted transparent rows and columns "trimmed" off of the original surface.
Documentation for Surface.get_bounding_rect: http://www.pygame.org/docs/ref/surface.html#Surface.get_bounding_rect