Putting an image in the center of a black rectangle - python

I would like to create a black rectangle with the bigger value of img.shape and then putting the image into the center of this black rectangle.
I coded this:
from skimage.io import imread
import numpy as np
#load the file_name
file_name = "/path/to/image/img.png"
#read in the image
img = imread(file_name)
#shape of image
img.shape
#create a black rectangle with length of sizes equal to the max of image.shape[0] or image.shape[1]
longSide = max(image.shape[0], image.shape[1])
#create a black square
rectangle = np.zeros((longSide, longSide), dtype="bool")
I now would like to paste the image in the center of this black rectangle (the black rectangle in the background). In the end it should look like this:

You can try using the PIL (Pillow) image library:
from skimage.io import imread
import numpy as np
from PIL import Image, ImageDraw, ImageFilter
#load the file_name
file_name = "/path/to/image/img.png"
#read in the image
img = imread(file_name)
#shape of image
img.shape
#create a black rectangle with length of sizes equal to the max of image.shape[0] or image.shape[1]
longSide = max(image.shape[0], image.shape[1])
#create a black square
rectangle = np.zeros((longSide, longSide), dtype="bool")
final_im = rectangle.copy()
final_im.paste(img, (100, 50))
# the final command is pasting the previous image on the rectangle, and positioning it using `(x coordinate in upper left, y coordinate in upper left)`.
More info: https://note.nkmk.me/en/python-pillow-paste/

Related

Fill concentric binary image in Numpy

