Welcome, guest | Sign In | My Account | Store | Cart

This script uses the Python Imaging libraries to resize images in a folder and its sub-folders iteratively. The details like the directory hierarchy, current progress etc are displayed dynamically. We can optionally specify more image formats, required size etc.(See command-line argument --help). This is meant for processing large collections of images, which justifies the display of many details.

Python, 150 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#!/usr/bin/env python

from os import *
import sys
try:
	from PIL import Image
except ImportError:
	print "Python image libraries not found!"
	sys.exit()		

# Default options
size=1024,9000
extlist=['.jpg', '.jpeg']
directory=getcwd()
getchild=False

def resize(folder, level=0):
	"Does the actual image scaling action" 
	          	
	tick = unichr(0x2714).encode("utf-8")
	dot= unichr(0x2726).encode("utf-8")	
	unitSegment=5     #The basic unit in the loading bar
	linesPerP=1.0/unitSegment
	
	if path.isdir(folder):						
		imageQue=[item for item in listdir(folder) if path.splitext(item.lower())[1] in extlist]		
		total=len(imageQue)		
		folderData=dot+' '+ folder.split(sep)[-1]
		done=0
		maxImageName=0 #resets to previous image name's length everytime(to clear the exact length) 
		
		for image in imageQue:
			maxImageName= max(maxImageName, len(image))
			percentage= int(float(done)/total*100)
			
			#the dynamic loading bar data 
			sys.stdout.write( "\r{indent}{fold}  [{imgno}] [{bar}] {per}%    {img}".format( 
			indent='   '+' '*level,
			fold=folderData,
			imgno=str(total)+' images',
			bar=('='*int(percentage*linesPerP)).ljust(int(100*linesPerP)),
			per=percentage, 
			img= image.ljust(maxImageName) ))			
			sys.stdout.flush()		
			
			#the resizing process
			maxImageName=len(image)
			image=path.join(folder,image)
			im=Image.open(image)
			im.thumbnail(size, Image.ANTIALIAS)
    			im.save(image, im.format)			
			done+=1
			
		
		#the completed loading bar data	
		sys.stdout.write( "\r{indent}{fold}  [{imgno}] [{alldone}] {clear}\n".format(
		indent='   '+' '*level,
		fold=folderData, 
		alldone=tick, 
		imgno=str(total)+' images',
		clear=' '*(6+maxImageName+int(100*linesPerP))  ))
		
		#attacking sub directories!				
		if getchild:
			for subfolder in listdir(folder):						
				resize(folder+sep+subfolder,level+5)				
				
def invalidArgs():
	print "Invalid arguments!"
	sys.exit()				

def parseArgs():
	"Analyse the commandline parameters; Calls resize function if things are okay "
	arglist=['-d','-o','-e','-c'] # -c is alone (no additional data); others work in pairs
	global size
	global extlist
	global directory
	global getchild
	
	if '--help' in sys.argv or '--start' not in sys.argv:     # --help is the show stopper :)
		print """		
Usage: imageresize --start [options] 
		
Scale images to given size
  * Default directory is current directory (includes subdirectories)
  * Default resize option is '1024' 
    (scale width of image if it exceeds 1024px; retain aspect ratio)
  * Default extensions are jpg, jpeg (not case-sensitive)
  * Overwrites original image
           
Parameters:
  --start              begins the process  
  -d <path>	       <path> is the path to the required directory 	
  -o '<option>'	       <option> can be of the following formats   
     '100'      : scale width of image to 100px
     'x60'      : scale height of image to 60px
     '100x60'   : scale image within given bounds
     (aspect ratio preserved)
  -e <ext1>,<ext2>...  <extn> is any valid extension (.png, .gif) 
  -c                   Scale images in sub-folders  		   			
  --help	       Displays this help
"""
	else:
		sys.argv.remove('--start')
		l=len(sys.argv)
		i=1
		while i<l:
			arg=sys.argv[i]
			if arg in arglist:
				if arg=='-c':     # -c is alone 
					getchild=True
					i+=1
					continue
				elif i+1<l:
					data=sys.argv[i+1]
					if data in arglist or data==None:
						invalidArgs()   #only pairs; no odd args after -c!
					elif arg=='-d':
						if not path.exists(data):
							print "Directory does not exist"
							invalidArgs()
						directory=data	
					elif arg=='-o':				 					
						validOptionChars=[str(letter) for letter in range(0,10)]
						validOptionChars.extend(['x'])
						for letter in data:  #validating option string
							if letter not in validOptionChars:
								invalidArgs()
						if 'x' in data.replace('x',''): 
							invalidArgs()		
						dim=data.split('x')
						if data.startswith('x'): #scale height
							height=int(dim[0])
							size=height*9,height
						elif 'x' not in data:   #scale width
							width=int(dim[0])
							size=width,width*9
						else:   #scale to fit bounding box
							size=int(dim[0]),int(dim[1])				
					else:				
						extlist.extend(data.lower().split(","))
					i+=2  #Done with the pair, move on
				else:
					invalidArgs() #Expected data not found 		
						
			else:  #if a stranger is present in positions where args are expected
				invalidArgs()		
		resize(directory)   #Okies!				

parseArgs() #So lets get started
	

This script came out of a need to optimize my huge image collection. Most of the digital camera images have dimensions like 4000x3200 and a size around 3MB. But we rarely need all those images in big sizes. Usually they only require dimensions that fits our desktop size. When we resize an image of 4000x3200 to fit 1024x768, the storage size is considerably reduced. This can save a lot of space when the processing is done for big collections, except those special images u want to be in the best size. This can be especially useful if you're troubled with lack of space in the hard drive. Since the whole image resizing process can take up lot of time, its desirable that the details be printed along. That is what exactly the script does!

More flexibility can be obtained if we use the script with ImageMagick program, using ImageMagick commands to resize image. This also obtains better quality images. This code can be obtained here

Commandline arguments are as follows: Scale images to given size * Default directory is current directory (includes subdirectories) * Default resize option is '1024' (scale width of image if it exceeds 1024px; retain aspect ratio) * Default extensions are jpg, jpeg (not case-sensitive) * Overwrites original image

Parameters:
  --start          begin the process 
  -d <path>        <path> is the path to the required directory     
  -o '<option>'    <option> can be of the following formats   
     '100'      : scale width of image to 100px
     'x60'      : scale height of image to 60px
     '100x60'   : scale image within given bounds
     (aspect ratio preserved)
  -e <ext1>,<ext2>...  <extn> is any valid extension (.png, .gif) 
  -c                   Scale images in sub-folders
  --help           Displays this help

2 comments

Denis Barmenkov 13 years, 11 months ago  # | flag
  1. Recipe will be nicer if there was no percent gauge and arguments parsed in standard way (optparse).

  2. str.format() unavailable in my Python 2.4.5, please specify your version.

  3. when running without parameters on Python 2.6 script started doing something in my directory. Its dangerous! When started without parameters script should write usage message instead of doing something dark with images from my disk!!

  4. there was no code for triming apostrophe signs from -o parameters, so usage message are inconsistent.

Rahul Anand (author) 13 years, 11 months ago  # | flag

@ Denis

  1. I need to add many extra lines of code to make optparse print multiline help messages correctly. So I settled with the custom fewer lines of code.

  2. I coded it using Python 2.6

  3. The script now requires a --start argument for starting the action. Also, limited the processing of images in sub-folders by default.

  4. The -o parameters, with or without apostrophe is received as strings. Hence it works correctly i think.

Thanks for the feedback!