Example #1
0
    def editBorder(self, blob, lines):
        points = [blob.drawLine(line) for line in lines]

        if points is None or len(points) == 0:
            return

        # compute the box for the outer contour
        intersected = False
        (mask, box,
         intersected) = self.editBorderContour(blob, blob.contour, points)

        pointIntersectsContours = intersected
        for contour in blob.inner_contours:
            (inner_mask, inner_box,
             intersected) = self.editBorderContour(blob, contour, points)
            pointIntersectsContours = pointIntersectsContours or intersected
            Mask.paintMask(mask, box, inner_mask, inner_box, 0)

        if not pointIntersectsContours:
            #probably a hole, draw the points fill the hole and subtract from mask
            allpoints = np.empty(shape=(0, 2), dtype=int)
            for arc in points:
                allpoints = np.append(allpoints, arc, axis=0)
            points_box = Mask.pointsBox(allpoints, 4)
            (points_mask, points_box) = Mask.jointMask(points_box, points_box)
            Mask.paintPoints(points_mask, points_box, allpoints, 1)
            origin = np.array([points_box[1], points_box[0]])
            Mask.paintPoints(points_mask, points_box, allpoints - origin, 1)
            points_mask = ndi.binary_fill_holes(points_mask)
            points_mask = binary_erosion(points_mask)
            Mask.paintMask(mask, box, points_mask, points_box, 0)

        blob.updateUsingMask(box, mask)
Example #2
0
    def editBorderContour(self, blob, contour, points):
        snapped_points = np.empty(shape=(0, 2), dtype=int)
        for arc in points:
            snapped = blob.snapToContour(arc, contour)
            if snapped is not None:
                snapped_points = np.append(snapped_points, snapped, axis=0)

        contour_box = Mask.pointsBox(contour, 4)

        #if the countour did not intersect with the outer contour, get the mask of the outer contour
        if snapped_points is None or len(snapped_points) == 0:
            # not very elegant repeated code...
            (mask, box) = Mask.jointMask(contour_box, contour_box)
            origin = np.array([box[1], box[0]])
            contour_points = contour.round().astype(int)
            fillPoly(mask, pts=[contour_points - origin], color=(1))
            return (mask, box, False)

        points_box = Mask.pointsBox(snapped_points, 4)

        # create a mask large enough to accomodate the points and the contour and paint.
        (mask, box) = Mask.jointMask(contour_box, points_box)

        origin = np.array([box[1], box[0]])
        contour_points = contour.round().astype(int)
        fillPoly(mask, pts=[contour_points - origin], color=(1, 1, 1))

        Mask.paintPoints(mask, box, snapped_points, 1)

        mask = ndi.binary_fill_holes(mask)

        # now draw in black the part of the points inside the contour
        Mask.paintPoints(mask, box, snapped_points, 0)

        # now we label all the parts and keep the larges only
        regions = measure.regionprops(measure.label(mask, connectivity=1))

        largest = max(regions, key=lambda region: region.area)

        # adjust the image bounding box (relative to the region mask) to directly use area.image mask
        box = np.array([
            box[0] + largest.bbox[0], box[1] + largest.bbox[1],
            largest.bbox[3] - largest.bbox[1],
            largest.bbox[2] - largest.bbox[0]
        ])
        return (largest.image, box, True)
Example #3
0
    def editBorder(self, blob, lines):
        #need padding

        #would be lovely to be able do edit holes too.
        #the main problem is snapping to the external contour

        points = blob.lineToPoints(lines, snap=True)

        if points is None:
            return

        if len(points) == 0:
            return

        pointsbox = Mask.pointsBox(points, 3)
        blobmask = blob.getMask()

        #add to mask painting the points as 1 and filling the holes.
        (mask, box) = Mask.jointMask(blob.bbox, pointsbox)
        Mask.paintMask(mask, box, blobmask, blob.bbox, 1)

        #save holes
        full = ndi.binary_fill_holes(mask.astype(int))
        holes = full & ~mask

        #cut from mask
        Mask.paintPoints(mask, box, points, 1)
        mask = ndi.binary_fill_holes(mask.astype(int))

        #erase the points to carve to remove the internal parts.
        Mask.paintPoints(mask, box, points, 0)

        #add back holes
        mask = mask & ~holes

        regions = measure.regionprops(measure.label(mask))

        if len(regions):
            largest = regions[0]
            for region in regions:
                if region.area > largest.area:
                    largest = region

            #adjust the image bounding box (relative to the region mask) to directly use area.image mask
            #image box is standard (minx, miny, maxx, maxy)
            box = np.array([
                box[0] + largest.bbox[0], box[1] + largest.bbox[1],
                largest.bbox[3], largest.bbox[2]
            ])

            blob.updateUsingMask(box, largest.image.astype(int))