I have an input binary (black and white) image containing two concentric ellipses. The background and the internal ellipse are black while the external ellipse is white.
My goal is to change all pixels of the internal ellipse from black to white.
This is a simple code snippet to generate two concentric ellipses:
import cv2
import numpy as np
# costants
min_height, max_height = 15, 60
min_width, max_width = 15, 60
min_rot, max_rot = 0, 360
center = (max_width*2, max_height*2)
# empty image
image = np.zeros((max_width*4, max_height*4), dtype=np.uint8)
# randomly selecting angle, horizontal and vertical compression
width = int(np.random.uniform(min_width, max_width))
height = int(np.random.uniform(min_height, max_height))
angle = np.random.uniform(min_rot, max_rot)
# draw external ellipse
image = cv2.ellipse(image, center, (width, height), angle, 0, 360, color=255, thickness=-1)
# draw internal ellipse
image = cv2.ellipse(image, center, (width//2, height//2), angle, 0, 360, color=0, thickness=-1)
cv2.imshow('concentric ellipses', image)
cv2.waitKey(0)
My goal is to change all pixels of the internal ellipse from black to white. Like this:
example
A Naive approach could be a loop for on each image but this seems very slow to me:
def fill_image(img):
import numpy as np
num_rows = img.shape[0]
for row in range(num_rows):
# get col index for white pixels (external ellipse)
white_pixels = np.where(img[row, :] == 255)[0]
# assign white pixels
start_px, end_px = min(white_pixels), max(white_pixels)
img[row, start_px:end_px] = 255
return img
Which is the most pythonic and efficient way to do that?

What's the most simple way to crop a circle thumbnail from an image?

I am trying to crop a centered (or not centered) circle from this image:
I stole this code from the existing questions regarding this topic on stack overflow, something goes wrong though:
import cv2
file = 'dog.png'
img = cv2.imread(file)
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
circle = cv2.HoughCircles(img,
3,
dp=1.5,
minDist=10,
minRadius=1,
maxRadius=10)
x = circle[0][0][0]
y = circle[0][0][1]
r = circle[0][0][2]
rectX = (x - r)
rectY = (y - r)
crop_img = img[rectY:(rectY+2*r), rectX:(rectX+2*r)]
cv2.imwrite('dog_circle.png', crop_img)
Output:
Traceback (most recent call last):
File "C:\Users\Artur\Desktop\crop_circle - Kopie\crop_circle.py", line 14, in <module>
x = circle[0][0][0]
TypeError: 'NoneType' object is not subscriptable
cv2.HoughCircles() seems to produce None instead of a cropped circle array. How do I fix this?
first: HoughCircles is used to detect circles on image, not to crop it.
You can't have circle image. Image is always rectangle but some of pixels can be transparent (alpha channel in RGBA) and programs will not display them.
So you can first crop image to have square and later add alpha channel with information which pixels should be visible. And here you can use mask with white circle on black background. At the end you have to save it as png or tiff because jpg can't keep alpha channel.
I use module PIL/pillow for this.
I crop square region in center of image but you can use different coordinates for this.
Next I create grayscale image with the same size and black background and draw white circle/ellipse.
Finally I add this image as alpha channel to cropped image and save it as png.
from PIL import Image, ImageDraw
filename = 'dog.jpg'
# load image
img = Image.open(filename)
# crop image
width, height = img.size
x = (width - height)//2
img_cropped = img.crop((x, 0, x+height, height))
# create grayscale image with white circle (255) on black background (0)
mask = Image.new('L', img_cropped.size)
mask_draw = ImageDraw.Draw(mask)
width, height = img_cropped.size
mask_draw.ellipse((0, 0, width, height), fill=255)
#mask.show()
# add mask as alpha channel
img_cropped.putalpha(mask)
# save as png which keeps alpha channel
img_cropped.save('dog_circle.png')
img_cropped.show()
Result
BTW:
In mask you can use values from 0 to 255 and different pixels may have different transparency - some of them can be half-transparent to make smooth border.
If you want to use it in HTML on own page then you don't have to create circle image because web browser can round corners of image and display it as circle. You have to use CSS for this.
EDIT: Example with more circles on mask.
from PIL import Image, ImageDraw
filename = 'dog.jpg'
# load image
img = Image.open(filename)
# crop image
width, height = img.size
x = (width - height)//2
img_cropped = img.crop((x, 0, x+height, height))
# create grayscale image with white circle (255) on black background (0)
mask = Image.new('L', img_cropped.size)
mask_draw = ImageDraw.Draw(mask)
width, height = img_cropped.size
mask_draw.ellipse((50, 50, width-50, height-50), fill=255)
mask_draw.ellipse((0, 0, 250, 250), fill=255)
mask_draw.ellipse((width-250, 0, width, 250), fill=255)
# add mask as alpha channel
img_cropped.putalpha(mask)
# save as png which keeps alpha channel
img_cropped.save('dog_2.png')
img_cropped.show()
This answer explains how to apply a mask. First, read in the image:
import cv2
import numpy as np
img = cv2.imread('dog.jpg')
Next create a mask, or a blank image that is the same size as the source image:
h,w,_ = img.shape
mask = np.zeros((h,w), np.uint8)
Then, draw a circle on the mask. Change these parameters based on where the face is:
cv2.circle(mask, (678,321), 5, 255, 654)
Finally, mask the source image:
img = cv2.bitwise_and(img, img, mask= mask)
Here is the mask:
And the output:
The idea is to create a black mask then draw the desired region to crop out in white using cv2.circle(). From there we can use cv2.bitwise_and() with the original image and the mask. To crop the result, we can use cv2.boundingRect() on the mask to obtain the ROI then use Numpy slicing to extract the result. For this example I used the center point derived from the image's width and height
import cv2
import numpy as np
# Create mask and draw circle onto mask
image = cv2.imread('1.jpg')
mask = np.zeros(image.shape, dtype=np.uint8)
x,y = image.shape[1], image.shape[0]
cv2.circle(mask, (x//2,y//2), 300, (255,255,255), -1)
# Bitwise-and for ROI
ROI = cv2.bitwise_and(image, mask)
# Crop mask and turn background white
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
x,y,w,h = cv2.boundingRect(mask)
result = ROI[y:y+h,x:x+w]
mask = mask[y:y+h,x:x+w]
result[mask==0] = (255,255,255)
cv2.imshow('result', result)
cv2.waitKey()

Adding an png image over a white canvas in numpy

I have a png image, which has to be overlaid on a white canvas. The png image dimensions are 200*200. The canvas dimensions are 512*512.
#SOURCE IMAGE
img=cv2.imread("xx.png")
import cv2
import numpy as np
img_1 = np.zeros([512,512,1],dtype=np.uint8)
img_1.fill(255)
# or img[:] = 255
cv2.imshow('Single Channel Window', img_1)
print("image shape: ", img_1.shape)
cv2.waitKey(0)
cv2.destroyAllWindows()
All you need now is to place image into white background. As image shape is 200X200 and white background shape is 512X512, the image white margins is going to be (512-200)/2 = 156. So:
import cv2
import numpy as np
img=cv2.imread("xx.png", 0)
row, col = img.shape # row = 200, col = 200
img_1 = np.zeros([512,512],dtype=np.uint8)
img_1.fill(255)
margin = (512 - row)//2
img_1[margin: margin+row, margin: margin+column] = img
cv2.imshow('Single Channel Window', img_1)
print("image shape: ", img_1.shape)
cv2.waitKey(0)
cv2.destroyAllWindows()
Its also possible to pad the original image with white pixels using numpy.pad and get the same result. So, for this case:
img = cv2.imread('1.png', 0)
margin = (512 - 200)//2
img_1 = np.pad(img, margin, 'constant', constant_values=255)
here canvas is the white region, and img is your input image.
canvas = np.zeros((512,512,3))
canvas.fill(255)
img = cv2.imread("xx.png",cv2.IMREAD_COLOR)
canvas[canvas.shape[0]//2-img.shape[0]//2:canvas.shape[0]//2+img.shape[0]//2,
canvas.shape[1]//2-img.shape[1]//2:canvas.shape[1]//2+img.shape[1]//2] = img
This is a simplistic code, with an assumption that both your source and canvas are square and RGB images.
The flag cv2.IMREAD_COLOR is important, because the canvas is 3 channels.
if you want to use grayscale, use 1 channel in canvas, and if you want to use png(alpha channel included) use 4 channels accordingly.

How to blur an ellipse with ImageDraw

I implemented an algorithm that detects faces and I want to blur faces. I'm using PIL for blurring it.
image = Image.open(path_img)
draw = ImageDraw.Draw(image)
draw.ellipse((top, left, bottom, right), fill = 'white', outline ='white')
I got this with my code
Face to blur
I would like to use :
blurred_image = cropped_image.filter(ImageFilter.GaussianBlur(radius=10 ))
But I can't use it because I'm using an ImageDraw and it works only with Image class. How can I blur with an ellipse (circular) the face?
Thank you
blur the ellipse is the best way
With Pillow, the best way to do this is to use the blurred ellipse as a blending mask for composite.
from PIL import Image, ImageDraw, ImageFilter
def make_ellipse_mask(size, x0, y0, x1, y1, blur_radius):
img = Image.new("L", size, color=0)
draw = ImageDraw.Draw(img)
draw.ellipse((x0, y0, x1, y1), fill=255)
return img.filter(ImageFilter.GaussianBlur(radius=blur_radius))
kitten_image = Image.open("kitten.jpg")
overlay_image = Image.new("RGB", kitten_image.size, color="orange") # This could be a bitmap fill too, but let's just make it orange
mask_image = make_ellipse_mask(kitten_image.size, 150, 70, 350, 250, 5)
masked_image = Image.composite(overlay_image, kitten_image, mask_image)
masked_image.show()
Given this adorable kitten as input, the output is
EDIT: Inspired by Mark Setchell's answer, simply changing the overlay_image line to
overlay_image = kitten_image.filter(ImageFilter.GaussianBlur(radius=15))
gives us this blur variant (with smooth edges for the blur :) )
Not sure if you want to composite something over the image to conceal the contents, or blur it. This is more blurry :-)
Starting with Paddington:
You can go to "Stealth Mode" like this:
#!/usr/bin/env python3
from PIL import Image, ImageDraw, ImageFilter
import numpy as np
# Open image
im = Image.open('paddington.png')
# Make a mask the same size as the image filled with black
mask = Image.new('RGB',im.size)
# Draw a filled white circle onto the black mask
draw = ImageDraw.Draw(mask)
draw.ellipse([90,40,300,250],fill=(255,255,255))
# Blur the entire image
blurred = im.filter(ImageFilter.GaussianBlur(radius=15))
# Select either the original or the blurred image at each pixel, depending on the mask
res = np.where(np.array(mask)>0,np.array(blurred),np.array(im))
# Convert back to PIL Image and save
Image.fromarray(res).save('result.png')
Or, as suggested by #AKX, you can remove the Numpy dependency and make the code a bit smaller too yet still get same result:
#!/usr/bin/env python3
from PIL import Image, ImageDraw, ImageFilter
import numpy as np
# Open image
im = Image.open('paddington.png')
# Make a mask the same size as the image filled with black
mask = Image.new('L',im.size)
# Draw a filled white circle onto the black mask
draw = ImageDraw.Draw(mask)
draw.ellipse([90,40,300,250],fill=255)
# Blur the entire image
blurred = im.filter(ImageFilter.GaussianBlur(radius=15))
# Composite blurred image over sharp one within mask
res = Image.composite(blurred, im, mask)
# Save
res.save('result.png')

How do I make rectangular image squared using OpenCV and Python?

I have all sorts of images of rectangular shape. I need to modify them to uniform square shape (different size ok).
For that I have to layer it on top of larger squared shape.
Background is black.
I figured it to the point when I need to layer 2 images:
import cv2
import numpy as np
if 1:
img = cv2.imread(in_img)
#get size
height, width, channels = img.shape
print (in_img,height, width, channels)
# Create a black image
x = height if height > width else width
y = height if height > width else width
square= np.zeros((x,y,3), np.uint8)
cv2.imshow("original", img)
cv2.imshow("black square", square)
cv2.waitKey(0)
How do I stack them on top of each other so original image is centered vertically and horizontally on top of black shape?
I figured it. You need to "broadcast into shape":
square[(y-height)/2:y-(y-height)/2, (x-width)/2:x-(x-width)/2] = img
Final draft:
import cv2
import numpy as np
if 1:
img = cv2.imread(in_img)
#get size
height, width, channels = img.shape
print (in_img,height, width, channels)
# Create a black image
x = height if height > width else width
y = height if height > width else width
square= np.zeros((x,y,3), np.uint8)
#
#This does the job
#
square[int((y-height)/2):int(y-(y-height)/2), int((x-width)/2):int(x-(x-width)/2)] = img
cv2.imwrite(out_img,square)
cv2.imshow("original", img)
cv2.imshow("black square", square)
cv2.waitKey(0)
You can use numpy.vstack to stack your images vertically and numpy.hstack to stack your images horizontally.
Please mark answered if you this resolves your problem.

Categories

Resources