示例#1
0
 def getImageWithVideo(self, num):
     video = Video()
     filename = self.testMedia + "chessBoard%03d.jpg" % (num)
     image = video.readImage(filename)
     height, width = image.shape[:2]
     print ("read image %s: %dx%d" % (filename, width, height))
     return image,video
示例#2
0
 def test_getSubRect(self):
     video = Video()
     image = video.readImage(testenv.testMedia+"chessBoard001.jpg")
     subImage = Video.getSubRect(image, (0, 0, 200, 200))
     iheight, iwidth, channels = subImage.shape
     assert iheight == 200
     assert iwidth == 200
     assert channels == 3
示例#3
0
 def getVideo(self) -> Video:
     '''
     get a Video (potentially headless)
     
     Returns:
         Video: the video handler for openCV
     '''
     video = Video()
     video.headless = self.headless
     return video
示例#4
0
 def test_CreateBlank(self):
     video = Video()
     width = 400
     height = 400
     image = video.createBlank(width, height)
     iheight, iwidth, channels = image.shape
     print ("created blank %dx%d image with %d channels" % (iwidth, iheight, channels))
     assert height == iheight
     assert width == iwidth
     assert channels == 3
示例#5
0
 def test_ReadJpg(self):
     video = Video()
     video.open(testenv.testMedia+'emptyBoard001.avi')
     for frame in range(0, 52):
         ret, jpgImage, quit = video.readFrame(show=True)
         assert ret
         assert jpgImage is not None
         height, width = jpgImage.shape[:2]
         # print ("%d: %d x %d" % (frame,width,height))
         assert (width, height) == (640, 480)
     assert video.frames == 52
 def test_ReadAvi(self):
     debug = False
     # "0","1"
     titles = ['scholarsmate', 'emptyBoard001']
     expectedFrames = [334, 52]
     # (1920,1080),(1280,720),
     expectedSize = [(640, 480), (640, 480)]
     for index, title in enumerate(titles):
         args = Args("test")
         if Video.is_int(title):
             device = title
         else:
             device = testEnv.testMedia + title + ".avi"
         args.parse(["--input", device])
         vision = ChessBoardVision(args.args)
         vision.showDebug = debug
         vision.open(args.args.input)
         frameIndex = 0
         while True:
             cbImageSet = vision.readChessBoardImage()
             if not vision.hasImage:
                 break
             frameIndex += 1
             cbImage = cbImageSet.cbImage
             if debug: print("%d x %d" % (cbImage.width, cbImage.height))
             assert (cbImage.width, cbImage.height) == expectedSize[index]
             assert (cbImageSet.frameIndex == frameIndex)
             if debug:
                 print("%3d %.2fs" %
                       (cbImageSet.frameIndex, cbImageSet.timeStamp))
         assert frameIndex == expectedFrames[index]
         vision.save()