Example #4
0
    def createFromClosedCurve(self, lines):
        """
        It creates a blob starting from a closed curve. If the curve is not closed False is returned.
        If the curve intersect itself many times the first segmented region is created.
        """
        points = self.lineToPoints(lines)
        box = Mask.pointsBox(points, 4)

        (mask, box) = Mask.jointMask(box, box)
        Mask.paintPoints(mask, box, points, 1)
        mask = ndi.binary_fill_holes(mask)

        mask = binary_erosion(mask)
        mask = binary_dilation(mask)
        self.updateUsingMask(box, mask)
        return True
Example #5
0
    def createFromClosedCurve(self, lines):
        """
        It creates a blob starting from a closed curve. If the curve is not closed False is returned.
        If the curve intersect itself many times the first segmented region is created.
        """
        points = self.lineToPoints(lines)
        box = Mask.pointsBox(points, 4)

        (mask, box) = Mask.jointMask(box, box)
        Mask.paintPoints(mask, box, points, 1)
        before = np.count_nonzero(mask)
        mask = ndi.binary_fill_holes(mask)
        after = np.count_nonzero(mask)

        if before == after:
            return False

        selem = np.array([[0, 0, 0], [0, 0, 1], [0, 1, 0]])
        mask = binary_erosion(mask, selem)
        self.updateUsingMask(box, mask)
        return True
Example #6
0
    def createContourFromMask(self, mask, bbox):
        """
        It creates the contour (and the corrisponding polygon) from the blob mask.
        """

        # NOTE: The mask is expected to be cropped around its bbox (!!) (see the __init__)

        self.inner_contours.clear()

        # we need to pad the mask to avoid to break the contour that touches the borders
        PADDED_SIZE = 4
        img_padded = pad(mask, (PADDED_SIZE, PADDED_SIZE),
                         mode="constant",
                         constant_values=(0, 0))

        contours = measure.find_contours(img_padded, 0.6)
        inner_contours = measure.find_contours(img_padded, 0.4)
        number_of_contours = len(contours)

        threshold = 20  #min number of points in a small hole

        if number_of_contours > 1:

            # search the contour with the largest bounding box (area)
            max_area = 0
            longest = 0
            for i, contour in enumerate(contours):
                cbox = Mask.pointsBox(contour, 0)
                area = cbox[2] * cbox[3]
                if area > max_area:
                    max_area = area
                    longest = i

            max_area = 0
            inner_longest = 0
            for i, contour in enumerate(inner_contours):
                cbox = Mask.pointsBox(contour, 0)
                area = cbox[2] * cbox[3]
                if area > max_area:
                    max_area = area
                    inner_longest = i

            # divide the contours in OUTER contour and INNER contours
            for i, contour in enumerate(contours):
                if i == longest:
                    self.contour = np.array(contour)

            for i, contour in enumerate(inner_contours):
                if i != inner_longest:
                    if contour.shape[0] > threshold:
                        coordinates = np.array(contour)
                        self.inner_contours.append(coordinates)

            # adjust the coordinates of the outer contour
            # (NOTE THAT THE COORDINATES OF THE BBOX ARE IN THE GLOBAL MAP COORDINATES SYSTEM)
            for i in range(self.contour.shape[0]):
                ycoor = self.contour[i, 0]
                xcoor = self.contour[i, 1]
                self.contour[i, 0] = xcoor - PADDED_SIZE + bbox[1]
                self.contour[i, 1] = ycoor - PADDED_SIZE + bbox[0]

            # adjust coordinates of the INNER contours
            for j, contour in enumerate(self.inner_contours):
                for i in range(contour.shape[0]):
                    ycoor = contour[i, 0]
                    xcoor = contour[i, 1]
                    self.inner_contours[j][i,
                                           0] = xcoor - PADDED_SIZE + bbox[1]
                    self.inner_contours[j][i,
                                           1] = ycoor - PADDED_SIZE + bbox[0]
        elif number_of_contours == 1:

            coords = measure.approximate_polygon(contours[0], tolerance=0.2)
            self.contour = np.array(coords)

            # adjust the coordinates of the outer contour
            # (NOTE THAT THE COORDINATES OF THE BBOX ARE IN THE GLOBAL MAP COORDINATES SYSTEM)
            for i in range(self.contour.shape[0]):
                ycoor = self.contour[i, 0]
                xcoor = self.contour[i, 1]
                self.contour[i, 0] = xcoor - PADDED_SIZE + bbox[1]
                self.contour[i, 1] = ycoor - PADDED_SIZE + bbox[0]
        else:
            raise Exception("Empty contour")
        #TODO optimize the bbox
        self.bbox = bbox
