Demonstration of OpenCV's cv2 API for manipulating images, creating contours, simplifying those contours and creating various bounding structures. I started with Abid Rahman's Contours 1 and 2 blog posts. Those posts do not directly give you all that you need to run them. By virtue of being an IPython Notebook, these are the actual command that worked for me. You will need wget, numpy, matplotlib, OpenCV and shapely. -kurt schwehr 2013-Jan-31

In [1]:
# BEGIN http://www.opencvpython.blogspot.com/2012/06/hi-this-article-is-tutorial-which-try.html

In [2]:
!wget http://3.bp.blogspot.com/-a5blM3JLkIU/T9OXg1YhN0I/AAAAAAAAASQ/MbdfSG2oaYg/s200/test.jpg


--2013-07-08 16:08:23--  http://3.bp.blogspot.com/-a5blM3JLkIU/T9OXg1YhN0I/AAAAAAAAASQ/MbdfSG2oaYg/s200/test.jpg
Resolving 3.bp.blogspot.com (3.bp.blogspot.com)... 74.125.228.43, 74.125.228.42, 74.125.228.44, ...
Connecting to 3.bp.blogspot.com (3.bp.blogspot.com)|74.125.228.43|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2604 (2.5K) [image/jpeg]
Saving to: `test.jpg'

100%[======================================>] 2,604       --.-K/s   in 0s      

2013-07-08 16:08:23 (44.0 MB/s) - `test.jpg' saved [2604/2604]


In [4]:
import numpy as np
import cv2
import cv
import shapely.geometry


---------------------------------------------------------------------------
OSError                                   Traceback (most recent call last)
<ipython-input-4-74e45dce3cce> in <module>()
      2 import cv2
      3 import cv
----> 4 import shapely.geometry

/usr/local/lib/python2.7/dist-packages/shapely/geometry/__init__.py in <module>()
      2 """
      3 
----> 4 from geo import box, shape, asShape, mapping
      5 from point import Point, asPoint
      6 from linestring import LineString, asLineString

/usr/local/lib/python2.7/dist-packages/shapely/geometry/geo.py in <module>()
      3 """
      4 
----> 5 from point import Point, asPoint
      6 from linestring import LineString, asLineString
      7 from polygon import Polygon, asPolygon

/usr/local/lib/python2.7/dist-packages/shapely/geometry/point.py in <module>()
      5 from ctypes import cast, POINTER
      6 
----> 7 from shapely.coords import required
      8 from shapely.geos import lgeos, DimensionError
      9 from shapely.geometry.base import BaseGeometry

/usr/local/lib/python2.7/dist-packages/shapely/coords.py in <module>()
      6 import sys
      7 
----> 8 from shapely.geos import lgeos
      9 from shapely.topology import Validating
     10 

/usr/local/lib/python2.7/dist-packages/shapely/geos.py in <module>()
     45 
     46 if sys.platform.startswith('linux'):
---> 47     _lgeos = load_dll('geos_c', fallbacks=['libgeos_c.so.1', 'libgeos_c.so'])
     48     free = load_dll('c').free
     49     free.argtypes = [c_void_p]

/usr/local/lib/python2.7/dist-packages/shapely/geos.py in load_dll(libname, fallbacks)
     42         raise OSError(
     43             "Could not find library %s or load any of its variants %s" % (
---> 44                 libname, fallbacks or []))
     45 
     46 if sys.platform.startswith('linux'):

OSError: Could not find library geos_c or load any of its variants ['libgeos_c.so.1', 'libgeos_c.so']

In [28]:
im = cv2.imread('test.jpg')

In [29]:
whos


