How do I generate circular image thumbnails using PIL?
The space outside the circle should be transparent.
Snippets would be highly appreciated, thank you in advance.
The easiest way to do it is by using masks. Create a black and white mask with any shape you want. And use putalpha to put that shape as an alpha layer:
from PIL import Image, ImageOps
mask = Image.open('mask.png').convert('L')
im = Image.open('image.png')
output = ImageOps.fit(im, mask.size, centering=(0.5, 0.5))
output.putalpha(mask)
output.save('output.png')
Here is the mask I used:
If you want the thumbnail size to be variable you can use ImageDraw and draw the mask:
from PIL import Image, ImageOps, ImageDraw
size = (128, 128)
mask = Image.new('L', size, 0)
draw = ImageDraw.Draw(mask)
draw.ellipse((0, 0) + size, fill=255)
im = Image.open('image.jpg')
output = ImageOps.fit(im, mask.size, centering=(0.5, 0.5))
output.putalpha(mask)
output.save('output.png')
If you want the output in GIF then you need to use the paste function instead of putalpha:
from PIL import Image, ImageOps, ImageDraw
size = (128, 128)
mask = Image.new('L', size, 255)
draw = ImageDraw.Draw(mask)
draw.ellipse((0, 0) + size, fill=0)
im = Image.open('image.jpg')
output = ImageOps.fit(im, mask.size, centering=(0.5, 0.5))
output.paste(0, mask=mask)
output.convert('P', palette=Image.ADAPTIVE)
output.save('output.gif', transparency=0)
Note that I did the following changes:
The mask is now inverted. The white
was replaced with black and vice versa.
I'm converting into 'P' with an 'adaptive' palette. Otherwise, PIL will only use web-safe colors and the result will look bad.
I'm adding transparency info to the image.
Please note: There is a big issue with this approach. If the GIF image contained black parts, all of them will become transparent as well. You can work around this by choosing another color for the transparency.
I would strongly advise you to use PNG format for this. But if you can't then that is the best you could do.
I would like to add to the already accepted answer a solution to antialias the resulting circle, the trick is to produce a bigger mask and then scale it down using an ANTIALIAS filter:
here is the code
from PIL import Image, ImageOps, ImageDraw
im = Image.open('image.jpg')
bigsize = (im.size[0] * 3, im.size[1] * 3)
mask = Image.new('L', bigsize, 0)
draw = ImageDraw.Draw(mask)
draw.ellipse((0, 0) + bigsize, fill=255)
mask = mask.resize(im.size, Image.ANTIALIAS)
im.putalpha(mask)
this produces a far better result in my opinion.
Slight modification on #DRC's solution to also support images which already have transparency. He sets the alpha channel to 0 (invisible) outside the circle and to 255 inside (opaque), so I use darker which takes the min of the mask and the original alpha channel (which can be anywhere betwen 0-255) :-)
from PIL import Image, ImageChops, ImageDraw
def crop_to_circle(im):
bigsize = (im.size[0] * 3, im.size[1] * 3)
mask = Image.new('L', bigsize, 0)
ImageDraw.Draw(mask).ellipse((0, 0) + bigsize, fill=255)
mask = mask.resize(im.size, Image.ANTIALIAS)
mask = ImageChops.darker(mask, im.split()[-1])
im.putalpha(mask)
im = Image.open('0.png').convert('RGBA')
crop_to_circle(im)
im.save('cropped.png')
Thank you very much. I was looking for hours and your idea does the trick.
Together with this other script from there.
PIL round edges and add border
it works perfectly for me.
from PIL import Image
from PIL import ImageDraw, ImageChops
def add_corners( im, rad=100):
circle = Image.new('L', (rad * 2, rad * 2), 0)
draw = ImageDraw.Draw(circle)
draw.ellipse((0, 0, rad * 2, rad * 2), fill=255)
alpha = Image.new('L', im.size, "white")
w, h = im.size
alpha.paste(circle.crop((0, 0, rad, rad)), (0, 0))
alpha.paste(circle.crop((0, rad, rad, rad * 2)), (0, h - rad))
alpha.paste(circle.crop((rad, 0, rad * 2, rad)), (w - rad, 0))
alpha.paste(circle.crop((rad, rad, rad * 2, rad * 2)), (w - rad, h - rad))
alpha = ImageChops.darker(alpha, im.split()[-1])
im.putalpha(alpha)
return im
im = Image.open ('AceOfSpades.png').convert('RGBA')
im = add_corners (im, 24)
im.show()
im.save("perfect.png")
Name this image AceOfSpades.png for testing
Related
Wanted to have two images where i have a mask stacked on top of another image. But in doing so, i wish to not have two images blend together, rather have the final image stacked onto each other like layers
Here's my original images
masked image
code
import cv2
import numpy as np
image = cv2.imread('test72.jpg')
image2 = cv2.imread('test63.jpg')
blank = np.full((image.shape[0], image.shape[1], 3), (255,255,255), np.uint8)
circle = cv2.circle(blank, (300,300), 10, (0, 0, 0), thickness= 100)
blur = cv2.blur(circle, (50, 50), 0)
subtract = cv2.subtract(image, blur)
blended = cv2.addWeighted(image2, 1, subtract, 1, 0)
cv2.imwrite('mask.jpg', subtract)
cv2.imwrite('blend.jpg', blended)
cv2.waitKey(0)
cv2.destroyAllWindows()
Here is what the result looks like when the function cv2.addweighted is added, which results in the bannanas blending in the shoes, is there another function in OpenCv i could do to make this stack rather than blend?
Something like this might work in your case:
im1_alpha = blur/255
im2_alpha = (255-blur)/255
out_img = ((image2 * im1_alpha) + (image * im2_alpha)).astype(np.uint8)
plt.imshow(out_img)
Here is another answer!
I followed this tutorial right here! Alpha blending tutorial right here
import cv2
import numpy as np
image = cv2.imread('test72.jpg')
image2 = cv2.imread('test63.jpg')
blank = np.full((image.shape[0], image.shape[1], 3), (255,255,255), np.uint8)
circle = cv2.circle(blank, (300,350), 10, (0, 0, 0), thickness= 100)
blur = cv2.blur(circle, (50, 50), 0)
image = image.astype(float)
image2 = image2.astype(float)
alpha = blur.astype(float)/255
multiply = cv2.multiply(alpha, image2)
multiply2 = cv2.multiply(1.0 - alpha, image)
add = cv2.add(multiply, multiply2).astype(np.uint8)
cv2.imshow('alpha', add)
cv2.waitKey(0)
cv2.destroyAllWindows()
I am trying to do image classification task and want to make sure my input data all have the same orientation.
The code bellow did not match all the images to the same directions and some flipped wrongly.
I will be thankful if anyone can help me with this matter, Thank you
original image 1
original image 2
import cv2
import numpy as np
import matplotlib.pyplot as plt
def getSubImage(rect, image):
center, size, theta = rect
center, size = tuple(map(int, center)), tuple(map(int, size))
M = cv2.getRotationMatrix2D( center, theta, 1)
dst = cv2.warpAffine(image, M, src.shape[:2])
out = cv2.getRectSubPix(dst, size, center)
return out
image = cv2.imread('orginal1.png')
im_bw = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
blur = cv2.GaussianBlur(im_bw, (5,5), 0)
im_bw = cv2.Canny(blur, 10, 90)
contours, hierarchy = cv2.findContours(im_bw, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
rect = cv2.minAreaRect(contours[0])
out = getSubImage(rect, image)
cv2.imwrite('rotedorginal1.jpg', out)
plt.imshow(out)
plt.show()
You just need to build the matrix that rotates the image the right angle using cv2.getRotationMatrix2D and applying the operation with the matrix using cv2.warpAffine:
(x, y), (w, h), angle = cv2.minAreaRect(contours[0])
result = cv2.warpAffine(image, cv2.getRotationMatrix2D((image.shape[1]//2, image.shape[0]//2), angle-90, 1), (image.shape))
The code above transforms this image:
into this one:
How can I make watermark image transparent? For example, 60% transparent. I have tried with putalpha but seems that it's doesn't work as expected
from PIL import Image
temp_image = Image.open('test1.jpg')
watermark = Image.open('watermark.png')
x, y = temp_image.size
image_with_watermark = Image.new('RGBA', (x, y), (0, 0, 0, 0))
image_with_watermark.paste(temp_image, (0, 0))
image_with_watermark.paste(watermark, (0, 0), mask=watermark)
image_with_watermark.show()
EDIT:
ok, this works, need to figure out how to set it up using %
from PIL import Image
temp_image = Image.open('test1.jpg')
watermark = Image.open('watermark.png')
x, y = temp_image.size
watermask = watermark.convert("L").point(lambda x: min(x, 50))
watermark.putalpha(watermask)
image_with_watermark = Image.new('RGBA', (x, y), (0, 0, 0, 0))
image_with_watermark.paste(temp_image, (0, 0))
image_with_watermark.paste(watermark, (0, 0), mask=watermark)
image_with_watermark.show()
meh, quality of the watermark is very low after:
watermask = watermark.convert("L").point(lambda x: min(x, 50))
watermark.putalpha(watermask)
What is the best way to achieve what I need?
Here's a solution that will work for both RGB and RGBA watermark images:
from PIL import Image
TRANSPARENCY = 65 # percentage
temp_image = Image.open('test1.jpg')
watermark = Image.open('watermark.png')
if watermark.mode!='RGBA':
alpha = Image.new('L', watermark.size, 255)
watermark.putalpha(alpha)
paste_mask = watermark.split()[3].point(lambda i: i * TRANSPARENCY / 100.)
temp_image.paste(watermark, (0,0), mask=paste_mask)
temp_image.save('res.png')
Sample image (author - Neil Howard):
Sample watermark (the background is transparent):
Sample result:
Currently I am working with an image processing project in which I need to split the image into several segments and then apply watermark on each of the segment.
I have written a code which divides the image into segments by masking. You may find the code here. Now i want to implement watermark on each of these segments. The tutorial for watermarking can be found here.
How am I supposed to do that?
Please help as I am new to OpenCV and Python.
Feel free to ask for any further information needed to solve this.
Thank you!
EDIT
I am adding some code for your inference:
`
segment= 'segment storing location'
image = cv2.imread(image path)
segments = slic(img_as_float(image),compactness= 100.0, n_segments = 10, sigma = 5) #segmentation of image
row, col, _ = image.shape
for (i, segVal) in enumerate(np.unique(segments)):
# construct a mask for the segment
print "[x] inspecting segment %d" % (i)
mask = np.zeros(image.shape[:2], dtype = "uint8")
mask[segments == segVal] = 255 #masking image with different mask to create unique segments
bb= (cv2.bitwise_and(image, image, mask = mask) )
cv2.imwrite(segment + str(i) + ".png",bb) #save image segments created
`
Now after saving the segments, I need to watermark each one of them by calling them one after another. This is the code for watermarking:
import numpy as np
import cv2
import os
wk= 'D:\\watermark\\wm.png'
input_im= 'D:\\watermark\\input\\image_01.jpg'
op= 'D:\\watermark\\output'
alpha = 0.25
watermark = cv2.imread(wk, cv2.IMREAD_UNCHANGED)
(wH, wW) = watermark.shape[:2]
image = cv2.imread(input_im)
(h, w) = image.shape[:2]
image = np.dstack([image, np.ones((h, w), dtype="uint8") * 255])
overlay = np.zeros((h, w, 4), dtype="uint8")
overlay[h - wH - 500:h - 500, w - wW - 500:w - 500] = watermark #This is the line where we can set the watermark's coordinates
output = image.copy()
cv2.addWeighted(overlay,alpha, output, 1.0, 0, output)
filename = input_im[input_im.rfind(os.path.sep) + 1:]
p = os.path.sep.join((op, filename))
cv2.imwrite(p, output)
Now how can I extract the coordinates of this segment in order to watermark it?
Edit
This is what I get when the lines
`cv2.circle(im, (cX, cY), 7, (255, 255, 255), -1)
cv2.putText(im, "center", (cX - 20, cY - 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2`
are kept outside the loop:
And this is what I get when they are executed within the loop:
You need to find the countour of the image (I've downloaded your segment image to try this), then compute the center of the contour.
To find the contour, you need to convert the image to gray scale and threshold it, dividing totally black pixels (black background) from non-black ones (your segment).
Finding the center of the segment
The only assumption I've made is that the pixel values of your segments are different from 0 (total black). This assumption may be invalid but, since you're working with photos of natural landscape (like the one you posted) this should not be a problem.
Feel free to ask for further details.
import numpy as np
import cv2
im = cv2.imread('try.png')
imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(imgray,1,255,0) # Threshold to highlight non black pixels
image, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
for c in contours:
# compute the center of the contour
M = cv2.moments(c)
cX = int(M["m10"] / M["m00"])
cY = int(M["m01"] / M["m00"])
# draw the contour and center of the shape on the image
cv2.drawContours(im, [c], -1, (0, 255, 0), 2)
cv2.circle(im, (cX, cY), 7, (255, 255, 255), -1)
cv2.putText(im, "center", (cX - 20, cY - 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)
# show the image
cv2.imshow("Image", im)
cv2.waitKey(0)
This is what I get:
Placing the watermark
Let's say you have the coordinates of the center of the segment region. Knowing the size of the watermark you can convert them coordinates locating the point of the image where to put the left upper corner of the watermark. In this example I assume that them are (x=10,y=10).
I've reused the last image you posted (I'm not drawing the contours, just the watermark).
import numpy as np
import cv2 as cv
# Coordinates where to put the watermark (left upper corner)
cy = 10
cx = 10
# Reading the image
image = cv.imread("try.png")
(h,w) = image.shape[:2]
image = np.dstack([image, np.ones((h, w), dtype="uint8") * 255])
# Reading the watermark
watermark = cv.imread("watermark.png", cv.IMREAD_UNCHANGED)
(wH, wW) = watermark.shape[:2]
(B, G, R, A) = cv.split(watermark)
B = cv.bitwise_and(B, B, mask=A)
G = cv.bitwise_and(G, G, mask=A)
R = cv.bitwise_and(R, R, mask=A)
watermark = cv.merge([B, G, R, A])
# Creating the image's overlay with the watermark
overlay = np.zeros((h, w, 4), dtype="uint8")
overlay[cy:wH + cy, cx:wW + cx] = watermark
# Applying the overlay
output = image.copy()
cv.addWeighted(overlay, 0.4, output, 1.0, 0, output)
cv.imshow("out", output)
cv.waitKey()
I am trying to open an image that I added transparent corners to. I save it as a .png and when I open it in python the corners are still there but when I open the same image in preview the edges are present. I am also using Pillow for my image processing.
Also the image that I am trying to add rounded corners on is a black and white "1" bit pixel depth. I have also tried converting it to "RGBA" before and after adding corners I also tried the same with "RGB" to no avail.
Here is the method that I am using to make the transparent corners.
from PIL import Image, ImageChops, ImageOps, ImageDraw
'''
http://stackoverflow.com/questions/11287402/how-to-round-corner-a-logo-without-white-backgroundtransparent-on-it-using-pi
'''
def add_corners(self, im, rad):
circle = Image.new('L', (rad * 2, rad * 2), 0)
draw = ImageDraw.Draw(circle)
draw.ellipse((0, 0, rad * 2, rad * 2), fill=255)
alpha = Image.new('L', im.size, 255)
w, h = im.size
alpha.paste(circle.crop((0, 0, rad, rad)), (0, 0))
alpha.paste(circle.crop((0, rad, rad, rad * 2)), (0, h - rad))
alpha.paste(circle.crop((rad, 0, rad * 2, rad)), (w - rad, 0))
alpha.paste(circle.crop((rad, rad, rad * 2, rad * 2)), (w - rad, h - rad))
im.putalpha(alpha)
return im
The function as you have it should work perfectly. You can pass it something like a JPG image, but you must save the resulting file in PNG format as JPG does not support transparency. For example:
img = Image.open('test.jpg')
img_corners = add_corners(img, 40)
img_corners.save('with_corners.png')
Note, if img_corners.show() is used, on Windows this will cause the library to create a temporary file in BMP format which does not support the transparency layer.