insPic2dnn のソースコード

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