Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hi, I want to put a limit on the maximum width of a photo, so that if it is smaller than the width it shows full size, but if it is bigger it is reduced to the limit. Is there any way to obtain the width in pixels of the photo without using image magik? Thanks...
  • Comment on getting the dimensions of a picture (jpeg)

Replies are listed 'Best First'.
Re: getting the dimensions of a picture (jpeg)
by JediWizard (Deacon) on Feb 16, 2005 at 16:53 UTC

    If all you want to due is find the image's size, try Image::Size. This will not resize the image for you, but it will tell you it's width and hight in pixels.

    May the Force be with you
Re: getting the dimensions of a picture (jpeg)
by Random_Walk (Prior) on Feb 16, 2005 at 16:52 UTC

    Indeed there is, this answer is from this jpeg faq

    Subject: 22 How can my program extract image dimensions from a JPEG file?

    The header of a JPEG file consists of a series of blocks, called "markers". The image height and width are stored in a marker of type SOFn (Start Of Frame, type N). To find the SOFn you must skip over the preceding markers; you don't have to know what's in the other types of markers, just use their length words to skip over them. The minimum logic needed is perhaps a page of C code. (Some people have recommended just searching for the byte pair representing SOFn, without paying attention to the marker block structure. This is unsafe because a prior marker might contain the SOFn pattern, either by chance or because it contains a JPEG-compressed thumbnail image. If you don't follow the marker structure you will retrieve the thumbnail's size instead of the main image size.) A profusely commented example in C can be found in rdjpgcom.c in the IJG distribution (see part 2, item 15). Perl code can be found in wwwis, from http://www.tardis.ed.ac.uk/~ark/wwwis/.

    That link (the tardis one) is dead, or at least it is for me but what looks like the same code is also to be found here

    Cheers,
    R.

    Pereant, qui ante nos nostra dixerunt!
Re: getting the dimensions of a picture (jpeg)
by borisz (Canon) on Feb 16, 2005 at 16:56 UTC
    If you are just interesteds in the dimmensions of a image, Image::Size may the right choice.
    Boris
Re: getting the dimensions of a picture (jpeg)
by dragonchild (Archbishop) on Feb 16, 2005 at 16:45 UTC
    If you're doing this in HTML, you can use the width and height attributes of the IMG tag.

    If that's not acceptable, you can use both ImageMagick or GD. But, you have to have something that will parse the jpeg format.

    Being right, does not endow the right to be rude; politeness costs nothing.
    Being unknowing, is not the same as being stupid.
    Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
    Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

Re: getting the dimensions of a picture (jpeg)
by haukex (Archbishop) on Aug 01, 2019 at 21:27 UTC

    Sorry for the necromancy, but since it wasn't mentioned yet, there is now also Imager, a well-maintained module that is an alternative to Image::Magick - IMHO although it may not be as feature-rich as Magick, it's got enough features, and it seems easier to install. For this task, see getwidth and getheight in Imager::ImageTypes, and scale in Imager::Transformations.

Re: getting the dimensions of a picture (jpeg)
by halley (Prior) on Feb 16, 2005 at 17:36 UTC
    Just as an aside, if you're already using ImageMagick to make the image of viewable size, you can tell ImageMagick to do exactly the decision you mention. For example, to keep small images small, but also to resize images bigger than 500x500, use -geometry '500x500>' (with the greater-than sign). This tells it to resize only if the image is greater than your specified limit.

    --
    [ e d @ h a l l e y . c c ]

Re: getting the dimensions of a picture (jpeg)
by jbrugger (Parson) on Feb 16, 2005 at 18:20 UTC
    i've written a bigger example in the node Re^2: Swimsuits2004, but to be short:
    #!/usr/bin/perl -w use strict; use Image::Magick; my $file="path/to/yourfile.jpg" my $image = Image::Magick->new; $image->Read($file); my ($x,$y) = $image->Get('width', 'height');
    Oops: no Image::Magic for you... Indeed use Image::Size, but it's not that powerfull:
    #!/usr/bin/perl -w use strict; use Image::Size; ($x, $y) = imgsize("path/to/yourfile.jpg");

    but for resizing i'd prefer Image::magic, but an alternative could be Image::Processor
    another idea is to use the Gimp in Perl.
    for the code below to work, refer to:Image size in pure perl, you need
    sub sizeJPG { return unless $_[0]; my ($width, $height); ($height,$width) = unpack( "nn", $1 ) if $_[0] =~ /\xFF\xC0...(... +.)/; return ($width,$height); }
