How to obtain image size using standard Python class (without using external library)?

PythonImagePython 2.5

Python Problem Overview


I am using Python 2.5. And using the standard classes from Python, I want to determine the image size of a file.

I've heard PIL (Python Image Library), but it requires installation to work.

How might I obtain an image's size without using any external library, just using Python 2.5's own modules?

Note I want to support common image formats, particularly JPG and PNG.

Python Solutions


Solution 1 - Python

Here's a python 3 script that returns a tuple containing an image height and width for .png, .gif and .jpeg without using any external libraries (ie what Kurt McKee referenced above). Should be relatively easy to transfer it to Python 2.

import struct
import imghdr
	
def get_image_size(fname):
    '''Determine the image type of fhandle and return its size.
    from draco'''
    with open(fname, 'rb') as fhandle:
		head = fhandle.read(24)
		if len(head) != 24:
			return
		if imghdr.what(fname) == 'png':
			check = struct.unpack('>i', head[4:8])[0]
			if check != 0x0d0a1a0a:
				return
			width, height = struct.unpack('>ii', head[16:24])
		elif imghdr.what(fname) == 'gif':
			width, height = struct.unpack('<HH', head[6:10])
		elif imghdr.what(fname) == 'jpeg':
			try:
				fhandle.seek(0) # Read 0xff next
				size = 2
				ftype = 0
				while not 0xc0 <= ftype <= 0xcf:
					fhandle.seek(size, 1)
					byte = fhandle.read(1)
					while ord(byte) == 0xff:
						byte = fhandle.read(1)
					ftype = ord(byte)
					size = struct.unpack('>H', fhandle.read(2))[0] - 2
				# We are at a SOFn block
				fhandle.seek(1, 1)  # Skip `precision' byte.
				height, width = struct.unpack('>HH', fhandle.read(4))
			except Exception: #IGNORE:W0703
				return
		else:
			return
		return width, height

Solution 2 - Python

Kurts answer needed to be slightly modified to work for me.

First, on ubuntu: sudo apt-get install python-imaging

Then:

from PIL import Image
im=Image.open(filepath)
im.size # (width,height) tuple

Check out the handbook for more info.

Solution 3 - Python

Here's a way to get dimensions of a png file without needing a third-party module. From http://coreygoldberg.blogspot.com/2013/01/python-verify-png-file-and-get-image.html

import struct

def get_image_info(data):
    if is_png(data):
        w, h = struct.unpack('>LL', data[16:24])
        width = int(w)
        height = int(h)
    else:
        raise Exception('not a png image')
    return width, height

def is_png(data):
    return (data[:8] == '\211PNG\r\n\032\n'and (data[12:16] == 'IHDR'))

if __name__ == '__main__':
    with open('foo.png', 'rb') as f:
        data = f.read()

    print is_png(data)
    print get_image_info(data)

When you run this, it will return:

True
(x, y)

And another example that includes handling of JPEGs as well: http://markasread.net/post/17551554979/get-image-size-info-using-pure-python-code

Solution 4 - Python

While it's possible to call open(filename, 'rb') and check through the binary image headers for the dimensions, it seems much more useful to install PIL and spend your time writing great new software! You gain greater file format support and the reliability that comes from widespread usage. From the PIL documentation, it appears that the code you would need to complete your task would be:

from PIL import Image
im = Image.open('filename.png')
print 'width: %d - height: %d' % im.size # returns (width, height) tuple

As for writing code yourself, I'm not aware of a module in the Python standard library that will do what you want. You'll have to open() the image in binary mode and start decoding it yourself. You can read about the formats at:

Solution 5 - Python

Regarding Fred the Fantastic's answer:

Not every JPEG marker between C0-CF are SOF markers; I excluded DHT (C4), DNL (C8) and DAC (CC). Note that I haven't looked into whether it is even possible to parse any frames other than C0 and C2 in this manner. However, the other ones seem to be fairly rare (I personally haven't encountered any other than C0 and C2).

