Source code for ocvu.contour

import logging

import numpy as np
import cv2


logger = logging.getLogger(__name__)


[docs]class Contour(object): """Provides a user-friendly object defining a contour in OpenCV. Adapted from: http://answers.opencv.org/question/6043/segmentation-and-contours/ """ def __init__(self, contour): self.contour = contour self.size = len(contour) @property def position(self): """Top left coordinate of the contour bounding box.""" x, y, _, _ = self.bounding_box return (x, y) @property def area(self): """Contour.area - Area bounded by the contour region""" return cv2.contourArea(self.contour) @property def perimeter(self): """Lorem""" return cv2.arcLength(self.contour, closed=True) @property def approx(self): """Lorem""" return cv2.approxPolyDP(self.contour, 0.02 * self.perimeter, True) @property def hull(self): """Lorem""" return cv2.convexHull(self.contour) @property def moments(self): """Lorem""" return cv2.moments(self.contour) @property def bounding_box(self): """Lorem""" return cv2.boundingRect(self.contour) @property def centroid(self): if self.moments['m00'] != 0.0: cx = self.moments['m10'] / self.moments['m00'] cy = self.moments['m01'] / self.moments['m00'] return (cx, cy) else: raise ValueError("Region has zero area") @property def ellipse(self): """Fits an ellipse and returns the rotated rectangle in which the ellipse is inscribed. The returned value is the tuple: ``((x, y), (major_axis, minor_axis), angle)`` """ return cv2.fitEllipse(self.contour) @property def diameter(self): """EquivDiameter: diameter of circle with same area as region""" return np.sqrt(4 * self.moments['m00'] / np.pi)
[docs] def draw(self, image, **kwargs): """Draw contour. ``kwargs`` are passed to ``cv2.rectangle`` method. Default color is green. """ color = kwargs.pop('color', (0, 255, 0)) cv2.drawContours(image, [self.contour], 0, color, **kwargs)
[docs] def draw_approx(self, image, **kwargs): """Draw approximated contour. `kwargs` are passed to `cv2.rectangle` method. Default color is red. """ color = kwargs.pop('color', (0, 0, 255)) cv2.drawContours(image, [self.approx], 0, color, **kwargs)
[docs] def draw_hull(self, image, **kwargs): """Draw convex hull. `kwargs` are passed to `cv2.rectangle` method. Default color is blue. """ color = kwargs.pop('color', (255, 0, 0)) cv2.drawContours(image, [self.hull], 0, color, **kwargs)
[docs] def draw_bounding_box(self, image, **kwargs): """Draw contour's bounding box on image. `kwargs` are passed to `cv2.rectangle` method. Default color is white. """ x, y, w, h = self.bounding_box pt1 = (x, y) pt2 = (x + w, y + h) color = kwargs.pop('color', (255, 255, 255)) cv2.rectangle(image, pt1, pt2, color, **kwargs)
[docs] def draw_ellipse(self, image, **kwargs): """Draw contour's ellipse. `kwargs` are passed to `cv2.circle` method. """ color = kwargs.pop('color', (0, 204, 204)) thickness = kwargs.pop('thickness', 2) cv2.ellipse(image, self.ellipse, color, thickness, **kwargs)
[docs] def draw_centroid(self, image, radius=3, **kwargs): """Draw contour's centroid. `kwargs` are passed to `cv2.circle` method. """ color = kwargs.pop('color', (0, 255, 0)) thickness = kwargs.pop('thickness', -1) center = tuple(map(int, self.centroid)) cv2.circle(image, center, radius, color, thickness, **kwargs)
[docs]def find_biggest_contours(mask, n=1, area_min=None, **kwargs): """Returns the `n` biggest contours in `mask`. Returns a list of contours sorted by area in descending order. If you provide a minimal area value, contours returned will be filtered like so. """ mode = kwargs.pop('mode', cv2.RETR_EXTERNAL) method = kwargs.pop('method', cv2.CHAIN_APPROX_SIMPLE) _, contours, _ = cv2.findContours(mask, mode, method, **kwargs) contours_found = list(map(Contour, contours)) if area_min is not None: contours = [cnt for cnt in contours_found if cnt.area > area_min] else: contours = list(contours_found) contours.sort(key=lambda cnt: cnt.area, reverse=True) contours = contours[:n] logger.debug("Retrieved {} contours from {} found".format( len(contours), len(contours_found))) return contours