Python PIL to draw a circle-segment of sunshine - python

I've got a problem for which I want to draw a circle/object at a certain position in an image, and then I want to draw rays emanating from that image--with each ray being separated by 1 degree. The rays would only be cast on a 145 degree segment, though--so it doesn't form a full circle. I am using Python PIL (to which I am a novice) to accomplish that task--although I'm not strict on language requirements.
def drawSunshine(im):
draw = ImageDraw.Draw(im)
x, y = im.size
draw.ellipse((370,200, 400,230), fill='red',outline='black')
draw.line((370,205,390,218), fill='black',width=3)
draw.point((100,100),'red')
im.show()
I was thinking that I could start by iterating over every pixel around the circle. And then I could just change the colour of that pixel.
Edit This approach made sense to me because I was planning on drawing this image on a black-and-white PNG file. If the current pixel was white, I was going to draw another pixel of the line, if the current pixel was black: I would consider it to be an obstacle and terminate the line at that point.
However, the main questions:
How do I iterate around an object at an arbitrary position in an image such that the area around said object is a circle or a circle segment?
And, how do I ensure that each "ray" I'm drawing per pixel is separated by a 1 degree?
EDIT Is there a way to allow for these "rays" to be interrupted by black pixels?

Here's a little function you can adjust;
import Image, ImageDraw
from math import sin, cos, pi
width, height = 400, 400
skyBlue = (135, 206, 235)
im = Image.new("RGBA", (width, height), skyBlue)
#Draw Sun
draw = ImageDraw.Draw(im)
def drawSun(draw, centre, radius, rays=False, startAngle=0, finishAngle=360, rayAngle=10, rayGap=10, rayLength=1000, rayColour="Yellow", rayOutline="Orange"):
x1,x2 = centre[0] - radius, centre[0] + radius
y1,y2 = centre[1] - radius, centre[1] + radius
if rays:
for rayStart in range(startAngle, finishAngle, rayAngle+rayGap):
rayEnd = (rayStart+rayAngle) * pi/180
rayStart *= pi/180
corner1 = centre[0] + rayLength*cos(rayStart), centre[1] + rayLength*sin(rayStart)
corner2 = centre[0] + rayLength*cos(rayEnd), centre[1] + rayLength*sin(rayEnd)
print [centre, corner1, corner2]
draw.polygon([centre, corner1, corner2], fill="Yellow", outline="Orange")
draw.ellipse((x1, y1, x2, y2), fill="Yellow", outline="Orange")
drawSun(draw, (100, 100), 40, rays=True, startAngle=0, finishAngle=145, rayAngle=3, rayGap=5)
im.save("example.png")

Related

Is there a method in openCV that crops an image and leave the uncropped area in black?

