def refineBorder(self, box, blob, img, mask, grow): try: from coraline.Coraline import segment segment(img, mask, 0.0, conservative=0.07, grow=grow, radius=30) except Exception as e: msgBox = QMessageBox() msgBox.setText(str(e)) msgBox.exec() return #TODO this should be moved to a function! area_th = 30 created_blobs = [] first = True label_image = measure.label(mask, connectivity=1) for region in measure.regionprops(label_image): if region.area > area_th: b = Blob(region, box[1], box[0], self.progressive_id) self.progressive_id += 1 b.class_color = blob.class_color b.class_name = blob.class_name created_blobs.append(b) return created_blobs
def apply(self): if len(self.edit_points.points) == 0: self.infoMessage.emit( "You need to draw something for this operation.") return blob = Blob(None, 0, 0, 0) try: flagValid = blob.createFromClosedCurve(self.edit_points.points) except Exception: self.infoMessage.emit("Failed creating area.") self.log.emit( "[TOOL][FREEHAND] FREEHAND operation not done (invalid snap).") return if flagValid is True: blob.setId(self.viewerplus.annotations.getFreeId()) self.viewerplus.resetSelection() self.viewerplus.addBlob(blob, selected=True) self.blobInfo.emit(blob, "[TOOL][FREEHAND][BLOB-CREATED]") self.viewerplus.saveUndo() self.log.emit("[TOOL][FREEHAND] Operation ends.") else: self.log.emit("[TOOL][FREEHAND] Operation ends (INVALID SNAP!).") return self.viewerplus.resetTools()
def import_label_map(self, filename, labels_info, w_target, h_target, create_holes=False): """ It imports a label map and create the corresponding blobs. The label map is rescaled such that it coincides with the reference map. """ qimg_label_map = QImage(filename) qimg_label_map = qimg_label_map.convertToFormat(QImage.Format_RGB32) if w_target > 0 and h_target > 0: qimg_label_map = qimg_label_map.scaled(w_target, h_target, Qt.IgnoreAspectRatio, Qt.FastTransformation) label_map = utils.qimageToNumpyArray(qimg_label_map) label_map = label_map.astype(np.int32) # RGB -> label code association (ok, it is a dirty trick but it saves time..) label_coded = label_map[:, :, 0] + (label_map[:, :, 1] << 8) + ( label_map[:, :, 2] << 16) labels = measure.label(label_coded, connectivity=1) too_much_small_area = 50 region_big = None created_blobs = [] for region in measure.regionprops(labels): if region.area > too_much_small_area: id = len(self.seg_blobs) blob = Blob(region, 0, 0, self.getFreeId()) # assign class row = region.coords[0, 0] col = region.coords[0, 1] color = label_map[row, col] for label_name in labels_info.keys(): c = labels_info[label_name] if c[0] == color[0] and c[1] == color[1] and c[2] == color[ 2]: blob.class_name = label_name blob.class_color = c break if create_holes or blob.class_name is not 'Empty': created_blobs.append(blob) return created_blobs
def autoMatch(self, blobs1, blobs2): self.correspondences.clear() for blob1 in blobs1: for blob2 in blobs2: # use bb to quickly calculate intersection x1 = max(blob1.bbox[0], blob2.bbox[0]) y1 = max(blob1.bbox[1], blob2.bbox[1]) x2 = min(blob1.bbox[0] + blob1.bbox[3], blob2.bbox[0] + blob2.bbox[3]) y2 = min(blob1.bbox[1] + blob1.bbox[2], blob2.bbox[1] + blob2.bbox[2]) # compute the area of intersection rectangle interArea = abs(max((x2 - x1, 0)) * max((y2 - y1), 0)) if interArea != 0 and blob2.class_name == blob1.class_name and blob1.class_name != 'Empty': # this is the get mask function for the outer contours, I put it here using two different if conditions so getMask just runs just on intersections mask1 = Blob.getMask(blob1) sizeblob1 = np.count_nonzero(mask1) mask2 = Blob.getMask(blob2) sizeblob2 = np.count_nonzero(mask2) minblob = min(sizeblob1, sizeblob2) mask, bbox = intersectMask(mask1, blob1.bbox, mask2, blob2.bbox) intersectionArea = np.count_nonzero(mask) if (intersectionArea < (0.6 * minblob)): continue if (sizeblob2 > sizeblob1 * self.threshold): self.correspondences.append([ blob1.id, blob2.id, blob1.area, blob2.area, blob1.class_name, 'grow', 'none' ]) elif (sizeblob2 < sizeblob1 / self.threshold): self.correspondences.append([ blob1.id, blob2.id, blob1.area, blob2.area, blob1.class_name, 'shrink', 'none' ]) else: self.correspondences.append([ blob1.id, blob2.id, blob1.area, blob2.area, blob1.class_name, 'same', 'none' ]) # operates on the correspondences found and update them self.assignSplit() self.assignFuse() # fill self.born and self.dead blob lists self.assignDead(blobs1) self.assignBorn(blobs2)
def import_label_map(self, filename, reference_map): """ It imports a label map and create the corresponding blobs. The label map is rescaled such that it coincides with the reference map. """ qimg_label_map = QImage(filename) qimg_label_map = qimg_label_map.convertToFormat(QImage.Format_RGB32) w = reference_map.width() h = reference_map.height() qimg_label_map = qimg_label_map.scaled(w, h, Qt.IgnoreAspectRatio, Qt.SmoothTransformation) label_map = utils.qimageToNumpyArray(qimg_label_map) label_map = label_map.astype(np.int32) # RGB -> label code association (ok, it is a dirty trick but it saves time..) label_coded = label_map[:, :, 0] + (label_map[:, :, 1] << 8) + ( label_map[:, :, 2] << 16) labels = measure.label(label_coded, connectivity=1) label_info = Labels() too_much_small_area = 10 region_big = None created_blobs = [] for region in measure.regionprops(labels): if region.area > too_much_small_area: id = len(self.seg_blobs) blob = Blob(region, 0, 0, id + 1) # assign class row = region.coords[0, 0] col = region.coords[0, 1] color = label_map[row, col] index = label_info.searchColor(color) if index >= 0: blob.class_name = label_info.getClassName(index) blob.class_color = label_info.getColorByIndex(index) created_blobs.append(blob) return created_blobs
def import_label_map(self, filename, reference_map): """ It imports a label map and create the corresponding blobs. The label map is rescaled such that it coincides with the reference map. """ qimg_label_map = QImage(filename) qimg_label_map = qimg_label_map.convertToFormat(QImage.Format_RGB32) w = reference_map.width() h = reference_map.height() qimg_label_map = qimg_label_map.scaled(w, h, Qt.IgnoreAspectRatio, Qt.SmoothTransformation) label_map = utils.qimageToNumpyArray(qimg_label_map) label_map = label_map.astype(np.int32) # RGB -> label code association (ok, it is a dirty trick but it saves time..) label_coded = label_map[:, :, 0] + (label_map[:, :, 1] << 8) + ( label_map[:, :, 2] << 16) labels = measure.label(label_coded, connectivity=1) too_much_small_area = 1000 region_big = None created_blobs = [] for region in measure.regionprops(labels): if region.area > too_much_small_area: id = len(self.seg_blobs) blob = Blob(region, 0, 0, self.progressive_id) self.progressive_id += 1 # assign class row = region.coords[0, 0] col = region.coords[0, 1] color = label_map[row, col] for label_name in self.labels_info.keys(): c = self.labels_info[label_name] if c[0] == color[0] and c[1] == color[1] and c[2] == color[ 2]: blob.class_name = label_name blob.class_color = c break created_blobs.append(blob) return created_blobs
def cut(self, blob, lines): """ Given a curve specified as a set of points and a selected blob, the operation cuts it in several separed new blobs """ points = blob.lineToPoints(lines, snap=False) mask = blob.getMask() original = mask.copy() box = blob.bbox #box is y, x, w, h Mask.paintPoints(mask, box, points, 0) label_image = measure.label(mask, connectivity=1) for point in points: x = point[0] - box[1] y = point[1] - box[0] if x <= 0 or y <= 0 or x >= box[2] - 1 or y >= box[3] - 1: continue if original[y][x] == 0: continue largest = 0 largest = max(label_image[y + 1][x], largest) largest = max(label_image[y - 1][x], largest) largest = max(label_image[y][x + 1], largest) largest = max(label_image[y][x - 1], largest) label_image[y][x] = largest area_th = 30 created_blobs = [] first = True for region in measure.regionprops(label_image): if region.area > area_th: b = Blob(region, box[1], box[0], self.progressive_id) self.progressive_id += 1 b.class_color = blob.class_color b.class_name = blob.class_name created_blobs.append(b) return created_blobs
def refineBorder(self, box, blob, img, depth, mask, grow, lastedit): clippoints = None if lastedit is not None: points = [blob.drawLine(line) for line in lastedit] if points is not None and len(points) > 0: clippoints = np.empty(shape=(0, 2), dtype=int) for arc in points: clippoints = np.append(clippoints, arc, axis=0) origin = np.array([box[1], box[0]]) clippoints = clippoints - origin try: from coraline.Coraline import segment segment(img, depth, mask, clippoints, 0.0, conservative=self.refine_conservative, grow=grow, radius=30, depth_weight=self.refine_depth_weight) except Exception as e: print(e, flush=True) #msgBox = QMessageBox() #msgBox.setText(str(e)) #msgBox.exec() # return #TODO this should be moved to a function! area_th = 2 created_blobs = [] first = True label_image = measure.label(mask, connectivity=1) for region in measure.regionprops(label_image): if region.area > area_th: b = Blob(region, box[1], box[0], self.getFreeId()) b.class_color = blob.class_color b.class_name = blob.class_name created_blobs.append(b) return created_blobs
def __init__(self, rect=[0.0, 0.0, 0.0, 0.0], map_px_to_mm_factor=1.0, width=None, height=None, channels=[], id=None, name=None, acquisition_date="", georef_filename="", workspace=[], metadata={}, annotations={}, working_area=[]): #we have to select a standanrd enforced! #in image standard (x, y, width height) #in numpy standard (y, x, height, width) #no the mixed format we use now I REFUSE to use it. #in range np format: (top, left, bottom, right) #in GIS standard (bottom, left, top, right) self.rect = rect #coordinates of the image. (in the spatial reference system) self.map_px_to_mm_factor = map_px_to_mm_factor #if we have a references system we should be able to recover this numner # otherwise we need to specify it. self.width = width self.height = height #in pixels! self.annotations = Annotation() for data in annotations: blob = Blob(None, 0, 0, 0) blob.fromDict(data) self.annotations.addBlob(blob) self.channels = list(map(lambda c: Channel(**c), channels)) self.id = id # internal id used in correspondences it will never changes self.name = name # a label for an annotated image self.workspace = workspace # a polygon in spatial reference system self.working_area = working_area # this is the working area of data exports for training self.acquisition_date = acquisition_date # acquisition date is mandatory (format YYYY-MM-DD) self.georef_filename = georef_filename # image file (GeoTiff) contained the georeferencing information self.metadata = metadata # this follows image_metadata_template, do we want to allow freedom to add custome values?
def loadOldProject(data, labels_dict): project = Project() project.importLabelsFromConfiguration(labels_dict) map_filename = data["Map File"] #convert to relative paths in case: dir = QDir(os.getcwd()) map_filename = dir.relativeFilePath(map_filename) image_name = os.path.basename(map_filename) image_name = image_name[:-4] image = Image(id=image_name) image.map_px_to_mm_factor = data["Map Scale"] image.acquisition_date = "1955-11-05" channel = Channel(filename=map_filename, type="RGB") image.channels.append(channel) for blob_data in data["Segmentation Data"]: blob = Blob(None, 0, 0, 0) blob.fromDict(blob_data) blob.setId(int( blob.id)) # id should be set again to update related info image.annotations.addBlob(blob) project.images.append(image) return project
def createCrack(self, blob, input_arr, x, y, tolerance, preview=True): """ Given a inner blob point (x,y), the function use it as a seed for a paint butcket tool and create a correspondent blob hole """ box = blob.bbox x_crop = x - box[1] y_crop = y - box[0] input_arr = gaussian(input_arr, 2) # input_arr = segmentation.inverse_gaussian_gradient(input_arr, alpha=1, sigma=1) blob_mask = blob.getMask() crack_mask = flood(input_arr, (int(y_crop), int(x_crop)), tolerance=tolerance).astype(int) cracked_blob = np.logical_and((blob_mask > 0), (crack_mask < 1)) cracked_blob = cracked_blob.astype(int) if preview: return cracked_blob regions = measure.regionprops(measure.label(cracked_blob)) area_th = 1000 created_blobs = [] for region in regions: if region.area > area_th: id = len(self.seg_blobs) b = Blob(region, box[1], box[0], self.progressive_id) self.progressive_id += 1 b.class_color = blob.class_color b.class_name = blob.class_name created_blobs.append(b) return created_blobs
def cut(self, blob, lines): """ Given a curve specified as a set of points and a selected blob, the operation cuts it in several separed new blobs """ points = blob.lineToPoints(lines) mask = blob.getMask() box = blob.bbox Mask.paintPoints(mask, box, points, 0) label_image = measure.label(mask) area_th = 30 created_blobs = [] for region in measure.regionprops(label_image): if region.area > area_th: id = len(self.seg_blobs) b = Blob(region, box[1], box[0], id + 1) b.class_color = blob.class_color b.class_name = blob.class_name created_blobs.append(b) return created_blobs
def splitBlob(self, map, blob, seeds): seeds = np.asarray(seeds) seeds = seeds.astype(int) mask = blob.getMask() box = blob.bbox cropimg = utils.cropQImage(map, box) cropimgnp = rgb2gray(utils.qimageToNumpyArray(cropimg)) edges = sobel(cropimgnp) # x,y seeds_matrix = np.zeros_like(mask) size = 40 # for i in range(0, seeds.shape[0]): #y,x seeds_matrix[seeds[i, 1] - box[0] - (size - 1):seeds[i, 1] - box[0] + (size - 1), seeds[i, 0] - box[1] - (size - 1):seeds[i, 0] - box[1] + (size - 1)] = 1 distance = ndi.distance_transform_edt(mask) # distance = ndi.distance_transform_edt(cropimg) seeds_matrix = seeds_matrix > 0.5 markers = ndi.label(seeds_matrix)[0] # labels = watershed(-distance, markers, mask=mask) labels = watershed((-distance + 100 * edges) / 2, markers, mask=mask) created_blobs = [] for region in measure.regionprops(labels): b = Blob(region, box[1], box[0], self.progressive_id) self.progressive_id += 1 b.class_color = blob.class_color b.class_name = blob.class_name created_blobs.append(b) return created_blobs
def blobsFromMask(self, seg_mask, map_pos_x, map_pos_y, area_mask): # create the blobs from the segmentation mask last_blobs_added = [] seg_mask = ndi.binary_fill_holes(seg_mask).astype(int) label_image = measure.label(seg_mask) area_th = area_mask * 0.2 for region in measure.regionprops(label_image): if region.area > area_th: blob = Blob(region, map_pos_x, map_pos_y, self.getFreeId()) last_blobs_added.append(blob) return last_blobs_added
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