+++ /dev/null
-# -*- coding: utf-8 -*-
-#######################################################################################
-# Plinn - http://plinn.org #
-# Copyright © 2009 Benoît Pin <pin@cri.ensmp.fr> #
-# #
-# This program is free software; you can redistribute it and/or #
-# modify it under the terms of the GNU General Public License #
-# as published by the Free Software Foundation; either version 2 #
-# of the License, or (at your option) any later version. #
-# #
-# This program is distributed in the hope that it will be useful, #
-# but WITHOUT ANY WARRANTY; without even the implied warranty of #
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
-# GNU General Public License for more details. #
-# #
-# You should have received a copy of the GNU General Public License #
-# along with this program; if not, write to the Free Software #
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #
-#######################################################################################
-""" PPM File support module
-
-
-
-"""
-
-from subprocess import Popen, PIPE
-from tempfile import TemporaryFile
-import os
-from math import ceil
-from PIL.Image import open as imgopen
-from PIL.Image import fromstring
-from PIL.Image import ANTIALIAS
-from cStringIO import StringIO
-
-DGJPEG = 'djpeg'
-RESIZING_TILE_SIZE = 1024
-
-class PPMFile(object) :
-
- def __init__(self, f, tileSize=256, isRaw=False) :
- # convert jpeg -> ppm with djpeg
- if not isRaw :
- # print 'djpeg'
- self.fp = TemporaryFile(mode='w+')
- p = Popen(DGJPEG, stdin=f, stdout=self.fp, stderr=PIPE, shell=True)
- p.wait()
- err = p.stderr.read()
- if err :
- raise SystemError, err
- else :
- self.fp = f
-
- # get image specs with PIL
- self.fp.seek(0)
- im = imgopen(self.fp)
- decoder, region, offset, parameters = im.tile[0]
- x, y, width, height = region
- del im
- assert decoder == 'raw'
- mode = parameters[0]
- assert mode in ('RGB', 'L'), "Unsupported mode %s" % mode
-
- if mode == 'RGB' :
- sampleSize = 3
- elif mode == 'L' :
- sampleSize = 1
-
- self.width = width
- self.height = height
- self.offset = offset
- self.mode = parameters[0]
- self.sampleSize = sampleSize
- self._setTileSize(tileSize)
-
- def _setTileSize(self, tileSize) :
- self.tileSize = tileSize
- self.tilesX = int(ceil(float(self.width) / self.tileSize))
- self.tilesY = int(ceil(float(self.height) / self.tileSize))
-
- def getTile(self, xt, yt) :
- f = self.fp
- ss = self.sampleSize
- x = xt * self.tileSize
- y = yt * self.tileSize
- start = (self.width * y + x) * ss + self.offset
-
- tw = th = self.tileSize
-
- bw = self.width - x
- if bw < self.tileSize :
- tw = bw
- bh = self.height - y
- if bh < self.tileSize :
- th = bh
-
- assert tw > 0 and th > 0, "Tile requested out of image."
-
- size = (tw, th)
- tll = tw * ss
- jump = (self.width - tw) * ss
-
- f.seek(start)
- data = StringIO()
-
- for line in xrange(size[1]) :
- data.write(f.read(tll))
- f.seek(jump, 1)
-
- data.seek(0)
- im = fromstring(self.mode, size, data.read())
- return im
-
- def getTileSequence(self):
- seq = []
- for y in xrange(self.tilesY) :
- for x in xrange(self.tilesX) :
- seq.append((x, y))
- return seq
-
- def resize(self, ratio=None, maxLength=None) :
- if ratio and maxLength :
- raise AttributeError("'ratio' and 'size' are mutually exclusive.")
- if maxLength :
- maxFullLength = max(self.width, self.height)
- ratio = float(maxLength) / maxFullLength
-
- tileSizeBak = self.tileSize
-
- self._setTileSize(RESIZING_TILE_SIZE)
-
- width = height = 0
- # cumul des arrondis
- width = int(round(self.tileSize * ratio)) * (self.tilesX -1)
- width += int(round((self.width - self.tileSize * (self.tilesX -1)) * ratio))
-
- height = int(round(self.tileSize * ratio)) * (self.tilesY -1)
- height += int(round((self.height - self.tileSize * (self.tilesY -1)) * ratio))
-
- magic = self.mode == 'RGB' and 6 or 5
- head = 'P%d %d %d 255\n' % (magic, width, height)
- offset = len(head)
-
- out = TemporaryFile(mode='w+')
- out.write(head)
-
- ss = self.sampleSize
- rTll = int(round(self.tileSize * ratio))
-
- for x, y in self.getTileSequence() :
- # print 'resize', (x,y)
- tile = self.getTile(x,y)
- tileSize = tile.size
- size = map(lambda l : int(round(l * ratio)), tileSize)
-
- if size[0] and size[1] :
- resized = tile.resize(size, ANTIALIAS)
- data = resized.tostring()
-
- start = (y * width + x) * ss * rTll + offset
- jump = (width - size[0]) * ss
-
- out.seek(start)
- tll = size[0] * ss
-
- # écriture dans le bon ordre (c'est quand même plus agréable à l'œil)
- for l in xrange(size[1]) :
- lineData = data[l*tll:(l+1)*tll]
- out.write(lineData)
- out.seek(jump, 1)
-
- out.seek(0,2)
- length = out.tell()
- assert length - len(head) == width * height * ss, (length - len(head), width * height * ss)
- out.seek(0)
-
- self._setTileSize(tileSizeBak)
- return PPMFile(out, tileSize=tileSizeBak, isRaw=True)
-
- def getImage(self) :
- self.fp.seek(0)
- return imgopen(self.fp)
-
- def __del__(self) :
- self.fp.close()
-
-
-if __name__ == '__main__' :
- f = open('/Users/pinbe/Desktop/Chauve_souris.jpg')
- try :
- ppm = PPMFile(f, tileSize=256)
- rppm = ppm.resize(maxLength=800)
- im = rppm.getImage()
- im.show()
- for x, y in ppm.getTileSequence() :
- im = ppm.getTile(x, y)
- im.save('testoutput/%d_%d.jpg' % (x, y), 'JPEG', quality=90)
- finally :
- f.close()