Warm tip: This article is reproduced from serverfault.com, please click

batch crop quad images with diffenrent sizes to a circle

发布于 2020-11-27 21:38:50

I have lots of images of planets in differing sizes like

enter image description here enter image description here enter image description here enter image description here

They are all positioned exactly in the middle of the square images but with different height. Now I want to crop them and make the black border transparent. I tried with convert (ImageMagick 6.9.10-23) like this:

for i in planet_*.jpg; do
  nr=$(echo ${i/planet_/}|sed s/.jpg//g|xargs)
  convert $i -fuzz 1% -transparent black trans/planet_${nr}.png
done

But this leaves some artifacts like:

enter image description here enter image description here enter image description here enter image description here

Is there a command to crop all images in a circle, so the planet is untouched? (It mustn't be imagemagick).

I could also imagine a solution where I would use a larger -fuzz value and then fill all transparent pixels in the inner planet circle with black.

Those are all planets, I want to convert: download zip

Questioner
rubo77
Viewed
0
fmw42 2020-11-29 08:35:03

Here is one way using Python Opencv from the minEclosingCircle.

Input:

enter image description here

import cv2
import numpy as np
import skimage.exposure

# read image
img = cv2.imread('planet.jpg')
h, w, c = img.shape

# convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# threshold
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]

# get contour
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)

# get enclosing circle
center, radius = cv2.minEnclosingCircle(big_contour)
cx = int(round(center[0]))
cy = int(round(center[1]))
rr = int(round(radius))

# draw outline circle over input
circle = img.copy()
cv2.circle(circle, (cx,cy), rr, (0, 0, 255), 1)

# draw white filled circle on black background as mask
mask = np.full((h,w), 0, dtype=np.uint8)
cv2.circle(mask, (cx,cy), rr, 255, -1)

# antialias
blur = cv2.GaussianBlur(mask, (0,0), sigmaX=2, sigmaY=2, borderType = cv2.BORDER_DEFAULT)
mask = skimage.exposure.rescale_intensity(blur, in_range=(127,255), out_range=(0,255))


# put mask into alpha channel to make outside transparent
imgT = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)
imgT[:,:,3] = mask

# crop the image
ulx = int(cx-rr+0.5)
uly = int(cy-rr+0.5)
brx = int(cx+rr+0.5)
bry = int(cy+rr+0.5)
print(ulx,brx,uly,bry)
crop = imgT[uly:bry+1, ulx:brx+1]

# write result to disk
cv2.imwrite("planet_thresh.jpg", thresh)
cv2.imwrite("planet_circle.jpg", circle)
cv2.imwrite("planet_mask.jpg", mask)
cv2.imwrite("planet_transparent.png", imgT)
cv2.imwrite("planet_crop.png", crop)

# display it
cv2.imshow("thresh", thresh)
cv2.imshow("circle", circle)
cv2.imshow("mask", mask)
cv2.waitKey(0)

Threshold image:

enter image description here

Circle on input:

enter image description here

Mask image:

enter image description here

Transparent image:

enter image description here

Cropped transparent image:

enter image description here

packages to install

sudo apt install python3-opencv python3-sklearn python3-skimage