clt60
clt60

Reputation: 63902

Image::Magick draw a blurred circle

Have an background image. (for this example the mesh.png)

To the image need add one circle, with blurred border. Currently have this:

use 5.014;
use warnings;
use Image::Magick;

my $bgimg = Image::Magick->new();
$bgimg->Read('png:mesh.png');

draw_my_circle($bgimg, 150, 150, 100);  #img, center_x, center_y, radius
$bgimg->Write("win:");  #display

sub draw_my_circle {
    my($img, $center_x, $center_y, $radius) = @_;
    $img->Draw(
        fill => 'black',
        points => "$center_x,$center_y @{[$center_x + $radius]},$center_y",
        primitive => 'circle',
    );
}

This produces the next image:

black circle on a mesh

but I need a circle with "blurred edge", like the next (created manually)

blurred circle on the mesh

Can anyone help how to get a such blurred circle with Image::Magick?

If someone want the mesh.png, it is created with the next script:

use 5.014;
use warnings;
use Image::Magick;

my $image=Image::Magick->new(size=>'300x300');
$image->Read('xc:white');

for( my $i=0; $i < 300; $i += 10 ) {
    $image->Draw(primitive=>'line',points=>"$i,0 $i,300",stroke=>"#ccf");
    $image->Draw(primitive=>'line',points=>"0,$i 300,$i",stroke=>"#ccf");
}
$image->Write('png:mesh.png');

Upvotes: 2

Views: 1480

Answers (2)

djconnel
djconnel

Reputation: 431

This is a very old thread, but it's still useful.

A problem with Gaussian blur is it doesn't scale well to very high resolutions. For a high-resolution image I'm making I used concentric circles instead, of graded transparency. The challenge is I can't just linearly grade the transparency, since the inner circles are also overlapped by the outer circles. So I calculated the incremental transparency of each layer to increase the opacity of the region to the target relative to the opacity of the layer immediately larger.

The below code is written in the "wand" python library. Here "radius" is the desired outer radius of the blurred circle. The inner radius is set to half of this. "image" is the image object.

I can't vouch for this code as I'm a python newbie:

# background                                                                                                                                                                 
nShades = 128
stderr.write(f"rendering {nShades} shaded background circles...\n")
bgMinR  = 0.5 * radius
bgMaxR  = radius

with Drawing() as draw:
  draw.gravity= "center"
  draw.stroke_color = "none"
  draw.stroke_width= 0
  draw.stroke_fill= "none"
  draw.antialias = False

  transparency = 1
  for i in range(0, nShades, 1):
    # target transparency for this line                                                                                                                                      
    t = (1 - cos(pi * (nShades - i) / nShades)) / 2
    r = (bgMaxR * (nShades - 1 - i) + bgMinR * i) / (nShades - 1)

    # target transparency for this line                                                                                                                                      
    # how much alpha we need to get this transparency                                                                                                                        
    # transparency * (1 - alpha/0xff) = t                                                                                                                                    
    # (1 - alpha)/0xff = t/transparency                                                                                                                                      
    # alpha/0xff = 1 - t/transparency                                                                                                                                        
    # alpha = 0xff * (1 - t/transparency)                                                                                                                                    
    alpha = int(0xff * (1 - t/transparency))
    transparency *= (1 - alpha / 0xff)
    draw.fill_color = "#000000%02x" % alpha
    draw.circle((xc, yc), (xc + r, yc))
  draw(image)

Upvotes: 0

Mark Setchell
Mark Setchell

Reputation: 207455

You can use Gaussian blur, like this:

#!/usr/bin/perl
use 5.014;
use strict;
use warnings;
use Image::Magick;
my $x;
my $circle;
my $mesh;

$mesh=Image::Magick->new(size=>'300x300');
$mesh->Read('xc:white');

for( my $i=0; $i < 300; $i += 10 ) {
    $mesh->Draw(primitive=>'line',points=>"$i,0 $i,300",stroke=>"#ccf");
    $mesh->Draw(primitive=>'line',points=>"0,$i 300,$i",stroke=>"#ccf");
}
$mesh->Write(filename=>'mesh.png');

$circle=Image::Magick->new(size=>'300x300');
$circle->ReadImage('xc:transparent');
$circle->Draw(
        fill => 'black',
        points => "150,150 @{[250]},100",
        primitive => 'circle',
    );
$circle->GaussianBlur('10x50');
$circle->Write(filename=>'circle.png');

$mesh->Composite(image => $circle, qw(compose SrcAtop  gravity Center));
$mesh->Write(filename=>'out.png');

Upvotes: 3

Related Questions