1 # -*- coding: utf-8 -*-
2 ####################################################
3 # Copyright © 2009 Luxia SAS. All rights reserved. #
6 # - Benoît Pin <pinbe@luxia.fr> #
7 ####################################################
8 """ PPM File support module
10 $Id: ppm.py 1276 2009-08-11 16:38:02Z pin $
11 $URL: http://svn.luxia.fr/svn/labo/projects/zope/Photo/trunk/ppm.py $
14 from subprocess
import Popen
, PIPE
15 from tempfile
import TemporaryFile
18 from PIL
.Image
import open as imgopen
19 from PIL
.Image
import fromstring
20 from PIL
.Image
import ANTIALIAS
21 from cStringIO
import StringIO
24 RESIZING_TILE_SIZE
= 1024
26 class PPMFile(object) :
28 def __init__(self
, f
, tileSize
=256, isRaw
=False) :
29 # convert jpeg -> ppm with djpeg
32 self
.fp
= TemporaryFile(mode
='w+')
33 p
= Popen(DGJPEG
, stdin
=f
, stdout
=self
.fp
, stderr
=PIPE
, shell
=True)
37 raise SystemError, err
41 # get image specs with PIL
44 decoder
, region
, offset
, parameters
= im
.tile
[0]
45 x
, y
, width
, height
= region
47 assert decoder
== 'raw'
49 assert mode
in ('RGB', 'L'), "Unsupported mode %s" % mode
59 self
.mode
= parameters
[0]
60 self
.sampleSize
= sampleSize
61 self
._setTileSize
(tileSize
)
63 def _setTileSize(self
, tileSize
) :
64 self
.tileSize
= tileSize
65 self
.tilesX
= int(ceil(float(self
.width
) / self
.tileSize
))
66 self
.tilesY
= int(ceil(float(self
.height
) / self
.tileSize
))
68 def getTile(self
, xt
, yt
) :
71 x
= xt
* self
.tileSize
72 y
= yt
* self
.tileSize
73 start
= (self
.width
* y
+ x
) * ss
+ self
.offset
75 tw
= th
= self
.tileSize
78 if bw
< self
.tileSize
:
81 if bh
< self
.tileSize
:
84 assert tw
> 0 and th
> 0, "Tile requested out of image."
88 jump
= (self
.width
- tw
) * ss
93 for line
in xrange(size
[1]) :
94 data
.write(f
.read(tll
))
98 im
= fromstring(self
.mode
, size
, data
.read())
101 def getTileSequence(self
):
103 for y
in xrange(self
.tilesY
) :
104 for x
in xrange(self
.tilesX
) :
108 def resize(self
, ratio
=None, maxLength
=None) :
109 if ratio
and maxLength
:
110 raise AttributeError("'ratio' and 'size' are mutually exclusive.")
112 maxFullLength
= max(self
.width
, self
.height
)
113 ratio
= float(maxLength
) / maxFullLength
115 tileSizeBak
= self
.tileSize
117 self
._setTileSize
(RESIZING_TILE_SIZE
)
121 width
= int(round(self
.tileSize
* ratio
)) * (self
.tilesX
-1)
122 width
+= int(round((self
.width
- self
.tileSize
* (self
.tilesX
-1)) * ratio
))
124 height
= int(round(self
.tileSize
* ratio
)) * (self
.tilesY
-1)
125 height
+= int(round((self
.height
- self
.tileSize
* (self
.tilesY
-1)) * ratio
))
127 magic
= self
.mode
== 'RGB' and 6 or 5
128 head
= 'P%d %d %d 255\n' % (magic
, width
, height
)
131 out
= TemporaryFile(mode
='w+')
135 rTll
= int(round(self
.tileSize
* ratio
))
137 for x
, y
in self
.getTileSequence() :
138 # print 'resize', (x,y)
139 tile
= self
.getTile(x
,y
)
141 size
= map(lambda l
: int(round(l
* ratio
)), tileSize
)
143 if size
[0] and size
[1] :
144 resized
= tile
.resize(size
, ANTIALIAS
)
145 data
= resized
.tostring()
147 start
= (y
* width
+ x
) * ss
* rTll
+ offset
148 jump
= (width
- size
[0]) * ss
153 # écriture dans le bon ordre (c'est quand même plus agréable à l'œil)
154 for l
in xrange(size
[1]) :
155 lineData
= data
[l
*tll
:(l
+1)*tll
]
161 assert length
- len(head
) == width
* height
* ss
, (length
- len(head
), width
* height
* ss
)
164 self
._setTileSize
(tileSizeBak
)
165 return PPMFile(out
, tileSize
=tileSizeBak
, isRaw
=True)
169 return imgopen(self
.fp
)
175 if __name__
== '__main__' :
176 f
= open('/Users/pinbe/Desktop/Chauve_souris.jpg')
178 ppm
= PPMFile(f
, tileSize
=256)
179 rppm
= ppm
.resize(maxLength
=800)
182 for x
, y
in ppm
.getTileSequence() :
183 im
= ppm
.getTile(x
, y
)
184 im
.save('testoutput/%d_%d.jpg' % (x
, y
), 'JPEG', quality
=90)