Search code examples
perlperl-module

Object method not found in Perl module : Imager


I am trying to use Imager module to generate gif images but I am getting an error

Can't locate object method "_valid_image" Can't locate object method "_valid_image" via package "D:\work...graph1.bmp" (perhaps you forgot to load "D:\work...\graph1.bmp"?) at C:/Perl/site/lib/Imager.pm line 1950.

I checked the .pm file for the module and the named function is well defined in it.

The code snippet that I am using:

use strict;
use warnings;
use File::Find::Rule;
use Imager;
my @path = $ARGV[0];

my @images = File::Find::Rule->file()
                            ->name( '*.bmp' )
                            ->in( @path );
my $type = 'bmp';
foreach my $filename(@images)
{
    my $img = Imager->new;
    $img->read(file=>$filename, type=>$type) or die "Cannot read $filename: ", $img->errstr;
}
my $filename1 = 'ctpdbn';
Imager->write_multi({ file=>$filename1, type=>'bmp' }, @images) or die Imager->errstr;

So I installed the Perl modules cpan Imager::valid_image and tried with adding use Imager::valid_image; or use Imager::_valid_image;, but it threw another error

Can't locate Imager/valid_image.pm in @INC (you may need to install the Imager::valid_image module) (@INC contains: C:/Perl/site/lib C:/Perl/lib .) at make_gif.pl line 14.

Really sorry for not putting up the complete code, I missed the part where actually the error is coming. Now I have updated it


Solution

  • Is that error really generated by the code that you show us? Line 1950 of Imager.pm is this piece of code:

    my $index = 1;
    for my $img (@images) {
      unless ($img->_valid_image("write_multi")) { # Line 1950 here
        $class->_set_error($img->errstr . " (image $index)");
        return;
      }
      ++$index;
    }
    

    This is inside a subroutine called write_multi() which is passed @images as one (or, really, several I suppose) of its parameters. The code you have shown us is all about reading files, not writing them.

    Looking more closely at the error message, its meaning is clear. It is trying to call the _valid_image() method on an image object. But instead of having an image object, it has a string which is the image filename. So it seems that somewhere, you are calling write_multi() passing it an array of image filenames where you should be passing it an array of image objects. Without seeing your real code, I can't be much more help.

    So I installed the Perl modules cpan Imager::valid_image and tried with adding use Imager::valid_image; or use Imager::_valid_image;

    That's just crazy talk! The error message is very clear that _valid_image is a method name, not a module name.

    Always read the error message carefully. Someone put a lot of effort into making it as clear as possible.

    Update: Ok. Now you've posted more of the code. And it looks like the problem is exactly as I said. Here's what I said yesterday:

    So it seems that somewhere, you are calling write_multi() passing it an array of image filenames where you should be passing it an array of image objects.

    And here's your code:

    Imager->write_multi({ ... }, @images) or die Imager->errstr;
    

    The important variable here is @images. How is @images populated?

    my @images = File::Find::Rule->file()
                                 ->name( '*.bmp' )
                                 ->in( @path );
    

    So @images contains a list of image filenames. Not a list of image objects.

    Look at your main loop.

    foreach my $filename(@images)
    {
        my $img = Imager->new;
        $img->read(file=>$filename, type=>$type)
            or die "Cannot read $filename: ", $img->errstr;
    }
    

    Here you loop over your array of image filenames and each time round the loop, you create a new Imager object. And then, when you get to the end of the loop iteration, you throw the object away. That's not a good idea.

    You probably want to create a new array containing your image objects. Something like this perhaps.

    @my @image_objects;
    foreach my $filename(@images)
    {
        my $img = Imager->new;
        $img->read(file=>$filename, type=>$type)
            or die "Cannot read $filename: ", $img->errstr;
        push @image_objects, $img;
    }
    

    Then you should pass this new array to write_multi().

    Imager->write_multi({ ... }, @image_objects) or die Imager->errstr;
    

    In your defence, the documentation for write_multi() isn't exactly clear. It says:

    write_multi()

    and if you want to write multiple images to a single file use the write_multi() method:

    Imager->write_multi({ file=> $filename, type=>$type }, @images)
      or die "Cannot write $filename: ", Imager->errstr;
    

    But it never bothers to tell you what should be in @images.