Variable    Type       Data/Info
--------------------------------
area        float      10033.5
cnt         ndarray    231x1x2: 462 elems, type `int32`, 1848 bytes
contours    list       n=4
cv          module     <module 'cv' from '/usr/l<...>odules/python2.7/cv.pyc'>
cv2         module     <module 'cv2' from '/usr/<...>odules/python2.7/cv2.so'>
h           int        3
hierarchy   ndarray    1x4x4: 16 elems, type `int32`, 64 bytes
im          ndarray    200x200x3: 120000 elems, type `uint8`, 120000 bytes (117 kb)
imgray      ndarray    300x300: 90000 elems, type `uint8`, 90000 bytes
mask        ndarray    300x300: 90000 elems, type `uint8`, 90000 bytes
moments     dict       n=23
perimeter   float      473.345233798
ret         float      100.0
simpler     ndarray    35x1x2: 70 elems, type `int32`, 280 bytes
thresh      ndarray    300x300: 90000 elems, type `uint8`, 90000 bytes
x           ndarray    231: 231 elems, type `int32`, 924 bytes
y           ndarray    231: 231 elems, type `int32`, 924 bytes

In [30]:
imgray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
imshow(imgray)


Out[30]:
<matplotlib.image.AxesImage at 0x415d0d0>

In [8]:
ret, thresh = cv2.threshold(imgray, 127, 255, 0)
imshow(thresh)


Out[8]:
<matplotlib.image.AxesImage at 0x2e69490>

In [9]:
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
imshow(thresh)


Out[9]:
<matplotlib.image.AxesImage at 0x2de0690>

In [10]:
len(contours), hierarchy


Out[10]:
(1, array([[[-1, -1, -1, -1]]], dtype=int32))

In [11]:
len(contours[0])


Out[11]:
170

In [12]:
cv2.drawContours(im, contours, -1, (0,255,0), 3)
imshow(im)


Out[12]:
<matplotlib.image.AxesImage at 0x2df5050>

In [13]:
cv2.drawContours(im, contours, -1, (0,255,0), -1)
imshow(im)


Out[13]:
<matplotlib.image.AxesImage at 0x347e9d0>

In [14]:
cv2.drawContours(im, contours, 0, (0,255,0), 1)
imshow(im)


Out[14]:
<matplotlib.image.AxesImage at 0x3595c50>

In [15]:
!wget http://3.bp.blogspot.com/-1UtLXb7c73U/T9QZT3tpVjI/AAAAAAAAATE/Nyo7SFg8T1o/s1600/balls.png


--2013-07-08 16:10:02--  http://3.bp.blogspot.com/-1UtLXb7c73U/T9QZT3tpVjI/AAAAAAAAATE/Nyo7SFg8T1o/s1600/balls.png
Resolving 3.bp.blogspot.com (3.bp.blogspot.com)... 173.194.43.42, 173.194.43.44, 173.194.43.43, ...
Connecting to 3.bp.blogspot.com (3.bp.blogspot.com)|173.194.43.42|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3499 (3.4K) [image/png]
Saving to: `balls.png'

100%[======================================>] 3,499       --.-K/s   in 0s      

2013-07-08 16:10:02 (37.9 MB/s) - `balls.png' saved [3499/3499]


In [16]:
im = cv2.imread('balls.png')
imgray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)

In [17]:
ret, thresh = cv2.threshold(imgray, 100, 255, 0)
imshow(thresh)


Out[17]:
<matplotlib.image.AxesImage at 0x35bae10>

In [18]:
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

In [19]:
for h, cnt in enumerate(contours):
    mask = np.zeros(imgray.shape, np.uint8)
    cv2.drawContours(mask, [cnt], 0, 255, -1)
    mean = cv2.mean(im, mask=mask)
    print h, mean


0 (0.0, 242.0, 255.0, 0.0)
1 (232.0, 162.0, 0.0, 0.0)
2 (76.0, 177.0, 34.0, 0.0)
3 (39.0, 127.0, 255.0, 0.0)

In [20]:
# END http://www.opencvpython.blogspot.com/2012/06/hi-this-article-is-tutorial-which-try.html

BEGIN Contours - 2


In [21]:
moments = cv2.moments(contours[0])
area = moments['m00']
moments


