15

Is there any way to make nice round corners with python? Currently PIL and GD2 are used in my project. Both of them have an arc() method, that allows you to draw a quater-circle, but the quater-circle is not antialiased, so the image looks crispy.

Is there any neat way to make antialiased/smooth round corners?

Evgeny
  • 10,698
  • 9
  • 60
  • 70
DataGreed
  • 13,245
  • 8
  • 45
  • 64
  • Would you give an example of the corners you are getting? I added more details to my answer by the way. – Nadia Alramli Dec 02 '09 at 13:01
  • The same as on the yellow rectangle in you answer :) They look ugly on photos, unfortunately. Check my answer at the bottom. And thank you for your help. – DataGreed Dec 03 '09 at 14:12

4 Answers4

16

What I usually do is use an image as a mask, like this one for example:

border.png

alt text

border = Image.open('border.png')
source = border.convert('RGB')
img.paste(source, mask=border)

The mask can be resized to fit the dimensions you want. Just make sure to use the Image.ANTIALIAS method.

You can refer to the tutorial I wrote a while ago for more details (shameless plug)

If you want more control over the size and the radius then you need to use arc or pieslice. I don't know about you but this rounded rectangle I created with PIL looks smooth enough to me:

alt text

Here is the code I used to draw it

Maybe you should check phatch: http://photobatch.wikidot.com/tutorials It is written in Python and PIL and can apply round corners to photos.

alt text

Here is a link to the code used to apply round corners: http://bazaar.launchpad.net/~stani/phatch/trunk/annotate/head:/phatch/actions/round.py

That also looks smooth enough to me.

Benoit Blanchon
  • 13,364
  • 4
  • 73
  • 81
Nadia Alramli
  • 111,714
  • 37
  • 173
  • 152
5

You'll have to supersample: draw the arc to a larger image and scale it down.

eg. a white, smoothly semi-transparent bottom-right 16x16 border, with 4x4x square supersampling:

>>> import Image, ImageDraw
>>> im= Image.new('RGBA', (16*4, 16*4), (255, 255, 255, 0))
>>> dr= ImageDraw.ImageDraw(im)
>>> dr.pieslice((-64, -64, 64, 64), 0, 340, fill=(255,255,255,255))
>>> im= im.resize((16, 16), Image.ANTIALIAS)
>>> im.save('border-se.png')
bobince
  • 528,062
  • 107
  • 651
  • 834
  • That's what i use now, but this method makes the modified image look blurry :( – DataGreed Dec 02 '09 at 10:38
  • I apply corners to photos and the photos should look like they used to + with smooth round corners – DataGreed Dec 02 '09 at 12:12
  • OK, then swap the transparency round in the above (so you're painting transparent-white (255,255,255,0) onto opaque-white (255,255,255,255). Now you've got a corner stencil. You can than get the photo and use `photoim.paste(cornerim, position, cornerim)` to put the white corner stencil on top of the bottom-right part of the photo. Flip the stencil with `transpose()` and do again for each of the corners. (If you need to make the output transparent that's a little bit more work, you'd have to make a separate complete mask image the same size as the photo then paste the photo and mask together.) – bobince Dec 02 '09 at 16:52
  • Nice solution, though i need transparent corners :) But thank you, anyway – DataGreed Dec 03 '09 at 14:06
4

So, okay, i found the solution.

I used the supersampling method bobince proposed before, but i found that the images become blurry. So, I would not provide the full code, but i'll explain my solution.

It may seem kind of idiotistic, but i haven't found any other way to do it without involving any other libraries.

With PIL you draw white (or whatever color you need, involving transparent ones) corners on the image. Then you copy it. You should now have two images with crispy non-antialiased white round corners.

Now you should use scale-up -> scale-down supersampling (see bobince's answer above for details) method to make one of the images smooth (i used 8x scaling).

Now you have two images - one with crispy corners and another one with smooth corners, but with blurry picture.

You have now to make corners on crispy image transparent and paste the whole image on the smooth one. I used the Image.composite(crispy_image, smooth_image, crispy_image) method in PIL library to do this.

Voila!

DataGreed
  • 13,245
  • 8
  • 45
  • 64
  • A possible improvement: Use a supersampled grayscale mask. There you draw a white image and then you can downscale it i.e. with a BICUBIC interpolation. This should give you round corners with grey pixels. Pasting this mask should give you smooth corners in the final image as well. – allo Feb 22 '17 at 22:27
0

Use Xiaolin Wu's circle algorithm as described in Graphics Gems II. A concise summary and example (in php) han be found here: http://create.stephan-brumme.com/antialiased-circle/

As for drawing an arc (partial circle), you must determine if a given point is within the arc's angular range before plotting it.