#!
# coding: utf-8
# Copyright (C) 2015-2016 TOPS SYSTEMS
### @file insPic2dnn.py
### @brief Instert picture to DNN object. then Run it.
###
### detectedOBJprofiler.pyに-F -aオプションをつけて実行した結果のLOGを読み取り
### LOGに記載されている画像をHEXファイル化し、DNNオブジェクトファイルの所定位置に挿入する
### 挿入後、DNNオブジェクトファイルをVPPLのスーパバイズ下で実行し、実行結果のLOGを出力する
### そのLOGファイルがdnn2pic.pyの入力となる
###
### Contact: izumida@topscom.co.jp
###
### @author: M.Izumida
### @date: December 29, 2016
###
# v01r00 オリジナルはinsPic2hex.py v01r02
#
# Written for Python 2.7 (NOT FOR 3.x)
#=======================================================================
# インポート宣言
import sys
import re
import argparse
import os
import struct
from datetime import datetime
from PIL import Image
import subprocess
from subprocess import Popen
import threading
import time
import shutil
import math
#=======================================================================
# バージョン文字列
versionSTR = "insPic2dnn.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
#-------------------------------------------------------------------
def monitorThread(timeup, proc):
"""child process monitor thread.
子プロセスモニタスレッド。子プロセスとして実行している外部プログラムが
完了しているかを監視するためのスレッド。2秒おきに監視する。
timeupになると終了する。
"""
running = True
counter = 0
while running and (counter < timeup):
if proc.poll() is not None:
sys.stderr.write("Child process finished: Return value = " + str(proc.returncode))
running = False
else:
time.sleep(2)
counter += 2
sys.stderr.write("Child process running: " + str(counter) + " seconds.\r")
sys.stderr.write("\n")
if running:
sys.stderr.write("Child process TIME UP!!!\n")
#-------------------------------------------------------------------
def runProgram(comLis, errStr, fnam):
"""run Program with output file redirect.
外部コマンドを実行し、その出力をファイル名fnamに出力する。
エラーがあれば偽を、そうでなければ真を返す。
実行状況を別スレッドでモニタする。
"""
global th_anchor
result = True
try:
with open(fnam, 'w') as hndl:
proc = Popen(comLis, bufsize=-1, stdout=hndl)
th_anchor = threading.Thread(target=monitorThread, name="monitor_thread", args=(1000, proc, ))
th_anchor.start()
th_anchor.join(1001)
if th_anchor.isAlive():
result = False
except:
stdExceptionHandler(errStr)
result = False
return result
#-------------------------------------------------------------------
def eSub(st):
"""convert string -> float -> check -> exp.
文字列を変換し、expに通した結果を返す
"""
fv = float(st)
if (fv >= -256.0) and (fv < 256.0):
return math.exp(fv)
else:
return 0.0
#-------------------------------------------------------------------
def eSub2(st):
"""convert string -> float.
文字列をfloatに変換する
"""
fv = float(st)
if (fv >= 0.0) and (fv < 1.0):
return fv
else:
return 0.0
#=======================================================================
# 解析オプション クラス
class Options:
"""Options Class.
グローバルオプションを保持するクラス
"""
pass
#=======================================================================
# LOGファイルリーダクラス
[ドキュメント]class logReader:
"""LOG File Reader Class.
detectedOBJprofilerの-F -aオプションでのLOGファイルを読み取って
リストとして記憶するクラス
DNN実行後の判定結果を付加して出力可能
"""
#--------------------------------------------------------------------
def __init__(self, ifnam):
"""LOG File Reader Constructor
コンストラクタ
"""
self.iFname = ifnam
#データ格納テーブル
# 0: 画像ファイル名
# 1: オリジナル sx位置
# 2: オリジナル sy位置
# 3: オリジナル ex位置
# 4: オリジナル ey位置
# 5: DNN判定結果 (0/1/2)
self.table = []
#
self.tablePTR = 0
#
self.debug = False
self.verbose = False
#--------------------------------------------------------------------
[ドキュメント] def read(self):
"""read method
ファイルから行をリードして処理するメソッド
"""
try:
with open(self.iFname, 'r') as f:
for line in f:
self.rLine(line)
except:
stdExceptionHandler("ERROR: in file reading = " + self.iFname)
return False
return True
#--------------------------------------------------------------------
[ドキュメント] def rLine(self, lin):
"""Read Line method
読み取った1行を処理するメソッド
"""
p0 = re.match('([a-zA-Z0-9_\.]+) : ([0-9]+), ([0-9]+), ([0-9]+), ([0-9]+)', lin)
if p0:
work = []
work.append(p0.group(1))
work.append(tryIntParse(p0.group(2), 0))
work.append(tryIntParse(p0.group(3), 0))
work.append(tryIntParse(p0.group(4), 0))
work.append(tryIntParse(p0.group(5), 0))
work.append(0) #結果格納用、初期値 0=other
self.table.append(work)
return
#--------------------------------------------------------------------
[ドキュメント] def dumpTable(self):
"""dump table
テーブルのダンプ出力。ベリファイ用。
"""
for item in self.table:
if len(item) > 4:
fnam = item[0]
sx = item[1]
sy = item[2]
ex = item[3]
ey = item[4]
if len(item) > 5:
sel = item[5]
else:
sel = -1
print "{0} : {1}, {2}, {3}, {4} => {5}".format(fnam, sx, sy, ex, ey, sel)
#--------------------------------------------------------------------
[ドキュメント] def begin(self):
"""begin method
テーブルポインタの初期化。
"""
self.tablePTR = 0
#--------------------------------------------------------------------
[ドキュメント] def getFileName(self):
"""get File name method
ファイル名を一つ取り出すメソッド。
取り出せなかったらNoneを返す。
"""
if self.tablePTR < len(self.table):
idx = self.tablePTR
self.tablePTR += 1
return self.table[idx][0]
else:
return None
#--------------------------------------------------------------------
[ドキュメント] def setResult(self, fnam, res):
"""set Result
結果をセットするメソッド。
"""
idx = 0
while idx < len(self.table):
if self.table[idx][0] == fnam:
self.table[idx][5] = res
break
idx += 1
#=======================================================================
# ELOGファイルリーダクラス
[ドキュメント]class elogReader:
"""EXEC LOG File Reader Class.
vppl -i -e -sの結果を読み取るクラス
"""
#--------------------------------------------------------------------
def __init__(self, ifnam):
"""ELOG File Reader Constructor
コンストラクタ
"""
self.iFname = ifnam
#
self.res = 0
#
self.debug = False
self.verbose = False
#--------------------------------------------------------------------
[ドキュメント] def read(self):
"""read method
ファイルから行をリードして処理するメソッド
"""
try:
with open(self.iFname, 'r') as f:
for line in f:
self.rLine(line)
except:
stdExceptionHandler("ERROR: in file reading = " + self.iFname)
return False
return True
#--------------------------------------------------------------------
[ドキュメント] def rLine(self, lin):
"""Read Line method
読み取った1行を処理するメソッド
"""
p1 = re.match('QCP1> results=([\-\.0-9E]+) ([\-\.0-9E]+) ([\-\.0-9E]+)', lin)
if p1:
otherP = eSub2(p1.group(1))
carP = eSub2(p1.group(2))
kumaP = eSub2(p1.group(3))
#print lin
#print p1.group(1), p1.group(2), p1.group(3)
#print otherP, carP, kumaP
if kumaP > 0.5:
print "Kuma."
self.res = 2
elif carP > 0.5:
print "Car."
self.res = 1
else:
print "Other."
self.res = 0
return
# resultでない場合は内部でsoftmax相当の処理を行う
p0 = re.match('QCP1> BFS=([\-\.0-9E]+) ([\-\.0-9E]+) ([\-\.0-9E]+)', lin)
if p0:
otherP = eSub(p0.group(1))
carP = eSub(p0.group(2))
kumaP = eSub(p0.group(3))
tot = otherP + carP + kumaP
if tot != 0:
otherP = otherP / tot
carP = carP / tot
kumaP = kumaP / tot
if kumaP > 0.5:
print "Kuma."
self.res = 2
elif carP > 0.5:
print "Car."
self.res = 1
else:
print "Other."
self.res = 0
return
#=======================================================================
# HEXファイルリーダライタ クラス
[ドキュメント]class HexFileRWriter:
"""HexFile loader Class.
HEXファイルを読みとって保持するクラス
以下部分を画像ファイルから生成したHEXに差し替える
60100000-60101FFF 64x64x2=8192バイト
"""
#--------------------------------------------------------------------
def __init__(self, inputFnam):
"""HEX File Reader/Writer Constructor
コンストラクタ
"""
self.iFname = inputFnam
self.before = []
self.after = []
self.position = 0
self.findFlag = False
self.pic = None
#
self.debug = False
self.verbose = False
#--------------------------------------------------------------------
[ドキュメント] def read(self):
"""Read method
HEXファイルを読み込むメソッド
"""
try:
with open(self.iFname, 'r') as f:
for line in f:
self.rLine(line)
except:
stdExceptionHandler("Error: in file reading = " + self.iFname)
return False
return True
#--------------------------------------------------------------------
[ドキュメント] def rLine(self, lin):
"""Read Line method
読み取った1行を処理するメソッド
"""
p0 = re.match('S3156010[0-1]', lin)
if p0:
if not self.findFlag:
self.findFlag = True
self.position += 1
else:
self.findFlag = False
if self.position == 0:
self.before.append(lin)
else:
self.after.append(lin)
return
#--------------------------------------------------------------------
[ドキュメント] def save(self, oFname):
"""save data
内部に保持するデータを書き出すメソッド
"""
try:
with open(oFname, 'w') as f:
for lin in self.before:
f.write(lin)
for lin in self.pic:
f.write(lin)
f.write('\n')
for lin in self.after:
f.write(lin)
except:
stdExceptionHandler("Error: in file writing = " + oFname)
return False
return True
#=======================================================================
# PNGファイルリーダ クラス
[ドキュメント]class PngFileReader:
"""PNG File Reader Class.
PNGファイルを読み込み、Sレコードにして保持するクラス
"""
#--------------------------------------------------------------------
def __init__(self, ifnam, sadr, f16):
"""PNG File Reader Constructor
コンストラクタ
"""
self.iFname = ifnam
self.SREC = []
#self.adr = 0xe2000000
self.adr = sadr
self.f16q = f16
self.FQ = 12
#
self.imX = 0
self.imY = 0
#--------------------------------------------------------------------
[ドキュメント] def load(self):
"""Image File loader
イメージファイルのローダ
"""
try:
self.im = Image.open(self.iFname)
self.imX = self.im.size[0]
self.imY = self.im.size[1]
self.imode = self.im.mode
if self.debug:
print "Image file name =", self.iFname
print "Image X size =", self.imX
print "Image Y size =", self.imY
print "Image format =", self.imode
if (self.imX != 64) or (self.imY != 64) or (self.imode != "L"):
return False
except:
stdExceptionHandler("Error: reading image file = " + self.iFname)
return False
return True
#--------------------------------------------------------------------
[ドキュメント] def convert(self):
"""Image to S3 convert method
Imageを読み取ってS3に変換する
"""
for iy in range(self.imY):
for ix in range(self.imX):
if (ix % 16) == 0:
work = []
work.append(self.im.getpixel( (ix, iy) ))
if (ix % 16) == 15:
self.makeS3( tuple(work) )
#--------------------------------------------------------------------
[ドキュメント] def makeS3(self, arg):
"""make S3 record
与えられたargタプル(16バイト要素)を
self.f16q が偽なら1つのS3レコードに
真ならワード変換して2つのS3レコードに変換する
"""
if self.f16q:
self.makeS3W(arg[0:8])
self.makeS3W(arg[8:])
else:
self.makeS3B(arg)
#--------------------------------------------------------------------
[ドキュメント] def makeS3B(self, arg):
"""make S3 record (BYTE)
与えられたargタプル(16バイト要素)をS3レコードに変換する
"""
temp = "S315" + "{0:08X}".format(self.adr)
csum = 0x15 + ((self.adr >> 24) & 0xFF) + ((self.adr >> 16) & 0xFF) + ((self.adr >> 8) & 0xFF) + (self.adr & 0xFF)
self.adr += 16
for idx in range(16):
csum += arg[idx]
temp = temp + "{0:02X}".format(arg[idx])
csum = (~csum) & 0xFF
temp = temp + "{0:02X}".format(csum)
self.SREC.append(temp)
#--------------------------------------------------------------------
[ドキュメント] def makeS3W(self, arg):
"""make S3 record (WORD)
与えられたargタプル(8バイト要素)を16ビットに拡張しながらS3レコードに変換する
"""
lis = []
for idx in range(8):
temp = arg[idx] << (self.FQ - 8)
lis.append( temp & 0xFF)
lis.append( (temp >> 8) & 0xFF )
self.makeS3B( tuple(lis) )
#--------------------------------------------------------------------
[ドキュメント] def dumpSrec(self):
"""dump S record
Sレコードのダンプ出力。ベリファイ用。
"""
for rec in self.SREC:
print rec
#=======================================================================
# メインプログラム
def main():
"""main.
メインプログラム
"""
#-----------------------------------------------------------------------
# コマンドラインオプション処理
#
parser = argparse.ArgumentParser(description='insPic2dnn DNN runner.')
parser.add_argument('--HEX', nargs=1, help='Specify input HEX file.')
parser.add_argument('--LOG', nargs=1, help='Specify detected OBJ profiler log file.')
parser.add_argument('--OUT', nargs=1, help='Specify output HEX name.')
parser.add_argument('-f16', dest='f16', help='F16Qn mode.', action='store_true', default=False)
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.HEX is None:
errPrint('ERROR: No Input HEX file specified.')
sys.exit(1)
else:
hexFname = args.HEX[0]
if not os.path.isfile(hexFname):
errPrint('ERROR: Input HEX file, NOT EXIST.')
sys.exit(1)
if args.LOG is None:
errPrint('ERROR: No Input LOG file specified.')
sys.exit(1)
else:
logFname = args.LOG[0]
if not os.path.isfile(logFname):
errPrint('ERROR: Input LOG file, NOT EXIST.')
sys.exit(1)
if args.OUT is None:
outFname = 'out.HEX'
else:
outFname = args.OUT[0]
#-----------------------------------------------------------------------
# パラメータ処理
#-----------------------------------------------------------------------
# 実処理
#
#ログファイルの読み出し
log = logReader(logFname)
log.verbose = args.verbose
log.debug = args.debug
if log.read():
if args.verbose:
log.dumpTable()
#オリジナルのHEXファイルの読み出し
obj = HexFileRWriter(hexFname)
obj.verbose = args.verbose
obj.debug = args.debug
if not obj.read():
sys.exit(1)
#logファイルに記載された画像ファイルを舐める
log.begin()
while True:
picfnam = log.getFileName()
if picfnam is None:
break
if args.verbose:
print picfnam
png = PngFileReader(picfnam, 0x60100000, args.f16)
png.verbose = args.verbose
png.debug = args.debug
if png.load():
print "PICTURE LOAD OK...", picfnam
png.convert()
if args.verbose:
png.dumpSrec()
if args.f16 and (png.adr != 0x60102000):
print "F16 ADDRESS ERROR = ", hex(png.adr)
elif (not args.f16) and (png.adr != 0x60101000):
print "F8 ADDRESS ERROR = ", hex(png.adr)
else:
sys.exit(1)
obj.pic = png.SREC
if not obj.save(outFname):
sys.exit(1)
name, ext = os.path.splitext(picfnam)
if runProgram("vppl -i -e -s", "ISIM ERROR.", name + "_exec.log"):
elog = elogReader(name + "_exec.log")
if elog.read():
log.setResult(picfnam, elog.res)
else:
sys.exit(1)
log.dumpTable()
today = datetime.today()
print today.strftime("FINISH: %Y/%m/%d %H:%M:%S")
#-----------------------------------------------------------------------
# 正常終了
#
sys.exit(0)
#=======================================================================
# メインプログラムの起動
if __name__ == "__main__":
main()