1 # -*- coding: utf-8 -*-
2 #######################################################################################
3 # Plinn - http://plinn.org #
4 # Copyright © 2009 Benoît Pin <pin@cri.ensmp.fr> #
6 # This program is free software; you can redistribute it and/or #
7 # modify it under the terms of the GNU General Public License #
8 # as published by the Free Software Foundation; either version 2 #
9 # of the License, or (at your option) any later version. #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program; if not, write to the Free Software #
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #
19 #######################################################################################
20 """ PPM File support module
26 from subprocess
import Popen
, PIPE
27 from tempfile
import TemporaryFile
30 from PIL
.Image
import open as imgopen
31 from PIL
.Image
import fromstring
32 from PIL
.Image
import ANTIALIAS
33 from cStringIO
import StringIO
36 RESIZING_TILE_SIZE
= 1024
38 class PPMFile(object) :
40 def __init__(self
, f
, tileSize
=256, isRaw
=False) :
41 # convert jpeg -> ppm with djpeg
44 self
.fp
= TemporaryFile(mode
='w+')
45 p
= Popen(DGJPEG
, stdin
=f
, stdout
=self
.fp
, stderr
=PIPE
, shell
=True)
49 raise SystemError, err
53 # get image specs with PIL
56 decoder
, region
, offset
, parameters
= im
.tile
[0]
57 x
, y
, width
, height
= region
59 assert decoder
== 'raw'
61 assert mode
in ('RGB', 'L'), "Unsupported mode %s" % mode
71 self
.mode
= parameters
[0]
72 self
.sampleSize
= sampleSize
73 self
._setTileSize
(tileSize
)
75 def _setTileSize(self
, tileSize
) :
76 self
.tileSize
= tileSize
77 self
.tilesX
= int(ceil(float(self
.width
) / self
.tileSize
))
78 self
.tilesY
= int(ceil(float(self
.height
) / self
.tileSize
))
80 def getTile(self
, xt
, yt
) :
83 x
= xt
* self
.tileSize
84 y
= yt
* self
.tileSize
85 start
= (self
.width
* y
+ x
) * ss
+ self
.offset
87 tw
= th
= self
.tileSize
90 if bw
< self
.tileSize
:
93 if bh
< self
.tileSize
:
96 assert tw
> 0 and th
> 0, "Tile requested out of image."
100 jump
= (self
.width
- tw
) * ss
105 for line
in xrange(size
[1]) :
106 data
.write(f
.read(tll
))
110 im
= fromstring(self
.mode
, size
, data
.read())
113 def getTileSequence(self
):
115 for y
in xrange(self
.tilesY
) :
116 for x
in xrange(self
.tilesX
) :
120 def resize(self
, ratio
=None, maxLength
=None) :
121 if ratio
and maxLength
:
122 raise AttributeError("'ratio' and 'size' are mutually exclusive.")
124 maxFullLength
= max(self
.width
, self
.height
)
125 ratio
= float(maxLength
) / maxFullLength
127 tileSizeBak
= self
.tileSize
129 self
._setTileSize
(RESIZING_TILE_SIZE
)
133 width
= int(round(self
.tileSize
* ratio
)) * (self
.tilesX
-1)
134 width
+= int(round((self
.width
- self
.tileSize
* (self
.tilesX
-1)) * ratio
))
136 height
= int(round(self
.tileSize
* ratio
)) * (self
.tilesY
-1)
137 height
+= int(round((self
.height
- self
.tileSize
* (self
.tilesY
-1)) * ratio
))
139 magic
= self
.mode
== 'RGB' and 6 or 5
140 head
= 'P%d %d %d 255\n' % (magic
, width
, height
)
143 out
= TemporaryFile(mode
='w+')
147 rTll
= int(round(self
.tileSize
* ratio
))
149 for x
, y
in self
.getTileSequence() :
150 # print 'resize', (x,y)
151 tile
= self
.getTile(x
,y
)
153 size
= map(lambda l
: int(round(l
* ratio
)), tileSize
)
155 if size
[0] and size
[1] :
156 resized
= tile
.resize(size
, ANTIALIAS
)
157 data
= resized
.tostring()
159 start
= (y
* width
+ x
) * ss
* rTll
+ offset
160 jump
= (width
- size
[0]) * ss
165 # écriture dans le bon ordre (c'est quand même plus agréable à l'œil)
166 for l
in xrange(size
[1]) :
167 lineData
= data
[l
*tll
:(l
+1)*tll
]
173 assert length
- len(head
) == width
* height
* ss
, (length
- len(head
), width
* height
* ss
)
176 self
._setTileSize
(tileSizeBak
)
177 return PPMFile(out
, tileSize
=tileSizeBak
, isRaw
=True)
181 return imgopen(self
.fp
)
187 if __name__
== '__main__' :
188 f
= open('/Users/pinbe/Desktop/Chauve_souris.jpg')
190 ppm
= PPMFile(f
, tileSize
=256)
191 rppm
= ppm
.resize(maxLength
=800)
194 for x
, y
in ppm
.getTileSequence() :
195 im
= ppm
.getTile(x
, y
)
196 im
.save('testoutput/%d_%d.jpg' % (x
, y
), 'JPEG', quality
=90)