Chapter 8. Contours

Although algorithms like the Canny edge detector can be used to find the edge pixels that separate different segments in an image, they do not tell you anything about those edges as entities in themselves. The next step is to be able to assemble those edge pixels into contours. By now you have probably come to expect that there is a convenient function in OpenCV that will do exactly this for you, and indeed there is: cvFindContours(). We will start out this chapter with some basics that we will need in order to use this function. Specifically, we will introduce memory storages, which are how OpenCV functions gain access to memory when they need to construct new objects dynamically; then we will learn some basics about sequences, which are the objects used to represent contours generally. With those concepts in hand, we will get into contour finding in some detail. Thereafter we will move on to the many things we can do with contours after they've been computed.

OpenCV uses an entity called a memory storage as its method of handling memory allocation for dynamic objects. Memory storages are linked lists of memory blocks that allow for fast allocation and de-allocation of continuous sets of blocks. OpenCV functions that require the ability to allocate memory as part of their normal functionality will require access to a memory storage from which to get the memory they require (typically this includes any function whose output is of variable size).

Memory storages are handled with the following four routines:

CvMemStorage* cvCreateMemStorage(
  int            block_size = 0
);
void cvReleaseMemStorage(
  CvMemStorage** storage
);
void cvClearMemStorage(
  CvMemStorage*  storage
);
void* cvMemStorageAlloc(
  CvMemStorage*  storage,
  size_t         size
);

To create a memory storage, the function cvCreateMemStorage() is used. This function takes as an argument a block size, which gives the size of memory blocks inside the store. If this argument is set to 0 then the default block size (64kB) will be used. The function returns a pointer to a new memory store.

The cvReleaseMemStorage() function takes a pointer to a valid memory storage and then de-allocates the storage. This is essentially equivalent to the OpenCV de-allocations of images, matrices, and other structures.

You can empty a memory storage by calling cvClearMemStorage(), which also takes a pointer to a valid storage. You must be aware of an important feature of this function: other than cvReleaseMemStorage(), it is the only way to reuse the memory allocated to a memory storage. This might not seem like much, but there will be other routines that delete objects inside of memory storages (we will introduce one of these momentarily) but do not return the memory they were using. In short, only cvClearMemStorage() (and, of course, cvReleaseMemStorage()) recycle the storage memory.[102] Deletion of any dynamic structure (CvSeq, CvSet, etc.) never returns any memory back to storage (although the structures are able to reuse some memory once taken from the storage for their own data).

You can also allocate your own continuous blocks from a memory storage—in a manner analogous to the way malloc() allocates memory from the heap—with the function cvMemStorageAlloc(). In this case you simply provide a pointer to the storage and the number of bytes you need. The return is a pointer of type void* (again, similar to malloc()).



[102] Actually, one other function, called cvRestoreMemStoragePos(), can restore memory to the storage. But this function is primarily for the library's internal use and is beyond the scope of this book.