2

I am using Perl and ImageMagick (Perl-API). In a first step i take a rectangle of an image and blur this part of the image (plus rotation of that rectangle). Thanks to Mark Setchell this works as further bellow (see stackoverflow question here: How to blur/pixelate part of an image using ImageMagick?).

Now my aim is to blur the rectangle of that image with the result of bigger pixels. How can i achieve that?

Here the code of Mark Setchell that i use so far:

#!/usr/bin/perl
use strict;
use warnings;
use Image::Magick;
my $x;
my $image;
my $blurred;
my $mask;

# Create original fishscale image
$image=Image::Magick->new(size=>'600x300');
$image->Read('pattern:fishscales');
$image->Write(filename=>"1.png");

# Copy original image and blur
$blurred = $image->Clone();
$blurred->GaussianBlur('x2');
$blurred->Write(filename=>"2.png");

# Make mask and rotate
$mask=Image::Magick->new(size=>'600x300');
$mask->Read('xc:white');
$mask->Draw(fill=>'black',primitive=>'rectangle',points=>'100,100,200,200');
$mask->Set('virtual-pixel'=>'white');
$mask->Rotate(20);
$mask->Transparent('white');
$mask->Write(filename=>"3.png");

# Copy mask as alpha channel into blurred image
$blurred->Composite(image=>$mask,qw(compose CopyOpacity gravity center));

# Composite blurred image onto original
$image->Composite(image=>$blurred);
$image->Write(filename=>'result.png');

UPDATE: Still one problem remains:

It works fine with a selected rectangle without rotation. But if i apply an angle i get wierd results. The area that gets pixelated is not the area i have defined but it differs the more i select the rectangle away from the middle of the image and/or increase the angle.

See the images below with different rectangles selected and the areas that gets pixelated. I have used an angle of 45 degree.

enter image description here

enter image description here

enter image description here

Any idea what the problem is here? (maybe 'compose CopyOpacity gravity center')

Community
  • 1
  • 1
Thariama
  • 50,002
  • 13
  • 138
  • 166

2 Answers2

4

Something like this?

#!/usr/bin/perl
use strict;
use warnings;
use Image::Magick;
my $x;
my $image;
my $blurred;
my $mask;

# Create original fishscale image
$image=Image::Magick->new(size=>'600x300');
$image->Read('pattern:fishscales');
$image->Write(filename=>"1.png");

# Copy original image and scale
$blurred = $image->Clone();
$blurred->Resample(x=>'100',y=>'50',filter=>'point');
$blurred->Scale(width=>'1200',height=>'600');
$blurred->Write(filename=>"2.png");

# Make mask and rotate
$mask=Image::Magick->new(size=>'600x300');
$mask->Read('xc:white');
$mask->Draw(fill=>'black',primitive=>'rectangle',points=>'20,20,220,120');
$mask->Set('virtual-pixel'=>'white');
$mask->Rotate(20);
$mask->Transparent('white');
$mask->Write(filename=>"3.png");

# Copy mask as alpha channel into blurred image
$blurred->Composite(image=>$mask,qw(compose CopyOpacity gravity center));

# Composite blurred image onto original
$image->Composite(image=>$blurred);
$image->Write(filename=>'result.png');

enter image description here

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • 1
    Ah, I get it. @Thariama want's the effect of pixelation you see on the television. Mark, you would achieve the effect by resampling down, than sample up to original size. – emcconville Sep 02 '15 at 11:18
  • @emcconville Hi Eric - thanks for the tip :-) I can be a bit dim sometimes! – Mark Setchell Sep 02 '15 at 11:23
  • No problem! Try some additional `filter=` arguments with the `Resample` method (for down sampling part). I have a felling `filter=point`, or `filter=box` will look very nice. – emcconville Sep 02 '15 at 11:27
  • And scale by 1/10, or 1/16. Pixelation should be dramatic. `$blurred->Rescale(width=>'60',height=>'30', filter='point');` – emcconville Sep 02 '15 at 11:30
  • I'm glad we got there in the end! The code is not very efficient or flexible - it resizes the entire image up to double the size when it could just resize enough for the magnified window. It is also not very deterministic about *where* in the original image it takes its data from for the enlarged window nor *where* it places it on the original image. Hopefully you can think about these things and adapt it to do what you want now you can see some of the functions I used. – Mark Setchell Sep 02 '15 at 12:54
  • Basically, I think you need a `Crop()` with `geometry =>'axb+c+d'` after the first call to `Clone()` to extract the piece you want to magnify. Then I think you need to add a `geometry` into the `Composite()` calls at the end in order to deterministically place the enlarged window onto the original. – Mark Setchell Sep 02 '15 at 12:57
  • @Mark: there is still one problem with the rotation, please see my updated question – Thariama Sep 08 '15 at 16:15
  • @Mark: i found a solution and posted an own answer – Thariama Sep 09 '15 at 11:12
1

I will keep Mark Setchells answer accepted, but post my solution to the updated question here. I use a helper function and i only modified the "Make mask and rotate"-part.

# helperfunction
sub listDraw
{
    my $image = shift;
    my $result = undef;

    for my $attrs (@_)
    {
        my ($left, $top, $width, $height, $angle) = delete @$attrs{qw(left top width height angle)};

        if ($angle - int($angle/180)*180)
        {
            my ($xm, $ym, $rad) = ($left + $width/2, $top + $height/2, $angle * PI/180);
            $attrs->{primitive} ||= 'Polygon';
            $attrs->{points} = join ' ', map {($xm + ($_->{x}-$xm)*cos($rad) + ($_->{y}-$ym)*sin($rad)) . ',' . ($ym - ($_->{x}-$xm)*sin($rad) + ($_->{y}-$ym)*cos($rad))}
                ({ x => $left, y => $top }, { x => $left + $width, y => $top }, { x => $left + $width, y => $top + $height }, { x => $left, y => $top + $height });
        }
        else
        {
            $attrs->{primitive} ||= 'Rectangle';
            $attrs->{points} ||= $left . ',' . $top . ' ' . ($left + $width - 1) . ',' . ($top + $height - 1);
        }
        last if $result = $image->Draw(%$attrs);
    }
    return $result;
}

    # Make mask and rotate
    $mask=Image::Magick->new(size=>'600x300');
    $mask->Read('xc:white');

    $mask = listDraw($mask, {
            left        => $selection_y, # y of top left corner
            top         => $selection_x, # x of top left corner
            width       => $selection_width,
            height      => $selection_height,
            angle       => 180 - $angle,
            fill        => 'black',
    });
    $mask->Set('virtual-pixel' => 'white');
    $mask->Transparent('white');
Thariama
  • 50,002
  • 13
  • 138
  • 166
  • Thank you for sharing your enhancements with us all - go ahead and accept your answer as it is the better one. – Mark Setchell Sep 09 '15 at 18:04
  • 1
    No, i won't. You put so much effort in your answer and it guided me to my actual solution. So take this as a small gift. – Thariama Sep 10 '15 at 13:37