#!
# coding: utf-8
# Copyright (C) 2017 TOPS SYSTEMS
### @file npy2flt.py
### @brief Numpy format binary array to FLT format converter
###
### Numpyフォーマットのバイナリ配列を読み取り、FLT形式にして出力する
###
### Contact: izumida@topscom.co.jp
###
### @author: M.Izumida
### @date: April 19, 2017
###
# Original: fltFormatter.py v01r01
# Written for Python 2.7 (NOT FOR 3.x)
#=======================================================================
# インポート宣言
from __future__ import division
import sys
import re
import argparse
import os
import numpy as np
from datetime import datetime
#=======================================================================
# バージョン文字列
versionSTR = "npy2flt.py v01r01 Tops Systems (pgm by mpi)"
#=======================================================================
# 共通サブルーチン
#-------------------------------------------------------------------
def errPrint(mes):
"""errPrint.
エラー出力へのメッセージ表示
後の始末はその後で別に書くこと
"""
sys.stderr.write(mes)
sys.stderr.write('\n')
#-------------------------------------------------------------------
def stdExceptionHandler(mes):
"""standard Exception Handler.
エラーメッセージを送出し、デバッグのための情報を出力する
"""
errPrint("Exception Captured: " + str(mes))
errPrint("0:" + str(sys.exc_info()[0]))
errPrint("1:" + str(sys.exc_info()[1]))
errPrint("2:" + str(sys.exc_info()[2]))
#-------------------------------------------------------------------
def tryIntParse(st, dval, radix=10):
"""try Parse string to Integer
文字列stをパースして整数化できれば値を返す、できなければデフォルト値dvalを返す
"""
try:
work = int(st, radix)
except:
return dval
return work
#=======================================================================
# 解析オプション クラス
class Options:
"""Options Class.
グローバルオプションを保持するクラス
"""
pass
#=======================================================================
# Numpyファイルリーダクラス
[ドキュメント]class NumpyFileReader:
"""NumpyFile Reader Class.
Numpy形式のバイナリファイルを読みとってNumpy ndarrayとして確保する。
読み取り時にshapeを報告する。
取り出し時には、FltFileWriterで出力できるようにshapeを加工したndarrayに変形する
"""
#--------------------------------------------------------------------
# 静的(クラス)変数
static_const_modeDic = dict()
static_const_modeDic['none'] = 0
static_const_modeDic['NHWC'] = 1
static_const_modeDic['NCHW'] = 2
static_const_modeDic['HWI_'] = 3
static_const_modeDic['HW_O'] = 4
#--------------------------------------------------------------------
# 静的(クラス)メソッド
@staticmethod
[ドキュメント] def getModeNumber(modeName):
"""get Mode Number.
モード文字列からモード番号を返す。
"""
if modeName in NumpyFileReader.static_const_modeDic:
return NumpyFileReader.static_const_modeDic[modeName]
else:
return 0
#--------------------------------------------------------------------
def __init__(self, inputFnam, bt):
"""Numpy File Reader Constructor
コンストラクタ
"""
self.iFname = inputFnam #<<! 入力.npyファイル名
self.nArray = None #<<! 確保した配列
self.prmBatch = bt #<<! 抽出するバッチ番号
# debug
self.debug = False
self.verbose = False
#--------------------------------------------------------------------
[ドキュメント] def read(self):
"""Read method
.npyファイルを読み込むメソッド。読み込み成功すれば真を返す。
"""
try:
self.nArray = np.load(self.iFname)
if self.verbose:
print "Shape : ", self.nArray.shape
print "dType : ", self.nArray.dtype
except:
stdExceptionHandler("Error: in file reading = " + self.iFname)
return False
return True
#--------------------------------------------------------------------
[ドキュメント] def getArray(self, mode):
"""Get Array method
読み込み済のndarrayをmodeに従って変形したndarrayに変換して返す。
'NHWC' = 1 ... 特定のN=prmBatch について、HWをWHに変換W*H->X, C->Y
'NCHW' = 2 ... 特定のN=prmBatch について、HWをWHに変換W*H->X, C->Y
'HWI\_' = 3 ... _=0固定、HWをWHに変換W*H->X, C->Y
'HW_O' = 4 ... _=0固定、HWをWHに変換W*H->X, C->Y
変換不能な場合は Noneを返す。
"""
if mode == 1:
return self.getNHWC()
elif mode == 2:
return self.getNCHW()
elif mode == 3:
return self.getHWI_()
elif mode == 4:
return self.getHW_O()
else:
return None
#--------------------------------------------------------------------
[ドキュメント] def getNHWC(self):
"""Get NHWC Array
'NHWC' = 1 ... 特定のN=prmBatch について、HWをWHに変換W*H->X, C->Y
"""
sh = self.nArray.shape
if len(sh) != 4:
print "ERROR: NOT A NHWC format.", sh
return None
wTEMP = sh[2]
wALIGN = ((wTEMP + 15) // 16) * 16
wFILL = wTEMP % 16
iX = sh[1] * wALIGN
iY = sh[3]
if self.verbose:
print "NHWC"
print "iX=", iX, "W(ORG)=", sh[2], "W(ALIGN)=", wALIGN, "H=", sh[1], "iY=", iY
oArray = np.zeros((iX, iY), dtype=self.nArray.dtype)
for cY in range(0, sh[3]):
for hX in range(0, sh[1]):
for wX in range(0, wALIGN):
if wX < sh[2]:
temp = self.nArray[self.prmBatch, hX, wX, cY]
else:
temp = 0
oArray[hX * wALIGN + wX, cY]=temp
return oArray
#--------------------------------------------------------------------
[ドキュメント] def getNCHW(self):
"""Get NCHW Array
'NCHW' = 2 ... 特定のN=prmBatch について、HWをWHに変換W*H->X, C->Y
"""
sh = self.nArray.shape
if len(sh) != 4:
print "ERROR: NOT A NHWC format.", sh
return None
wTEMP = sh[3]
wALIGN = ((wTEMP + 15) // 16) * 16
wFILL = wTEMP % 16
iX = sh[2] * wALIGN
iY = sh[1]
if self.verbose:
print "NHWC"
print "iX=", iX, "W(ORG)=", sh[3], "W(ALIGN)=", wALIGN, "H=", sh[2], "iY=", iY
oArray = np.zeros((iX, iY), dtype=self.nArray.dtype)
for cY in range(0, sh[1]):
for hX in range(0, sh[2]):
for wX in range(0, wALIGN):
if wX < sh[3]:
temp = self.nArray[self.prmBatch, cY, hX, wX]
else:
temp = 0
oArray[hX * wALIGN + wX, cY]=temp
return oArray
#--------------------------------------------------------------------
[ドキュメント] def getHWI_(self):
"""Get HWI\_ Array
'HWI\_' = 3 ... _=0固定、HWをWHに変換W*H->X, C->Y
"""
sh = self.nArray.shape
if len(sh) != 4:
print "ERROR: NOT A HW_O format.", sh
return None
iX = sh[0] * sh[1]
iY = sh[2]
if self.verbose:
print "HW_O"
print "iX=", iX, "W=", sh[1], "H=", sh[0], "iY=", iY
oArray = np.zeros((iX, iY), dtype=self.nArray.dtype)
for cY in range(0, sh[2]):
for hX in range(0, sh[0]):
for wX in range(0, sh[1]):
oArray[hX * sh[1] + wX, cY]=self.nArray[hX, wX, cY, 0]
return oArray
#--------------------------------------------------------------------
[ドキュメント] def getHW_O(self):
"""Get HW_O Array
'HW_O' = 4 ... _=0固定、HWをWHに変換W*H->X, C->Y
"""
sh = self.nArray.shape
if len(sh) != 4:
print "ERROR: NOT A HW_O format.", sh
return None
iX = sh[0] * sh[1]
iY = sh[3]
if self.verbose:
print "HW_O"
print "iX=", iX, "W=", sh[1], "H=", sh[0], "iY=", iY
oArray = np.zeros((iX, iY), dtype=self.nArray.dtype)
for cY in range(0, sh[3]):
for hX in range(0, sh[0]):
for wX in range(0, sh[1]):
oArray[hX * sh[1] + wX, cY]=self.nArray[hX, wX, 0, cY]
return oArray
#=======================================================================
# FLTファイルライタクラス
[ドキュメント]class FltFileWriter:
"""FltFile Writer Class.
Numpy 配列をFLT形式ファイルとして出力する。
モードにより整形用の「フィルタ」を適用する。
"""
#--------------------------------------------------------------------
# 静的(クラス)変数
static_const_modeDic = dict()
static_const_modeDic['none'] = 0
static_const_modeDic['ip8'] = 1
#--------------------------------------------------------------------
# 静的(クラス)メソッド
@staticmethod
[ドキュメント] def getModeNumber(modeName):
"""get Mode Number.
モード文字列からモード番号を返す。
"""
if modeName in FltFileWriter.static_const_modeDic:
return FltFileWriter.static_const_modeDic[modeName]
else:
return 0
#--------------------------------------------------------------------
def __init__(self, nary, aName, md, padX):
"""FLT File Writer Constructor
コンストラクタ
"""
# 入力配列
self.IN_nArray = nary #<<! 入力Numpy配列
self.IN_arrayName = aName #<<! 入力FLT配列名
self.IN_iX = 0 #<<! 入力Numpy配列のXサイズ
self.IN_iY = 0 #<<! 入力Numpy配列のYサイズ
if isinstance(self.IN_nArray, np.ndarray):
self.IN_iX, self.IN_iY = self.IN_nArray.shape
# 出力配列
self.nArray = None #<<! 出力するNumpy配列
self.arrayName = None #<<! 出力FLT配列名
self.iX = 0 #<<! 出力Numpy配列のXサイズ
self.iY = 0 #<<! 出力Numpy配列のYサイズ
self.fX = 0 #<<! 出力FLT配列のXサイズ
self.fY = 0 #<<! 出力FLT配列のYサイズ
# mode
self.mode = md #<<! モードフラグ
self.paddingX = padX #<<! 出力fX方向のパディングサイズ
# debug
self.debug = False
self.verbose = False
#--------------------------------------------------------------------
#--------------------------------------------------------------------
[ドキュメント] def makeIP8(self):
"""format IP8(inner product x 8 mode)
内積計算8並列モードとなるように入力Numpy配列の行8行を1行とした出力Numpy配列を準備する。
横幅8倍、縦幅8分の1
入力Numpy配列の行8行縦1列を横8要素として出力Numpy配列にコピー
(行が8行以下の場合は要素0フィルして8要素とする)
成功すれば真、そうでなければ偽。
"""
if self.IN_nArray is None:
return False
self.iX = self.IN_iX * 8
self.iY = (self.IN_iY + 7) / 8
if (self.iX > 0) and (self.iY > 0):
if self.verbose:
print "NEW ARRAY, x:{0} y:{1}".format(self.iX, self.iY)
self.nArray = np.zeros((self.iX, self.iY), dtype=np.float)
else:
return False
tempDat = np.zeros(8, dtype=np.float)
for oLine in range(0, self.iY): #出力側行番号で舐める
for iCol in range(0, self.IN_iX): #入力側列番号で舐める
self.nArray[iCol * 8 + 0, oLine] = float(self.IN_nArray[iCol, oLine * 8 + 0]) if (oLine * 8 + 0) < self.IN_iY else 0.0
self.nArray[iCol * 8 + 1, oLine] = float(self.IN_nArray[iCol, oLine * 8 + 1]) if (oLine * 8 + 1) < self.IN_iY else 0.0
self.nArray[iCol * 8 + 2, oLine] = float(self.IN_nArray[iCol, oLine * 8 + 2]) if (oLine * 8 + 2) < self.IN_iY else 0.0
self.nArray[iCol * 8 + 3, oLine] = float(self.IN_nArray[iCol, oLine * 8 + 3]) if (oLine * 8 + 3) < self.IN_iY else 0.0
self.nArray[iCol * 8 + 4, oLine] = float(self.IN_nArray[iCol, oLine * 8 + 4]) if (oLine * 8 + 4) < self.IN_iY else 0.0
self.nArray[iCol * 8 + 5, oLine] = float(self.IN_nArray[iCol, oLine * 8 + 5]) if (oLine * 8 + 5) < self.IN_iY else 0.0
self.nArray[iCol * 8 + 6, oLine] = float(self.IN_nArray[iCol, oLine * 8 + 6]) if (oLine * 8 + 6) < self.IN_iY else 0.0
self.nArray[iCol * 8 + 7, oLine] = float(self.IN_nArray[iCol, oLine * 8 + 7]) if (oLine * 8 + 7) < self.IN_iY else 0.0
return True
#--------------------------------------------------------------------
[ドキュメント] def makeSTRAIGHT(self):
"""format STRAIGHT
そのままのX, Yサイズで出力Numpy配列を準備する。
"""
if self.IN_nArray is None:
return False
self.iX = self.IN_iX
self.iY = self.IN_iY
if (self.iX > 0) and (self.iY > 0):
if self.verbose:
print "NEW ARRAY, x:{0} y:{1}".format(self.iX, self.iY)
self.nArray = np.zeros((self.iX, self.iY), dtype=np.float)
else:
return False
for oLine in range(0, self.iY): #出力側行番号で舐める
for iCol in range(0, self.iX): #入力側列番号で舐める
self.nArray[iCol, oLine] = float(self.IN_nArray[iCol, oLine])
return True
#--------------------------------------------------------------------
[ドキュメント] def writeFLT(self, fname):
"""write FLT file
出力Numpy配列をFLTファイルへ出力。
出力X方向がpaddingXの倍数でなければpaddingXの倍数になるまで0で埋める。
Numpy配列とFLTファイル上のxが異なることがある。
書き込み成功すれば真
"""
segLen = (self.iX + (self.paddingX - 1)) // self.paddingX
self.fX = segLen * self.paddingX
self.fY = self.iY
if self.debug:
print self.iX, self.fX, self.paddingX
try:
with open(fname, 'w') as f:
f.write('@{0}<FLOAT>[{1}, {2}] '.format(self.arrayName, self.fX, self.fY) + '{\n' )
lfNum = 0 #行送り用カウンタ
for ay in range(0, self.iY):
for ax in range(0, self.fX):
if ax < self.iX:
f.write(str(self.nArray[ax][ay])+", ")
else:
f.write("0, ")
lfNum += 1
if lfNum == 16: #FLTファイルは16要素毎に改行を入れる。
f.write('\n')
lfNum = 0
if lfNum != 0:
f.write('\n')
f.write('} ' + 'nREC={0}, CONV_ERR_CODE=0, CONV_ERR_COUNT=0\n'.format(self.fX*self.fY))
except:
stdExceptionHandler("ERROR: Unexpected Error in the writing dump file. ?=" + fname)
return False
return True
#=======================================================================
# メインプログラム
def main():
"""main.
メインプログラム
"""
#-----------------------------------------------------------------------
# コマンドラインオプション処理
#
parser = argparse.ArgumentParser(description='npy2flt, NPY to FLT format converter.')
parser.add_argument('--NPY', nargs=1, help='NPY format binary data file name.')
parser.add_argument('--NPFMT', nargs=1, help='NPY array Format, (NHWC, ...).')
parser.add_argument('--FOUT', nargs=1, help='Output FLT file name.')
parser.add_argument('--FARRAY', nargs=1, help='Output FLT Array name.')
parser.add_argument('--FMT', nargs=1, help='Formatting mode, (ip8, ...).')
parser.add_argument('--PADX', nargs=1, help='X PADDING SIZE (default=32).')
parser.add_argument('--BATCH', nargs=1, help='Extract # of NPY BATCH.')
parser.add_argument('-d', dest='debug', help='Debug mode, print debug information.', action='store_true', default=False)
parser.add_argument('-v', dest='verbose', help='Verbose mode.', action='store_true', default=False)
parser.add_argument('-V', dest='VERSION', help='Show Version, then exit', action='store_true', default=False)
args = parser.parse_args()
#-----------------------------------------------------------------------
# Version 表示
#
print versionSTR
if args.VERSION:
sys.exit(0)
#-----------------------------------------------------------------------
# ファイル名処理
#
if args.NPY is None:
errPrint('ERROR: NO .NPY file!')
sys.exit(1)
else:
npyFname = args.NPY[0]
if not os.path.isfile(npyFname):
errPrint('ERROR: NPY file, NOT EXIST. ?=' + npyFname)
sys.exit(1)
if args.FOUT is None:
print "WARNING: NO OUTPUT mode."
foutFname = None
else:
foutFname = args.FOUT[0]
if args.FARRAY is None:
farrayName = "fromNumpyArray"
else:
farrayName = args.FARRAY[0]
#-----------------------------------------------------------------------
# パラメータ処理
if args.NPFMT is None:
npMode = 0
else:
npMode = NumpyFileReader.getModeNumber(args.NPFMT[0])
if npMode == 0:
errPrint('WARNING: Unknown Numpy array Format.')
if args.FMT is None:
fmtMode = 0
else:
fmtMode = FltFileWriter.getModeNumber(args.FMT[0])
if fmtMode == 0:
errPrint('WARNING: FLT output is a Straight.')
if args.BATCH is None:
prmBatch = 0
else:
prmBatch = tryIntParse(args.BATCH[0], 0)
if args.PADX is None:
paddingX = 32
else:
paddingX = tryIntParse(args.PADX[0], 32)
#-----------------------------------------------------------------------
# 実処理
#
print "-"*50
#元ファイルを読み込み
npyOBJ = NumpyFileReader(npyFname, prmBatch)
npyOBJ.debug = args.debug
npyOBJ.verbose = args.verbose
if not npyOBJ.read():
sys.exit(1)
# 変換出力
if foutFname is not None:
fout = FltFileWriter(npyOBJ.getArray(npMode), farrayName, fmtMode, paddingX)
fout.debug = args.debug
fout.verbose = args.verbose
if fout.formatArray():
if not fout.writeFLT(foutFname):
errPrint("ERROR: Writing FLT file. ?=" + foutFname)
sys.exit(1)
else:
errPrint("ERROR: Formatting FLT file. ?mode=" + str(fmtMode))
sys.exit(1)
today = datetime.today()
print today.strftime("FINISH: %Y/%m/%d %H:%M:%S")
#-----------------------------------------------------------------------
# 正常終了
#
sys.exit(0)
#=======================================================================
# メインプログラムの起動
if __name__ == "__main__":
main()