Example #7
0
 def updateUsingMask(self, bbox, mask):
     self.createContourFromMask(mask, bbox)
     self.calculatePerimeter()
     self.calculateCentroid(mask, bbox)
     self.calculateArea(mask)
     self.bbox = Mask.pointsBox(self.contour, 4)
Example #8
0
    def segmentation(self):

        # compute bbox of scribbles (working area)
        bboxes = []
        for i, curve in enumerate(self.scribbles.points):
            bbox = Mask.pointsBox(curve, int(self.scribbles.size[i] / 2))
            bboxes.append(bbox)
        working_area = Mask.jointBox(bboxes)

        if working_area[0] < 0:
            working_area[0] = 0

        if working_area[1] < 0:
            working_area[1] = 0

        if working_area[0] + working_area[3] > self.viewerplus.img_map.height(
        ) - 1:
            working_area[3] = self.viewerplus.img_map.height(
            ) - 1 - working_area[0]

        if working_area[1] + working_area[2] > self.viewerplus.img_map.width(
        ) - 1:
            working_area[2] = self.viewerplus.img_map.width(
            ) - 1 - working_area[1]

        crop_img = utils.cropQImage(self.viewerplus.img_map, working_area)
        crop_imgnp = utils.qimageToNumpyArray(crop_img)

        # create markers
        mask = np.zeros((working_area[3], working_area[2], 3), dtype=np.int32)

        color_codes = dict()
        counter = 1
        for i, curve in enumerate(self.scribbles.points):

            col = self.scribbles.label[i].fill
            b = col[2]
            g = col[1]
            r = col[0]
            color = (b, g, r)

            color_code = b + 256 * g + 65536 * r
            color_key = str(color_code)
            if color_codes.get(color_key) is None:
                name = self.scribbles.label[i].name
                color_codes[color_key] = (counter, name)
                counter = counter + 1

            curve = np.int32(curve)

            curve[:, 0] = curve[:, 0] - working_area[1]
            curve[:, 1] = curve[:, 1] - working_area[0]

            curve = curve.reshape((-1, 1, 2))
            mask = cv2.polylines(mask,
                                 pts=[curve],
                                 isClosed=False,
                                 color=color,
                                 thickness=self.scribbles.size[i],
                                 lineType=cv2.LINE_8)

        mask = np.uint8(mask)

        markers = np.zeros((working_area[3], working_area[2]), dtype='int32')
        for label in self.scribbles.label:
            col = label.fill
            b = col[2]
            g = col[1]
            r = col[0]
            color_code = b + 256 * g + 65536 * r
            color_key = str(color_code)

            idx = np.where((mask[:, :, 0] == b) & (mask[:, :, 1] == g)
                           & (mask[:, :, 2] == r))
            (value, name) = color_codes[color_key]
            markers[idx] = value

        # markers = np.int32(255*rgb2gray(mask))
        # markersprint = 255*rgb2gray(mask)
        markersprint = markers
        cv2.imwrite('mask.png', markersprint)

        # watershed segmentation
        segmentation = cv2.watershed(crop_imgnp, markers)
        segmentation = filters.median(segmentation, disk(5), mode="mirror")
        cv2.imwrite('segmentation.png', segmentation)

        # the result of the segmentation must be converted into labels again
        lbls = measure.label(segmentation)

        blobs = []
        for region in measure.regionprops(lbls):
            blob = Blob(region, working_area[1], working_area[0],
                        self.viewerplus.annotations.getFreeId())
            color_index = segmentation[region.coords[0][0],
                                       region.coords[0][1]]
            data = list(color_codes.items())
            index = 0
            for i in range(len(data)):
                (color_code, t) = data[i]
                if t[0] == color_index:
                    color_code = int(color_code)
                    r = int(color_code / 65536)
                    g = int(int(color_code - r * 65536) / 256)
                    b = int(color_code - r * 65536 - g * 256)
                    color = [r, g, b]
                    name = t[1]
                    break

            blob.class_color = color
            blob.class_name = name

            blobs.append(blob)

        return blobs