title: OpenCV concave and convex corner points of polygons
body: <h2>Problem</h2>
  
  <p>I am working on a project where I need to get the bounding boxes of dumbell like shapes. However, I need the fewest points possible, and the boxes need to fit the shapes at all corners. Here's an Image I made to test: <a href="http://i.imgur.com/I455ocE.png" rel="nofollow">Blurry, cracked, dumbell shape</a></p>
  
  <p>I don't care about the gaps going into the shape, I just want to clean it up, and straighten the edges so that I can get the contours of a shape like this: <a href="http://i.imgur.com/EEvDbie.png" rel="nofollow">Cleaned up</a></p>
  
  <p>I have been attempting to <code>threshold()</code> it out, getting the contours of it using <code>findContours()</code> and then using <code>approxPolyDP()</code> to simplify the crazy amount of points the contours end up being. So, after fiddling with this for about three days now, how can I simply get either:</p>
  
  <ul>
  <li>Two boxes specifying the ends of the dumbell and a rectangle in the middle, or</li>
  <li>One contour with the twelve points for all the corners</li>
  </ul>
  
  <p>The second option would be preferred since that really is my ultimate goal: getting the points that are at those corners.</p>
  
  <h2>A few things to note:</h2>
  
  <ul>
  <li>I am using OpenCV for Python</li>
  <li>There will generally be many of these shapes of all sizes all over the input image</li>
  <li>They will have only horizontal or vertical positioning. No strange 27 degree angles...</li>
  </ul>
  
  <h2>What I need:</h2>
  
  <p>I really don't need someone to write the code for me, I just need some method or algorithm in order to get this done, preferably with some simple examples.</p>
  
  <h2>My Code</h2>
  
  <p>Here is my overly clean code with functions I don't even use but figure I would use them eventually:</p>
  
  <pre><code>import cv2
  import numpy as np
  
  class traceImage():
  
      def __init__(self, imageLocation):
          self.threshNum = 127
          self.im = cv2.imread(imageLocation)
          self.imOrig = self.im
          self.imGray = cv2.cvtColor(self.im, cv2.COLOR_BGR2GRAY)
          self.ret, self.imThresh = cv2.threshold(self.imGray, self.threshNum, 255, 0)
          self.contours, self.hierarchy = cv2.findContours(self.imThresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
  
      def createGray(self):
          self.imGray = cv2.cvtColor(self.im, cv2.COLOR_BGR2GRAY)
  
      def adjustThresh(self, threshNum):
          self.ret, self.imThresh = cv2.threshold(self.imGray, threshNum, 255, 0)
  
      def getContours(self):
          self.contours, self.hierarchy = cv2.findContours(self.imThresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
  
      def approximatePoly(self, percent):
          i=0
          for shape in self.contours:
              shape = cv2.approxPolyDP(shape, percent*cv2.arcLength(shape, True), True)
              self.contours[i] = shape
              i+=1
  
      def drawContours(self, blobWidth, color=(255,255,255)):
          cv2.drawContours(self.im, self.contours, -1, color, blobWidth)
  
      def newWindow(self, name):
          cv2.namedWindow(name)
  
      def showImage(self, window):
          cv2.imshow(window, self.im)
  
      def display(self):
          while True:
              cv2.waitKey()
  
      def displayUntil(self, key):
          while True:
              pressed = cv2.waitKey()
              if pressed == key:
                  break
  
  if __name__ == "__main__":
      blobWidth = 30
      ti = traceImage("dumbell.png")
      ti.approximatePoly(0.01)
      for thresh in range(127,256):
          ti.adjustThresh(thresh)
          ti.getContours()
          ti.drawContours(blobWidth)
      ti.showImage("Image")
      ti.displayUntil(10)
      ti.createGray()
      ti.adjustThresh(127)
      ti.getContours()
      ti.approximatePoly(0.0099)
      ti.drawContours(2, (0,255,0))
      ti.showImage("Image")
      ti.display()
  </code></pre>
  
  <h2>Code Explanation</h2>
  
  <p>I know I might not be doing some things right here, but hey, I'm proud of it :)</p>
  
  <p>So, the idea is that there are very often holes and gaps in these dumbells and so I figured that if I iterated through all the threshold values from 127 to 255 and drew the contours onto the image with large enough thickness, the thickness of drawing the contours would fill in any small enough holes, and I could use the new, blobby image to get the edges and then scale the sides back down to size. That was my thinking. There's got to be another, beter way though... </p>
  
  <h2>Summary</h2>
  
  <p>I want to end up with 12 points; one for each corner of the shape. </p>
  
  <h2>EDIT:</h2>
  
  <p>After trying out some erosion and dilation, it seems that the best option would be to slice the contours at certain points and then use bounding boxes around the sliced shapes to get the right boxy corners, and then doing some calculations to rejoin the boxes into one shape. A rather interesting challenge...</p>
  
  <h2>EDIT 2:</h2>
  
  <p>I discovered something that works well! I made my own line detection system, that only detects horizontal or vertical lines, and then on a detected line/contour edge, the program draws a black line that extends across the whole image, thus effectively slicing the image at the straight lines of the contours. Once it does that, it gets new contours of the sliced up boxes, draws bounding boxes around the pieces and then uses dilation to close the gaps. So far, it works well on large shapes, but when the shapes are small, it tends to lose a bit of the shape.</p>