Either way, this solves the problem mentioned in comments by Malandy with Bangles.jpg (DHT erroneously parsed as SOF).

The other problem mentioned with 1431588037-WgsI3vK.jpg is due to imghdr only being able detect the APP0 (EXIF) and APP1 (JFIF) headers.

This can be fixed by adding a more lax test to imghdr (e.g. simply FFD8 or maybe FFD8FF?) or something much more complex (possibly even data validation). With a more complex approach I've only found issues with: APP14 (FFEE) (Adobe); the first marker being DQT (FFDB); and APP2 and issues with embedded ICC_PROFILEs.

Revised code below, also altered the call to imghdr.what() slightly:

import struct
import imghdr

def test_jpeg(h, f):
    # SOI APP2 + ICC_PROFILE
    if h[0:4] == '\xff\xd8\xff\xe2' and h[6:17] == b'ICC_PROFILE':
        print "A"
        return 'jpeg'
    # SOI APP14 + Adobe
    if h[0:4] == '\xff\xd8\xff\xee' and h[6:11] == b'Adobe':
        return 'jpeg'
    # SOI DQT
    if h[0:4] == '\xff\xd8\xff\xdb':
        return 'jpeg'
imghdr.tests.append(test_jpeg)

def get_image_size(fname):
    '''Determine the image type of fhandle and return its size.
    from draco'''
    with open(fname, 'rb') as fhandle:
		head = fhandle.read(24)
		if len(head) != 24:
			return
		what = imghdr.what(None, head)
		if what == 'png':
			check = struct.unpack('>i', head[4:8])[0]
			if check != 0x0d0a1a0a:
				return
			width, height = struct.unpack('>ii', head[16:24])
		elif what == 'gif':
			width, height = struct.unpack('<HH', head[6:10])
		elif what == 'jpeg':
			try:
				fhandle.seek(0) # Read 0xff next
				size = 2
				ftype = 0
				while not 0xc0 <= ftype <= 0xcf or ftype in (0xc4, 0xc8, 0xcc):
					fhandle.seek(size, 1)
					byte = fhandle.read(1)
					while ord(byte) == 0xff:
						byte = fhandle.read(1)
					ftype = ord(byte)
					size = struct.unpack('>H', fhandle.read(2))[0] - 2
				# We are at a SOFn block
				fhandle.seek(1, 1)  # Skip `precision' byte.
				height, width = struct.unpack('>HH', fhandle.read(4))
			except Exception: #IGNORE:W0703
				return
		else:
			return
		return width, height

Note: Created a full answer instead of a comment, since I'm not yet allowed to.

Solution 6 - Python

If you happen to have ImageMagick installed, then you can use 'identify'. For example, you can call it like this:

path = "//folder/image.jpg"
dim = subprocess.Popen(["identify","-format","\"%w,%h\"",path], stdout=subprocess.PIPE).communicate()[0]
(width, height) = [ int(x) for x in re.sub('[\t\r\n"]', '', dim).split(',') ]

Solution 7 - Python

Found a nice solution in another Stackoverflow post (using only standard libraries + dealing with jpg as well): JohnTESlade answer

And another solution (the quick way) for those who can afford running 'file' command within python, run:

import os
info = os.popen("file foo.jpg").read()
print info

Output:

foo.jpg: JPEG image data...density 28x28, segment length 16, baseline, precision 8, 352x198, frames 3

All you gotta do now is to format the output to capture the dimensions. 352x198 in my case.

Solution 8 - Python

That code does accomplish 2 things:

  • Getting the image dimension

  • Find the real EOF of a jpg file

Well when googling I was more interest in the later one. The task was to cut out a jpg file from a datastream. Since I I didn't find any way to use Pythons 'image' to a way to get the EOF of so jpg-File I made up this.

