flt2pic のソースコード

#!
# coding: utf-8
# Copyright (C) 2015 - 2017 TOPS SYSTEMS
### @file flt2pic.py
### @brief FLT format data file to picture utility
###
### FLTファイルの情報を画像化して出力する
### 複数のFLTファイルから読み込んだ内容の比較出力も可能
###
### Contact: izumida@topscom.co.jp
###
### @author: M.Izumida
### @date: April 5, 2017
###
# Original: dump2pic.py         April 21, 2015
# Original: dnn2pic.py v01r01   January 4, 2017
# flt2pic.py v01r01             April 5, 2017
# flt2pic.py v01r02             April 7, 2017 FLT FILE OUT FUNCTIONS
# Written for Python 2.7 (NOT FOR 3.x)
#=======================================================================
# インポート宣言
import sys
import re
import argparse
import os
import numpy as np
from datetime import datetime
from PIL import Image, ImageDraw
#=======================================================================
# バージョン文字列
versionSTR = "flt2pic.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

#=======================================================================
# LOGファイルリーダクラス
[ドキュメント]class logReader: """LOG File Reader Class. VPPL -i で出力されるLOGファイルの中で指定部分をNumpyアレイとして読み取るクラス """ #-------------------------------------------------------------------- def __init__(self, ifnam, anam, xSize, ySize): """LOG File Reader Constructor コンストラクタ """ self.iFname = ifnam #<<! 入力ファイル名 self.arrayName = anam #<<! アレイ名 self.nArray = None #<<! Numpy 配列の格納用アンカー self.iX = xSize #<<! アレイのX Size self.iY = ySize #<<! アレイのY Size # レコードの検索表現 self.recRE = re.compile("^([a-zA-Z][a-zA-Z0-9]*)> ([a-zA-Z][a-zA-Z0-9]*)\(([0-9]+) \)\=(.+)") # self.readStatus = 0 # self.debug = False self.verbose = False #--------------------------------------------------------------------
[ドキュメント] def prepareArray(self): """prepare Array 読み込み配列を準備する """ self.nArray = np.zeros((self.iX, self.iY), dtype=np.float) # Numpy Arrayを0初期化で生成 self.nData = 0 self.nX = 0 self.nY = 0
#--------------------------------------------------------------------
[ドキュメント] def read(self): """read method ファイルから行をリードして処理するメソッド """ self.nLine = 0 try: with open(self.iFname, 'r') as f: for line in f: if self.rLine(line): break except: stdExceptionHandler("ERROR: in file reading = " + self.iFname) return False if self.readStatus > 0: return False if self.verbose: print "Total # of Log Data", self.nData return True
#--------------------------------------------------------------------
[ドキュメント] def rLine(self, lin): """Read Line method 読み取った1行を処理するメソッド """ p0 = self.recRE.match(lin) if p0: #pN = p0.group(1) aN = p0.group(2) #nN = p0.group(3) if aN == self.arrayName: self.nLine += 1 body = p0.group(4) dlis = body.split(" ") if self.debug: print "nLine=",self.nLine," ",str(dlis) for item in dlis: try: fdata = float(item.strip()) except: continue if self.nY >= self.iY: errPrint("ERROR: Too much data, Y=" + str(self.nY) + ":" + str(self.iY)) self.readStatus = 2 return True self.nData += 1 self.nArray[self.nX][self.nY] = fdata # nX, nYの更新 self.nX +=1 if self.nX >= self.iX: if self.debug: print "Y:" + str(self.nY) + " X:" + str(self.nX) + ":" + str(self.iX) + ":#=" + str(self.nData) self.nY += 1 self.nX = 0 return False
#======================================================================= # イメージファイルジェネレータクラス
[ドキュメント]class ImageFileGenerator: """Image File Generator Class. Numpyアレイからイメージを生成し、ファイル出力するためのクラス """ #-------------------------------------------------------------------- def __init__(self, ofnam, darray, w, h, ic, oc, mag): """Image File Generator コンストラクタ """ self.oFname = ofnam #<<! 出力画像ファイル名 self.nArray = darray #<<! Numpy画像配列 self.W = w #<<! 配列X方向分解 Wサイズ self.H = h #<<! 配列X方向分解 Hサイズ self.iCH = ic #<<! 配列Y方向分解 iCH(X)サイズ self.oCH = oc #<<! 配列Y方向分解 oCH(Y)サイズ self.mag = self.adjustMag(mag) #<<! 描画時描画倍率(1,2,4) self.imageX = 0 #<<! 画像Xサイズ self.imageY = 0 #<<! 画像Yサイズ self.ArrayX = 0 #<<! 配列Xサイズ self.ArrayY = 0 #<<! 配列Yサイズ # self.im = None #<<! 画像のアンカー #--------------------------------------------------------------------
[ドキュメント] def adjustMag(self, mag): """adujst MAG 描画倍率を(1, 2, 4)のどれかに設定する。 """ if mag > 2: return 4 elif mag > 1: return 2 else: return 1
#--------------------------------------------------------------------
[ドキュメント] def prepareImage(self): """prepare Image 描画用のブランク画像を準備。 W * H サイズの小画像を、iCH * oCH 個だけ並べられるブランク画像を作る。 小画像間のスペースは縦横とも2ピクセル決め打ち。 また、読み出し配列の素性も確認する 不整合があれば偽を返す。画像を準備できたら真を返す。 """ self.ArrayX, self.ArrayY = self.nArray.shape if (self.W * self.H) > self.ArrayX: return False if (self.iCH * self.oCH) > self.ArrayY: return False # self.imageX = (self.mag * self.W * self.iCH) + (2 * (self.iCH - 1)) self.imageY = (self.mag * self.H * self.oCH) + (2 * (self.oCH - 1)) # self.im = Image.new("RGB", (self.imageX, self.imageY), "white") return True
#--------------------------------------------------------------------
[ドキュメント] def save(self): """Image File Writer イメージファイルのライタ """ try: self.im.save(self.oFname) except: stdExceptionHandler("Error: writing image file = " + self.oFname) return False return True
#--------------------------------------------------------------------
[ドキュメント] def draw(self): """Draw whole image 配列に従ってイメージを描く """ for idy in range(0, self.oCH): for idx in range(0, self.iCH): self.subPicture(idx, idy)
#--------------------------------------------------------------------
[ドキュメント] def subPicture(self, idx, idy): """Draw sub picture. idx, idy位置の小画像を生成する """ if self.debug: print "subPicture", idx, idy baseX = self.mag * self.W * idx + (2 * idx) baseY = self.mag * self.H * idy + (2 * idy) basePos = (self.iCH * idy) + idx for ay in range(0, self.H): for ax in range(0, self.W): dat = self.nArray[(self.W * ay) + ax, basePos] self.drawPixel((ax * self.mag + baseX),(ay * self.mag + baseY),dat)
#--------------------------------------------------------------------
[ドキュメント] def drawPixel(self, px, py, dat): """Draw pixcel. MAGの数だけピクセルを書き込む """ rgb = self.dat2rgb(dat) for y in range(0, self.mag): for x in range(0, self.mag): self.im.putpixel((px+x, py+y), rgb)
#--------------------------------------------------------------------
[ドキュメント] def dat2rgb(self, dat): """Data(float) to RGB tuple. 浮動小数データをRGBタプルに変換する """ R=0 G=0 B=0 if dat > 10.0: G = 255 B = 255 elif dat > 1.0: G = int(255 * (dat - 1.0)/9.0) B = 255 elif dat >= 0.0: B = int(255 * dat) elif dat >= -1.0: R = int(255 * -dat) elif dat >= -10.0: G = int(255 * (-1.0 - dat)/9.0) R = 255 else: G = 255 R = 255 return (R, G, B)
#======================================================================= # FLTファイルリーダクラス
[ドキュメント]class FltFileReader: """FltFile Reader Class. FLTファイルを読みとり、ターゲットの配列であれば Numpy ndarrayとして確保する。 """ #-------------------------------------------------------------------- def __init__(self, inputFnam, targetArrayName): """FLT File Reader Constructor コンストラクタ """ self.iFname = inputFnam #<<! 入力FLTファイル名 self.tName = targetArrayName #<<! 処理対象FLT配列名 # レコードヘッダ、トレイラの検索表現 self.headerRE = re.compile("^\@([a-zA-Z_][a-zA-Z0-9_]*)\<FLOAT\>\[([0-9]+)\,\s*([0-9]+)\]\s*\{") self.trailerRE = re.compile("^}\s*nREC\=([0-9]+)\,\s*CONV_ERR_CODE\=([0-9]+)\,\s*CONV_ERR_COUNT\=([0-9]+)"); # 配列データ保持構造 self.clearArray() self.inData = False # debug self.debug = False self.verbose = False #--------------------------------------------------------------------
[ドキュメント] def clearArray(self): """Clear Array method 配列の一時記憶構造を初期化する """ self.nArray = None #<<! Numpy 配列の格納用アンカー self.iX = 0 #<<! FLT配列のXサイズ self.iY = 0 #<<! FLT配列のYサイズ self.nData = 0 #<<! FLT配列の要素カウント self.nX = 0 #<<! Numpy配列の処理ロケーションX self.nY = 0 #<<! Numpy配列の処理ロケーションY self.readStatus = 0 #<<! 読み取りステータス 0=読み取り開始, 1=読み取り正常終了, 2=読み取りエラー
#--------------------------------------------------------------------
[ドキュメント] def read(self): """Read method FLTファイルを読み込むメソッド。読み込み成功すれば真を返す。 """ try: with open(self.iFname, 'r') as f: for line in f: if self.rLine(line): break except: stdExceptionHandler("Error: in file reading = " + self.iFname) return False if self.readStatus == 1: return True return False
#--------------------------------------------------------------------
[ドキュメント] def rLine(self, lin): """Read Line method 読み取った1行を処理するメソッド inData フラグが立っていれば、 ①レコード末尾行であればパラメータを読み取ってチェックする チェック結果で問題あればエラー終了 問題なければ正常終了 inDataフラグを下す ②レコード末尾行でなければデータ行として、配列にため込む inData フラグが立っていなければ ①配列ヘッダ行であればパラメータを読み取って該当か否か判断する。 該当データであれば inData フラグを立て、配列を初期化して処理に進む。 読み取りが必要な間、Falseを返す。読み取り完了またはエラー時にはTrueを返す """ if self.inData: trailer = self.trailerRE.match(lin) if trailer: if self.nX != 0: print "WARNING: X=", self.nX, " Y=", self.nY nREC = tryIntParse(trailer.group(1), 0) if self.nData != nREC: errPrint("ERROR: " + lin ) errPrint("ERROR: Trailer size mismatch. Actual=" + str(self.nData) + " nREC=" + str(nREC)) self.readStatus = 2 else: if self.verbose: print "--> trailer, nREC=" + str(nREC) self.readStatus = 1 self.inData = False return True else: if self.nY >= self.iY: errPrint("ERROR: Too much data, Y=" + str(self.nY) + ":" + str(self.iY)) self.readStatus = 2 return True dlis = lin.split(",") for item in dlis: try: fdata = float(item.strip()) except: continue self.nData += 1 self.nArray[self.nX][self.nY] = fdata # nX, nYの更新 self.nX +=1 if self.nX >= self.iX: if self.debug: print "Y:" + str(self.nY) + " X:" + str(self.nX) + ":" + str(self.iX) + ":#=" + str(self.nData) self.nY += 1 self.nX = 0 else: header = self.headerRE.match(lin) if header: aname = header.group(1) if self.verbose: print "Find array header:" + aname if aname.startswith(self.tName): self.clearArray() self.iX = tryIntParse(header.group(2), 0) self.iY = tryIntParse(header.group(3), 0) if (self.iX * self.iY) < 1: errPrint("ERROR: Unexpected Array size ?=" + lin) return True self.inData = True if self.verbose: print "--> TARGET ARRAY, x:{0} y:{1}".format(self.iX, self.iY) self.nArray = np.zeros((self.iX, self.iY), dtype=np.float) # Numpy Arrayを0初期化で生成 return False
#--------------------------------------------------------------------
[ドキュメント] def compArray(self, nd, factor): """Compare Array method 保持しているnArrayと外から与えられたndの外形が一致すれば、各要素について nArray = (nArray - nd) * factor を計算する。 比較成功すれば真、失敗すれば偽を返す """ orgX, orgY = self.nArray.shape cmpX, cmpY = nd.shape if (orgX != cmpX) or (orgY != cmpY): return False maxDiff = 0 for cy in range(0, orgY): for cx in range(0, orgX): tempDiff = self.nArray[cx][cy] - nd[cx][cy] if abs(tempDiff) > maxDiff: maxDiff = abs(tempDiff) self.nArray[cx][cy] = tempDiff * factor if self.verbose: print "MAX absolute difference: ", maxDiff return True
#======================================================================= # FLTファイルライタクラス
[ドキュメント]class FltFileWriter: """FltFile Writer Class. Numpy 配列をFLT形式ファイルとして出力する。 Padding モードを指定した場合、X方向の特定要素数毎に16の倍数になるまでPaddingを行う """ #-------------------------------------------------------------------- def __init__(self, nary, aName, pad=False, padW=0): """FLT File Writer Constructor コンストラクタ """ self.nArray = nary #<<! 出力するNumpy配列 self.arrayName = aName #<<! FLT配列名 self.iX = 0 #<<! Numpy配列のXサイズ self.iY = 0 #<<! Numpy配列のYサイズ self.fX = 0 #<<! FLT配列のXサイズ self.fY = 0 #<<! FLT配列のYサイズ # padding mode self.padding = pad #<<! Paddingモードフラグ self.pW = padW #<<! 何要素に一回paddingを起こすか指定 self.nFILL = 0 #<<! paddingする個数 # debug self.debug = False self.verbose = False #--------------------------------------------------------------------
[ドキュメント] def checkArray(self): """check Array Numpy配列の形と、padding modeからiX, iY, fX, fYを決定する。 決定できれば真、そうでなければ偽。 """ self.iX, self.iY = self.nArray.shape if self.padding: nSEG = self.iX / self.pW nW = self.iX % self.pW self.nFILL = 16 - (self.pW % 16) if nW != 0 : #X方向がpWで割り切れないので等間隔にできないエラー return False self.fX = self.iX + (nSEG * self.nFILL) else: #padding 無しなら同じ self.fX = self.iX self.fY = self.iY if self.debug: print "padding=", str(self.padding) print "nSEG=", nSEG, "nFILL=", self.nFILL return True
#--------------------------------------------------------------------
[ドキュメント] def writeFLT(self, fname): """write FLT file Numpy配列をFLTファイルへ出力。 """ try: with open(fname, 'w') as f: f.write('@{0}<FLOAT>[{1}, {2}] '.format(self.arrayName, self.fX, self.fY) + '{\n' ) num = 0 #実データ数カウンタ lfNum = 0 #行送り用カウンタ totNum = 0 #総データ用カウンタ for ay in range(0, self.iY): for ax in range(0, self.iX): f.write(str(self.nArray[ax][ay])+", ") num += 1 lfNum += 1 totNum += 1 if lfNum == 16: f.write('\n') lfNum = 0 if self.padding and ((num % self.pW) == 0): #paddingの場所に来た for bx in range(0, self.nFILL): f.write("0.0, ") totNum += 1 f.write('\n') lfNum = 0 if lfNum != 0: f.write('\n') if (self.fX * self.fY) != totNum: eCode = 1 eCount = 1 else: eCode = 0 eCount = 0 f.write('} ' + 'nREC={0}, CONV_ERR_CODE={1}, CONV_ERR_COUNT={2}\n'.format(totNum, eCode, eCount)) except: stdExceptionHandler("ERROR: Unexpected Error in the writing dump file. ?=" + fname) return False return True
#======================================================================= # メインプログラム def main(): """main. メインプログラム """ #----------------------------------------------------------------------- # コマンドラインオプション処理 # parser = argparse.ArgumentParser(description='flt2pic FLT file imager.') parser.add_argument('--FLT1', nargs=1, help='1st. FLT file name.') parser.add_argument('--ARRAY1', nargs=1, help='Array name, in 1st. FLT file.') parser.add_argument('--FLT2', nargs=1, help='2nd. FLT file name, comparison mode.') parser.add_argument('--ARRAY2', nargs=1, help='Array name, in 2nd. FLT file.') parser.add_argument('--OUT', nargs=1, help='Output picture file name.') parser.add_argument('--MAG', nargs=1, help='Specify Magnification(default=1).') parser.add_argument('--W', nargs=1, help='mini picture W size.') parser.add_argument('--ICH', nargs=1, help='number of X mini pictures.') parser.add_argument('--FT', nargs=1, help='compare mode factor.') parser.add_argument('--LOG', nargs=1, help='VPPL -i LOG file name, fromLog mode.') parser.add_argument('--LARRAY', nargs=1, help='Array name, in fromLog mode.') parser.add_argument('--LX', nargs=1, help='X size, in fromLog mode.') parser.add_argument('--LY', nargs=1, help='Y size, in fromLog mode.') parser.add_argument('--FOUT', nargs=1, help='Output FLT file name.') parser.add_argument('--FXW', nargs=1, help='FLT file X-W size.') 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.LOG is None: fromLog = False else: fromLog = True logFname = args.LOG[0] if not os.path.isfile(logFname): errPrint('ERROR: LOG file, NOT EXIST. ?=' + logFname) sys.exit(1) if fromLog: if args.LARRAY is None: errPrint('ERROR: NO LARRAY specified!') sys.exit(1) else: arrayLname = args.LARRAY[0] if args.LX is not None: nLX = tryIntParse(args.LX[0], 1) else: nLX = 1 if args.LY is not None: nLY = tryIntParse(args.LY[0], 1) else: nLY = 1 else: if args.FLT1 is None: errPrint('ERROR: NO FLT file!') sys.exit(1) else: flt1Fname = args.FLT1[0] if not os.path.isfile(flt1Fname): errPrint('ERROR: FLT1 file, NOT EXIST. ?=' + flt1Fname) sys.exit(1) if args.ARRAY1 is None: errPrint('ERROR: NO ARRAY1 specified!') sys.exit(1) else: array1name = args.ARRAY1[0] if args.FLT2 is None: compMode = False else: compMode = True flt2Fname = args.FLT2[0] if not os.path.isfile(flt2Fname): errPrint('ERROR: FLT2 file, NOT EXIST. ?=' + flt2Fname) sys.exit(1) if compMode: if args.ARRAY2 is None: errPrint('ERROR: NO ARRAY2 specified!') sys.exit(1) else: array2name = args.ARRAY2[0] if args.OUT is None: pictureFlag = False else: pictureFlag = True outFname = args.OUT[0] if args.FOUT is None: fltOutFlag = False else: fltOutFlag = True foutFname = args.FOUT[0] #----------------------------------------------------------------------- # パラメータ処理 if args.MAG is not None: mag = tryIntParse(args.MAG[0], 1) else: mag = 1 if args.W is not None: ws = tryIntParse(args.W[0], 1) else: ws = 1 if args.ICH is not None: ic = tryIntParse(args.ICH[0], 1) else: ic = 1 if args.FT is not None: cmpFactor = tryIntParse(args.FT[0], 1) else: cmpFactor = 1 if args.FXW is not None: fxwSize = tryIntParse(args.FXW[0], 15) padMode = True else: fxwSize = 0 padMode = False #----------------------------------------------------------------------- # 実処理 # print "-"*50 if fromLog: log = logReader(logFname, arrayLname, nLX, nLY) log.debug = args.debug log.verbose = args.verbose log.prepareArray() if not log.read(): errPrint("ERROR: LOG file reader fail.") sys.exit(1) else: tempFname = "fromLog_file.flt" flt1 = FltFileReader(tempFname, arrayLname) flt1.debug = args.debug flt1.verbose = args.verbose flt1.nArray = log.nArray flt1.iX = log.iX flt1.iY = log.iY else: flt1 = FltFileReader(flt1Fname, array1name) flt1.debug = args.debug flt1.verbose = args.verbose if not flt1.read(): sys.exit(1) if fltOutFlag: fout = FltFileWriter(flt1.nArray, flt1.tName, pad=padMode, padW=fxwSize) fout.debug = args.debug fout.verbose = args.verbose if fout.checkArray(): if not fout.writeFLT(foutFname): errPrint("ERROR: Writing FLT file. ?=" + foutFname) sys.exit(1) else: errPrint("ERROR: FLT file Preparation.") sys.exit(1) if compMode: flt2 = FltFileReader(flt2Fname, array2name) flt2.debug = args.debug flt2.verbose = args.verbose if not flt2.read(): sys.exit(1) if not flt1.compArray(flt2.nArray, cmpFactor): errPrint("ERROR: FLT file comparison mode fail.") if pictureFlag: hs = flt1.iX / ws oc = flt1.iY / ic if args.verbose: print "W={0} H={1} ICH={2} OCH={3}".format(ws, hs, ic, oc) pic = ImageFileGenerator(outFname, flt1.nArray, ws, hs, ic, oc, mag) pic.debug = args.debug pic.verbose = args.verbose if pic.prepareImage(): pic.draw() if not pic.save(): sys.exit(1) else: errPrint("ERROR: Image preparation") sys.exit(1) today = datetime.today() print today.strftime("FINISH: %Y/%m/%d %H:%M:%S") #----------------------------------------------------------------------- # 正常終了 # sys.exit(0) #======================================================================= # メインプログラムの起動 if __name__ == "__main__": main()