http://www.perlmonks.org?node_id=11104598

Cody Fendant has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to write a module which turns images into data.

I've got it working with Image::Magick but what if I wanted it to work with that or GD::Image, depending what the user has? Plus other things as well maybe.

Should I try to detect what's on the system? Should I make MyModule::ImageMagick and MyModule::GD and make the user choose?

What's the right way to do this kind of thing?

  • Comment on Best practice for a module which can use one of multiple image modules

Replies are listed 'Best First'.
Re: Best practice for a module which can use one of multiple image modules
by Corion (Pope) on Aug 17, 2019 at 06:27 UTC

    Personally, I've settled on having one top-level module that looks at what is available (resp. what the programmer specifies), and a set of modules below that, which are the implementations for each "real" module:

    package Future::HTTP; use strict; use Filter::signatures; no warnings 'experimental::signatures'; use feature 'signatures'; our $VERSION = '0.12'; our @loops; push @loops, ( ['IO/Async.pm' => 'Future::HTTP::NetAsync' ], ['Mojo/IOLoop.pm' => 'Future::HTTP::Mojo' ], ['AnyEvent.pm' => 'Future::HTTP::AnyEvent'], ['AE.pm' => 'Future::HTTP::AnyEvent'], # POE support would be nice # LWP::UserAgent support would be nice # A threaded backend would also be nice but likely brings in other # interesting problems. How will we load this? We have two prerequ +isites # now, threads.pm and HTTP::Tiny... #['threads.pm' => 'Future::HTTP::Tiny::threaded' ], ['HTTP/Tiny/Paranoid.pm' => 'Future::HTTP::Tiny::Paranoid'], # The fallback, will always catch due to loading Future::HTTP ['Future/HTTP.pm' => 'Future::HTTP::Tiny'], ); our $implementation; sub new($factoryclass, @args) { $implementation ||= $factoryclass->best_implementation(); # return a new instance $implementation->new(@args); } sub best_implementation( $class, @candidates ) { if(! @candidates) { @candidates = @loops; }; # Find the currently running/loaded event loop(s) #use Data::Dumper; #warn Dumper \%INC; #warn Dumper \@candidates; my @applicable_implementations = map { $_->[1] } grep { $INC{$_->[0]} } @candidates; # Check which one we can load: for my $impl (@applicable_implementations) { if( eval "require $impl; 1" ) { return $impl; }; }; };

    This is likely overkill when the real meat is a subroutine of three or four lines and mostly parameter swapping, but I find the above approach easier, since each module can be conveniently tested.

    Otherwise, I'll likely do the above except that best_implementation returns a reference to a subroutine:

    sub do_imagemagick { require Image::Magick; Image::Magick->import(...); # ... } sub do_imager { require Imager; Imager->import(...); # ... }
Re: Best practice for a module which can use one of multiple image modules
by haukex (Chancellor) on Aug 17, 2019 at 06:28 UTC
    Should I try to detect what's on the system? Should I make MyModule::ImageMagick and MyModule::GD and make the user choose?

    I would say that it depends. If you are using the modules simply to generically load images, and nothing else, as your previous post implies, then it might be easier to auto-detect what modules the user has on their system (Imager is another good one, BTW). If you are building on features that the modules provide, then since your code will depend more tightly on the modules' API, then the second option might make more sense.

Re: Best practice for a module which can use one of multiple image modules
by Your Mother (Bishop) on Aug 17, 2019 at 16:44 UTC

    Along with what jcb suggested depending on what you're actually doing; a combination of Image::EXIF and PDL might cover what you want.

    On the actual question. I think the best practice for this kind of situation is to create a set of platform targeted binaries or build packages which is really a lot of work and not Perly. If the code is targeted at developers, an interactive installation is fair where the user chooses the backend—defaults need to be there for automated testing—or to install dependencies.

    I would say the average Perl installation does not have Image::Magick or GD::Image. Both are non-core and both can be a pain to build. If you have enough fall backs like Imager or the combination suggested above or some minimal pure Perl stuff you can package with your code then Corion’s suggestion starts to look quite sensible.

    It all sounds like a lot of work for the problem though. I would gravitate towards the minimum pure Perl with dependencies included in the distribution if portability is the real concern.

Re: Best practice for a module which can use one of multiple image modules
by jcb (Chaplain) on Aug 17, 2019 at 06:32 UTC

    Have you considered PDL?

Re: Best practice for a module which can use one of multiple image modules
by bliako (Vicar) on Aug 19, 2019 at 22:04 UTC
     I'm trying to write a module which turns images into data. 

    I would have all my methods receive input in the form of raw data (refs), say 32bit. You do not need any of Magick's or GD's functionality (drawing boxes, writing text onto image etc.) so why have your methods demand Magick or GD object as input? As a convenience to the API user, you could supply a static method to convert a Magick or GD object into the raw data (refs) type your methods require (something like the factory methods some fellow monk mentioned).

    But then, if the user already converted their image into 32bit raw data for input to your module's methods, you have little more to do than saving it into a file. Unless your aim is to provide data transformation/validation as well. If that's what you are after then ask for input in a common format, e.g. 32bit pixel data and start your module on this assumption. Much more productive than having to deal with Magick's or GD's internal formats. The biggest disadvantage is that getting a *reference* to image data from those objects may not be possible and your user may have to duplicate data ...

    bw, bliako, (swimming) under Poseidon's temple

Re: Best practice for a module which can use one of multiple image modules
by Anonymous Monk on Aug 19, 2019 at 17:35 UTC

    (In any programming language ...) This is a great place to use classes. The image-handling operations are defined by what (in other languages) would be called an "abstract parent-class" which defines how the rest of the application sees it. Then, for each actual implementation, you have a "concrete" child class that inherits from the parent and implements all of its methods.

    It is also common practice to build a "factory" which, given various appropriate parameters to describe the input that is to be handled, automatically returns a child object of the appropriate concrete class. The rest of the application, therefore, doesn't have to care. It simply treats the returned object as though it were the abstract parent.