Changeset - 42f4533a5029
[Not reviewed]
default
0 2 2
Laman - 10 years ago 2015-09-01 22:44:11

workable static image analysis
included testing images
added ImageAnalyzer
added Go
4 files changed with 138 insertions and 10 deletions:
0 comments (0 inline, 0 general)
.hgignore
Show inline comments
 
^images/
 
^src/__pycache__/
 
\ No newline at end of file
src/go.py
Show inline comments
 
new file 100644
 
class Go:
 
  EMPTY=0
 
  BLACK=1
 
  WHITE=-1
 
  
 
  ## Initializes self.board to a list[r][c]=Go.EMPTY.
 
  def __init__(self,boardSize=19):
 
    self.boardSize=boardSize
 
    self.board=[[Go.EMPTY]*self.boardSize for x in self.board]
 
  
 
  ## Executes a move.
 
  #  
 
  #  Doesn't check for kos. Suicide not allowed.
 
  #  
 
  #  @param color Go.BLACK or Go.WHITE
 
  #  @return True on success, False on failure (illegal move)
 
  def move(self,color,row,col):
 
    if self.board[row][col]!=Go.EMPTY: return False
 
 
    self.board[row][col]=color
 
 
    # capture neighbors
 
    for r,c in ((-1,0),(1,0),(0,-1),(0,1)):
 
      self.temp=[[False]*self.boardSize for x in self.board]
 
      if not self._floodFill(-color,row+r,col+c): self._remove()
 
    
 
    # check for suicide
 
    self.temp=[[False]*self.boardSize for x in self.board]
 
    if not self._floodFill(color,row,col):
 
      self.board[row][col]=Go.EMPTY
 
      return False
 
    return True
 
    
 
  ## Checks for liberties of a stone at given coordinates.
 
  #  
 
  #  The stone's group is marked with True in self.temp, ready for capture if needed. Recursively called for stone's neighbors.
 
  #  
 
  #  @return True if alive, False if captured
 
  def _floodFill(self,color,row,col):
 
    if col<0 or col>=self.boardSize or row<0 or row>=self.boardSize: return False # out of range
 
    if self.temp[row][col]: return False # already visited
 
    if self.board[row][col]==Go.EMPTY: return True # found a liberty
 
    if self.board[row][col]!=color: return False # opponent's stone
 
    self.temp[row][col]=True # set visited
 
    return self._floodFill(color,row,col-1) or self._floodFill(color,row,col+1) or self._floodFill(color,row-1,col) or self._floodFill(color,row+1,col) # check neighbors
 
  
 
  ## Removes stones at coordinates marked with True in self.temp.
 
  def _remove(self):
 
    for r in range(self.boardSize):
 
      for c in range(self.boardSize):
 
        if self.temp[r][c]: self.board[r][c]=Go.EMPTY
src/gui.py
Show inline comments
 
@@ -5,6 +5,8 @@ import math
 
from epoint import *
 
from corners import *
 
from grid import *
 
from image_analyzer import *
 
from go import *
 
 
 
class Application(tk.Frame):
 
@@ -20,24 +22,26 @@ class Application(tk.Frame):
 
    # a captured frame with overlay graphics
 
    self.imgView=tk.Canvas(self)
 
    self.imgView.configure(width=480,height=360)
 
    imgOrig=PIL.Image.open("../images/7.jpg")
 
    self.imgOrig=PIL.Image.open("../images/7.jpg")
 
    
 
    self.img=ImageTk.PhotoImage(imgOrig.resize((int(self.imgView['width']),int(self.imgView['height'])),resample=PIL.Image.BILINEAR))
 
    self.img=ImageTk.PhotoImage(self.imgOrig.resize((int(self.imgView['width']),int(self.imgView['height'])),resample=PIL.Image.BILINEAR))
 
    
 
    self.imgView.bind('<1>',lambda e: self.addCorner(e.x,e.y))
 
    self.redrawImgView()
 
    
 
    self.imgView.grid(column=0,row=0)
 
    
 
    # board with detected stones
 
    self.boardView=BoardView(self)
 
    self.boardView.grid(column=1,row=0)
 
    
 
    self.redrawImgView()
 
  
 
  ## Stores a grid corner located at x,y coordinates.
 
  def addCorner(self,x,y):
 
    self.corners.add(x,y)
 
    if self.corners.canonizeOrder():
 
      self.boardGrid=Grid(self.corners)
 
      self.boardView.setBoardGrid(self.boardGrid)
 
    
 
    self.redrawImgView()
 
    
 
