Skip to content

Commit

Permalink
implement the convert() method for converting between numbers of chan…
Browse files Browse the repository at this point in the history
…nels
  • Loading branch information
Tony Cook committed May 9, 2001
1 parent b9029e2 commit f5991c0
Show file tree
Hide file tree
Showing 7 changed files with 467 additions and 2 deletions.
229 changes: 228 additions & 1 deletion Imager.pm
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ use Imager::Font;
i_gaussian
i_conv
i_convert
i_img_diff
i_init_fonts
Expand Down Expand Up @@ -1174,6 +1176,117 @@ sub polybezier {
return $self;
}

# make an identity matrix of the given size
sub _identity {
my ($size) = @_;

my $matrix = [ map { [ (0) x $size ] } 1..$size ];
for my $c (0 .. ($size-1)) {
$matrix->[$c][$c] = 1;
}
return $matrix;
}

# general function to convert an image
sub convert {
my ($self, %opts) = @_;
my $matrix;

# the user can either specify a matrix or preset
# the matrix overrides the preset
if (!exists($opts{matrix})) {
unless (exists($opts{preset})) {
$self->{ERRSTR} = "convert() needs a matrix or preset";
return;
}
else {
if ($opts{preset} eq 'gray' || $opts{preset} eq 'grey') {
# convert to greyscale, keeping the alpha channel if any
if ($self->getchannels == 3) {
$matrix = [ [ 0.222, 0.707, 0.071 ] ];
}
elsif ($self->getchannels == 4) {
# preserve the alpha channel
$matrix = [ [ 0.222, 0.707, 0.071, 0 ],
[ 0, 0, 0, 1 ] ];
}
else {
# an identity
$matrix = _identity($self->getchannels);
}
}
elsif ($opts{preset} eq 'noalpha') {
# strip the alpha channel
if ($self->getchannels == 2 or $self->getchannels == 4) {
$matrix = _identity($self->getchannels);
pop(@$matrix); # lose the alpha entry
}
else {
$matrix = _identity($self->getchannels);
}
}
elsif ($opts{preset} eq 'red' || $opts{preset} eq 'channel0') {
# extract channel 0
$matrix = [ [ 1 ] ];
}
elsif ($opts{preset} eq 'green' || $opts{preset} eq 'channel1') {
$matrix = [ [ 0, 1 ] ];
}
elsif ($opts{preset} eq 'blue' || $opts{preset} eq 'channel2') {
$matrix = [ [ 0, 0, 1 ] ];
}
elsif ($opts{preset} eq 'alpha') {
if ($self->getchannels == 2 or $self->getchannels == 4) {
$matrix = [ [ (0) x ($self->getchannels-1), 1 ] ];
}
else {
# the alpha is just 1 <shrug>
$matrix = [ [ (0) x $self->getchannels, 1 ] ];
}
}
elsif ($opts{preset} eq 'rgb') {
if ($self->getchannels == 1) {
$matrix = [ [ 1 ], [ 1 ], [ 1 ] ];
}
elsif ($self->getchannels == 2) {
# preserve the alpha channel
$matrix = [ [ 1, 0 ], [ 1, 0 ], [ 1, 0 ], [ 0, 1 ] ];
}
else {
$matrix = _identity($self->getchannels);
}
}
elsif ($opts{preset} eq 'addalpha') {
if ($self->getchannels == 1) {
$matrix = _identity(2);
}
elsif ($self->getchannels == 3) {
$matrix = _identity(4);
}
else {
$matrix = _identity($self->getchannels);
}
}
else {
$self->{ERRSTR} = "Unknown convert preset $opts{preset}";
return undef;
}
}
}
else {
$matrix = $opts{matrix};
}

my $new = Imager->new();
$new->{IMG} = i_img_new();
unless (i_convert($new->{IMG}, $self->{IMG}, $matrix)) {
# most likely a bad matrix
$self->{ERRSTR} = _error_as_msg();
return undef;
}
return $new;
}


# destructive border - image is shrunk by one pixel all around

