残念ながらIronPythonには[記事] のようなcProfileは無いので、別の方法で行う。
見つけた方法は、An IronPython Profiler。[Iron Curt]
IronPython2.7で動作確認したけど、動くようだ。
ipy –X:EnableProfiler プログラム名
「type」で始まるのが.Netで、ModuleはPythonのコードらしい。
PILでは扱えなかった[記事]が、IronPtyhonの.Netライブラリだと普通に扱えたので、更新しておく。
#!/usr/bin/env python # -*- coding: utf-8 -*- # # 引数で指定したフォルダの画像ファイルを # 出力先フォルダに縮小・回転しながらコピーする from __future__ import print_function from __future__ import division import os,sys,glob import shutil import clr clr.AddReferenceByPartialName('System.Drawing') from System.Drawing import * # 出力先フォルダ指定。 TARGET_DIR = 'C:\\Photo' #COWON A3向け #max_size = (800, 480) #PSP向け #max_size = (480, 270) #Ziio向け max_size = (480, 800) #対象拡張子 ext_dic = ['.jpg','.jpeg','.png','.bmp','.tiff'] #強制変換対象フォーマット forceCompressFormat = [Imaging.ImageFormat.Bmp,Imaging.ImageFormat.Tiff] #ファイル名のprefix DEST_PREFIX = 's_' #JPEG品質 JPEG_QUALITY = 75 def makeOutputDirectory(outputDirPath): if os.path.isdir(outputDirPath) == False: os.makedirs(outputDirPath) def isTarget(ext): return True if ext.lower() in ext_dic else False class ProgressBar: def __init__(self,totalNum): self.totalNum = totalNum self.counter = 0 self.chr = u"*" self.width = 40 def addCounter(self,incrementNum): self.counter += incrementNum def show(self): pbar_chrs = self.chr * (self.width * self.counter // self.totalNum) percentage = 100 * self.counter // self.totalNum meter = '\r|{0}{1}| {2}/{3}個 ({4}%)'.format(pbar_chrs,u' ' * (self.width - len(pbar_chrs)),self.counter,self.totalNum, percentage) sys.stdout.write(meter) sys.stdout.flush() def createJpegEncoder(): jpgEncoder = [codec for codec in Imaging.ImageCodecInfo.GetImageEncoders() if codec.FormatID == Imaging.ImageFormat.Jpeg.Guid][0] encParam = Imaging.EncoderParameter(Imaging.Encoder.Quality,JPEG_QUALITY) encParams = Imaging.EncoderParameters(1) encParams.Param[0] = encParam return (jpgEncoder,encParams) def shrinkImage(srcFileName, dstFileName,jpgEncoder): srcImg = Bitmap.FromFile(srcFileName) if srcImg.Width > max_size[0] or srcImg.Height > max_size[1] or (srcImg.RawFormat in forceCompressFormat): # BMP/TIFFの場合、JPEGにする if srcImg.RawFormat in forceCompressFormat: format = Imaging.ImageFormat.Jpeg root,ext = os.path.splitext(dstFileName) dstFileName = root + '.jpg' else: format = srcImg.RawFormat srcWidth = srcImg.Width srcHeight = srcImg.Height if max_size[0] > max_size[1]: #横が長いディスプレイの場合は縦長の画像を回転させた絵を作る if srcImg.Width < srcImg.Height: srcWidth = srcImg.Height srcHeight = srcImg.Width srcImg.RotateFlip(RotateFlipType.Rotate90FlipNone); else: #縦が長いディスプレイの場合は横長の画像を回転させた絵を作る if srcImg.Width > srcImg.Height: srcWidth = srcImg.Height srcHeight = srcImg.Width srcImg.RotateFlip(RotateFlipType.Rotate90FlipNone); # 画面より画像が小さい場合はスケーリングしない if max_size[0] < srcWidth or max_size[1] < srcHeight: scalerate = min(max_size[0]/srcWidth,max_size[1]/srcHeight) destWidth = int(round(scalerate * srcWidth)) destHeight = int(round(scalerate * srcHeight)) else: destWidth = srcWidth destHeight = srcHeight destImg = Bitmap(destWidth,destHeight) grphics = Graphics.FromImage(destImg) grphics.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic grphics.DrawImage(srcImg,0,0,destWidth,destHeight) if format == Imaging.ImageFormat.Jpeg: destImg.Save(dstFileName,jpgEncoder[0], jpgEncoder[1]) else: destImg.Save(dstFileName,format) destImg.Dispose() else: # 画面に収まらない or BMP/TIFF形式以外の場合は、再圧縮する必要ないので普通のコピーにする shutil.copy(srcFileName,dstFileName) srcImg.Dispose() def copyImageTree(targetList,progress): jpgEncoder = createJpegEncoder() for target in targetList: srcname = target[0] dstname = target[1] try: makeOutputDirectory(os.path.dirname(dstname)) shrinkImage(srcname,dstname,jpgEncoder) progress.addCounter(1) progress.show() except (IOError, os.error), why: print("Cant' copy",srcname,'to',dstname,':',str(why)) def createTargetImagePath( path,targetBaseDirectory ): targetPath = [] names = os.listdir(path) targetDirectory = targetBaseDirectory for name in names: srcname = os.path.join(path, name) try: if os.path.isdir(srcname): targetDirectory = os.path.join(targetBaseDirectory, name) targetPath += createTargetImagePath( srcname,targetDirectory ) else: root, ext = os.path.splitext(srcname) if isTarget(ext) == True: targetPath.append((srcname,os.path.join(targetDirectory,DEST_PREFIX + name))) except (IOError, os.error), why: print("Can't copy",srcname,':',str(why)) return (targetPath) if __name__=='__main__': #引数がおかしい場合 if len(sys.argv) < 2: print('Usage: # Python ',sys.argv[0],' [SouceImageDirectoryPath]...') raw_input() quit() targetPathList = [] for x in range(1,len(sys.argv)): sourcePath = os.path.dirname(sys.argv[x] + '\\') if os.path.isdir(sourcePath) == False: #存在しないPathを指定した場合 print('[',sourcePath,']: Src directory does not exist.') raw_input() quit() elif os.path.isdir(TARGET_DIR) == False: #存在しないPathを指定した場合 print('[',TARGET_DIR,']: Dest directory does not exist.') raw_input() quit() elif sourcePath == TARGET_DIR: #入力先と出力先が同じ場合 print('The input destination is the same as the output destination.') raw_input() quit() else: targetPathList += createTargetImagePath(sourcePath,(TARGET_DIR + '\\' + os.path.basename(sourcePath))) if len(targetPathList) == 0: print('File does not exist.') quit() progress = ProgressBar(len(targetPathList)) copyImageTree(targetPathList,progress)
IronPythonには、pythonのモジュールであるcsvモジュールが無い。
なので、VB.NetのCSVを扱うライブラリを使う。
以下は、UTF-8のtest.csvを読み込むときのコード。
Excelからcsvを保存するとShift-JISになるので、その時はGetEncodingを「Shift_JIS」に変えればよい。
#!/usr/bin/env python # -*- coding: utf-8 -*- # from __future__ import print_function import clr clr.AddReferenceByPartialName('Microsoft.VisualBasic') clr.AddReferenceByPartialName('mscorlib') from Microsoft.VisualBasic.FileIO import * from System.Text import * if __name__ == "__main__": parser = TextFieldParser("test.csv",Encoding.GetEncoding("UTF-8")) parser.TextFieldType = FieldType.Delimited; parser.SetDelimiters(","); while parser.EndOfData == False: print(parser.ReadFields())
#!/usr/bin/env python # -*- coding: utf-8 -*- # from __future__ import print_function import clr clr.AddReferenceByPartialName('Microsoft.VisualBasic') clr.AddReferenceByPartialName('mscorlib') from Microsoft.VisualBasic.FileIO import * from System.Text import * if __name__ == "__main__": parser = TextFieldParser("test.csv",Encoding.GetEncoding("UTF-8")) parser.TextFieldType = FieldType.Delimited; parser.SetDelimiters(","); print(parser.ReadToEnd())
Python[記事]とIronPython[記事]で同じプログラム(っていっても微妙に違うけど)を作ってみたので、簡単に速度比較してみた。
519枚のBMP,JPG,PNGのファイルを圧縮するのを、6回ほど計測してみた。
回数\実装 | Iron Python 単位:sec | CPython 単位:sec |
---|---|---|
1回目 | 45.94 | 23.17 |
2回目 | 45.84 | 23.21 |
3回目 | 45.81 | 23.21 |
4回目 | 45.61 | 23.02 |
5回目 | 45.64 | 23.05 |
6回目 | 46.00 | 23.46 |
平均 | 45.81 | 23.19 |
Google Chartで棒グラフにもしてみた。
IronPythonがダブルスコアで負けてる・・・。
遅すぎるぜ、IronPython。
関数単位でもうちょい解析してみるかなぁ。自分のコーディングがタコなのもあるのだろうし。
Python[記事]で書いていたが、Pythonのサイトに「IronPython has no GIL and multi-threaded code can use multi core processors 」と書いてあったので、ひとまず移植してみた。
勉強がてら、Python Imaging Libraryから.Netのライブラリに変更してみたんだけど、
移植して思うのはPILってよくできてるよな〜ってところ。
PILだと26行くらいで済んでたコードが、55行とほぼ倍になってしまった。
IronPythonの文字は全部Unicodeで統一されているので、日本語の処理はCPythonより楽でよいんだけど。
しかし、IronPythonはやっぱ日本語のページがあんま無いね。
基本は.Netだから同じなんだけど、C#もVB.Netも殆どやってないから、C#の画像圧縮について書いてあるサイトを見ながら、なんとか作ってみた。
#!/usr/bin/env python # -*- coding: utf-8 -*- # # 引数で指定したフォルダの画像ファイルを # 出力先フォルダに縮小・回転しながらコピーする from __future__ import print_function from __future__ import division import os,sys,glob import shutil import clr clr.AddReferenceByPartialName('System.Drawing') from System.Drawing import * # 出力先フォルダ指定。 TARGET_DIR = 'C:\\Photo' #COWON A3向け #max_size = (800, 480) #PSP向け #max_size = (480, 270) #Ziio向け max_size = (480, 800) #対象拡張子 ext_dic = ['.jpg','.jpeg','.png','.bmp'] #ファイル名のprefix DEST_PREFIX = 's_' #JPEG品質 JPEG_QUALITY = 75 def makeOutputDirectory(outputDirPath): if os.path.isdir(outputDirPath) == False: os.makedirs(outputDirPath) def isTarget(ext): return True if ext.lower() in ext_dic else False class ProgressBar: def __init__(self,totalNum): self.totalNum = totalNum self.counter = 0 self.chr = u"*" self.width = 40 def addCounter(self,incrementNum): self.counter += incrementNum def show(self): pbar_chrs = self.chr * (self.width * self.counter // self.totalNum) percentage = 100 * self.counter // self.totalNum meter = '\r|{0}{1}| {2}/{3}個 ({4}%)'.format(pbar_chrs,u' ' * (self.width - len(pbar_chrs)),self.counter,self.totalNum, percentage) sys.stdout.write(meter) sys.stdout.flush() def createJpegEncoder(): jpgEncoder = [codec for codec in Imaging.ImageCodecInfo.GetImageEncoders() if codec.FormatID == Imaging.ImageFormat.Jpeg.Guid][0] encParam = Imaging.EncoderParameter(Imaging.Encoder.Quality,JPEG_QUALITY) encParams = Imaging.EncoderParameters(1) encParams.Param[0] = encParam return (jpgEncoder,encParams) def shrinkImage(srcFileName, dstFileName,jpgEncoder): srcImg = Bitmap.FromFile(srcFileName) if srcImg.Width > max_size[0] or srcImg.Height > max_size[1] or srcImg.RawFormat.Equals(Imaging.ImageFormat.Bmp): # BMPの場合、JPEGにする if srcImg.RawFormat.Equals(Imaging.ImageFormat.Bmp): format = Imaging.ImageFormat.Jpeg root,ext = os.path.splitext(dstFileName) dstFileName = root + '.jpg' else: format = srcImg.RawFormat srcWidth = srcImg.Width srcHeight = srcImg.Height if max_size[0] > max_size[1]: #横が長いディスプレイの場合は縦長の画像を回転させた絵を作る if srcImg.Width < srcImg.Height: srcWidth = srcImg.Height srcHeight = srcImg.Width srcImg.RotateFlip(RotateFlipType.Rotate90FlipNone); else: #縦が長いディスプレイの場合は横長の画像を回転させた絵を作る if srcImg.Width > srcImg.Height: srcWidth = srcImg.Height srcHeight = srcImg.Width srcImg.RotateFlip(RotateFlipType.Rotate90FlipNone); # 画面より画像が小さい場合はスケーリングしない if max_size[0] < srcWidth or max_size[1] < srcHeight: scalerate = min(max_size[0]/srcWidth,max_size[1]/srcHeight) destWidth = int(round(scalerate * srcWidth)) destHeight = int(round(scalerate * srcHeight)) else: destWidth = srcWidth destHeight = srcHeight destImg = Bitmap(destWidth,destHeight) grphics = Graphics.FromImage(destImg) grphics.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic grphics.DrawImage(srcImg,0,0,destWidth,destHeight) if format == Imaging.ImageFormat.Jpeg: destImg.Save(dstFileName,jpgEncoder[0], jpgEncoder[1]) else: destImg.Save(dstFileName,format) destImg.Dispose() else: # 画面に収まらないor BMP形式以外の場合は、再圧縮する必要ないので普通のコピーにする shutil.copy(srcFileName,dstFileName) srcImg.Dispose() def copyImageTree(targetList,progress): jpgEncoder = createJpegEncoder() for target in targetList: srcname = target[0] dstname = target[1] try: makeOutputDirectory(os.path.dirname(dstname)) shrinkImage(srcname,dstname,jpgEncoder) progress.addCounter(1) progress.show() except (IOError, os.error), why: print("Cant' copy",srcname,'to',dstname,':',str(why)) def createTargetImagePath( path,targetBaseDirectory ): targetPath = [] names = os.listdir(path) targetDirectory = targetBaseDirectory for name in names: srcname = os.path.join(path, name) try: if os.path.isdir(srcname): targetDirectory = os.path.join(targetBaseDirectory, name) targetPath += createTargetImagePath( srcname,targetDirectory ) else: root, ext = os.path.splitext(srcname) if isTarget(ext) == True: targetPath.append((srcname,os.path.join(targetDirectory,DEST_PREFIX + name))) except (IOError, os.error), why: print("Can't copy",srcname,':',str(why)) return (targetPath) if __name__=='__main__': #引数がおかしい場合 if len(sys.argv) < 2: print('Usage: # Python ',sys.argv[0],' [SouceImageDirectoryPath]...') raw_input() quit() targetPathList = [] for x in range(1,len(sys.argv)): sourcePath = os.path.dirname(sys.argv[x] + '\\') if os.path.isdir(sourcePath) == False: #存在しないPathを指定した場合 print('[',sourcePath,']: Src directory does not exist.') raw_input() quit() elif os.path.isdir(TARGET_DIR) == False: #存在しないPathを指定した場合 print('[',TARGET_DIR,']: Dest directory does not exist.') raw_input() quit() elif sourcePath == TARGET_DIR: #入力先と出力先が同じ場合 print('The input destination is the same as the output destination.') raw_input() quit() else: targetPathList += createTargetImagePath(sourcePath,(TARGET_DIR + '\\' + os.path.basename(sourcePath))) if len(targetPathList) == 0: print('File does not exist.') quit() progress = ProgressBar(len(targetPathList)) copyImageTree(targetPathList,progress)