Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I increase the RGB value of a specific pixel with Image::Magic in Perl?

I want get 1 pixel (x=3, y=3) and change its RGB values (R from 100 to 101, G from 99 to 100, B from 193 to 194).

enter image description here

use strict;
use Image::Magick;
my $p = new Image::Magick;
   $p->Read( 'myfile.jpg' );

   my $pix = $p->GetPixel(
            width     => 1,
            height    => 1,
            x         => 3,
            y         => 3,
            map       => 'RGB',
            normalize => 0
        );

   # in $pix RGB value now? 

How do I add 1 for all RGB components?

Can I split decimal RGB to 3 values (r,g,b) and increment separately, and then merge three R,G,B values to one RGB? :) How do I do that?

   $pix = .... something code here...

   # make changes
   $p->SetPixel(
            x       => 3,
            y       => 3,
            channel => 'RGB',
            color   => [ $pix ]
        );
   $p->Write ('my_new_file.jpg');
like image 795
Anton Shevtsov Avatar asked Oct 15 '15 06:10

Anton Shevtsov


1 Answers

This was a bit tricky to figure out, but here we go. I'll show you what I did to get to the result, not just how it works.

I'm using a small image that has your starting color (100, 99, 193).

starting image and color

At the top of my program I will always have this code.

use strict;
use warnings;
use Data::Printer;
use Image::Magick;
my $p = new Image::Magick;
$p->Read('33141038.jpg');

my @pixel = $p->GetPixel(
    x         => 1,
    y         => 1,
    map       => 'RGB',
    normalize => 1,
);

I checked the documentation at imagemagick.org.. It is linked in Image::Magick on CPAN. There I searched GetPixel. That yields two helpful things. One is the explanation, the other one an example that shows that an array @pixel is returned, and not a scalar like you tried.

Here we reduce the intensity of the red component at (1,1) by half:

@pixels = $image->GetPixel(x=>1,y=>1);

Ok. Let's use that. I've already got the @pixel in my code above. Note that I also turned on the normalize option. You can leave that out as it's on by default.

p @pixel;

# [
#     [0] 0.392156862745098,
#     [1] 0.388235294117647,
#     [2] 0.756862745098039
# ]

So those are floats. After some googling I found this answer, which deals with something similar. It looks like a fraction of 255. Let's multiply. We can modify things in @pixel by assigning to $_ in the postfix foreach. That's neat.

$_ *= 255 foreach @pixel;
p @pixel;

# [
#     [0] 100,
#     [1] 99,
#     [2] 193
# ]

That's what we wanted. Easy enough. Let's add one each.

$_ = ( $_ * 255 ) + 1 foreach @pixel;
p @pixel;

# [
#     [0] 101,
#     [1] 100,
#     [2] 194
# ]

Still good. But how do we get that back in? The docs have something to say about SetPixel in the Manipulate section.

color=>array of float values
[...]
set a single pixel. By default normalized pixel values are expected.

So apparently we need to go back to the float. No problem.

$_ = ( ( $_ * 255 ) + 1 ) / 255 foreach  @pixel;
p @pixel;

# [
#     [0] 0.396078431372549,
#     [1] 0.392156862745098,
#     [2] 0.76078431372549
# ]

Nice. We can of course also make the math a bit shorter. The result is the same.

$_ = $_ + 1 / 255 foreach @pixel;

Now let's write it back to the image.

$p->SetPixel(
    x => 1,
    y => 1,
    color => \@pixel, # need the array ref here
);

$p->Write('my_new_file.jpg');

In the screenshot, I changed it to add 20 instead of 1 so it's more visible.

New image with +20 including freehand circles

After cleaning up the code looks like this.

use strict;
use warnings;
use Data::Printer;
use Image::Magick;

my $p = new Image::Magick;
$p->Read('33141038.jpg');

my @pixel = $p->GetPixel(
    x => 1,
    y => 1,
);

# increase RGB by 1 each
$_ = $_ + 1 / 255 foerach @pixel;

$p->SetPixel(
    x     => 1,
    y     => 1,
    color => \@pixel,
);

$p->Write('my_new_file.jpg');

I've removed the map and channel arguments from GetPixel and SetPixel as RGB is the default. Same for the normalize.

like image 136
simbabque Avatar answered Sep 29 '22 15:09

simbabque