def rotated_square_filter(contour_list, min_area_ratio=0.8, min_ratio=0.95, max_ratio=1.05): """ Action: receives a list of contours and returns only the ones that are approximately square Relation checked is [minimum ratio < (Circle radius / width * height * (square root of 2)) < maximum ratio] :param contour_list: List of Contours to filter :param max_ratio: maximum ratio between radius and sides of bounding rectangle :type max_ratio: float :param min_ratio: minimum ratio between radius and sides of bounding rectangle :type min_ratio: float :param min_area_ratio: minimum ratio between the area of the contours and the bounding shape :return: the contour list filtered. """ ratio_list = [] output_list = [] if type(contour_list) is not list: contour_list = [contour_list] for currentContour in contour_list: fill_ratio, w, h = Geometry.get_fill_ratio_rotating(currentContour) _, enclosing_radius = cv2.minEnclosingCircle(currentContour) peri = cv2.arcLength(currentContour, True) approx = cv2.approxPolyDP(currentContour, 0.02 * peri, True) if fill_ratio > min_area_ratio and len(approx) == 4: p1 = root(2, 2) * root(w * h, 2) p2 = 2 * enclosing_radius radius_ratio = p2 / p1 if min_ratio < radius_ratio < max_ratio: output_list.append(currentContour) if radius_ratio > 1: radius_ratio = 1 / radius_ratio ratio_list.append(fill_ratio * radius_ratio) return output_list, ratio_list
def regular_polygon_filter(contour_list, side_amount=6, min_angle_ratio=0.9, min_area_ratio=0.85, min_len_ratio=0.9, approx_coef=0.02): """ Action: A filter that Detects regular polygon of n-sides sides :param contour_list: the list of contours to be filtered :param side_amount: the amount of sides the wanted polygon has. :param min_angle_ratio: the minimum ratio between the each angle and the average angle and the average angle and the target angle of a shape of side_amount :param min_area_ratio: The minimum ratio between the contour's area and the target area :param min_len_ratio: The minimum ratio between the length of each side and the average length and between the average length. :param approx_coef: the coefficient for the function cv2.approxPolyDP. :return: """ if side_amount < 3: return [] output = [] ratio_list = [] if type(contour_list) is not list: contour_list = [contour_list] for currentContour in contour_list: vertices, lengths, angles = Geometry.get_lengths_and_angles( currentContour, approx_coef) if len(vertices) == side_amount: target_angle = Geometry.n_polygon_angle(side_amount) average_length = round(sum(lengths) / float(len(lengths)), 3) average_angle = round(sum(angles) / float(len(lengths)), 3) invalid_value = False for length in lengths: if 1 - (root((length - average_length)**2, 2) / average_length) < min_len_ratio: invalid_value = True break if not invalid_value: for angle in angles: if 1 - (root((angle - average_angle)**2, 2) / average_angle) < min_angle_ratio: invalid_value = True break if not invalid_value: if 1 - (root((target_angle - average_angle)**2, 2) / target_angle) < min_angle_ratio: invalid_value = True if not invalid_value: ratio = cv2.contourArea( currentContour) / Geometry.n_polygon_area( average_length, side_amount) if min_area_ratio <= ratio <= 1 / min_area_ratio: ratio_list.append(ratio) output.append(currentContour) return output, ratio_list
def distance_between_points(p1, p2): """ Action: Calculates the Distance between 2 points. :param p1: tuple of x and y value of point 1 :param p2: tuple of x and y value of point 2 :return: Float value of the distance between the 2 points :rtype: float """ return root((p1[0] - p2[0])**2 + (p1[1] - p2[1])**2, 2)
def circle_rating(contour, area_factor=0.9, radius_factor=0.8): """ Action: returns a rating of how close is the circle to being a circle :param contour: the contour that its rating is calculated :param area_factor: the factor (p-value) that separates an area ratio that is a circle and one that isn't :param radius_factor: the factor (p-value) that separates an radius ratio that is a circle and one that isn't :return: """ fill_ratio, radius = get_fill_ratio_circle(contour) _, _, w, h = cv2.boundingRect(contour) radius_ratio = root(((radius * 2)**2) / float(w) * h, 2) rating = (radius_ratio * radius_factor) * (fill_ratio * area_factor) return rating
def circle_filter(contour_list, min_area_ratio=0.80, min_len_ratio=0.9): """ Action: filters out contour which are not approximately circle. :param contour_list: list of arrays (numpy uint8 ndarrays (contours) to be filtered :param min_area_ratio: minimum ratio between the area of the enclosing circle and the contour (contour /enclosing circle) :param min_len_ratio: minimum ratio between the radius of the enclosing circle and the enclosing rectangle. :return: the list of contours that fit the criteria """ output = [] ratio_list = [] if type(contour_list) is not list: contour_list = [contour_list] for currentContour in contour_list: fill_ratio, radius = Geometry.get_fill_ratio_circle(currentContour) _, _, w, h = cv2.boundingRect(currentContour) radius_ratio = ((2 * radius)**2) / float(w) * h if min_len_ratio <= root(radius_ratio, 2): if min_area_ratio <= fill_ratio: output.append(currentContour) ratio_list.append((fill_ratio, radius_ratio)) return output, ratio_list