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

﹥>﹥吖頭↗ 提交于 2020-02-03 08:27:07

问题


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?


回答1:


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()



回答2:


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:




回答3:


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()


来源:https://stackoverflow.com/questions/58543750/whats-the-most-simple-way-to-crop-a-circle-thumbnail-from-an-image

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!