diff --git a/src/analyzer/__init__.py b/src/analyzer/__init__.py --- a/src/analyzer/__init__.py +++ b/src/analyzer/__init__.py @@ -6,13 +6,28 @@ from util import BLACK,WHITE,EMPTY, expo log=logging.getLogger(__name__) +class Params: + def __init__(self): + self.tresB=30 + self.tresW=60 + self.corners=[] + + def copy(self): + res=Params() + res.tresB=self.tresB + res.tresW=self.tresW + res.corners=[p*1 for p in self.corners] # *1 forces copy + return res + + class ImageAnalyzer: def __init__(self,tresB=30,tresW=60): self.board=[[EMPTY]*19 for r in range(19)] self.grid=None - self.tresB=tresB - self.tresW=tresW + self.params=Params() + self.params.tresB=tresB + self.params.tresW=tresW # let's not concern ourselves with sizecoef and shift here anymore. we want corners to come already properly recomputed def analyze(self,image): @@ -43,8 +58,8 @@ class ImageAnalyzer: I=(red+green+blue)/255/3 m=min(red,green,blue) S=1-m/I if I!=0 else 0 - if 100*Iself.tresW: w+=1 + if 100*Iself.params.tresW: w+=1 else: e+=1 log.debug("(%d,%d) ... (b=%d,w=%d,e=%d)", row, col, b, w, e) @@ -53,7 +68,13 @@ class ImageAnalyzer: if w>=b and w>=e: return WHITE return EMPTY - def setGridCorners(self,corners): + def setParams(self,params): + self.params=params.copy() + if len(params.corners)==4: + self.grid=Grid(self.params.corners) + + def setCorners(self,corners): + self.params.corners=[p*1 for p in corners] self.grid=Grid(corners) diff --git a/src/analyzer/framecache.py b/src/analyzer/framecache.py --- a/src/analyzer/framecache.py +++ b/src/analyzer/framecache.py @@ -4,9 +4,21 @@ from collections import deque class FrameCache: def __init__(self,capacity=600): # 600 is enough for 10 min of 1 fps self._capacity=capacity - self._cache=deque() + self._keys=deque() + self._values=dict() + self._i=0 - def put(self,frame): - if len(self._cache)>=self._capacity: - self._cache.popleft() - self._cache.append(frame) + def put(self,item): + self._i+=1 + if len(self._keys)>=self._capacity: + k=self._keys.popleft() + del self._values[k] + self._values[self._i]=item + self._keys.append(self._i) + return self._i + + def get(self,k): + return self._values.get(k) + + def getRelative(self,k): + return (self._keys[k], self._values[self._keys[k]]) diff --git a/src/core.py b/src/core.py --- a/src/core.py +++ b/src/core.py @@ -1,4 +1,3 @@ -import os import multiprocessing import threading import logging @@ -27,10 +26,7 @@ class Core: self._guiMessages=MsgQueue() self._vidMessages=MsgQueue() - self._imgs=sorted(os.listdir(cfg.misc.imgDir)) - self._imgIndex=cfg.misc.defaultImage - imgPath=os.path.join(cfg.misc.imgDir,self._imgs[self._imgIndex]) - self._frame=PIL.Image.open(imgPath) + self._frame=None self._guiProc=multiprocessing.Process(name="gui", target=gui, args=(self._guiMessages,self._ownMessages)) self._guiProc.start() @@ -41,42 +37,23 @@ class Core: ) self._vidProc.start() - def setCorners(self,corners): - self.detector.setGridCorners(corners) - self.analyze() - - def setTresholds(self,tresB=None,tresW=None): - if tresB is not None: self.detector.tresB=tresB - if tresW is not None: self.detector.tresW=tresW - self.preview() - - def relativeFrame(self,step): - self._imgIndex=(self._imgIndex+step)%len(self._imgs) - imgPath=os.path.join(cfg.misc.imgDir,self._imgs[self._imgIndex]) - self._frame=PIL.Image.open(imgPath) - self._guiMessages.send("setCurrentFrame",(self._frame.copy(),gui.PREVIEW)) - self.preview() + def showFrame(self,key): + frame=self._cache.get(key) + if frame is None: + (key,frame)=self._cache.getRelative(10) + self._guiMessages.send("setCurrentFrame", (frame.copy(), gui.PREVIEW, key)) def putFrame(self,frame): - img=PIL.Image.fromarray(frame) - self._cache.put(img) - self._guiMessages.send("setCurrentFrame",(img,gui.PREVIEW)) # !! RECORDING - # self.analyze() + self._frame=PIL.Image.fromarray(frame) + k=self._cache.put(self._frame) + self._guiMessages.send("setCurrentFrame", (self._frame, gui.RECORDING, k)) + self.analyze() def sendParams(self): - params={ - "tresB": self.detector.tresB, - "tresW": self.detector.tresW - } - self._guiMessages.send("setParams",tuple(),params) + self._guiMessages.send("setParams",(self.detector.params.copy(),)) - def setParams(self,tresB=0,tresW=0): - self.detector.tresB=tresB - self.detector.tresW=tresW - - def preview(self): - if self.detector.analyze(self._frame): - self._guiMessages.send("setGameState", (self.detector.board,[])) + def setParams(self,params): + self.detector.setParams(params) def analyze(self): if self.detector.analyze(self._frame): @@ -105,8 +82,6 @@ class Core: def _handleEvent(self,e): actions={ - "setCorners": self.setCorners, - "setTresholds": self.setTresholds, "prevFrame": lambda: self.relativeFrame(-1), "nextFrame": lambda: self.relativeFrame(1), "putFrame": self.putFrame, diff --git a/src/gui/__init__.py b/src/gui/__init__.py --- a/src/gui/__init__.py +++ b/src/gui/__init__.py @@ -3,6 +3,7 @@ import threading import tkinter as tk import config +from analyzer import ImageAnalyzer from .mainwindow import MainWindow from .boardview import BoardView from .settings import Settings @@ -19,6 +20,9 @@ class GUI: self.root.title("OneEye {0}.{1}.{2}".format(*config.misc.version)) self.root.option_add('*tearOff',False) # for menu + self.detector=ImageAnalyzer() + self._frameKey=0 + self._ownMessages=None self._coreMessages=None @@ -61,6 +65,10 @@ class GUI: if self.settings: self.settings.destroy() self.settings=None + self.sendParams() + + def sendParams(self): + self.sendMsg("setParams",(self.detector.params.copy(),)) def _handleEvent(self,e): actions={ @@ -72,10 +80,11 @@ class GUI: return actions[actionName](*args,**kwargs) - def _frameHandler(self,newFrame,type): - if self._state!=type: + def _frameHandler(self,newFrame,type,key): + if self._state!=type and self.mainWindow.imgView.isSet(): log.info("ignored setCurrentFrame event, wrong type") return + self._frameKey=key self.mainWindow.setCurrentFrame(newFrame) self.root.event_generate("<>") @@ -83,11 +92,12 @@ class GUI: labels={(row,col):(i+1) for (i,(c,row,col)) in enumerate(moves)} self.mainWindow.boardView.redrawState(gameState,labels) - def _paramsHandler(self,**params): + def _paramsHandler(self,params): if not self.settings: log.warning("received 'setParams' message while settings is '%s'",str(self.settings)) return - self.settings.setParams(**params) + self.detector.setParams(params) + self.settings.setParams(params) gui=GUI() diff --git a/src/gui/imgview.py b/src/gui/imgview.py --- a/src/gui/imgview.py +++ b/src/gui/imgview.py @@ -66,7 +66,7 @@ class ImgView(ResizableCanvas): log.debug(self._corners.corners) self._boardGrid=Grid(self._corners.corners) corners=[self._transformPoint(c) for c in self._corners.corners] - self._parent.sendMsg("setCorners",(corners,)) + self._parent.detector.setCorners(corners) self.redraw() @@ -81,6 +81,9 @@ class ImgView(ResizableCanvas): def setRecording(self): self.bind('<1>',lambda e: None) + def isSet(self): + return self._img is not None + def _onResize(self,event): w=self._width super()._onResize(event) diff --git a/src/gui/settings.py b/src/gui/settings.py --- a/src/gui/settings.py +++ b/src/gui/settings.py @@ -40,19 +40,14 @@ class Settings(tk.Toplevel,MsgMixin): self.cancelButton.pack(side=LEFT) def refreshTresholds(self,_): - self.parent.sendMsg("setTresholds",tuple(),self._compileParams()) + params=self.parent.detector.params + params.tresB=self.scaleTresB.get() + params.tresW=self.scaleTresW.get() - def setParams(self,tresB=0,tresW=0): - self.scaleTresB.set(tresB) - self.scaleTresW.set(tresW) + def setParams(self,params): + self.scaleTresB.set(params.tresB) + self.scaleTresW.set(params.tresW) def sendParams(self): - self.parent.sendMsg("setParams",tuple(),self._compileParams()) + self.parent.sendParams() self.destroy() - - def _compileParams(self): - params={ - "tresB": self.scaleTresB.get(), - "tresW": self.scaleTresW.get() - } - return params