@@ -59,6 +63,15 @@ class Application(tk.Frame):
 
    
 
    self.imgView.grid()
 
    
 
    origWidth,origHeight=self.imgOrig.size
 
    widthRatio=origWidth/self.img.width()
 
    heightRatio=origHeight/self.img.height()
 
    sizeCoef=max(widthRatio,heightRatio)
 
    
 
    shift=EPoint(origWidth-self.img.width()*sizeCoef,origHeight-self.img.height()*sizeCoef)/2
 
    
 
    self.boardView.redrawState(self.imgOrig,sizeCoef,shift)
 
    
 
  ## Marks a point at the image with a green cross. Used for corners.
 
  def markPoint(self,x,y):
 
    self.imgView.create_line(x-3,y-3,x+4,y+4,fill="#00ff00")
 
@@ -70,12 +83,23 @@ class Application(tk.Frame):
 
## Handles and presents the game state as detected by the program.   
 
class BoardView(tk.Canvas):
 
  def __init__(self, master=None):
 
    self.detector=ImageAnalyzer()
 
    self.boardGrid=None
 
    
 
    tk.Canvas.__init__(self, master)
 
    self.configure(width=360,height=360,background="#ffcc00")
 
    
 
    self.drawGrid()
 
    self.drawStones()
 
    
 
    self.grid()
 
    
 
  def redrawState(self,img,sizeCoef,shift):
 
    self.detector.analyze(img,30,65,sizeCoef,shift)
 
    
 
    for r,row in enumerate(self.detector.board):
 
      for c,point in enumerate(row):
 
        self.drawStone(r,c,point)
 
        
 
    self.grid()
 
    
 
  def drawGrid(self):
 
@@ -90,16 +114,24 @@ class BoardView(tk.Canvas):
 
      for c in range(4,19,6):
 
        self.create_oval(r*18-2,c*18-2,r*18+2,c*18+2,fill='#000000')
 
      
 
  def drawStones(self):
 
    pass
 
      
 
  ## Draws a stone at provided coordinates.
 
  #  
 
  #  For an unknown color draws nothing and returns False.
 
  #  
 
  #  @param r row coordinate, [0-18], counted from top
 
  #  @param c column coordinate, [0-18], counted from left
 
  #  @param color color indicator, {'b','w'}
 
  #  @param color color indicator, Go.BLACK or Go.WHITE
 
  def drawStone(self,r,c,color):
 
    self.create_oval(r*18-9,c*18-9,r*18+9,r*18+9,'#000000' if color=='b' else '#ffffff')
 
    if color==Go.BLACK: hexCode='#000000'
 
    elif color==Go.WHITE: hexCode='#ffffff'
 
    else: return False
 
    r+=1
 
    c+=1
 
    self.create_oval(c*18-9,r*18-9,c*18+9,r*18+9,fill=hexCode)
 
    
 
  def setBoardGrid(self,boardGrid):
 
    self.boardGrid=boardGrid
 
    self.detector.setGrid(boardGrid)
 
 
 
root = tk.Tk()
src/image_analyzer.py
Show inline comments
 
new file 100644
 
from go import *
 
 
 
class ImageAnalyzer:
 
  
 
  def __init__(self):
 
    self.board=[[Go.EMPTY]*19 for r in range(19)]
 
    self.grid=None
 
  
 
  def analyze(self,image,tresB,tresW,sizeCoef,shift):
 
    if self.grid==None: return False
 
    
 
    for r in range(19):
 
      for c in range(19):
 
        intersection=self.grid.intersections[r][c]
 
        
 
        if c>0: stoneWidth=sizeCoef*(intersection.x-self.grid.intersections[r][c-1].x)
 
        else: stoneWidth=sizeCoef*(self.grid.intersections[r][c+1].x-intersection.x)
 
        if r>0: stoneHeight=sizeCoef*(intersection.y-self.grid.intersections[r-1][c].y)
 
        else: stoneHeight=sizeCoef*(self.grid.intersections[r+1][c].y-intersection.y)
 
        
 
        self.board[r][c]=self.analyzePoint(image,intersection*sizeCoef+shift,stoneWidth,stoneHeight,tresB,tresW)
 
  
 
  def analyzePoint(self,image,imageCoords,stoneWidth,stoneHeight,tresB,tresW):
 
    b=w=e=0
 
    
 
    cmax=max(int(stoneWidth*2//5),2)
 
    rmax=max(int(stoneHeight*2//5),2)
 
    
 
    for r in range(-rmax,rmax+1):
 
      for c in range(-cmax,cmax+1):
 
        red,green,blue=image.getpixel((imageCoords.x+c,imageCoords.y+r))
 
        
 
        I=(red+green+blue)/255/3
 
        m=min(red,green,blue)
 
        S=1-m/I
 
        if 100*I<tresB: b+=1
 
        elif 100*I>tresW: w+=1
 
        else: e+=1
 
        
 
    if b>=w and b>=e: return Go.BLACK
 
    if w>=b and w>=e: return Go.WHITE
 
    return Go.EMPTY
 
    
 
  def setGrid(self,grid):
 
    self.grid=grid
0 comments (0 inline, 0 general)