Re: getting the dimensions of a picture (jpeg)
by zentara (Cardinal) on Feb 16, 2005 at 19:16 UTC
    #!/usr/bin/perl -w use strict; my $jpg = shift or die "$!\n"; my $data = get_file($jpg); my @res = sizeJPG($data); print "$jpg width $res[0] height $res[1]\n"; sub sizeJPG { return unless $_[0]; my ( $width, $height ); ( $height, $width ) = unpack( "nn", $1 ) if $_[0] =~ /\xFF\xC0...(....)/; return ( $width, $height ); } sub get_file { open FILE, $_[0] or die $!; binmode FILE; local $/; my $data = <FILE>; close FILE; return $data; }

    I'm not really a human, but I play one on earth. flash japh
      As side notes:
      #!/usr/bin/perl -w use strict;
      Notice that nowadays (unless you have serious backward compatibility issues) it is recommended to
      use warnings;
      instead. Since when I made a similar remark somebody popped out asking why I thought the latter was a better alternative, I'll point out in advance that no, I'm not explaining it. perldoc warnings and perldoc perlrun give some clues about it.
      my $jpg = shift or die "$!\n";
      Huh?!? $! is meaningful only after a failure of a system call (well, "system library"). At this point it may be anything...
      my $data = get_file($jpg); my @res = sizeJPG($data); print "$jpg width $res[0] height $res[1]\n";
      Please, do not misunderstand me: I know that this is a minimal example, but why do you put the return values of your sub into an array only to later use its entries separately? I.e., wouldn't have
      my $data = get_file($jpg); my ($h,$v) = sizeJPG($data); print "$jpg width $h height $v\n";
      been clearer?

      Also, in case the regex below is not matched, the return values will be undef, and this will issue a warning in the print statement above. And if you locally disable 'uninitialized' warnings, then you'll get an IMHO inconsistent output. You may want to treat this case in a different way...

      sub sizeJPG { return unless $_[0]; my ( $width, $height ); ( $height, $width ) = unpack( "nn", $1 ) if $_[0] =~ /\xFF\xC0...(....)/; return ( $width, $height ); }
      I like to do
      local $_=shift;
      if possible, but that's just a personal preference.

      As a general remark you can also use the return value of the match operator, but indeed in this case it can be slightly awkward:

      my ($width,$height)=unpack "nn", (/\xFF\xC0...(....)/)[0];
      and it will also issue a warning if the regex is not matched. So a possibly better solution may be:
      my ($width,$height)=unpack "nn", /\xFF\xC0...(....)/ ? $1 : ''; # or "\0" x 4, maybe?
      Let's go on...
      sub get_file { open FILE, $_[0] or die $!; binmode FILE; local $/; my $data = <FILE>; close FILE; return $data; }
      Why not
      sub get_file { local $/; open my $fh, '<:raw', shift or die $!; <$fh>; }
      instead?

      (But then I'd probably use a do block. In the meantime we continue to wait for Perl6's .slurp!!)

        All good points. I was just in a hurry, and saw I had an old snippet which showed it. Last night I realized it could also probably be sped up too, since it prints on each pixel. It probably could be re-written to process chunks at a time, and even regexed chunks at a time. The only problem is making sure the chunk boundaries are on pixel boundaries, so you don't break up the pixel-triples.

        I'm not really a human, but I play one on earth. flash japh
        I'm trying to play around with all this code you've posted but I can't seem to get past the original script's line 18-- "No such file or directory; it's also got a syntax error, "my $data = ;" crashes the script. Any way all these snippets can be squished together to make a usable script? Replacing that 'shift or die' thing at the top with 'picture.jpg' doesn't run as smoothly as I'd like it to :(
Re: getting the dimensions of a picture (jpeg)
by Anonymous Monk on Feb 16, 2005 at 17:23 UTC
    Sure, there are tons of ways. But I find image magick the easiest. Now, I could give another way of doing it, but since you don't want to use image magick, I presume there's a reason for that. And that reason could apply to hundreds of other ways as well. If you could tell why you don't want to use image magick, we might be able to supply you with a useful alternative, instead of having to guess what's acceptable for you.