Expand Down Expand Up @@ -1633,6 +1746,8 @@ options>.
=back
You must also specify the file format using the 'type' option.
The current aim is to support other multiple image formats in the
future, such as TIFF, and to support reading multiple images from a
single file.
Expand All @@ -1643,7 +1758,7 @@ A simple example:
# ... code to put images in @images
Imager->write_multi({type=>'gif',
file=>'anim.gif',
gif_delays=>[ 10 x @images ] },
gif_delays=>[ (10) x @images ] },
@images)
or die "Oh dear!";
Expand Down Expand Up @@ -2135,6 +2250,118 @@ calling the filter function.
FIXME: make a seperate pod for filters?
=head2 Color transformations
You can use the convert method to transform the color space of an
image using a matrix. For ease of use some presets are provided.
The convert method can be used to:
=over 4
=item *
convert an RGB or RGBA image to grayscale.
=item *
convert a grayscale image to RGB.
=item *
extract a single channel from an image.
=item *
set a given channel to a particular value (or from another channel)
=back
The currently defined presets are:
=over
=item gray
=item grey
converts an RGBA image into a grayscale image with alpha channel, or
an RGB image into a grayscale image without an alpha channel.
This weights the RGB channels at 22.2%, 70.7% and 7.1% respectively.
=item noalpha
removes the alpha channel from a 2 or 4 channel image. An identity
for other images.
=item red
=item channel0
extracts the first channel of the image into a single channel image
=item green
=item channel1
extracts the second channel of the image into a single channel image
=item blue
=item channel2
extracts the third channel of the image into a single channel image
=item alpha
extracts the alpha channel of the image into a single channel image.
If the image has 1 or 3 channels (assumed to be grayscale of RGB) then
the resulting image will be all white.
=item rgb
converts a grayscale image to RGB, preserving the alpha channel if any
=item addalpha
adds an alpha channel to a grayscale or RGB image. Preserves an
existing alpha channel for a 2 or 4 channel image.
=back
For example, to convert an RGB image into a greyscale image:
$new = $img->convert(preset=>'grey'); # or gray
or to convert a grayscale image to an RGB image:
$new = $img->convert(preset=>'rgb');
The presets aren't necessary simple constants in the code, some are
generated based on the number of channels in the input image.
If you want to perform some other colour transformation, you can use
the 'matrix' parameter.
For each output pixel the following matrix multiplication is done:
channel[0] [ [ $c00, $c01, ... ] inchannel[0]
[ ... ] = ... x [ ... ]
channel[n-1] [ $cn0, ..., $cnn ] ] inchannel[max]
1
So if you want to swap the red and green channels on a 3 channel image:
$new = $img->convert(matrix=>[ [ 0, 1, 0 ],
[ 1, 0, 0 ],
[ 0, 0, 1 ] ]);
or to convert a 3 channel image to greyscale using equal weightings:
$new = $img->convert(matrix=>[ [ 0.333, 0.333, 0.334 ] ])
=head2 Transformations
Another special image method is transform. It can be used to generate
Expand Down
61 changes: 61 additions & 0 deletions Imager.xs
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,56 @@ i_conv(im,pcoef)
i_conv(im,coeff,len);
myfree(coeff);

undef_int
i_convert(im, src, coeff)
Imager::ImgRaw im
Imager::ImgRaw src
PREINIT:
float *coeff;
int outchan;
int inchan;
AV *avmain;
SV **temp;
SV *svsub;
AV *avsub;
int len;
int i, j;
CODE:
printf("i_convert\n");
if (!SvROK(ST(2)) || SvTYPE(SvRV(ST(2))) != SVt_PVAV)
croak("i_convert: parameter 3 must be an arrayref\n");
avmain = (AV*)SvRV(ST(2));
outchan = av_len(avmain)+1;
/* find the biggest */
inchan = 0;
for (j=0; j < outchan; ++j) {
temp = av_fetch(avmain, j, 0);
if (temp && SvROK(*temp) && SvTYPE(SvRV(*temp)) == SVt_PVAV) {
avsub = (AV*)SvRV(*temp);
len = av_len(avsub)+1;
if (len > inchan)
inchan = len;
}
}
coeff = mymalloc(sizeof(float) * outchan * inchan);
for (j = 0; j < outchan; ++j) {
avsub = (AV*)SvRV(*av_fetch(avmain, j, 0));
len = av_len(avsub)+1;
for (i = 0; i < len; ++i) {
temp = av_fetch(avsub, i, 0);
if (temp)
coeff[i+j*inchan] = SvNV(*temp);
else
coeff[i+j*inchan] = 0;
}
while (i < inchan)
coeff[i++ + j*inchan] = 0;
}
RETVAL = i_convert(im, src, coeff, outchan, inchan);
myfree(coeff);
printf("i_convert returns %d\n", RETVAL);
OUTPUT:
RETVAL

float
i_img_diff(im1,im2)
Expand Down Expand Up @@ -1853,4 +1903,15 @@ DSO_call(handle,func_index,hv)



# this is mostly for testing...
Imager::Color
i_get_pixel(im, x, y)
Imager::ImgRaw im
int x
int y;
CODE:
RETVAL = (i_color *)mymalloc(sizeof(i_color));
i_gpix(im, x, y, RETVAL);
OUTPUT:
RETVAL

1 change: 1 addition & 0 deletions MANIFEST
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Makefile.PL
draw.c
draw.h
conv.c
convert.c
error.c
gaussian.c
ppport.h
Expand Down
2 changes: 1 addition & 1 deletion Makefile.PL
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ if (defined $Config{'d_dlsymun'}) { $OSDEF .= ' -DDLSYMUN'; }
@objs = qw(Imager.o draw.o image.o io.o iolayer.o log.o
gaussian.o conv.o pnm.o raw.o feat.o font.o
filters.o dynaload.o stackmach.o datatypes.o
regmach.o trans2.o quant.o error.o);
regmach.o trans2.o quant.o error.o convert.o);

%opts=(
'NAME' => 'Imager',
Expand Down
Loading

0 comments on commit f5991c0

Please sign in to comment.