Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations IamaSherpa on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Reading JPEG height and width 4

Status
Not open for further replies.

Nage

Programmer
Jul 10, 2000
10
NL
Hi,

Does anybody know how to read the heigth and the width of a JPEG-file?
I only found one for a GIF:
------------------------------------------------------------------
#!/usr/local/bin/perl
# Program name: gifsize
# Author: Rajiv Pant (Betul)
# Version: 1.0. October 1994.

foreach $gif (@ARGV)
{
($width, $height, $type) = &gifsize ($gif) ;
($type =~ m/GIF/i)
? print &quot;<img src=\&quot;$gif\&quot; width=$width height=$height border=0>\n&quot;
: print &quot;$gif is not a GIF.\n&quot; ;
}

sub gifsize
{
local ($gif) = @_ ;
local ($w, $w2, $h, $h2, $gifwidth, $gifsize, $type) = () ;
open (GIF, $gif) ; read (GIF, $type, 3) ;
seek (GIF, 6, 0) ; read (GIF, $w, 1) ;
read (GIF, $w2, 1) ;
$width = ord ($w) + ord ($w2) * 256 ;
read (GIF, $h, 1) ; read (GIF, $h2, 1) ;
$height = ord ($h) + ord ($h2) * 256 ;
close (GIF) ;
return ($width, $height, $type) ;
} # end of sub gifsize

------------------------------------------------------------------

Greetings,

Nage s-)
 
Here's some code that will do it. You have to read the entire jpeg file into a scalar and pass a reference to the scalar to the subroutine. It returns a 3-element list: width, height, and jpeg type.
Code:
#---------------------------------------------------------------------
# JpegSize
#---------------------------------------------------------------------
# Determines the width and height of a jpeg image
#
# Pass a REFERENCE to a scalar containing the image
#
# Returns LIST (width, height, type)
#---------------------------------------------------------------------

sub JpegSize {

local ($rjpeg) = @_;
local ($type, $tag, $marker, $lhob, $llob, $blocklen) = ();
local ($whob, $wlob, $hhob, $hlob, $width, $height) = ();

# required jpeg markers
$M_SOF0 = &quot;\xc0&quot;;
$M_SOF1 = &quot;\xc1&quot;;
$M_SOF2 = &quot;\xc2&quot;;
$M_SOF3 = &quot;\xc3&quot;;
$M_SOI  = &quot;\xd8&quot;;
$M_EOI  = &quot;\xd9&quot;;
$M_SOS  = &quot;\xda&quot;;
$M_BLK  = &quot;\xff&quot;;

$pos = 0;
$type = substr ${$rjpeg}, $pos, 2;
$ctype = PrintHex($type);

# check for jpeg file type (start of image) marker
unless ($type eq $M_BLK.$M_SOI) {
#	print &quot;Error: not a jpeg file!\n&quot;;
	return ( 0, 0, $ctype );
}
$pos += 2;

for (;;) {
	# check for block tag
	$tag = substr ${$rjpeg}, $pos++, 1;
	if ($tag ne $M_BLK) {
#		print &quot;Error: No start of block marker (0xff) found!\n&quot;;
		return ( 0, 0, $ctype );
	}
	# get marker type & block length
	$marker = substr ${$rjpeg}, $pos++, 1;
	$lhob = substr ${$rjpeg}, $pos++, 1;
	$llob = substr ${$rjpeg}, $pos++, 1;
	$blocklen = (ord($lhob) * 256) + ord($llob) - 2;
	# check for any start of field marker
	if ( $marker ge $M_SOF0 && $marker le $M_SOF3 ) {
		# ignore data precision
		$pos++;
		# read the height and width.
		$hhob = substr ${$rjpeg}, $pos++, 1;
		$hlob = substr ${$rjpeg}, $pos++, 1;
		$height = (ord($hhob) * 256) + ord($hlob);
		$whob = substr ${$rjpeg}, $pos++, 1;
		$wlob = substr ${$rjpeg}, $pos++, 1;
		$width = (ord($whob) * 256) + ord($wlob);
		# ignore components & rest of file...
		return ($width, $height, &quot;JPEG &quot;.$ctype);
	} else {
		if ( $marker eq M_SOS || $marker eq M_EOI ) {
			## past header data; size indeterminable
			return (0, 0, &quot;JPEG &quot;.$ctype);
		}
		## skip to next marker
		$pos += $blocklen;
	}
} # for (;;)

} # JpegSize
I have a similar routine for gifsize if anyone wants it.

Tracy Dryden
tracy@bydisn.com

Meddle not in the affairs of dragons,
For you are crunchy, and good with mustard.
 
#Hey, tsdragon cool code! I'll probably use it.
#Is the following code good for use with your subroutine?
#It works, but I was wondering about line 3, is it
#necessary to read in the whole file, or is the header
#always within the first 7k of a jpg?

open (X, &quot;storage/galactica4.jpg&quot;) || die &quot;Coundn't open image&quot;;
binmode(X);
read (X, $pic , 7000);
close(X);


($width, $height, $type) = JpegSize(*pic);

print &quot;width=$width, height=$height, type=$type\n&quot;;
 
Both, thanks!

Greetings,

