Drawing a narrow filled arc in python - python

I am trying to draw a narrow filled arc in python--the height is small and the width is very wide. I want it to startX at 250, startY at 550, i want the width to be 245, the height to be 15... and then I am stuck.
I have tried a few different things for the start/angle but it doesn't look right:
0/90.
I just want it to be a straight horizontal line with a small arc attached to make a semi-circle.
Here is an example of what I tried:
addArcFilled(pic, 250, 550,245,15, 0, 90, blue)

#knells Probably this functions does exactly what you need: http://pillow.readthedocs.io/en/3.1.x/reference/ImageDraw.html#PIL.ImageDraw.PIL.ImageDraw.Draw.arc
IL.ImageDraw.Draw.arc(xy, start, end, fill=None)
Draws an arc (a portion of a circle outline) between the start and end angles, inside the given bounding box.
PIL.ImageDraw.Draw.chord(xy, start, end, fill=None, outline=None)
Same as arc(), but connects the end points with a straight line.
Sample code below:
from PIL import Image, ImageDraw
# get an image
im = Image.open('test.png').convert('RGBA')
# create draw context and draw arc
draw = ImageDraw.Draw(im)
draw.chord([10,10, 200, 200], 0,45, fill=0)
del draw
im.save('test_out.png', "PNG")
im.show()

Related

PIL arc position on Oled Display

I am busy with something witch is not so familiar to me. Trying to design a small logo to be displayed on a Oled display sh1106, 128x64
I am using PIL and Luma libraries.
I am battling to position a small arc in the right position.
that small little arc in the yellow circle should be positioned where the arrow is pointing.
This is the code I am using for the arc:
shape = [(32, 32), (36,36)]
draw.arc(shape, start = 160, end = 20, fill ="white")
As soon as I change any of the parameters in those two lines above, the shape, size of the arc changes. Even the position changes, but I assume is because of the size change. Thats not I won't.
Is there any other parameter which I am missing to position the arc in the right place?
Hopefully this little example will show you how it works. The bbox specifies the top-left and bottom-right corners of the bounding box that encloses the arc. I have drawn in the bounding box of each arc in the same colour:
#!/usr/bin/env python3
from PIL import Image, ImageDraw
# Create black image and get drawing context
im = Image.new("RGB", (200, 100))
draw = ImageDraw.Draw(im)
# Draw white arc with bbox
bbox = [(10, 20), (80, 60)]
draw.arc(bbox, start=180, end=360, fill='white')
draw.rectangle(bbox, outline='white')
# Draw red arc with bbox
bbox = [(60, 30), (190, 90)]
draw.arc(bbox, start=180, end=360, fill='red')
draw.rectangle(bbox, outline='red')
# Save result
im.save('result.png')

How do I draw a matrix round in python?

I have a matrix filled with 0 and 1. I would like to draw it round instead of square. That is, divide the circle into sectors and color them according to the matrix as in this picture:
Each value in this array corresponds to the color that should be filled in the image area. I painted the area pink for clarity:
I managed to create pie slices that I can shade, which works for the sections in circle b for example, but not for the other two:
from PIL import Image, ImageDraw
x_center = 400 // 2
y_center = 400 //2
img = Image.new('RGBA', (400, 400), 'white') Here's what happened
idraw = ImageDraw.Draw(img)
idraw.pieslice([x_center-100, x_center-100,
y_center + 106, y_center + 106], 225, 315, fill='blue')
Here's what happened:
Do you know how to do it in matplotlib or plotly? Theoretically, I understand how to do it, practically-no... Can you help me please??

Line styles in pygame

In pygame, when I draw a rectangle, corners are not filled like I want them to be. It looks like:
The problem is the same even with thick lines so I don't think it is the problem.
In another drawing library there were line styles like square or round. I searched everywhere and I couldn't find something similar. Anyone knows how this can be done without filling the corners manually?
import pygame
pygame.init()
while True:
window = pygame.display.set_mode((500, 500))
pygame.draw.rect(window, (255, 255, 255), (100, 100, 100, 100), 10)
pygame.display.flip()
draw.rect is really just a shortcut for draw.polygon using the four corners of the Rect. For each edge of the polygon, it draws a line of the specified width from one vertex to the next. It doesn't go beyond the vertex when you increase the line width. This will make it look like there are gaps in the corners for widths > 1. If you notice there are no gaps for widths of 0 and 1. Also notice that when increasing the line width, the lines grow on both sides (inside and outside of the specified rect). In fact if you increase your width to half the size of the rect, you get something that looks like a cross. If you are wanting better thick rectangles where you can specify the exact inside and outside, I'd use Surface.fill() for the outer Rect and the inside Rect.
Add example of Surface.fill()
window.fill((255, 255, 255), Rect(100, 100, 100, 100)) # outer rect
window.fill((0, 0, 0), Rect(110, 110, 80, 80)) # inner rect

Draw Ellipse in Python PIL with line thickness

I am trying to draw a circle on an image, using Python. I tried this using PIL but I would like to specify a linewidth. Currently, PIL draws a circle but the border is too thin.
Here is what I have done.
For a test image: I created a 1632 X 1200 image in MS Paint and filled it green. I called it test_1.jpg. Here is the input file:
from PIL import Image, ImageDraw
im = Image.open('test_1.jpg')
width, height = im.size
eX, eY = 816,816 #Size of Bounding Box for ellipse
bbox = (width/2 - eX/2, height/2 - eY/2, width/2 + eX/2, height/2 + eY/2)
draw = ImageDraw.Draw(im)
bbox_L = []
for j in range(0,5):
bbox_L.append([element+j for element in bbox])
draw.ellipse(tuple(bbox_L[j]), outline ='white')
im.show()
Basically, I tried to draw multiple circles that would be centered at the same spot but with a different radius. My thinking was that this would create the effect of a thicker line.
However, this is producing the output shown in the attached file below:
Problem: As you can see, the bottom-left and top-right are too thin. Also, there are gaps between the various circles (see top left and bottom right).
The circle has a varying thickness. I am looking a circle with a uniform thickness.
Question:
Is there a way to do draw a circle in Python, on an image like test_1.jpg, using PIL, NumPy, etc. and to specify line thickness?
I had the same problem, and decided to write a helper function, similar to yours. This function draws two concentric ellipses in black and white on a mask layer, and the intended outline colour is stamped onto the original image through the mask. To get smoother results (antialias), the ellipses and mask is drawn in higher resolution.
Output with and without antialias
The white ellipse is 20 pixels wide, and the black ellipse is 0.5 pixels wide.
Code
from PIL import Image, ImageDraw
def draw_ellipse(image, bounds, width=1, outline='white', antialias=4):
"""Improved ellipse drawing function, based on PIL.ImageDraw."""
# Use a single channel image (mode='L') as mask.
# The size of the mask can be increased relative to the imput image
# to get smoother looking results.
mask = Image.new(
size=[int(dim * antialias) for dim in image.size],
mode='L', color='black')
draw = ImageDraw.Draw(mask)
# draw outer shape in white (color) and inner shape in black (transparent)
for offset, fill in (width/-2.0, 'white'), (width/2.0, 'black'):
left, top = [(value + offset) * antialias for value in bounds[:2]]
right, bottom = [(value - offset) * antialias for value in bounds[2:]]
draw.ellipse([left, top, right, bottom], fill=fill)
# downsample the mask using PIL.Image.LANCZOS
# (a high-quality downsampling filter).
mask = mask.resize(image.size, Image.LANCZOS)
# paste outline color to input image through the mask
image.paste(outline, mask=mask)
# green background image
image = Image.new(mode='RGB', size=(700, 300), color='green')
ellipse_box = [50, 50, 300, 250]
# draw a thick white ellipse and a thin black ellipse
draw_ellipse(image, ellipse_box, width=20)
# draw a thin black line, using higher antialias to preserve finer detail
draw_ellipse(image, ellipse_box, outline='black', width=.5, antialias=8)
# Lets try without antialiasing
ellipse_box[0] += 350
ellipse_box[2] += 350
draw_ellipse(image, ellipse_box, width=20, antialias=1)
draw_ellipse(image, ellipse_box, outline='black', width=1, antialias=1)
image.show()
I've only tested this code in python 3.4, but I think it should work with 2.7 without major modification.
Simple (but not nice) solution is to draw two circles (the smaller one with color of background):
outline = 10 # line thickness
draw.ellipse((x1-outline, y1-outline, x2+outline, y2+outline), fill=outline_color)
draw.ellipse((x1, y1, x2, y2), fill=background_color)
From version 5.3.0 onwards, released on 18 Oct 2018, Pillow has supported width for ImageDraw.ellipse. I doubt many people are using PIL nowadays.
I don't think there's a way to specify ellipse thickness, but you probably can draw lines at each pixel where ellipse pass, with the argument width=...
NB: I'm foreign, so sorry if my english is wrong.
You can use the Image.core.draw method like this:
zero_array = np.zeros((224,224))
im = Image.fromarray(np.uint8(zero_array))
draw = ImageDraw.Draw(im)
dr_im = Image.core.draw(im.getdata(), 0)
dr_im.draw_rectangle((22,33, 150,100),220,2)
dr_im.draw_rectangle((22,33, 150,100),125,0)
#draw.rectangle((22,33, 150,100), fill=220,outline = 125)
print(np.array(im)[33][23])
im.show()

Python/Pygame: Drawing graph origin mathematically?

I am working on a graphing program that I am calling PyGraph.
It allows you to create a graph of any size and draw on it, and later in development I will provide coordinates and things, but for now I have one question: How can I draw a intersecting lines through the center to represent the origin?
Here is what I have so far:
#pygraph
import pygame
from pygame.locals import *
pygame.init()
screen=pygame.display.set_mode((640,480))
x=0
y=0
size=16
screen.fill((255,255,255))
pygame.draw.line(screen, (0,0,0), (screen.get_width()/2,0),(screen.get_width()/2,screen.get_height()),5)
pygame.draw.line(screen, (0,0,0), (0,screen.get_height()/2),(screen.get_width(),screen.get_height()/2),5)
while True:
while y<480:
pygame.draw.rect(screen,(0,0,0),(x,y,size,size),1)
if x>640:
x=0
y+=size
pygame.draw.rect(screen,(0,0,0),(x,y,size,size),1)
x+=size
for e in pygame.event.get():
if e.type==QUIT:
exit()
if e.type==KEYUP:
if e.key==K_SPACE:
x=0
y=0
screen.fill((255,255,255))
pygame.draw.line(screen, (0,0,0), (screen.get_width()/2,0),(screen.get_width()/2,screen.get_height()),5)
pygame.draw.line(screen, (0,0,0), (0,screen.get_height()/2),(screen.get_width(),screen.get_height()/2),5)
size=input('Enter size: ')
pygame.display.flip()
The lines go though the center, but it doesn't work for every size graph. I'm not the best at math, but I hope this isn't obvious.. any advice?
The problem is that you draw the grid using the top left corner as your anchor. That is, all your grid rectangles have one corner in the top left. This becomes a problem when the distance between the center line and the screen edge is not divisible by the size - you can't divide a line of 640 units into even divisions of 15, for example.
A far better solution would be to use the center as the anchor. So basically, all the grid rectangles have one corner in the center of the graph, which means you will never get any "remainder" on the center line, and the "remainder" will instead be on the border of the graph, which looks much nicer.
Here is code for anchoring your rectangles at the center (should replace your original while y<480 loop):
while y<=480/2+size:
pygame.draw.rect(screen,(0,0,0),(640/2+x, 480/2+y,size,size),1)
pygame.draw.rect(screen,(0,0,0),(640/2-x, 480/2+y,size,size),1)
pygame.draw.rect(screen,(0,0,0),(640/2+x, 480/2-y,size,size),1)
pygame.draw.rect(screen,(0,0,0),(640/2-x, 480/2-y,size,size),1)
x+=size
if x>=640/2+size:
x=0
y+=size
Brief explanation:
I change the anchor of the rectangle (the point you pass into pygame.draw.rect) to the center of the graph, and instead of drawing one rectangle, I draw four - one in each quadrant of the graph.
I also fixed the code a bit to not need to call pygame.draw.rect() in the if statement.
A minor style tip:
Replace 480 and 640 with "screen.width" and "screen.height", so you can adjust the width and height later without problems.

Categories

Resources