Esempio n. 1
0
def run_analysis(contours, filename, message):
    filename = filename.replace("images\\", "").replace("images/", "")
    if message != "":
        results = message
        sys.stdout.write(str(results))
        sys.stdout.flush()
    else:
        properties = imgpheno.contour_properties(contours, (
            'Area',
            'MajorAxisLength',
        ))
        if properties is None:
            pass
        else:
            properties = imgpheno.contour_properties(contours, (
                'Area',
                'MajorAxisLength',
            ))
            major_axes = [i['MajorAxisLength'] for i in properties]

            if yml.detailed_size_classes is True:
                b_0_1 = [i for i in major_axes if i < 4]
                b_1_4 = [i for i in major_axes if 4 <= i < 15]
                b_4_7 = [i for i in major_axes if 15 <= i < 26]
                b_7_12 = [i for i in major_axes if 26 <= i < 45]
                larger_12 = [i for i in major_axes if i >= 45]

                areas = [i['Area'] for i in properties]
                average_area = np.mean(areas)
                number_of_insects = (len(b_0_1) + len(b_1_4) + len(b_4_7) +
                                     len(b_7_12) + len(larger_12))

                results = """%s \t %s \t %f \t %s \t %s \t %s \t %s \t %s
            """ % (filename, number_of_insects, (average_area / 4), len(b_0_1),
                   len(b_1_4), len(b_4_7), len(b_7_12), len(larger_12))

                sys.stdout.write(str(results.replace("    ", "")))
                sys.stdout.flush()

            else:
                smaller_than_4 = [i for i in major_axes if 4 <= i < 15]
                between_4_and_10 = [i for i in major_axes if 15 <= i < 38]
                larger_than_10 = [i for i in major_axes if 38 <= i < 45]

                areas = [i['Area'] for i in properties]
                average_area = np.mean(areas)
                number_of_insects = (len(smaller_than_4) +
                                     len(between_4_and_10) +
                                     len(larger_than_10))

                results = """%s \t %s \t %d \t %s \t %s \t %s
            """ % (filename, number_of_insects,
                   (average_area / 4), len(smaller_than_4),
                   len(between_4_and_10), len(larger_than_10))

                sys.stdout.write(str(results.replace("    ", "")))
                sys.stdout.flush()
