def convert_colors_to_labels(self, target_classes, labels_colors): """ Convert the label image to a numpy array with the labels' values. """ label_w = self.label_image.width() label_h = self.label_image.height() imglbl = utils.qimageToNumpyArray(self.label_image) num_classes = len(target_classes) # class 0 --> background self.labels = np.zeros((label_h, label_w), dtype='int64') for i, cl in enumerate(target_classes): class_colors = labels_colors.get(cl) if class_colors is None: if cl == "Background": class_colors = [0, 0, 0] else: class_colors = [255, 255, 255] idx = np.where((imglbl[:, :, 0] == class_colors[0]) & (imglbl[:, :, 1] == class_colors[1]) & (imglbl[:, :, 2] == class_colors[2])) self.labels[idx] = i + 1
def classFrequenciesOnDataset(self, labels_dir, target_classes, labels_colors): """ Returns the frequencies of the target classes on the given dataset. """ num_classes = len(target_classes) image_label_names = [x for x in glob.glob(os.path.join(labels_dir, '*.png'))] total_pixels = 0 counters = np.zeros(num_classes, dtype='float') for label_name in image_label_names: image_label = QImage(label_name) # image_label = image_label.convertToFormat(QImage.Format_RGB32) label_w = image_label.width() label_h = image_label.height() total_pixels += label_w * label_h imglbl = utils.qimageToNumpyArray(image_label) # class 0 --> background labelsint = np.zeros((label_h, label_w), dtype='int64') for i, cl in enumerate(target_classes): class_colors = labels_colors[cl] idx = np.where((imglbl[:, :, 0] == class_colors[0]) & (imglbl[:, :, 1] == class_colors[1]) & ( imglbl[:, :, 2] == class_colors[2])) labelsint[idx] = i + 1 for i in range(len(target_classes)): counters[i] += float(np.count_nonzero(labelsint == i + 1)) freq = counters / float(total_pixels) print(freq)
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 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 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 __init__(self, map, annotations, blob, x, y, parent=None): super(QtCrackWidget, self).__init__(parent) self.setStyleSheet("background-color: rgb(60,60,65); color: white") self.qimg_cropped = utils.cropQImage(map, blob.bbox) arr = utils.qimageToNumpyArray(self.qimg_cropped) self.input_arr = rgb2gray(arr) * 255 self.tolerance = 20 self.annotations = annotations self.blob = blob self.xmap = x self.ymap = y self.qimg_crack = QImage(self.qimg_cropped.width(), self.qimg_cropped.height(), QImage.Format_RGB32) self.qimg_crack.fill(qRgb(0, 0, 0)) self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.setFixedWidth(400) self.setFixedHeight(400) SLIDER_WIDTH = 200 IMAGEVIEWER_SIZE = 300 # SIZE x SIZE self.sliderTolerance = QSlider(Qt.Horizontal) self.sliderTolerance.setFocusPolicy(Qt.StrongFocus) self.sliderTolerance.setMinimumWidth(SLIDER_WIDTH) self.sliderTolerance.setMinimum(1) self.sliderTolerance.setMaximum(100) self.sliderTolerance.setValue(self.tolerance) self.sliderTolerance.setTickInterval(5) self.sliderTolerance.setAutoFillBackground(True) self.sliderTolerance.valueChanged.connect(self.sliderToleranceChanged) self.lblTolerance = QLabel("Tolerance: 20") self.lblTolerance.setAutoFillBackground(True) str = "Tolerance {}".format(self.tolerance) self.lblTolerance.setText(str) layoutTolerance = QHBoxLayout() layoutTolerance.addWidget(self.lblTolerance) layoutTolerance.addWidget(self.sliderTolerance) self.viewerplus = QtImageViewerPlus() self.viewerplus.disableScrollBars() self.viewerplus.setFixedWidth(IMAGEVIEWER_SIZE) self.viewerplus.setFixedHeight(IMAGEVIEWER_SIZE) self.btnCancel = QPushButton("Cancel") self.btnCancel.setAutoFillBackground(True) self.btnApply = QPushButton("Apply") self.btnApply.setAutoFillBackground(True) layoutButtons = QHBoxLayout() layoutButtons.addWidget(self.btnCancel) layoutButtons.addWidget(self.btnApply) layoutV = QVBoxLayout() layoutV.addLayout(layoutTolerance) layoutV.addWidget(self.viewerplus) layoutV.addLayout(layoutButtons) layoutV.setSpacing(10) self.setLayout(layoutV) self.viewerplus.setImage(self.qimg_cropped) self.preview() self.setAutoFillBackground(True) self.setWindowTitle("Crack") self.setWindowFlags(Qt.Window | Qt.CustomizeWindowHint | Qt.WindowTitleHint)
def run(self, img_map, TILE_SIZE, AGGREGATION_WINDOW_SIZE, AGGREGATION_STEP): """ :param TILE_SIZE: Base tile. This corresponds to the INPUT SIZE of the network. :param AGGREGATION_WINDOW_SIZE: Size of the sub-windows to consider for the aggregation. :param AGGREGATION_STEP: Step, in pixels, to calculate the different scores. :return: """ # create a temporary folder to store the processing temp_dir = "temp" if not os.path.exists(temp_dir): os.mkdir(temp_dir) # prepare for running.. STEP_SIZE = AGGREGATION_WINDOW_SIZE W = img_map.width() H = img_map.height() # top, left, width, height working_area = [0, 0, W, H] wa_top = working_area[0] wa_left = working_area[1] wa_width = working_area[2] wa_height = working_area[3] if wa_top < AGGREGATION_STEP: wa_top = AGGREGATION_STEP if wa_left < AGGREGATION_STEP: wa_left = AGGREGATION_STEP if wa_left + wa_width >= W - AGGREGATION_STEP: wa_width = W - AGGREGATION_STEP - wa_left - 1 if wa_top + wa_height >= H - AGGREGATION_STEP: wa_height = H - AGGREGATION_STEP - wa_top - 1 tile_cols = int(wa_width / AGGREGATION_WINDOW_SIZE) + 1 tile_rows = int(wa_height / AGGREGATION_WINDOW_SIZE) + 1 if torch.cuda.is_available(): device = torch.device("cuda") self.net.to(device) torch.cuda.synchronize() self.net.eval() # classification (per-tiles) tiles_number = tile_rows * tile_cols self.processing_step = 0 self.total_processing_steps = 19 * tiles_number for row in range(tile_rows): if self.flagStopProcessing is True: break for col in range(tile_cols): if self.flagStopProcessing is True: break scores = np.zeros((9, self.nclasses, TILE_SIZE, TILE_SIZE)) k = 0 for i in range(-1,2): for j in range(-1,2): top = wa_top - AGGREGATION_STEP + row * STEP_SIZE + i * AGGREGATION_STEP left = wa_left - AGGREGATION_STEP + col * STEP_SIZE + j * AGGREGATION_STEP cropimg = utils.cropQImage(img_map, [top, left, TILE_SIZE, TILE_SIZE]) img_np = utils.qimageToNumpyArray(cropimg) img_np = img_np.astype(np.float32) img_np = img_np / 255.0 # H x W x C --> C x H x W img_np = img_np.transpose(2, 0, 1) # Normalization (average subtraction) img_np[0] = img_np[0] - self.average_norm[0] img_np[1] = img_np[1] - self.average_norm[1] img_np[2] = img_np[2] - self.average_norm[2] with torch.no_grad(): img_tensor = torch.from_numpy(img_np) input = img_tensor.unsqueeze(0) if torch.cuda.is_available(): input = input.to(device) outputs = self.net(input) scores[k] = outputs[0].cpu().numpy() k = k + 1 self.processing_step += 1 self.updateProgress.emit( (100.0 * self.processing_step) / self.total_processing_steps ) QCoreApplication.processEvents() if self.flagStopProcessing is True: break # preds_avg, preds_bayesian = self.aggregateScores(scores, tile_sz=TILE_SIZE, # center_window_size=AGGREGATION_WINDOW_SIZE, step=AGGREGATION_STEP) preds_avg = self.aggregateScores(scores, tile_sz=TILE_SIZE, center_window_size=AGGREGATION_WINDOW_SIZE, step=AGGREGATION_STEP) values_t, predictions_t = torch.max(torch.from_numpy(preds_avg), 0) preds = predictions_t.cpu().numpy() resimg = np.zeros((preds.shape[0], preds.shape[1], 3), dtype='uint8') for label_index in range(self.nclasses): resimg[preds == label_index, :] = self.label_colors[label_index] tilename = str(row) + "_" + str(col) + ".png" filename = os.path.join(temp_dir, tilename) utils.rgbToQImage(resimg).save(filename) self.processing_step += 1 self.updateProgress.emit( (100.0 * self.processing_step) / self.total_processing_steps ) QCoreApplication.processEvents() # put tiles together qimglabel = QImage(W, H, QImage.Format_RGB32) xoffset = 0 yoffset = 0 painter = QPainter(qimglabel) for r in range(tile_rows): for c in range(tile_cols): tilename = str(r) + "_" + str(c) + ".png" filename = os.path.join(temp_dir, tilename) qimg = QImage(filename) xoffset = wa_left + c * AGGREGATION_WINDOW_SIZE yoffset = wa_top + r * AGGREGATION_WINDOW_SIZE cut = False W_prime = wa_width H_prime = wa_height if xoffset + AGGREGATION_WINDOW_SIZE > wa_left + wa_width - 1: W_prime = wa_width + wa_left - xoffset - 1 cut = True if yoffset + AGGREGATION_WINDOW_SIZE > wa_top + wa_height - 1: H_prime = wa_height + wa_top - yoffset - 1 cut = True if cut is True: qimg2 = qimg.copy(0, 0, W_prime, H_prime) painter.drawImage(xoffset, yoffset, qimg2) else: painter.drawImage(xoffset, yoffset, qimg) # detach the qimglabel otherwise the Qt EXPLODES when memory is free painter.end() labelfile = os.path.join(temp_dir, "labelmap.png") qimglabel.save(labelfile) torch.cuda.empty_cache() del self.net self.net = None
def run(self, TILE_SIZE, AGGREGATION_WINDOW_SIZE, AGGREGATION_STEP, save_scores=False): """ :param TILE_SIZE: Base tile. This corresponds to the INPUT SIZE of the network. :param AGGREGATION_WINDOW_SIZE: Size of the center window considered for the aggregation. :param AGGREGATION_STEP: Step, in pixels, to calculate the different scores. :return: """ # create a temporary folder to store the processing if not os.path.exists(self.temp_dir): os.mkdir(self.temp_dir) # prepare for running.. DELTA_CROP = int((TILE_SIZE - AGGREGATION_WINDOW_SIZE) / 2) tile_cols = int(self.wa_width / AGGREGATION_WINDOW_SIZE) + 1 tile_rows = int(self.wa_height / AGGREGATION_WINDOW_SIZE) + 1 if torch.cuda.is_available(): device = torch.device("cuda") self.net.to(device) torch.cuda.synchronize() self.net.eval() # classification (per-tiles) tiles_number = tile_rows * tile_cols self.processing_step = 0 self.total_processing_steps = 19 * tiles_number for row in range(tile_rows): if self.flagStopProcessing is True: break for col in range(tile_cols): if self.flagStopProcessing is True: break scores = np.zeros((9, self.nclasses, TILE_SIZE, TILE_SIZE)) k = 0 for i in range(-1, 2): for j in range(-1, 2): top = self.wa_top - DELTA_CROP + row * AGGREGATION_WINDOW_SIZE + i * AGGREGATION_STEP left = self.wa_left - DELTA_CROP + col * AGGREGATION_WINDOW_SIZE + j * AGGREGATION_STEP tileimg = utils.cropQImage( self.input_image, [top, left, TILE_SIZE, TILE_SIZE]) img_np = utils.qimageToNumpyArray(tileimg) img_np = img_np.astype(np.float32) img_np = img_np / 255.0 # H x W x C --> C x H x W img_np = img_np.transpose(2, 0, 1) # Normalization (average subtraction) img_np[0] = img_np[0] - self.average_norm[0] img_np[1] = img_np[1] - self.average_norm[1] img_np[2] = img_np[2] - self.average_norm[2] with torch.no_grad(): img_tensor = torch.from_numpy(img_np) input = img_tensor.unsqueeze(0) if torch.cuda.is_available(): input = input.to(device) outputs = self.net(input) scores[k] = outputs[0].cpu().numpy() k = k + 1 self.processing_step += 1 self.updateProgress.emit( (100.0 * self.processing_step) / self.total_processing_steps) QCoreApplication.processEvents() if self.flagStopProcessing is True: break preds_avg = self.aggregateScores( scores, tile_sz=TILE_SIZE, center_window_size=AGGREGATION_WINDOW_SIZE, step=AGGREGATION_STEP) values_t, predictions_t = torch.max( torch.from_numpy(preds_avg), 0) preds = predictions_t.cpu().numpy() resimg = np.zeros((preds.shape[0], preds.shape[1], 3), dtype='uint8') for label_index in range(self.nclasses): resimg[preds == label_index, :] = self.label_colors[label_index] tilename = str(row) + "_" + str(col) + ".png" filename = os.path.join(self.temp_dir, tilename) utils.rgbToQImage(resimg).save(filename) if save_scores is True: tilename = str(row) + "_" + str(col) + ".dat" filename = os.path.join(self.temp_dir, tilename) fileobject = open(filename, 'wb') pkl.dump(preds_avg, fileobject) fileobject.close() self.processing_step += 1 self.updateProgress.emit((100.0 * self.processing_step) / self.total_processing_steps) QCoreApplication.processEvents() self.assembleTiles(tile_rows, tile_cols, AGGREGATION_WINDOW_SIZE, ass_scores=save_scores) torch.cuda.empty_cache() del self.net self.net = None
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