Out[21]:
{'m00': 10033.5,
 'm01': 1850134.5,
 'm02': 352044973.5833333,
 'm03': 68983799276.15001,
 'm10': 2010061.8333333333,
 'm11': 368666693.125,
 'm12': 69763579350.98334,
 'm20': 409360323.5833333,
 'm21': 74691021944.88333,
 'm30': 84692116672.95001,
 'mu02': 10888082.359906793,
 'mu03': 0.005234025965704581,
 'mu11': -1980114.5675549507,
 'mu12': -33122544.260385513,
 'mu20': 6674463.831166983,
 'mu21': 101313.30416250229,
 'mu30': 8633090.369003296,
 'nu02': 0.10815497152071127,
 'nu11': -0.019669141689288665,
 'nu12': -0.0032846761082870463,
 'nu20': 0.06629968636479547,
 'nu21': 1.0046975468372928e-05,
 'nu30': 0.0008561209988226333}

In [22]:
cv2.contourArea(contours[0])


Out[22]:
10033.5

In [23]:
perimeter = cv2.arcLength(contours[0], True) # True says that curve is closed
perimeter


Out[23]:
473.34523379802704

In [24]:
cnt = contours[0]
len(cnt)


Out[24]:
231

In [25]:
x = cnt[:,0, 0]; y = cnt[:,0,1]
plot(x,y)


Out[25]:
[<matplotlib.lines.Line2D at 0x3b41dd0>]

In [26]:
simpler = cv2.approxPolyDP(cnt, 2, True)
plot(simpler[:,0,0], simpler[:,0,1])


Out[26]:
[<matplotlib.lines.Line2D at 0x3dc68d0>]

In [63]:
hull = cv2.convexHull(cnt)
plot(hull[:,0,0], hull[:,0,1])


Out[63]:
[<matplotlib.lines.Line2D at 0x6381fd0>]

In [69]:
simpler_hull = cv2.approxPolyDP(hull, 2.5, True)
plot(simpler_hull[:,0,0], simpler_hull[:,0,1])
plot(x,y)


Out[69]:
[<matplotlib.lines.Line2D at 0x6957e10>]

In [73]:
r_x, r_y, r_w, r_h = cv2.boundingRect(cnt)
r_x, r_y, r_w, r_h  # (150, 121, 103, 146)
plot((r_x, r_x, r_x+r_w, r_x+r_w, r_x), (r_y, r_y+r_h, r_y+r_h, r_y, r_y))
plot(x,y)


Out[73]:
[<matplotlib.lines.Line2D at 0x6eab610>]

In [88]:
rect = cv2.minAreaRect(cnt)
rect # ((202.134521484375, 192.14178466796875), (102.39618682861328, 140.3079376220703), -5.128190994262695)
box = cv2.cv.BoxPoints(rect)
box # ((157.41201782226562, 266.59124755859375), (144.87069702148438, 126.84494018554688), (246.85702514648438, 117.69232177734375), (259.3983459472656, 257.4386291503906))
# plot( [p[0] for p in box] + [box[0][0]], [p[1] for p in box] + [box[0][1]] )
box_list = list(box)
box_list.append(box[0])
ba = np.array(box_list) # Box array
plot(ba[:,0], ba[:,1])
plot(x,y)


Out[88]:
[<matplotlib.lines.Line2D at 0x7746a10>]

In [97]:
(c_x, c_y), radius = cv2.minEnclosingCircle(cnt)
c_x, c_y, radius # (197.0, 194.5, 82.92139434814453)
center = shapely.geometry.Point(c_x, c_y)
circle = np.array(center.buffer(radius).boundary.coords)
len(circle) # 66 points
plot(circle[:,0], circle[:,1])
plot(x,y)


Out[97]:
[<matplotlib.lines.Line2D at 0x7ba81d0>]

In [100]:
ellipse = cv2.fitEllipse(cnt)
ellipse # ((199.31251525878906, 185.9192352294922), (93.7149658203125, 138.58531188964844), 202.948486328125)
# TODO: what is a convenient way to get the coords for an ellipse?


Out[100]:
((199.31251525878906, 185.9192352294922),
 (93.7149658203125, 138.58531188964844),
 202.948486328125)

In [ ]: