How to lower transparency to line in Pillow? - python

How to lower opacity to line? I would like to lower opacity to one of line in example bellow.
from PIL import Image, ImageDraw
img = Image.new('RGB', (100, 100), (255, 255, 255))
draw = ImageDraw.Draw(img)
draw.line((100, 30, 0, 30), (0, 0, 0), 20)
draw.line((100, 70, 0, 70), (0, 0, 0), 20)
img.show()
I have seen in one example they created opacity like this...
TRANSPARENCY = .25 # Degree of transparency, 0-100%
OPACITY = int(255 * TRANSPARENCY)
But don't know how to apply to one of lines. Any ideas?
EDIT
I made some changes (based on answer of #Pedro Maia), it still doesn't work, just changes a color, it doesn't lower opacity to see background color.
from PIL import Image, ImageDraw
img = Image.new('RGBA', (500, 500), (255, 255, 255))
draw = ImageDraw.Draw(img)
TRANSPARENCY = .25 # Degree of transparency, 0-100%
draw.line((200, 0, 200, 600),(255, 0, 0), 60)
draw.line((500, 100, 0, 100), (0, 0, 0, int(255 * TRANSPARENCY)), 60)
draw.line((500, 400, 0, 400),(0, 0, 0), 60)
img
And I have to convert it to RGB to export it as 'jpg'

You would have to do something like this, which is similar to how the example code works, to do what (I think) you want to. I changed the code you added to your question in the EDIT slightly so it better demonstrates that lines of different amounts of transparency can be drawn.
from PIL import Image, ImageDraw
RED = (255, 0, 0)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
# Calculate alpha given a 0-100% opacity value.
opacity = lambda transparency: (int(255 * (transparency/100.)),) # Returns a monuple.
def draw_transp_line(image, xy, color, width=1, joint=None):
""" Draw line with transparent color on the specified image. """
if len(color) < 4: # Missing alpha?
color += opacity(100) # Opaque since alpha wasn't specified.
# Make an overlay image the same size as the specified image, initialized to
# a fully transparent (0% opaque) version of the line color, then draw a
# semi-transparent line on it.
overlay = Image.new('RGBA', image.size, color[:3]+opacity(0))
draw = ImageDraw.Draw(overlay) # Create a context for drawing things on it.
draw.line(xy, color, width, joint)
# Alpha composite the overlay image onto the original.
image.alpha_composite(overlay)
# Create opaque white RGBA background image.
img = Image.new('RGBA', (500, 500), (255, 255, 255)+opacity(100))
draw_transp_line(img, ((200, 0), (200, 600)), RED+opacity(100), 60)
draw_transp_line(img, ((500, 100), (0, 100)), BLACK+opacity(25), 60)
draw_transp_line(img, ((150, 50), (600, 400)), BLACK+opacity(50), 60)
img = img.convert("RGB") # Remove alpha for saving in jpg format.
img.save('transparent_lines.jpg')
img.show()
JPG image created:

With draw.line you can pass as argument RGB or RGBA just pass the value of the transparency:
draw.line((100, 30, 0, 30), (0, 0, 0, int(255 * TRANSPARENCY)), 20)
Also when creating the image set it as RGBA:
img = Image.new('RGBA', (100, 100), (255, 255, 255))

Related

To cut a piece from a circle, I have to do the math manually first. Is there a smarter way to do the job using Python along with opencv?

I'm trying to cut a piece from a circle using Python along with opencv, here is the code
firstly, I constructed the circle
layer1 = np.zeros((48, 48, 4))
cv2.circle(layer1, (24, 24), 23, (0, 0, 0, 255), -1)
res = layer1[:]
and I got
and then, I drew a smaller square on it
start_point = (24, 0); end_point = (48, 24); color = (255, 0, 0)
cv2.rectangle(res, start_point, end_point, color, -1)
which gives
similarly, I drew a triangle on the circle
pt1 = (24, 0); pt2 = (48, 0); pt3 = (24, 24)
triangle_cnt = np.array( [pt1, pt2, pt3] )
cv2.drawContours(res, [triangle_cnt], 0, (255,0,0), -1)
which gives
I can go along this way to draw a smaller triangle, 1/16, 1/32 and so on.
I have to do the math manually to get the vertices.
Is there a smarter (more elegant) way to do the job?
import cv2
import numpy as np
# Colors (B, G, R)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
# Create new blank 300x150 white image
width, height = 800, 500
img = np.zeros((height, width, 3), np.uint8)
img[...] = BLACK
center = (width//2, height//2)
axes = (200, 200) # axes radius, keep equal to draw circle.
angle = 0 #clockwise first axis
startAngle = 0
endAngle = 90
color = WHITE
img = cv2.ellipse(img, center, axes, angle, startAngle, endAngle, color, thickness=-1)
cv2.imshow('image', img)
cv2.waitKey(-1)
You can play with startAngle and endAngle to change the position of the white part.
Another option is to change the angle option (to -90 for example to rotate counter clockwise).
EDIT to show the different end angles add
img = cv2.ellipse(img, center, axes, angle, startAngle, endAngle/2, (255, 0, 0), thickness=-1)
img = cv2.ellipse(img, center, axes, angle, startAngle, endAngle/4, (0, 255, 0), thickness=-1)
img = cv2.ellipse(img, center, axes, angle, startAngle, endAngle/8, (0, 0, 255), thickness=-1)

How to change a direction of lines in Pillow python?

I'm trying to make a grid, and I made a lines in one way but other way I it doesn't seem to work, I got some weird lines going in other directions. Any idea how to get it right?
from PIL import Image, ImageDraw
img = Image.new('RGB', (1000, 1000), (255, 255, 255))
draw = ImageDraw.Draw(img)
for y in range (-2000, 2000, 200):
draw.line(((y, 2000), (2000, y)), (0, 0, 0), 20)
img
Output produced by code sample
Thanks in advance!
you are trying to draw : y = -x, so:
from PIL import Image, ImageDraw
img = Image.new('RGB', (1000, 1000), (255, 255, 255))
draw = ImageDraw.Draw(img)
for x in range (-1000, 1000, 100):
draw.line(((x,-x),(x+img.size[0], -x+img.size[1])), (0, 0, 0), 20)
img.show()
I used img.size to dynamically choose the second point of the line , you can hardcode it to 2000 (x+2000,-x+2000) , If you want to keep it that way

Why does the transparency in my image become black, and how do I remove it?

I am loading a partially-transparent image into pygame and cropping it the following way:
img = pygame.image.load(image_url).convert_alpha()
cropped_img = pygame.Surface((100, 100)).convert_alpha()
cropped_img.blit(img, (0, 0))
The transparent sections of the image are seen as black. set_colorkey((0, 0, 0)) makes the black transparent, but it also makes the black in the image transparent. How would I only get rid of the transparency-caused black?
The line
cropped_img = pygame.Surface((100, 100)).convert_alpha()
does not create a transparent image. pygame.Surface((100, 100)) generates a completely black image. convert_alpha() does not change that. If you create a new surface and use convert_alpha(), the alpha channels are initially set to maximum. The initial value of the pixels is (0, 0, 0, 255)
If you want to crate a transparent image, you have 2 options. Either set a black color key with pygame.Surface.set_colorkey, before converting the foramt of the Surface:
cropped_img = pygame.Surface((100, 100))
cropped_img.set_colorkey(0)
cropped_img = cropped_img.convert_alpha()
or use the SRCALPHA flag to create a Surface with a per pixel alpha format.
cropped_img = pygame.Surface((100, 100), pygame.SRCALPHA)
Final code:
img = pygame.image.load(image_url).convert_alpha()
cropped_img = pygame.Surface((100, 100), pygame.SRCALPHA)
cropped_img.blit(img, (0, 0))

Stacking different types of transparency in pygame

I have a custom GUI module which uses objects in a tree to manage the interface, and blits them in the right order above one another.
Now amongst my objects, I have some which are just surfaces with per-pixel transparency, and the others use a colorkey.
My problem is that when blitting a surface with per-pixel transparency on another filled with a colorkey, the first surface changes the color of some of the pixels in the second, which means they are no longer transparent. How could I mix those without having to get rid of the per-pixel transparency ?
You could just convert your Surfaces that use a colorkey to use per-pixel transparency before blitting another per-pixel transparency Surface on it using convert_alpha.
Example:
COLORKEY=(127, 127, 0)
TRANSPARENCY=(0, 0, 0, 0)
import pygame
pygame.init()
screen = pygame.display.set_mode((200, 200))
for x in xrange(0, 200, 20):
pygame.draw.line(screen, (255, 255, 255), (x, 0),(x, 480))
# create red circle using a colorkey
red_circle = pygame.Surface((200, 200))
red_circle.fill(COLORKEY)
red_circle.set_colorkey(COLORKEY)
pygame.draw.circle(red_circle, (255, 0, 0), (100, 100), 25)
#create a green circle using alpha channel (per-pixel transparency)
green_circle = pygame.Surface((100, 100)).convert_alpha()
green_circle.fill(TRANSPARENCY)
pygame.draw.circle(green_circle, (0, 255, 0, 127), (50, 50), 25)
# convert colorkey surface to alpha channel surface before blitting
red_circle = red_circle.convert_alpha()
red_circle.blit(green_circle, (75, 75))
screen.blit(red_circle, (0, 0))
pygame.display.flip()
Result:

Merging background with transparent image in PIL

I have a png image as background and I want to add a transparent mesh to this background but this doesn't work as expected. The background image is converted to transparent on places where I apply transparent mesh.
I am doing:
from PIL import Image, ImageDraw
map_background = Image.open(MAP_BACKGROUND_FILE).convert('RGBA')
map_mesh = Image.new('RGBA', (width, height), (0, 0, 0, 0))
draw = ImageDraw.Draw(map_mesh)
# Create mesh using: draw.line([...], fill=(255, 255, 255, 50), width=1)
...
map_background.paste(map_mesh, (0, 0), map_mesh)
But the result is:
You can see a chessboard pattern if you look carefully (used in graphics programs as no background). Transparent lines makes the background layer transparent too in places where both layers met. But I only want the transparent line to be added on top of the background.
I can solve it with:
map_background.paste((255,255,255), (0, 0), map_mesh)
but as I use different colors for different lines, I would have to make for every color this process. If I had 100 colors, then I need 100 layers what is not very good solution.
What you are trying to do is to composite the grid onto the background, and for that you need to use Image.blend or Image.composite. Here's an example using the latter to composite red lines with random alpha values onto a white background:
import Image, ImageDraw, random
background = Image.new('RGB', (100, 100), (255, 255, 255))
foreground = Image.new('RGB', (100, 100), (255, 0, 0))
mask = Image.new('L', (100, 100), 0)
draw = ImageDraw.Draw(mask)
for i in range(5, 100, 10):
draw.line((i, 0, i, 100), fill=random.randrange(256))
draw.line((0, i, 100, i), fill=random.randrange(256))
result = Image.composite(background, foreground, mask)
From left to right:
[background] [mask]
[foreground]
[result]
(If you are happy to write the result back to the background image, then you can use one of the masked versions of Image.paste, as pointed out by Paulo Scardine in a deleted answer.)
I had trouble getting the above examples to work well. Instead, this worked for me:
import numpy as np
import Image
import ImageDraw
def add_craters(image, craterization=20.0, width=256, height=256):
foreground = Image.new('RGBA', (width, height), (0, 0, 0, 0))
draw = ImageDraw.Draw(foreground)
for c in range(0, craterization):
x = np.random.randint(10, width-10)
y = np.random.randint(10, height-10)
radius = np.random.randint(2, 10)
dark_color = (0, 0, 0, 128)
draw.ellipse((x-radius, y-radius, x+radius, y+radius), fill=dark_color)
image_new = Image.composite(foreground, image, foreground)
return image_new

Categories