Re: getting the dimensions of a picture (jpeg)
by JonBass (Initiate) on Nov 09, 2015 at 15:14 UTC
    Hi All, I know this is a 10 year old thread, but I stumbled across it as I was searching for the same thing... Here is my solution:
    sub _getJpegDimensions { my $path = shift; # read first 100kb - this should be enough to get the SOF sec +tion. open my $fh, '<', $path; read $fh, my $data, 102400; close $fh; my @chars = split("", $data); my @asciis = map { ord() } @chars; # convert all chars to ascii v +alues # jpg images start with xff xd8, so check this first and remov +e if found if ((shift @asciis == 0xff) and (shift @asciis == 0xd8)) { # loop through each section until the SOF section is found while(@asciis) { my $val = shift @asciis; # check if $val is possibly the start of a marker (all + markers are 0xffXX) if ( $val != 0xff ) { next; } else { $val = shift @asciis; # Start of frame marker is usually 0xffc0 # check for c0 to c3 to include extended sequentia +l, progressive and lossless markers if (($val >= 0xc0) and ($val <= 0xc3)) { # SOF block is [0xffc0][ushort length][uchar preci +sion][ushort x][ushort y] # [0xffc0][ 2 Bytes ][ 1 Byte + ][2 Bytes ][2 Bytes ] # note that x is height, y is width my $width = ($asciis[5] * 256) + $asciis[6]; # +convert the 2 byte ushort into a decimal number my $height = ($asciis[3] * 256) + $asciis[4]; return ($width, $height); } else { # if this isnt an SOF section, skip the rest # section length is immediately after marker # section length includes marker, hence why 2 is s +ubtracted my $section_length = shift @asciis; if ($section_length - 2 > 0) { splice @asciis, 0, $section_length - 2; } } } } } else { return (0,0); } }
    This is called as follows:
    my ($width, $height) = &_getJpegDimensions("path to jpg as string");
    Note - this code makes the following assumptions: 1. 100kb is enough to get the SOF header. 2. The .jpg is encoded using Baseline, Extended Sequential, Progressive or Lossless schemes. Hope this helps! Jon
Re: getting the dimensions of a picture (jpeg)
by Anonymous Monk on Aug 01, 2019 at 13:17 UTC
    Had the same issue, and some research came up with the following code:
    use strict; my $debug=0; sub readUInt8 { my ($fh) = @_; my $data; read($fh, $data, 1); return unpack("C", $data); } sub readUInt16 { my ($fh) = @_; my $data; read($fh, $data, 2); return unpack("n16", $data); } sub skipBytes { my ($fh, $len) = @_; my $data; printf(" Skipping %d bytes\n", $len) if ($debug); read($fh, $data, $len); } sub skipSection { my ($fh, $v) = @_; my $len = readUInt16($fh); skipBytes($fh, $len-2); } sub getImageInfo { my ($filename) = @_; my $v; my $fh; my $width=0; my $height=0; my $type=""; open($fh, $filename); binmode($fh); $v = readUInt16($fh); if ($v == 0xffd8) { my $done=0; while (!$done) { $v = readUInt16($fh); printf("Section %x\n", $v) if ($debug); if ($v == 0xffe0 || $v == 0xffdb || $v == 0xffe0 || $v == 0xffe1 || $v == 0xffe2 || $v == 0xffe3 || $v == 0xffe4 || $v == 0xffe5 || $v == 0xffe6 || $v == 0xffe7 || $v == 0xffe8 || $v == 0xffe9 || $v == 0xffea || $v == 0xffeb || $v == 0xffec || $v == 0xffed || $v == 0xffee || $v == 0xffc4 || $v == 0xffe0 || $v == 0xfffe ) { skipSection($fh, $v); } elsif ($v == 0xffdd) { skipBytes($fh, 4); } elsif ($v == 0xffc0) { $v = readUInt16($fh); # len $v = readUInt8($fh); # precision $height = readUInt16($fh); # width $width = readUInt16($fh); # height $type = "jpg"; last; } else { printf("$filename - Unexpected value %x\n", $v); last; } } } close($fh); return ($type, $width, $height); }