Esempio n. 2
0
def process_image(args, path):
    global img, img_src

    img = cv2.imread(path)
    if img == None or img.size == 0:
        logging.error("Failed to read %s" % path)
        exit(1)

    logging.info("Processing %s..." % path)

    # Scale the image down if its perimeter exceeds the maximum (if set).
    img = common.scale_max_perimeter(img, args.max_size)
    img_src = img.copy()

    # Perform segmentation
    logging.info("- Segmenting...")
    mask = common.grabcut(img, args.iters, None, args.margin)
    bin_mask = np.where((mask==cv2.GC_FGD) + (mask==cv2.GC_PR_FGD), 255, 0).astype('uint8')
    contours, hierarchy = cv2.findContours(bin_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    # Calculate contour properties.
    logging.info("- Computing contour properties...")
    props = ft.contour_properties(contours, 'all')

    # Print properties.
    pprint.pprint(props)

    # Draw some properties.
    draw_props(props)
Esempio n. 3
0
def process_image(args, path):
    global img, img_src, outline, box, bin_mask

    img = cv2.imread(path)
    if img == None or img.size == 0:
        logging.error("Failed to read %s" % path)
        exit(1)

    logging.info("Processing %s..." % path)

    # Scale the image down if its perimeter exceeds the maximum (if set).
    img = common.scale_max_perimeter(img, args.max_size)
    img_src = img.copy()

    # Perform segmentation.
    logging.info("- Segmenting...")
    mask = common.grabcut(img, args.iters, None, args.margin)
    bin_mask = np.where((mask == cv2.GC_FGD) + (mask == cv2.GC_PR_FGD), 255,
                        0).astype('uint8')

    # Obtain contours (all points) from the mask.
    contour = ft.get_largest_contour(bin_mask.copy(), cv2.RETR_EXTERNAL,
                                     cv2.CHAIN_APPROX_NONE)

    # Get bounding rectange of the largest contour.
    props = ft.contour_properties([contour], 'BoundingRect')
    box = props[0]['BoundingRect']

    # And draw it.
    logging.info("- Done")
    draw_sections(0, args.bins)
Esempio n. 4
0
def process_image(args, path):
    global img, img_src, outline, box, bin_mask

    img = cv2.imread(path)
    if img == None or img.size == 0:
        logging.error("Failed to read %s" % path)
        exit(1)

    logging.info("Processing %s..." % path)

    # Scale the image down if its perimeter exceeds the maximum (if set).
    img = common.scale_max_perimeter(img, args.max_size)
    img_src = img.copy()

    # Perform segmentation.
    logging.info("- Segmenting...")
    mask = common.grabcut(img, args.iters, None, args.margin)
    bin_mask = np.where((mask==cv2.GC_FGD) + (mask==cv2.GC_PR_FGD),
        255, 0).astype('uint8')

    # Obtain contours (all points) from the mask.
    contour = ft.get_largest_contour(bin_mask.copy(), cv2.RETR_EXTERNAL,
        cv2.CHAIN_APPROX_NONE)

    # Get bounding rectange of the largest contour.
    props = ft.contour_properties([contour], 'BoundingRect')
    box = props[0]['BoundingRect']

    # And draw it.
    logging.info("- Done")
    draw_sections(0, args.bins)
Esempio n. 5
0
def shape_360_v2(contour, rotation=0, step=1, t=8):
    """Returns a shape feature from a contour.

    The rotation in degrees of the contour can be set with `rotation`, which
    must be a value between 0 and 179 inclusive. A rotation above 90 degrees
    is interpreted as a rotation to the left (e.g. a rotation of 120 is
    interpreted as 60 degrees to the left). The returned shape is shifted by
    the rotation. The step size for the angle can be set with `step`. Argument
    `t` is passed to :meth:`weighted_points_nearest`. The returned shape is
    rotation invariant provided that the rotation argument is set properly and
    the rotation is no more than 90 degrees left or right.

    Shape is returned as a tuple ``(intersects, center)``, where ``intersects``
    is a dict where the keys are 0 based angles and the values are lists
    of intersecting points. If `step` is set to 1, then the dict will contain
    the intersections for 360 degrees. ``center`` specifies the center of the
    contour and can be used to calculate distances from center to contour.
    """
    if len(contour) < 6:
        raise ValueError("Contour must have at least 6 points, found %d" % len(contour))
    if not 0 <= rotation <= 179:
        raise ValueError("The rotation must be between 0 and 179 inclusive, found %d" % rotation)

    # Get the center.
    props = ft.contour_properties([contour], 'Centroid')
    center = props[0]['Centroid']

    # If the rotation is more than 90 degrees, assume the object is rotated to
    # the left.
    if rotation <= 90:
        a = 0
        b = 180
    else:
        a = 180
        b = 0

    # Define the slope for each point and group points by slope.
    slopes = {}
    for p in contour:
        p = tuple(p[0])
        x = p[0] - center[0]
        y = p[1] - center[1]

        if x == 0:
            s = float('inf')
        else:
            s = float(y) / x
            s = round(s, 4)

        if s in slopes:
            slopes[s].append(p)
        else:
            slopes[s] = [p]

    # Get the intersecting points with the contour for every degree from the
    # symmetry axis.
    intersects = {}
    for angle in range(0, 180, step):
        # Get the slope for the linear function of this angle and account for
        # the rotation of the object.
        slope = ft.slope_from_angle(angle + rotation, inverse=True)

        # Since pixel points can only be defined with natural numbers,
        # resulting in gaps in between two points, means the maximum
        # gap is equal to the slope of the linear function.
        gap_max = math.ceil(abs(slope))

        # Dmax set empirically.
        dmax = gap_max * 0.20

        # Make a selection of the contour points which somewhat fit the
        # slope for this angle.
        candidates = []
        for s in slopes:
            if math.isinf(slope):
                if math.isinf(s):
                    d = 0
                else:
                    continue
            else:
                d = abs(slope - s)

            if d == 0 or d <= dmax:
                for p in slopes[s]:
                    candidates.append(p)

        # Find the contour points from the list of candidate points that
        # closely fit the angle's linear function.
        # Only save points for which the distance to the expected point is
        # no more than the maximum gap. Save each point with a weight value,
        # which is used for clustering.
        weighted_points = []
        for p in candidates:
            x = p[0] - center[0]
            y = p[1] - center[1]
            if math.isinf(slope):
                if x == 0:
                    # Save points that are on the vertical axis.
                    weighted_points.append( (1, tuple(p)) )
            else:
                y_exp = slope * x
                d = abs(y - y_exp)
                if d <= gap_max:
                    w = 1 / (d+1)
                    weighted_points.append( (w, tuple(p)) )

        assert len(weighted_points) > 0, "No intersections found for angle %d" % angle

        # Cluster the points.
        weighted_points = ft.weighted_points_nearest(weighted_points, t)
        _, points = zip(*weighted_points)

        # Figure out on which side of the symmetry the points lie.
        # Create a line that separates points on the left from points on
        # the right.
        if (angle + rotation) != 0:
            division_line = ft.angled_line(center, angle + rotation + 90, 100)
        else:
            # Points cannot be separated when the division line is horizontal.
            # So make a division line that is rotated 45 degrees to the left
            # instead, so that the points are properly separated.
            division_line = ft.angled_line(center, angle + rotation - 45, 100)

        intersects[angle] = []
        intersects[angle+180] = []
        for p in points:
            side = ft.side_of_line(division_line, p)
            if side < 0:
                intersects[angle+a].append(p)
            elif side > 0:
                intersects[angle+b].append(p)
            else:
                assert side != 0, "A point cannot be on the division line"

    return (intersects, center)
Esempio n. 6
0
def run_analysis(contours, filename, message):
    filename = filename.replace("images/sticky-traps\\", "").replace("images/sticky-traps/", "")
    if message != "":
        results = message
        if yml.result_file == "":
            pass
        else:
            resultfile = open(yml.result_file, "a+")
            resultfile.write(str(results))
            resultfile.close()
    else:
        properties = imgpheno.contour_properties(contours, ('Area', 'MajorAxisLength',))
        if properties is None:
            pass
        else:
            properties = imgpheno.contour_properties(contours, ('Area', 'MajorAxisLength',))
            major_axes = [i['MajorAxisLength'] for i in properties]

            if yml.detailed_size_classes is True:
                b_0_1 = [i for i in major_axes if i < 4]
                b_1_4 = [i for i in major_axes if 4 <= i < 15]
                b_4_7 = [i for i in major_axes if 15 <= i < 26]
                b_7_12 = [i for i in major_axes if 26 <= i < 45]
                larger_12 = [i for i in major_axes if i >= 45]

                areas = [i['Area'] for i in properties]
                average_area = np.mean(areas)
                number_of_insects = (len(b_0_1) + len(b_1_4) + len(b_4_7) + len(b_7_12) + len(larger_12))
                print """There are %s insects on the trap in %s.
    The average area of the insects in %s is %f mm square.
    The number of insects between 0 and 1 mm is %s
    The number of insects between 1 and 4 mm is %s
    The number of insects between 4 and 7 mm is %s
    The number of insects between 7 and 12 mm is %s
    The number of insects larger than 12 mm is %s
            """ % (number_of_insects, filename, filename,
                   (average_area / 4), len(b_0_1), len(b_1_4), len(b_4_7), len(b_7_12), len(larger_12))

                results = """%s \t %s \t %f \t %s \t %s \t %s \t %s \t %s
            """ % (filename, number_of_insects, (average_area / 4), len(b_0_1),
                   len(b_1_4), len(b_4_7), len(b_7_12), len(larger_12))

                if yml.result_file == "":
                    pass
                else:
                    resultfile = open(yml.result_file, "a+")
                    resultfile.write(str(results.replace("    ", "")))
                    resultfile.close()

            else:
                smaller_than_4 = [i for i in major_axes if 4 <= i < 15]
                between_4_and_10 = [i for i in major_axes if 15 <= i < 38]
                larger_than_10 = [i for i in major_axes if 38 <= i < 45]

                areas = [i['Area'] for i in properties]
                average_area = np.mean(areas)
                number_of_insects = (len(smaller_than_4) + len(between_4_and_10) + len(larger_than_10))

                print """There are %s insects on the trap in %s.
    The average area of the insects in %s is %d mm square.
    The number of insects smaller than 4 mm is %s
    The number of insects between 4 and 10 mm is %s
    The number of insects larger than 10 mm is %s
                """ % (number_of_insects, filename, filename,
                       (average_area / 4), len(smaller_than_4), len(between_4_and_10), len(larger_than_10))

                results = """%s \t %s \t %d \t %s \t %s \t %s
            """ % (filename, number_of_insects, (average_area / 4),
                   len(smaller_than_4),
                   len(between_4_and_10), len(larger_than_10))

                if yml.result_file == "":
                    pass
                else:
                    resultfile = open(yml.result_file, "a+")
                    resultfile.write(str(results.replace("    ", "")))
                    resultfile.close()
Esempio n. 7
0
def shape_360_v2(contour, rotation=0, step=1, t=8):
    """Returns a shape feature from a contour.

    The rotation in degrees of the contour can be set with `rotation`, which
    must be a value between 0 and 179 inclusive. A rotation above 90 degrees
    is interpreted as a rotation to the left (e.g. a rotation of 120 is
    interpreted as 60 degrees to the left). The returned shape is shifted by
    the rotation. The step size for the angle can be set with `step`. Argument
    `t` is passed to :meth:`weighted_points_nearest`. The returned shape is
    rotation invariant provided that the rotation argument is set properly and
    the rotation is no more than 90 degrees left or right.

    Shape is returned as a tuple ``(intersects, center)``, where ``intersects``
    is a dict where the keys are 0 based angles and the values are lists
    of intersecting points. If `step` is set to 1, then the dict will contain
    the intersections for 360 degrees. ``center`` specifies the center of the
    contour and can be used to calculate distances from center to contour.
    """
    if len(contour) < 6:
        raise ValueError("Contour must have at least 6 points, found %d" %
                         len(contour))
    if not 0 <= rotation <= 179:
        raise ValueError(
            "The rotation must be between 0 and 179 inclusive, found %d" %
            rotation)

    # Get the center.
    props = ft.contour_properties([contour], 'Centroid')
    center = props[0]['Centroid']

    # If the rotation is more than 90 degrees, assume the object is rotated to
    # the left.
    if rotation <= 90:
        a = 0
        b = 180
    else:
        a = 180
        b = 0

    # Define the slope for each point and group points by slope.
    slopes = {}
    for p in contour:
        p = tuple(p[0])
        x = p[0] - center[0]
        y = p[1] - center[1]

        if x == 0:
            s = float('inf')
        else:
            s = float(y) / x
            s = round(s, 4)

        if s in slopes:
            slopes[s].append(p)
        else:
            slopes[s] = [p]

    # Get the intersecting points with the contour for every degree from the
    # symmetry axis.
    intersects = {}
    for angle in range(0, 180, step):
        # Get the slope for the linear function of this angle and account for
        # the rotation of the object.
        slope = ft.slope_from_angle(angle + rotation, inverse=True)

        # Since pixel points can only be defined with natural numbers,
        # resulting in gaps in between two points, means the maximum
        # gap is equal to the slope of the linear function.
        gap_max = math.ceil(abs(slope))

        # Dmax set empirically.
        dmax = gap_max * 0.20

        # Make a selection of the contour points which somewhat fit the
        # slope for this angle.
        candidates = []
        for s in slopes:
            if math.isinf(slope):
                if math.isinf(s):
                    d = 0
                else:
                    continue
            else:
                d = abs(slope - s)

            if d == 0 or d <= dmax:
                for p in slopes[s]:
                    candidates.append(p)

        # Find the contour points from the list of candidate points that
        # closely fit the angle's linear function.
        # Only save points for which the distance to the expected point is
        # no more than the maximum gap. Save each point with a weight value,
        # which is used for clustering.
        weighted_points = []
        for p in candidates:
            x = p[0] - center[0]
            y = p[1] - center[1]
            if math.isinf(slope):
                if x == 0:
                    # Save points that are on the vertical axis.
                    weighted_points.append((1, tuple(p)))
            else:
                y_exp = slope * x
                d = abs(y - y_exp)
                if d <= gap_max:
                    w = 1 / (d + 1)
                    weighted_points.append((w, tuple(p)))

        assert len(
            weighted_points) > 0, "No intersections found for angle %d" % angle

        # Cluster the points.
        weighted_points = ft.weighted_points_nearest(weighted_points, t)
        _, points = zip(*weighted_points)

        # Figure out on which side of the symmetry the points lie.
        # Create a line that separates points on the left from points on
        # the right.
        if (angle + rotation) != 0:
            division_line = ft.angled_line(center, angle + rotation + 90, 100)
        else:
            # Points cannot be separated when the division line is horizontal.
            # So make a division line that is rotated 45 degrees to the left
            # instead, so that the points are properly separated.
            division_line = ft.angled_line(center, angle + rotation - 45, 100)

        intersects[angle] = []
        intersects[angle + 180] = []
        for p in points:
            side = ft.side_of_line(division_line, p)
            if side < 0:
                intersects[angle + a].append(p)
            elif side > 0:
                intersects[angle + b].append(p)
            else:
                assert side != 0, "A point cannot be on the division line"

    return (intersects, center)