Interesting things /changes/notes in this sample:

  • extending the normal Python file class with the method uInt16 making source code better readable and maintainable. Messing around with struct.unpack() quickly makes code to look ugly

  • Replaced read over'uninteresting' areas/chunk with seek

  • Incase you just like to get the dimensions you may remove the line:

     hasChunk = ord(byte) not in range( 0xD0, 0xDA) + [0x00] 
    

    ->since that only get's important when reading over the image data chunk and comment in

     #break
    

    to stop reading as soon as the dimension were found. ...but smile what I'm telling - you're the Coder ;)

       import struct
       import io,os
    
       class myFile(file):
            
           def byte( self ):
                return file.read( self,  1);
            
           def uInt16( self ):
                tmp = file.read( self,  2)
                return struct.unpack( ">H", tmp )[0];
    
       jpeg = myFile('grafx_ui.s00_\\08521678_Unknown.jpg', 'rb')
    
       try:
           height = -1
           width  = -1
           EOI    = -1
    
           type_check = jpeg.read(2)
           if type_check != b'\xff\xd8':
             print("Not a JPG")
             
           else:
                
             byte = jpeg.byte()
    
             while byte != b"":
                  
               while byte != b'\xff': byte = jpeg.byte()
               while byte == b'\xff': byte = jpeg.byte()
    
    
               # FF D8	    SOI	Start of Image
               # FF D0..7  RST DRI Define Restart Interval inside CompressedData
               # FF 00	        Masked FF inside CompressedData
               # FF D9	    EOI	End of Image
               # http://en.wikipedia.org/wiki/JPEG#Syntax_and_structure
               hasChunk = ord(byte) not in range( 0xD0, 0xDA) + [0x00]
               if hasChunk:
                    ChunkSize   =  jpeg.uInt16()  - 2
                    ChunkOffset =  jpeg.tell()
                    Next_ChunkOffset = ChunkOffset + ChunkSize
    
               
               # Find bytes \xFF \xC0..C3 That marks the Start of Frame
               if (byte >= b'\xC0' and byte <= b'\xC3'):
                    
                 # Found  SOF1..3 data chunk - Read it and quit
                 jpeg.seek(1, os.SEEK_CUR)
                 h = jpeg.uInt16()
                 w = jpeg.uInt16()
    
    
                 #break
    
               
               elif (byte == b'\xD9'):
                    # Found End of Image
                    EOI = jpeg.tell()
                    break
               else:
                   # Seek to next data chunk
                  print "Pos: %.4x %x" % (jpeg.tell(), ChunkSize)
    
               if hasChunk:       
                  jpeg.seek(Next_ChunkOffset)
                    
               byte = jpeg.byte()
    
             width  = int(w)
             height = int(h)
    
             print("Width: %s, Height: %s  JpgFileDataSize: %x" % (width, height, EOI))
       finally:
           jpeg.close()
    

Solution 9 - Python

It depends on the output of file which I am not sure is standardized on all systems. Some JPEGs don't report the image size

import subprocess, re
image_size = list(map(int, re.findall('(\d+)x(\d+)', subprocess.getoutput("file" + filename))[-1]))

Solution 10 - Python

Stumbled upon this one but you can get it by using the following as long as you import numpy.

import numpy as np

[y, x] = np.shape(img[:,:,0])

It works because you ignore all but one color and then the image is just 2D so shape tells you how bid it is. Still kinda new to Python but seems like a simple way to do it.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionerosView Question on Stackoverflow
Solution 1 - PythonFred the FantasticView Answer on Stackoverflow
Solution 2 - PythontjbView Answer on Stackoverflow
Solution 3 - PythoncbautistaView Answer on Stackoverflow
Solution 4 - PythonKurt McKeeView Answer on Stackoverflow
Solution 5 - PythonDagh BunnstadView Answer on Stackoverflow
Solution 6 - PythonjensphView Answer on Stackoverflow
Solution 7 - PythonMercuryView Answer on Stackoverflow
Solution 8 - PythonNaduView Answer on Stackoverflow
Solution 9 - PythonSafvan CKView Answer on Stackoverflow
Solution 10 - PythonParkerView Answer on Stackoverflow