Nage :cool:
 
Could you use that to make all images load up but within 100 x 100 pixels and not distorting image width:height ratio?

Thanks,
Chris
 
Nope nope.
The JpegSize subroutine only reads what's in the jpeg header info... it doesn't change it.

I seriously doubt if you can shrink the size of a jpeg just by editing its header info.

I'm sure there's some thumbnail-making subroutine out there somewhere... I can imagine how it would work, pulling out pixels in a regular fashion.

-k
 
No i mean, shrinking the size using html....
But i want it to shrink it's size so it is within 100x100, t's size could be 100x50 which is within that.

So it would be something like
<img src=&quot;$image&quot; width=&quot;$newheight&quot; height=&quot;$newheight&quot;>
It doesn't actually change the actual images size, but just resizes it on screen.
 
kempis555: the better way to call it is:
Code:
($width, $height, $type) = JpegSize(\$pic);
The backslash in front of the variable name will take a reference to the variable.

I don't know for sure if the header info is always in the first 7K or not. It would seem likely, but I wouldn't bet on it without seeing proof.

I got the basis for this code from a free program somewhere that actually opens a jpeg file and reads in bits and pieces as necessary until it finds what it wants. For what you're doing that might be more efficient. My PC is dead and I'm using my boss's, so I can't tell if I still have the original code or not. If I can get my PC back I'll post that here too. I redid the code to use a scalar containing the image because I was fetching images from other web sites and checking the size of them wanted to make sure all the 120x60 banners were actually 120x60).
Tracy Dryden
tracy@bydisn.com

Meddle not in the affairs of dragons,
For you are crunchy, and good with mustard.
 
MagicChris: I also have a subtrn that will take the size of an image and the desired maximum size and recalc what the width and height should be to fit within that maximum. Once I get my PC back up I'll post that too.
Tracy Dryden
tracy@bydisn.com

Meddle not in the affairs of dragons,
For you are crunchy, and good with mustard.
 
It looks like my hard drive has crashed pretty badly. I doubt I will be able to get anything off it until next week. Keep an eye out here and I'll post the code as soon as I can get to it (assuming I can get to it). Tracy Dryden
tracy@bydisn.com

Meddle not in the affairs of dragons,
For you are crunchy, and good with mustard.
 
Tsdragon,

Nice code there. I'm not in a position right now to try to run it, but I'm curious as to how it benchmarks? The code I worked out -- though significantly shorter -- tends to crawl at about my mind's pace. It would move quite a bit faster if there was a set number of bytes for the header information, but like you, I haven't found proof of that yet. I've included the routine here for anyone who cares.
Code:
sub jpegxy($) {
    my $j=shift;
    @_=map{ord}split//,join//,<$j>;
    return [0,0] if shift!=0xff&&shift!=0xd8;
    while(@_) {
        shift==0xff?$_=shift:next;
        while($_==0xff) {$_=shift}
        my $l=$_>=0xc0&&$_<=0xc3?return [@_[5]<<8|@_[6],@_[3]<<8|@_[4]]:(shift)<<8|shift;
        shift for 1..$l-2;
    }
    return [0,0]
}
And you would call it like so:
Code:
open(FH,@ARGV[0]);
$_=jpegxy(\*FH);
print $_->[0],'x',$_->[1];

I put this together this morning and haven't had much of a chance to sit and ponder it, so if anyone else wants to give optimization (ie. get rid of that map,ord,split,join junk) a shot on this one, I'd love to see the results.


brendanc@icehouse.net
 
MagicChris, in the HTML you can go like this:
<img src=&quot;ranma.gif&quot; width=&quot;100&quot;>
and the browser will automatically scale the height to the correct proportions.

Wow tsdragon that jpegxy sub is rather obfuscated. Is line 8 actually Perl code? ^_^

-k
 
Actually, I wrote the jpegxy routine. Tsdragon did the cleaner, and likely more efficient JpegSize sub above it. And line 8 is, indeed, valid code. :) I don't intentionally try to make my code appear complicated, obfuscated, or whathaveyou...I apologize if it comes across that way. But I do try to utilize the Perl internals (special variables, default arguments and the like) as much as possible. Hence all the $_'s, @_'s and shifts. When I first started, I, like so many other programmers, invented wheel after wheel after wheel. With a few years under the belt, though.. it just doesn't make sense to do that anymore. Perl's got thousands of nifty, hidden tricks. I encourage everyone to read through the perldocs and figure out how to use them.

brendanc@icehouse.net
 
I can't take too much credit for the code. Like I said, I found the code somewhere and just cleaned it up, made it a little easier to understand, and made it work on a scalar string instead of reading bytes from a file. I firmly believe in not reinventing the wheel if I can help it, so I find myself shopping for free bits of code like this a lot. I got a great subroutine for converting an RGB value to HSV the same way.

Apparently jpeg headers are a LOT more complicated than gif headers. The gifsize subroutine code is much shorter and easier to understand. I actually took a couple of jpeg files and worked the code thru them manually to see how it worked. It was a pain, but a great learning exercise. Tracy Dryden
tracy@bydisn.com

Meddle not in the affairs of dragons,
For you are crunchy, and good with mustard.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top