/samples/python/squares.py

https://github.com/nielsgm/opencv · Python · 153 lines · 89 code · 16 blank · 48 comment · 18 complexity · 6e9890ec1a2d3a86712b82adce66ecf9 MD5 · raw file

  1. #!/usr/bin/python
  2. #
  3. # The full "Square Detector" program.
  4. # It loads several images subsequentally and tries to find squares in
  5. # each image
  6. #
  7. import urllib2
  8. from math import sqrt
  9. import cv2.cv as cv
  10. thresh = 50
  11. img = None
  12. img0 = None
  13. storage = None
  14. wndname = "Square Detection Demo"
  15. def angle(pt1, pt2, pt0):
  16. dx1 = pt1.x - pt0.x
  17. dy1 = pt1.y - pt0.y
  18. dx2 = pt2.x - pt0.x
  19. dy2 = pt2.y - pt0.y
  20. return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10)
  21. def findSquares4(img, storage):
  22. N = 11
  23. sz = (img.width & -2, img.height & -2)
  24. timg = cv.CloneImage(img); # make a copy of input image
  25. gray = cv.CreateImage(sz, 8, 1)
  26. pyr = cv.CreateImage((sz.width/2, sz.height/2), 8, 3)
  27. # create empty sequence that will contain points -
  28. # 4 points per square (the square's vertices)
  29. squares = cv.CreateSeq(0, sizeof_CvSeq, sizeof_CvPoint, storage)
  30. squares = CvSeq_CvPoint.cast(squares)
  31. # select the maximum ROI in the image
  32. # with the width and height divisible by 2
  33. subimage = cv.GetSubRect(timg, cv.Rect(0, 0, sz.width, sz.height))
  34. # down-scale and upscale the image to filter out the noise
  35. cv.PyrDown(subimage, pyr, 7)
  36. cv.PyrUp(pyr, subimage, 7)
  37. tgray = cv.CreateImage(sz, 8, 1)
  38. # find squares in every color plane of the image
  39. for c in range(3):
  40. # extract the c-th color plane
  41. channels = [None, None, None]
  42. channels[c] = tgray
  43. cv.Split(subimage, channels[0], channels[1], channels[2], None)
  44. for l in range(N):
  45. # hack: use Canny instead of zero threshold level.
  46. # Canny helps to catch squares with gradient shading
  47. if(l == 0):
  48. # apply Canny. Take the upper threshold from slider
  49. # and set the lower to 0 (which forces edges merging)
  50. cv.Canny(tgray, gray, 0, thresh, 5)
  51. # dilate canny output to remove potential
  52. # holes between edge segments
  53. cv.Dilate(gray, gray, None, 1)
  54. else:
  55. # apply threshold if l!=0:
  56. # tgray(x, y) = gray(x, y) < (l+1)*255/N ? 255 : 0
  57. cv.Threshold(tgray, gray, (l+1)*255/N, 255, cv.CV_THRESH_BINARY)
  58. # find contours and store them all as a list
  59. count, contours = cv.FindContours(gray, storage, sizeof_CvContour,
  60. cv.CV_RETR_LIST, cv. CV_CHAIN_APPROX_SIMPLE, (0, 0))
  61. if not contours:
  62. continue
  63. # test each contour
  64. for contour in contours.hrange():
  65. # approximate contour with accuracy proportional
  66. # to the contour perimeter
  67. result = cv.ApproxPoly(contour, sizeof_CvContour, storage,
  68. cv.CV_POLY_APPROX_DP, cv.ContourPerimeter(contours)*0.02, 0)
  69. # square contours should have 4 vertices after approximation
  70. # relatively large area (to filter out noisy contours)
  71. # and be convex.
  72. # Note: absolute value of an area is used because
  73. # area may be positive or negative - in accordance with the
  74. # contour orientation
  75. if(result.total == 4 and
  76. abs(cv.ContourArea(result)) > 1000 and
  77. cv.CheckContourConvexity(result)):
  78. s = 0
  79. for i in range(5):
  80. # find minimum angle between joint
  81. # edges (maximum of cosine)
  82. if(i >= 2):
  83. t = abs(angle(result[i], result[i-2], result[i-1]))
  84. if s<t:
  85. s=t
  86. # if cosines of all angles are small
  87. # (all angles are ~90 degree) then write quandrange
  88. # vertices to resultant sequence
  89. if(s < 0.3):
  90. for i in range(4):
  91. squares.append(result[i])
  92. return squares
  93. # the function draws all the squares in the image
  94. def drawSquares(img, squares):
  95. cpy = cv.CloneImage(img)
  96. # read 4 sequence elements at a time (all vertices of a square)
  97. i=0
  98. while i<squares.total:
  99. pt = []
  100. # read 4 vertices
  101. pt.append(squares[i])
  102. pt.append(squares[i+1])
  103. pt.append(squares[i+2])
  104. pt.append(squares[i+3])
  105. # draw the square as a closed polyline
  106. cv.PolyLine(cpy, [pt], 1, cv.CV_RGB(0, 255, 0), 3, cv. CV_AA, 0)
  107. i+=4
  108. # show the resultant image
  109. cv.ShowImage(wndname, cpy)
  110. def on_trackbar(a):
  111. if(img):
  112. drawSquares(img, findSquares4(img, storage))
  113. names = ["../c/pic1.png", "../c/pic2.png", "../c/pic3.png",
  114. "../c/pic4.png", "../c/pic5.png", "../c/pic6.png" ]
  115. if __name__ == "__main__":
  116. # create memory storage that will contain all the dynamic data
  117. storage = cv.CreateMemStorage(0)
  118. for name in names:
  119. img0 = cv.LoadImage(name, 1)
  120. if not img0:
  121. print "Couldn't load %s" % name
  122. continue
  123. img = cv.CloneImage(img0)
  124. # create window and a trackbar (slider) with parent "image" and set callback
  125. # (the slider regulates upper threshold, passed to Canny edge detector)
  126. cv.NamedWindow(wndname, 1)
  127. cv.CreateTrackbar("canny thresh", wndname, thresh, 1000, on_trackbar)
  128. # force the image processing
  129. on_trackbar(0)
  130. # wait for key.
  131. # Also the function cv.WaitKey takes care of event processing
  132. c = cv.WaitKey(0) % 0x100
  133. # clear memory storage - reset free space position
  134. cv.ClearMemStorage(storage)
  135. if(c == '\x1b'):
  136. break
  137. cv.DestroyWindow(wndname)