Hi all as the title said is there a way for this? For example, I want to crop fourth quadrant of an image and the other area will be turned to black while retaining its original size. Currently, I am getting the center width and height of the image then accessing the pixel:
Cropped = I[centerHeight:,centerWidth:]
but that just stores the fourth quadrant cropped image. Thanks!
I don't think there is a function in OpenCV that does that work. This function will solve your problem.
import numpy as np
def crop_image(img, cx, cy, w, h):
"""
args:
cx: x coordinate of center
cy: y coordinate of center
w: width of crop
h: height of crop
"""
result = np.zeros(img.shape, dtype=np.uint8)
result[cx - w//2 :cx + w//2, cy - h//2:cy + h//2] = img[cx - w//2 :cx + w//2, cy - h//2:cy + h//2]
return result

Python PIL - Rounded Polygon

Is it possible to dynamically draw a polygon of N-sides with rounded corners? I've seen examples done for rectangles/squares, but not for other polygons. I can easily draw the polygon, but I'm looking to achieve a rounded affect for each corner. Any help is greatly appreciated!
from PIL import Image, ImageDraw
#Triangle
inset = 40
W, H = (300,300)
# Create empty black canvas
im = Image.new('RGBA', (W, H), '#558353')
# Draw polygon
draw = ImageDraw.Draw(im)
draw.polygon([(W/2,inset), (W-inset, H-inset), (inset,H-inset)], fill = 'black')
im.show()
Output:
Desired (created in Lucid Chart):
Here's my best shot at it. The ImageDraw rasterizer isn't so good at drawing wide lines. I had to fudge the line width (with +2) to make it look a little better.
from PIL import Image, ImageDraw
import operator
def vadd(a, b):
""" Vector addition. """
return tuple(map(operator.add, a, b))
#Triangle
inset = 40
W, H = (300,300)
# Create empty black canvas
im = Image.new('RGBA', (W, H), '#558353')
# Draw polygon
draw = ImageDraw.Draw(im)
# Vertices of the polygon.
v = [
(inset, H-inset),
(W-inset, H-inset),
(W/2, inset) ]
# Radius of rounded corner.
r = 10
d = 2*r
# Outline of the polygon.
[ draw.line((v[i], v[i+1]), fill='black', width=d+2) for i in range(len(v)-1) ]
draw.line((v[-1], v[0]), fill='black', width=d+2)
# Draw a circle centered on each vertex.
for corner in v:
c = [vadd(corner, (-r, -r)), vadd(corner, (r, r))]
draw.pieslice(c, 0, 360, 'black')
# Now fill in the middle.
ImageDraw.floodfill(im, (W/2, H/2), (0, 0, 0))
im.show()

Pycairo scale distortion

I am struggling to draw a circle onto a rectangular png background. Whenever I try to draw a circle, it is stretched according to the scale of my image--making it an ellipse instead of a circle.
Here is the set up:
#set background image
surface = cairo.ImageSurface.create_from_png (image)
context = cairo.Context (surface)
WIDTH = cairo.ImageSurface.get_width(surface)
HEIGHT = cairo.ImageSurface.get_height(surface)
context.scale (WIDTH, HEIGHT)
And here is the circle drawing function, (x coord, y coord, radius, color value) and functions used for positioning:
def draw_node(cx,cy,r, rgb):
context.arc(x_scale(cx), y_scale(cy), r, 0, 2 * math.pi)
context.set_source_rgb(rgb[0]/255.0,rgb[1]/255.0,rgb[2]/255.0)
context.fill()
context.stroke()
def x_scale (x):
width = 1000.0
return (x + .5) / width
def y_scale (y):
height = 1000.0 * HEIGHT / WIDTH
return (y + .5) / height
I would particularly appreciate any advice on how to resolve the systemic scaling issue, but I would also appreciate advice on how to draw a circle within the confines of my existing setup.
Thank you very much.
there is probably no way to avoid that context.arc will draw an ellipse when your scaling is not symmetric. with your x_scale (x) function you only scale the center of the circle to be where it should be.
as a workaround i'd suggest to scale both directions by the same amount; sacrifice therefore that x_max and y_max will both be equal to 1.
i'd change in your code:
context.scale(HEIGHT, HEIGHT)
print('surface = 1 x {:1.2f}'.format(WIDTH/HEIGHT))
def draw_node(cx,cy,r, rgb):
context.arc(cx, cy, r, 0, 2 * math.pi)
context.set_source_rgb(rgb[0]/255.0,rgb[1]/255.0,rgb[2]/255.0)
context.fill()
context.stroke()
and not do any scalings (or scale by 1000 if that's what you prefer).

Rectangle Rotation in Python/Pygame

Hey I'm trying to rotate a rectangle around its center and when I try to rotate the rectangle, it moves up and to the left at the same time. Does anyone have any ideas on how to fix this?
def rotatePoint(self, angle, point, origin):
sinT = sin(radians(angle))
cosT = cos(radians(angle))
return (origin[0] + (cosT * (point[0] - origin[0]) - sinT * (point[1] - origin[1])),
origin[1] + (sinT * (point[0] - origin[0]) + cosT * (point[1] - origin[1])))
def rotateRect(self, degrees):
center = (self.collideRect.centerx, self.collideRect.centery)
self.collideRect.topleft = self.rotatePoint(degrees, self.collideRect.topleft, center)
self.collideRect.topright = self.rotatePoint(degrees, self.collideRect.topright, center)
self.collideRect.bottomleft = self.rotatePoint(degrees, self.collideRect.bottomleft, center)
self.collideRect.bottomright = self.rotatePoint(degrees, self.collideRect.bottomright, center)
The rotation code looks to be fine - but, you are aware that pygame's internals don't work with rotated rectangles, do you?
Unless you have some code you wrote yourself with the new rectangle corners, what this does is to define a new rectangle, with sides parallel to the Surface edges, where the original rectangle, when rotated, could be inscribed to, not a rectangle at the same size than the original at a skewed angle. Any Pygame function to which you pass the "self.collideRect" object after the rotation will just do that: treat the rectangle as aligned to the surface,
just as if it has been created with the corners it has now.
If your code requires you to check for things, or even draw, inside a rotated rectangle, you have to perform all the calculations as they where prior to the rotation, and just perform the coordinate rotation at the time of displaying what you want. That is, you work with a global coordinate transform, that is applied in the last step of rendering.
Maybe this can help you:
#load image
image1 = pygame.image.load(file)
#get width and height of unrotated image
width1,height1 = image1.get_size()
#rotate image
image2 = pygame.transform.rotate(image1, angle)
#get width,height of rotated image
width2,height2 = image2.get_size()
#blit rotated image (positon - difference of width or height /2)
display.blit(image2,[round(x - (width1 - width2)/2),round(y - (height1 - height2)/2)])

Python PIL: How to draw an ellipse in the middle of an image?

I seem to be having some trouble getting this code to work:
import Image, ImageDraw
im = Image.open("1.jpg")
draw = ImageDraw.Draw(im)
draw.ellipse((60, 60, 40, 40), fill=128)
del draw
im.save('output.png')
im.show()
This should draw an ellipse at (60,60) which is 40 by 40 pixels. The image returns nothing.
This code works fine however:
draw.ellipse ((0,0,40,40), fill=128)
It just seems that when i change the first 2 co-ords (for where the ellipse should be placed) it won't work if they are larger than the size of the ellipse to be drawn. For example:
draw.ellipse ((5,5,15,15), fill=128)
Works, but only shows part of the rect. Whereas
draw.ellipse ((5,5,3,3), fill=128)
shows nothing at all.
This happens when drawing a rectangle too.
The bounding box is a 4-tuple (x0, y0, x1, y1) where (x0, y0) is the top-left bound of the box and (x1, y1) is the lower-right bound of the box.
To draw an ellipse to the center of the image, you need to define how large you want your ellipse's bounding box to be (variables eX and eY in my code snippet below).
With that said, below is a code snippet that draws an ellipse to the center of an image:
from PIL import Image, ImageDraw
im = Image.open("1.jpg")
x, y = im.size
eX, eY = 30, 60 #Size of Bounding Box for ellipse
bbox = (x/2 - eX/2, y/2 - eY/2, x/2 + eX/2, y/2 + eY/2)
draw = ImageDraw.Draw(im)
draw.ellipse(bbox, fill=128)
del draw
im.save("output.png")
im.show()
This yields the following result (1.jpg on left, output.png on right):
The ellipse function draws an ellipse within a bounding box. So you need to use draw.ellipse((40,40,60,60)) or other coordinates where the top left is smaller than the bottom right.

Categories