From b47464c19c81e26fb5c8086d66f05c012a6c36cc Mon Sep 17 00:00:00 2001 From: Tony Cook Date: Sun, 31 Oct 2010 12:58:45 +0000 Subject: [PATCH] add the combine method --- Changes | 4 ++ Imager.pm | 45 +++++++++++++++ Imager.xs | 41 +++++++++++++ MANIFEST | 3 + Makefile.PL | 2 +- combine.im | 81 ++++++++++++++++++++++++++ imager.h | 3 + lib/Imager/Transformations.pod | 40 +++++++++++++ t/t63combine.t | 101 +++++++++++++++++++++++++++++++++ 9 files changed, 319 insertions(+), 1 deletion(-) create mode 100644 combine.im create mode 100644 t/t63combine.t diff --git a/Changes b/Changes index a1adfa98..f077dcf9 100644 --- a/Changes +++ b/Changes @@ -13,6 +13,10 @@ Imager 0.79 - unreleased - add wiggle.pl sample, as suggested by Dan Oppenheim. + - add the combine() method to combine channels from multiple source + images into a new image + https://rt.cpan.org/Ticket/Display.html?id=11872 + Bug fixes: - treat the co-efficients for convert() as doubles instead of floats. diff --git a/Imager.pm b/Imager.pm index 89a3a9a3..ad5ba70a 100644 --- a/Imager.pm +++ b/Imager.pm @@ -3466,6 +3466,46 @@ sub convert { return $new; } +# combine channels from multiple input images, a class method +sub combine { + my ($class, %opts) = @_; + + my $src = delete $opts{src}; + unless ($src) { + $class->_set_error("src parameter missing"); + return; + } + my @imgs; + my $index = 0; + for my $img (@$src) { + unless (eval { $img->isa("Imager") }) { + $class->_set_error("src must contain image objects"); + return; + } + unless ($img->{IMG}) { + $class->_set_error("empty input image"); + return; + } + push @imgs, $img->{IMG}; + } + my $result; + if (my $channels = delete $opts{channels}) { + $result = i_combine(\@imgs, $channels); + } + else { + $result = i_combine(\@imgs); + } + unless ($result) { + $class->_set_error($class->_error_as_msg); + return; + } + + my $img = $class->new; + $img->{IMG} = $result; + + return $img; +} + # general function to map an image through lookup tables @@ -4195,6 +4235,9 @@ circle() - L - draw a filled circle colorcount() - L - the number of colors in an image's palette (paletted images only) +combine() - L - combine channels from one or +more images. + combines() - L - return a list of the different combine type keywords @@ -4399,6 +4442,8 @@ boxes, drawing - L changes between image - L +channels, combine into one image - L + color - L color names - L, L diff --git a/Imager.xs b/Imager.xs index 3a9ee1bb..4a82c12f 100644 --- a/Imager.xs +++ b/Imager.xs @@ -1757,6 +1757,47 @@ i_compose_mask(out, src, mask, out_left, out_top, src_left, src_top, mask_left, int combine double opacity +Imager::ImgRaw +i_combine(src_av, channels_av = NULL) + AV *src_av + AV *channels_av + PREINIT: + i_img **imgs = NULL; + STRLEN in_count; + int *channels = NULL; + int i; + SV **psv; + IV tmp; + CODE: + in_count = av_len(src_av) + 1; + if (in_count > 0) { + imgs = mymalloc(sizeof(i_img*) * in_count); + channels = mymalloc(sizeof(int) * in_count); + for (i = 0; i < in_count; ++i) { + psv = av_fetch(src_av, i, 0); + if (!psv || !*psv || !sv_derived_from(*psv, "Imager::ImgRaw")) { + myfree(imgs); + myfree(channels); + croak("imgs must contain only images"); + } + tmp = SvIV((SV*)SvRV(*psv)); + imgs[i] = INT2PTR(i_img*, tmp); + if (channels_av && + (psv = av_fetch(channels_av, i, 0)) != NULL && + *psv) { + channels[i] = SvIV(*psv); + } + else { + channels[i] = 0; + } + } + } + RETVAL = i_combine(imgs, channels, in_count); + myfree(imgs); + myfree(channels); + OUTPUT: + RETVAL + undef_int i_flipxy(im, direction) Imager::ImgRaw im diff --git a/MANIFEST b/MANIFEST index ce6501d9..08f408a0 100644 --- a/MANIFEST +++ b/MANIFEST @@ -170,6 +170,7 @@ apidocs.perl Build lib/Imager/APIRef.pm bigtest.perl Library selection tester bmp.c Reading and writing Windows BMP files color.c Color translation and handling +combine.im Channel combine compose.im conv.im convert.im @@ -311,6 +312,7 @@ samples/samp-tags.cgi Demonstrate image upload via a HTML form samples/samp-tags.html Form for samp-tags.cgi samples/slant_text.pl Using $font->transform() to slant text samples/tk-photo.pl +samples/wiggle.pl "Wiggle" stereoscopy scale.im Newer scaling code spot.perl For making an ordered dither matrix from a spot function stackmach.c @@ -349,6 +351,7 @@ t/t57infix.t t/t58trans2.t t/t59assem.t t/t61filters.t +t/t63combine.t Test combine() method t/t64copyflip.t Test copy, flip, rotate, matrix_transform t/t65crop.t t/t66paste.t diff --git a/Makefile.PL b/Makefile.PL index ecc05825..42d20ae5 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -157,7 +157,7 @@ if ($^O eq 'hpux') { $OSLIBS .= ' -ldld'; } if (defined $Config{'d_dlsymun'}) { $OSDEF .= ' -DDLSYMUN'; } my @objs = qw(Imager.o draw.o polygon.o image.o io.o iolayer.o - log.o gaussian.o conv.o pnm.o raw.o feat.o font.o + log.o gaussian.o conv.o pnm.o raw.o feat.o font.o combine.o filters.o dynaload.o stackmach.o datatypes.o regmach.o trans2.o quant.o error.o convert.o map.o tags.o palimg.o maskimg.o img16.o rotate.o diff --git a/combine.im b/combine.im new file mode 100644 index 00000000..37e174d3 --- /dev/null +++ b/combine.im @@ -0,0 +1,81 @@ +/* +=head1 NAME + +combine.im - combining channels into an image + +=head1 SYNOPSIS + + out = i_combine(imgs, channels, count); + +=head1 DESCRIPTION + +Combines channels from the input images into an output image. + +=over + +=cut +*/ + +#include "imager.h" + +i_img * +i_combine(i_img **imgs, const int *channels, int in_count) { + i_img *out = NULL; + int maxbits = 0; + i_img *maximg = NULL; + int i; + i_img_dim width, height; + i_img_dim x, y; + + i_clear_error(); + if (in_count <= 0) { + i_push_error(0, "At least one image must be supplied"); + return NULL; + } + if (in_count > MAXCHANNELS) { + i_push_errorf(0, "Maximum of %d channels, you supplied %d", + MAXCHANNELS, in_count); + return NULL; + } + + width = imgs[0]->xsize; + height = imgs[0]->ysize; + for (i = 0; i < in_count; ++i) { + if (imgs[i]->bits > maxbits) { + maximg = imgs[i]; + maxbits = maximg->bits; + } + if (imgs[i]->xsize < width) + width = imgs[i]->xsize; + if (imgs[i]->ysize < height) + height = imgs[i]->ysize; + if (channels[i] < 0) { + i_push_error(0, "Channel numbers must be zero or positive"); + return NULL; + } + if (channels[i] >= imgs[i]->channels) { + i_push_errorf(0, "Channel %d for image %d is too high (%d channels)", + channels[i], i, imgs[i]->channels); + return NULL; + } + } + + out = i_sametype_chans(maximg, width, height, in_count); + if (!out) + return NULL; +#code maxbits <= i_8_bits + IM_SAMPLE_T *in_row = mymalloc(sizeof(IM_SAMPLE_T) * width); + IM_COLOR *out_row = mymalloc(sizeof(IM_COLOR) * width); + + for (y = 0; y < height; ++y) { + for (i = 0; i < in_count; ++i) { + IM_GSAMP(imgs[i], 0, width, y, in_row, channels + i, 1); + for (x = 0; x < width; ++x) + out_row[x].channel[i] = in_row[x]; + } + IM_PLIN(out, 0, width, y, out_row); + } +#/code + + return out; +} diff --git a/imager.h b/imager.h index 085fea7b..a803382f 100644 --- a/imager.h +++ b/imager.h @@ -190,6 +190,9 @@ i_compose(i_img *out, i_img *src, int out_left, int out_top, int src_left, int src_top, int width, int height, int combine, double opacity); +extern i_img * +i_combine(i_img **src, const int *channels, int in_count); + undef_int i_flipxy (i_img *im, int direction); extern i_img *i_rotate90(i_img *im, int degrees); extern i_img *i_rotate_exact(i_img *im, double amount); diff --git a/lib/Imager/Transformations.pod b/lib/Imager/Transformations.pod index 4a6557c0..39aea2a3 100644 --- a/lib/Imager/Transformations.pod +++ b/lib/Imager/Transformations.pod @@ -48,6 +48,11 @@ Imager::Transformations - Simple transformations of one image into another. [ 1, 0, 0 ], [ 0, 0, 1 ] ]); + # build an image using channels from multiple input images + $new = $img->combine(src => [ $im1, $im2, $im3 ]); + $new = $img->combine(src => [ $im1, $im2, $im3 ], + channels => [ 2, 1, 0 ]); + # limit the range of red channel from 0..255 to 0..127 @map = map { int( $_/2 } 0..255; $img->map( red=>\@map ); @@ -867,6 +872,41 @@ alpha channel: [ 0, 0, 0, 0.5 ], ]); +=item combine +X + +Combine channels from one or more input images into a new image. + +Parameters: + +=over + +=item * + +C - a reference to an array of input images. There must be at least +one input image. A given image may appear more than once in C. + +=item * + +C - a reference to an array of channels corresponding to the +source images. If C is not supplied then the first channel +from each input image is used. If the array referenced by C +is shorter than that referenced by C then the first channel is +used from the extra images. + +=back + + # make an rgb image from red, green, and blue images + my $rgb = Imager->combine(src => [ $red, $green, $blue ]); + + # convert a BGR image into RGB + my $rgb = Imager->combine(src => [ $bgr, $bgr, $bgr ], + channels => [ 2, 1, 0 ]); + + # add an alpha channel from another image + my $rgba = Imager->combine(src => [ $rgb, $rgb, $rgb, $alpha ], + channels => [ 0, 1, 2, 0 ]); + =back =head2 Color Mappings diff --git a/t/t63combine.t b/t/t63combine.t new file mode 100644 index 00000000..b94b97a2 --- /dev/null +++ b/t/t63combine.t @@ -0,0 +1,101 @@ +#!perl -w +use strict; +use Imager; +use Test::More tests => 31; +use Imager::Test qw/test_image test_image_double is_image/; + +my $test_im = test_image; +my $test_im_dbl = test_image_double; + +{ + # split out channels and put it back together + my $red = Imager->combine(src => [ $test_im ]); + ok($red, "extracted the red channel"); + is($red->getchannels, 1, "red should be a single channel"); + my $green = Imager->combine(src => [ $test_im ], channels => [ 1 ]); + ok($green, "extracted the green channel"); + is($green->getchannels, 1, "green should be a single channel"); + my $blue = $test_im->convert(preset => "blue"); + ok($blue, "extracted blue (via convert)"); + + # put them back together + my $combined = Imager->combine(src => [ $red, $green, $blue ]); + is($combined->getchannels, 3, "check we got a three channel image"); + is_image($combined, $test_im, "presto! check it's the same"); +} + +{ + # no src + ok(!Imager->combine(), "no src"); + is(Imager->errstr, "src parameter missing", "check message"); +} + +{ + # bad image error + my $im = Imager->new; + ok(!Imager->combine(src => [ $im ]), "empty image"); + is(Imager->errstr, "empty input image", "check message"); +} + +{ + # not an image + my $im = {}; + ok(!Imager->combine(src => [ $im ]), "not an image"); + is(Imager->errstr, "src must contain image objects", "check message"); +} + +{ + # no images + ok(!Imager->combine(src => []), "no images"); + is(Imager->errstr, "At least one image must be supplied", + "check message"); +} + +{ + # too many images + ok(!Imager->combine(src => [ ($test_im) x 5 ]), "too many source images"); + is(Imager->errstr, "Maximum of 4 channels, you supplied 5", + "check message"); +} + +{ + # negative channel + ok(!Imager->combine(src => [ $test_im ], channels => [ -1 ]), + "negative channel"); + is(Imager->errstr, "Channel numbers must be zero or positive", + "check message"); +} + +{ + # channel too high + ok(!Imager->combine(src => [ $test_im ], channels => [ 3 ]), + "too high channel"); + is(Imager->errstr, "Channel 3 for image 0 is too high (3 channels)", + "check message"); +} + +{ + # make sure we get the higher of the bits + my $out = Imager->combine(src => [ $test_im, $test_im_dbl ]); + ok($out, "make from 8 and double/sample images"); + is($out->bits, "double", "check output bits"); +} + +{ + # check high-bit processing + # split out channels and put it back together + my $red = Imager->combine(src => [ $test_im_dbl ]); + ok($red, "extracted the red channel"); + is($red->getchannels, 1, "red should be a single channel"); + my $green = Imager->combine(src => [ $test_im_dbl ], channels => [ 1 ]); + ok($green, "extracted the green channel"); + is($green->getchannels, 1, "green should be a single channel"); + my $blue = $test_im_dbl->convert(preset => "blue"); + ok($blue, "extracted blue (via convert)"); + + # put them back together + my $combined = Imager->combine(src => [ $red, $green, $blue ]); + is($combined->getchannels, 3, "check we got a three channel image"); + is_image($combined, $test_im_dbl, "presto! check it's the same"); + is($combined->bits, "double", "and we got a double image output"); +} -- 2.39.5