Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:

enter image description here

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?

like image 316
Artur Müller Romanov Avatar asked Jan 25 '23 16:01

Artur Müller Romanov


2 Answers

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

enter image description here


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.

enter image description here

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()
like image 168
furas Avatar answered Jan 28 '23 07:01

furas


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:

mask

And the output:

output

like image 25
Stephen Meschke Avatar answered Jan 28 '23 07:01

Stephen Meschke