In my open source project https://github.com/WolfgangFahl/play-chess-with-a-webcam i am using the class Warp (see https://github.com/WolfgangFahl/play-chess-with-a-webcam/blob/master/src/WebApp.py and the code excerpt below) to track the warp points for a chessboard. The ideas is that the user clicks one point of a trapezoid after another until the chessboard position in in a image is known. Then the image is "warped" accordingly. To get back to the non-warped image another click will reset the warp.
This logic is implemented by counting the number of points already added to the "pointlist". The default for the pointlist is set to empty but it can also be specified via the commandline.
this is the relevant constructor snippet:
def __init__(self,pointList=[],rotation=0,bgrColor=(0, 255, 0)):
self.pointList = pointList
self.updatePoints()
Now there is a pytest for testing this class see https://github.com/WolfgangFahl/play-chess-with-a-webcam/blob/master/src/test_Warp.py and the code below.
When running this code directly
python3 test_Warp.py
the code runs successful on different machines, python3.x versions and travis. But if i run the whole test suite of the project
scripts/test
The behavior depends on whether the getTestWarp function has:
def getTestWarp():
warp=Warp([])
which works. Or:
def getTestWarp():
warp=Warp()
which leads to the testcase to fail if other tests have run before. So the context makes a difference.
How can this be?
Is it a bug or an expected behavior and explainable by some python specific way of handling default parameters and instance attributes?
Warp class
class Warp(YamlAbleMixin,JsonAbleMixin):
""" holds the trapezoid points to be use for warping an image take from a peculiar angle """
# construct me from the given setting
def __init__(self,pointList=[],rotation=0,bgrColor=(0, 255, 0)):
self.rotation=rotation
self.bgrColor=bgrColor
self.pointList = pointList
self.updatePoints()
def rotate(self,angle):
self.rotation=self.rotation+angle
if self.rotation>=360:
self.rotation=self.rotation % 360
def updatePoints(self):
pointLen=len(self.pointList)
if pointLen==0:
self.points=None
else:
self.points=np.array(self.pointList)
self.warping=pointLen==4
def addPoint(self,px,py):
""" add a point with the given px,py coordinate
to the warp points make sure we have a maximum of 4 warpPoints if warppoints are complete when adding reset them
this allows to support click UIs that need an unwarped image before setting new warp points.
px,py is irrelevant for reset """
if len(self.pointList)>=4:
self.pointList=[]
else:
self.pointList.append([px,py])
self.updatePoints()
test_Warp.py
def getTestWarp():
warp=Warp([])
warp.addPoint(678,25)
warp.addPoint(1406,270)
warp.addPoint(1136,1048)
warp.addPoint(236,666)
return warp
def test_WarpPoints():
warp=getTestWarp()
print (warp.pointList)
print (warp.points)
assert warp.pointList==[[678, 25], [1406, 270], [1136, 1048], [236, 666]]
# simulate clear click
warp.addPoint(0,0)
warp.addPoint(679,25)
warp.addPoint(1408,270)
warp.addPoint(1136,1049)
warp.addPoint(236,667)
print (warp.pointList)
print (warp.points)
assert warp.pointList==[[679, 25], [1408, 270], [1136, 1049], [236, 667]]
test_WarpPoints()