Changeset - 6f9ec51a8142
[Not reviewed]
default
0 6 0
Laman - 6 years ago 2018-12-12 22:28:59

moving responsibilities
GUI got its own ImageAnalyzer instance for doing previews
accordingly changed APIs to keep only useful messages
ImageAnalyzer params wrapped in a separate class
6 files changed with 81 insertions and 65 deletions:
0 comments (0 inline, 0 general)
src/analyzer/__init__.py
Show inline comments
 
@@ -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*I<self.tresB: b+=1
 
				elif 100*I>self.tresW: w+=1
 
				if 100*I<self.params.tresB: b+=1
 
				elif 100*I>self.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)
 

	
 

	
src/analyzer/framecache.py
Show inline comments
 
@@ -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]])
src/core.py
Show inline comments
 
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,
src/gui/__init__.py
Show inline comments
 
@@ -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("<<redrawImgView>>")
 

	
 
@@ -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()
src/gui/imgview.py
Show inline comments
 
@@ -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)
src/gui/settings.py
Show inline comments
 
@@ -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
0 comments (0 inline, 0 general)