#!
# 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()