示例#7
0
 def test_Concatenate(self):
     video=Video()
     w=400
     h=400
     #https://stackoverflow.com/a/21170291/1497139
     image1=video.createBlank(w, h, (192,192,192))
     image2=video.createBlank(w, h, (255,128,0))
     image3=video.createBlank(w, h, (255//3,255//3,255/3))
     image4=video.createBlank(w, h, (0,0,255))
     combined=video.as2x2(image1,image2,image3,image4,downScale=1)
     hc, wc= combined.shape[:2]
     assert hc==2*h
     assert wc==2*w    
     video.showImage(combined, "combined",keyWait=1000)
示例#8
0
 def test_ReadVideoWithPostProcess(self):
     '''
     test reading video frames with a post processing call back
     '''
     video = Video.getVideo()
     video.open(testenv.testMedia + 'emptyBoard001.avi')
     for frame in range(0, 52):
         ret, jpgImage, quit = video.readFrame(
             show=True, postProcess=video.addTimeStamp)
示例#9
0
 def test_ReadVideo(self):
     '''
     test reading an example video
     '''
     video = Video.getVideo()
     video.open(testenv.testMedia + 'emptyBoard001.avi')
     video.play()
     print("played %d frames" % (video.frames))
     assert video.frames == 52
 def __init__(self, image, video=None):
     """ construct me from the given input image"""
     if video is None:
         video = Video()
     self.video = video
     self.image = image
     # guess the topleft color
     self.topleft = chess.WHITE
     self.height, self.width = self.image.shape[:2]
示例#11
0
 def __init__(self,frames,totalFrames,path,points,rotation=270,idealSize=800,ans=None):
     self.path=path
     self.title=Video.title(path)
     self.frames=frames
     self.totalFrames=totalFrames,
     self.points=points
     self.rotation=rotation
     self.idealSize=idealSize
     self.ans=ans
示例#12
0
 def test_ReadVideoWithPause(self):
     video = Video()
     video.open(testenv.testMedia+'emptyBoard001.avi')
     for frame in range(0, 62):
         if frame >= 10 and frame < 20:
             video.pause(True)
         else:
             video.pause(False)
         ret, jpgImage, quit = video.readFrame(show=True)
         # print (video.frames)
         assert ret
         assert jpgImage is not None
         height, width = jpgImage.shape[:2]
         # print ("%d: %d x %d" % (frame,width,height))
         assert (width, height) == (640, 480)
     assert video.frames == 52
 def __getstate__(self):
     state={}
     state["title"]=self.title
     device=self.device
     if not Video.is_int(device):
         cwd=os.getcwd()
         devicepath=os.path.dirname(device)
         root=os.path.commonpath([cwd,devicepath])
         device=os.path.relpath(devicepath,root)+'/'+os.path.basename(device)
     state["device"]=device
     state["timestamps"]=self.timestamps
     return state
 def setup(self, idealSize=640, video=None):
     # video access (for debugging and partly hiding open cv details)
     if video is None:
         self.video = Video()
     self.idealSize = idealSize
     s = idealSize
     self.pts_IdealSquare = np.asarray(
         [[0.0, 0.0], [s, 0.0], [s, s], [0.0, s]], dtype=np.float32)
     self.inverseTransform = cv2.getPerspectiveTransform(
         self.pts_dst, self.pts_IdealSquare)
     self.rotation = 0
     # dict for average Colors
     self.averageColors = {}
     self.diffSumAverage = MovingAverage(
         ChessTrapezoid.DiffSumMovingAverageLength)
     # trapezoid representation of squares
     self.tsquares = {}
     for square in chess.SQUARES:
         tsquare = ChessTSquare(self, square)
         if ChessTrapezoid.debug:
             print(vars(tsquare))
         self.tsquares[tsquare.square] = tsquare
 def __init__(self,args,board=None):
     self.device=args.input
     self.title=Video.title(self.device)
     self.video=Video(self.title)
     self.args=args
     self.showDebug=args.debug
     self.start=None
     self.quitWanted=False
     self.hasImage=False
     self.timestamps=[]
     self.debug=args.debug
     if board is None:
         board=Board(args=args)
     self.board = board
     if self.args.fen is not None:
         self.board.updatePieces(self.args.fen)
     self.warp = Warp(args.warpPointList)
     self.warp.rotation = args.rotation
     if self.args.nowarp:
         self.warp.warping=True
     self.firstFrame=True    
     self.speedup=args.speedup
     pass
示例#16
0
 def home(self):
     self.videoAnalyzer.vision.video = Video()
     return self.index("Home")
class ChessTrapezoid(Trapez2Square):
    """ Chess board Trapezoid (UK) / Trapezium (US) / Trapez (DE)  as seen via a webcam image """

    debug = False
    colorDebug = False
    showDebugImage = False
    rows = 8
    cols = 8
    # default radius of pieces
    PieceRadiusFactor = 3
    DiffSumMovingAverageLength = 5

    def __init__(self, trapezPoints, idealSize=640, rotation=0, video=None):
        self.rotation = rotation
        #trapezPoints=[topLeft,topRight,bottomRight,bottomLeft]
        shifts = self.rotation // 90
        for shift in range(shifts):
            left = trapezPoints.pop(0)
            trapezPoints.append(left)
        topLeft, topRight, bottomRight, bottomLeft = trapezPoints
        super().__init__(topLeft, topRight, bottomRight, bottomLeft)
        self.setup(idealSize, video)

    def setup(self, idealSize=640, video=None):
        # video access (for debugging and partly hiding open cv details)
        if video is None:
            self.video = Video()
        self.idealSize = idealSize
        s = idealSize
        self.pts_IdealSquare = np.asarray(
            [[0.0, 0.0], [s, 0.0], [s, s], [0.0, s]], dtype=np.float32)
        self.inverseTransform = cv2.getPerspectiveTransform(
            self.pts_dst, self.pts_IdealSquare)
        self.rotation = 0
        # dict for average Colors
        self.averageColors = {}
        self.diffSumAverage = MovingAverage(
            ChessTrapezoid.DiffSumMovingAverageLength)
        # trapezoid representation of squares
        self.tsquares = {}
        for square in chess.SQUARES:
            tsquare = ChessTSquare(self, square)
            if ChessTrapezoid.debug:
                print(vars(tsquare))
            self.tsquares[tsquare.square] = tsquare

    def relativeToIdealXY(self, rx, ry):
        x = int(rx * self.idealSize)
        y = int(ry * self.idealSize)
        return x, y

    def tSquareAt(self, row, col, rotation=0):
        """ get the trapezoid chessboard square for the given row and column"""
        row, col = self.rotateIndices(row, col, rotation)
        squareIndex = (ChessTrapezoid.rows - 1 -
                       row) * ChessTrapezoid.cols + col
        square = chess.SQUARES[squareIndex]
        return self.tsquares[square]

    def rotateIndices(self, row, col, rotation):
        """ rotate the indices or rows and columns according to the board rotation"""
        if rotation == 0:
            return row, col
        elif rotation == 90:
            return ChessTrapezoid.cols - 1 - col, row
        elif rotation == 180:
            return ChessTrapezoid.rows - 1 - row, ChessTrapezoid.cols - 1 - col
        elif rotation == 270:
            return col, ChessTrapezoid.rows - 1 - row
        else:
            raise Exception("invalid rotation %d for rotateIndices" % rotation)

    def genSquares(self):
        """ generator for all chess squares """
        for square in chess.SQUARES:
            tsquare = self.tsquares[square]
            yield tsquare

    def drawCircle(self, image, center, radius, color, thickness=-1):
        """ draw a circle onto the given image at the given center point with the given radius, color and thickness. """
        if color is not None:
            cv2.circle(image, center, radius, color=color, thickness=thickness)

    def drawRCircle(self, image, rcenter, rradius, color, thickness=-1):
        """ draw a circle with relative coordinates"""
        radius = int(rradius * self.idealSize)
        rx, ry = rcenter
        center = self.relativeToIdealXY(rx, ry)
        self.drawCircle(image, center, radius, color, thickness)

    def drawRCenteredText(self, image, text, rx, ry, color=(255, 255, 255)):
        x, y = self.relativeToIdealXY(rx, ry)
        self.video.drawCenteredText(image, text, x, y, fontBGRColor=color)

    def updatePieces(self, fen):
        """ update the piece positions according to the given FEN"""
        self.board = chess.Board(fen)
        for tsquare in self.genSquares():
            piece = self.board.piece_at(tsquare.square)
            tsquare.piece = piece
            tsquare.fieldState = tsquare.getFieldState()

    def drawFieldStates(self,
                        image,
                        fieldStates,
                        transformation=Transformation.ORIGINAL,
                        channels=3):
        """ draw the states for fields with the given field states e.g. to set the mask image that will filter the trapezoid view according to piece positions when using maskImage"""
        if self.board is not None:
            for tsquare in self.genSquares():
                if tsquare.fieldState in fieldStates:
                    tsquare.drawState(image, transformation, channels)

    def prepareImageSet(self, cbImageSet):
        """ prepare the image set"""
        cbWarped = self.warpedBoardImage(cbImageSet.cbImage.image)
        averageColors = self.analyzeColors(cbWarped)
        cbImageSet.cbWarped = cbWarped
        cbIdeal = self.idealColoredBoard(cbWarped.width, cbWarped.height)
        cbImageSet.cbIdeal = cbIdeal
        cbImageSet.cbPreMove = self.preMoveBoard(cbWarped.width,
                                                 cbWarped.height)
        cbImageSet.cbDiff = cbWarped.diffBoardImage(cbIdeal)
        return averageColors

    def warpedBoardImage(self, image):
        warped = cv2.warpPerspective(image, self.inverseTransform,
                                     (self.idealSize, self.idealSize))
        return ChessBoardImage(warped, "warped")

    def diffSum(self, image, other):
        #diffImage=self.diff(other)
        #return diffImage.sum()
        # https://stackoverflow.com/questions/17829092/opencv-cv2-absdiffimg1-img2-sum-without-temporary-img
        diffSumValue = cv2.norm(image, other, cv2.NORM_L1)
        if ChessTrapezoid.debug:
            print("diffSum %.0f" % (diffSumValue))
        return diffSumValue

    def idealColoredBoard(self, w, h, transformation=Transformation.IDEAL):
        """ draw an 'ideal' colored board according to a given set of parameters e.g. fieldColor, pieceColor, pieceRadius"""
        idealImage = self.video.getEmptyImage4WidthAndHeight(w, h, 3)
        for tsquare in self.genSquares():
            tsquare.drawState(idealImage, transformation, 3)
        return ChessBoardImage(idealImage, "ideal")

    def preMoveBoard(self, w, h):
        """ get an image of the board as it was before any move """
        refImage = self.video.getEmptyImage4WidthAndHeight(w, h, 3)
        for tsquare in self.genSquares():
            tsquare.addPreMoveImage(refImage)
        return ChessBoardImage(refImage, "preMove ref")

    def drawDebug(self, image, color=(255, 255, 255)):
        """ draw debug information e.g. piecel symbol and an onto the given image"""
        for square in chess.SQUARES:
            tsquare = self.tsquares[square]
            tsquare.drawDebug(image, color)

    def byFieldState(self):
        # get a dict of fields sorted by field state
        sortedTSquares = {}
        for fieldState in FieldState:
            sortedTSquares[fieldState] = []
        for tsquare in self.genSquares():
            sortedTSquares[tsquare.fieldState].append(tsquare)
        return sortedTSquares

    def analyzeColors(self, cbImage):
        """ get the average colors per fieldState """
        byFieldState = self.byFieldState()
        for fieldState in byFieldState.keys():
            mask = self.video.getEmptyImage(cbImage.image)
            self.drawFieldStates(mask, [fieldState], Transformation.IDEAL, 1)
            masked = self.video.maskImage(cbImage.image, mask)
            countedFields = len(byFieldState[fieldState])
            averageColor = Color(masked)
            self.averageColors[fieldState] = averageColor
            if ChessTrapezoid.showDebugImage:
                self.video.showImage(masked, fieldState.title())
            if ChessTrapezoid.colorDebug:
                print("%15s (%2d): %s" %
                      (fieldState.title(), countedFields, averageColor))
        return self.averageColors

    def optimizeColorCheck(self, cbImage, averageColors, debug=False):
        optimalSelectivity = -100
        colorStats = None
        for factor in [x * 0.05 for x in range(20, 41)]:
            """ optimize the factor for the color check"""
            startc = timer()
            fieldColorStatsCandidate = self.checkColors(
                cbImage, averageColors, factor)
            endc = timer()
            fieldColorStatsCandidate.analyzeStats(factor, endc - startc)
            if fieldColorStatsCandidate.minSelectivity > optimalSelectivity:
                optimalSelectivity = fieldColorStatsCandidate.minSelectivity
                colorStats = fieldColorStatsCandidate
                if debug:
                    print("selectivity %5.1f white: %5.1f black: %5.1f " %
                          (self.minSelectivity, self.whiteSelectivity,
                           self.blackSelectivity))
        return colorStats

    def checkColors(self, cbImage, averageColors, rangeFactor=1.0):
        """ check the colors against the expectation """
        byFieldState = self.byFieldState()
        colorStats = FieldColorStats()
        for fieldState in byFieldState.keys():
            # https://stackoverflow.com/questions/54019108/how-to-count-the-pixels-of-a-certain-color-with-opencv
            if fieldState in [
                    FieldState.WHITE_BLACK, FieldState.WHITE_EMPTY,
                    FieldState.WHITE_WHITE
            ]:
                averageColor = averageColors[FieldState.WHITE_EMPTY]
            else:
                averageColor = averageColors[FieldState.BLACK_EMPTY]
            fields = byFieldState[fieldState]
            lower, upper = averageColor.colorRange(rangeFactor)
            if ChessTrapezoid.colorDebug:
                print("%25s (%2d): %s -> %s - %s" %
                      (fieldState.title(), len(fields), averageColor, lower,
                       upper))
            for tsquare in fields:
                squareImage = tsquare.getSquareImage(cbImage)
                asExpected = cv2.inRange(squareImage, lower, upper)
                h, w = squareImage.shape[:2]
                pixels = h * w
                nonzero = cv2.countNonZero(asExpected)
                colorStats.push(fieldState, tsquare.an, nonzero / pixels * 100)
                #self.video.showImage(asExpected,tsquare.an)
        return colorStats

    def detectChanges(self, cbImageSet, detectState):
        """ detect the changes of the given imageset using the given detect state machine"""
        detectState.nextFrame()
        changes = {}
        validChanges = 0
        diffSum = 0
        cbImage = cbImageSet.cbImage
        cbDiff = cbImageSet.cbDiff
        for tsquare in self.genSquares():
            squareChange = tsquare.squareChange(cbImage.image, cbDiff.image)
            changes[tsquare.an] = squareChange
            diffSum += abs(squareChange.diff)
            if squareChange.valid:
                validChanges += 1
            #if self.frames==1:
            #    tsquare.preMoveImage=np.copy(tsquare.squareImage)

        self.diffSumAverage.push(diffSum)
        diffSumDelta = self.diffSumAverage.mean() - diffSum
        detectState.check(validChanges, diffSum, diffSumDelta,
                          squareChange.meanFrameCount)
        for tsquare in self.genSquares():
            squareChange = changes[tsquare.an]
            tsquare.checkMoved(detectState)

        changes["validBoard"] = detectState.validBoard
        changes["valid"] = validChanges
        changes["diffSum"] = diffSum
        changes["diffSumDelta"] = diffSumDelta
        changes["validFrames"] = detectState.validFrames
        changes["invalidFrames"] = detectState.invalidFrames
        return changes
示例#18
0
 def test_ReadVideoWithPostProcess(self):
     video = Video()
     video.open(testenv.testMedia+'emptyBoard001.avi')
     for frame in range(0, 52):
         ret, jpgImage, quit = video.readFrame(show=True, postProcess=video.addTimeStamp)
示例#19
0
 def showDebug(self, video=None, keyWait=5):
     if video is None:
         video = Video()
     video.showImage(self.image, self.title, keyWait=keyWait)
示例#20
0
class ChessBoardVision(JsonAbleMixin):
    """ implements access to chessboard images"""
    def __init__(self, args, board=None):
        self.device = args.input
        self.title = Video.title(self.device)
        self.video = Video(self.title)
        self.video.headless = Environment.inContinuousIntegration()
        self.args = args
        self.showDebug = args.debug
        self.start = None
        self.quitWanted = False
        self.hasImage = False
        self.timestamps = []
        self.debug = args.debug
        if board is None:
            board = Board(args=args)
        self.board = board
        if self.args.fen is not None:
            self.board.updatePieces(self.args.fen)
        self.warp = Warp(args.warpPointList)
        self.warp.rotation = args.rotation
        if self.args.nowarp:
            self.warp.warping = True
        self.firstFrame = True
        self.speedup = args.speedup
        pass

    def open(self, device):
        self.video.capture(device)
        self.device = device
        self.firstFrame = True

    def readChessBoardImage(self):
        for i in range(self.speedup):
            self.hasImage, image, self.quitWanted = self.video.readFrame(
                self.showDebug)
            if self.quitWanted:
                return self.previous
        frames = self.video.frames
        if self.firstFrame:
            self.start = timer()
        timestamp = timer() - self.start
        self.chessBoardImageSet = ChessBoardImageSet(self, image,
                                                     frames // self.speedup,
                                                     timestamp)
        self.firstFrame = False
        self.timestamps.append(timestamp)
        return self.chessBoardImageSet

    def close(self):
        self.video.close()

    def __getstate__(self):
        state = {}
        state["title"] = self.title
        device = self.device
        if not Video.is_int(device):
            cwd = os.getcwd()
            devicepath = os.path.dirname(device)
            root = os.path.commonpath([cwd, devicepath])
            device = os.path.relpath(devicepath,
                                     root) + '/' + os.path.basename(device)
        state["device"] = device
        state["timestamps"] = self.timestamps
        return state

    def __setstate__(self, state):
        self.title = state["title"]
        self.device = state["device"]
        self.timestamps = state["timestamps"]

    def save(self, path="games/videos"):
        env = Environment()
        savepath = str(env.projectPath) + "/" + path
        Environment.checkDir(savepath)
        jsonFile = savepath + "/" + self.title
        self.writeJson(jsonFile)
示例#21
0
 def test_ReadVideo(self):
     video = Video()
     video.open(testenv.testMedia+'emptyBoard001.avi')
     video.play()
     print ("played %d frames" % (video.frames))
     assert video.frames == 52
示例#22
0
 def test_device(self):
     assert Video.is_int("0")
     v0 = int("0")
     assert v0 == 0