예제 #1
0
    def queueWorker(self):
        debugModule = False
        quitSignal = False
        oldFilename = ''
        oldArchive = ''
        oldSlide = ''
        oldThres = -1
        while not quitSignal:
            job = SlideRunnerPlugin.pluginJob(self.inQueue.get())
            print(job)

            if (job.jobDescription ==
                    SlideRunnerPlugin.JobDescription.QUIT_PLUGIN_THREAD):
                # signal to exit this thread
                quitSignal = True
                continue

            if (job.configuration['file'] == oldArchive) and (job.slideFilename
                                                              == oldSlide):
                continue

            if not (os.path.exists(job.configuration['file'])):
                continue
            self.sendAnnotationLabelUpdate()

            oldArchive = job.configuration['file']
            oldSlide = job.slideFilename

            self.secondaryDB.open(oldArchive)

            self.annos = list()
            self.annotationLabels = dict()

            for key, (label, annoId,
                      col) in enumerate(self.secondaryDB.getAllClasses()):
                self.annotationLabels[
                    annoId] = SlideRunnerPlugin.PluginAnnotationLabel(
                        0, '%s' % label, [*hex_to_rgb(col), 0])

            pname, fname = os.path.split(job.slideFilename)
            self.slideUID = self.secondaryDB.findSlideWithFilename(
                fname, pname)
            self.secondaryDB.loadIntoMemory(self.slideUID)
            self.annos = list()

            for annoId in self.secondaryDB.annotations.keys():
                anno = self.secondaryDB.annotations[annoId]
                anno.pluginAnnotationLabel = self.annotationLabels[
                    anno.agreedClass]
                anno.clickable = job.configuration['mode'] == 0
                self.annos.append(anno)
            self.sendAnnotationLabelUpdate()

            self.updateAnnotations()
            self.setProgressBar(-1)
            self.setMessage('found %d annotations.' % len(self.annos))
    def queueWorker(self):
        quitSignal = False
        while not quitSignal:
            job = SlideRunnerPlugin.pluginJob(self.inQueue.get())
            self.total_number = int(job.configuration[0])

            if (job.jobDescription ==
                    SlideRunnerPlugin.JobDescription.QUIT_PLUGIN_THREAD):
                # signal to exit this thread
                quitSignal = True
                continue

            database = job.openedDatabase

            annotations_count = len(database.annotations)
            if annotations_count != self.last_count:
                self.last_count = annotations_count
                if self.total_number - annotations_count == 0:
                    self.setMessage('Done. Thanks for your help :)')
                    self.showMessageBox('Done. Thanks for your help :)')
                elif self.total_number - annotations_count < 0:
                    self.setMessage(
                        'You are ambitious, thats gread, thank you')
                else:
                    self.setMessage(
                        '{0} anotations to go'.format(self.total_number -
                                                      annotations_count))
예제 #3
0
    def queueWorker(self):
        quitSignal = False
        while not quitSignal:
            job = SlideRunnerPlugin.pluginJob(self.inQueue.get())
            image = job.currentImage

            if (job.jobDescription ==
                    SlideRunnerPlugin.JobDescription.QUIT_PLUGIN_THREAD):
                # signal to exit this thread
                quitSignal = True
                continue
            print('OTSU plugin: received 1 image from queue')
            self.setProgressBar(0)

            rgb = np.copy(image[:, :, 0:3])

            gray = cv2.cvtColor(rgb, cv2.COLOR_RGB2GRAY)
            # OTSU thresholding
            ret, thresh = cv2.threshold(
                gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

            self.returnImage(np.float32(thresh / 255.0), job.procId)
            self.setMessage('OTSU calculation done.')
            print('OTSU plugin: done')
            self.setProgressBar(-1)
예제 #4
0
    def processWholeSlide(self, job: SlideRunnerPlugin.pluginJob):

        filename = job.slideFilename
        self.slide = openslide.open_slide(filename)

        # 1 HPF = 0.237 mm^2
        A = 0.237  # mm^2
        W_hpf_microns = np.sqrt(A * 4 / 3) * 1000  # in microns
        H_hpf_microns = np.sqrt(A * 3 / 4) * 1000  # in microns

        micronsPerPixel = self.slide.properties[openslide.PROPERTY_NAME_MPP_X]

        W_hpf = int(W_hpf_microns / float(micronsPerPixel))
        H_hpf = int(H_hpf_microns / float(micronsPerPixel))

        center = (int((job.coordinates[0] + 0.5 * job.coordinates[2])),
                  int((job.coordinates[1] + 0.5 * job.coordinates[3])))

        self.annos = list()
        myanno = SlideRunnerPlugin.rectangularAnnotation(
            center[0] - W_hpf / 2, center[1] - H_hpf / 2,
            center[0] + W_hpf / 2, center[1] + H_hpf / 2, 'High-Power Field')
        self.annos.append(myanno)

        self.updateAnnotations()
예제 #5
0
    def __init__(self, statusQueue: Queue):
        self.statusQueue = statusQueue
        self.annotationLabels = {
            'Detection':
            SlideRunnerPlugin.PluginAnnotationLabel(0, 'Detection',
                                                    [0, 180, 0, 255]),
        }
        self.p = Thread(target=self.queueWorker, daemon=True)
        self.p.start()

        pass
예제 #6
0
    def queueWorker(self):
        quitSignal = False
        while not quitSignal:
            job = SlideRunnerPlugin.pluginJob(self.inQueue.get())
            image = job.currentImage

            if (job.jobDescription ==
                    SlideRunnerPlugin.JobDescription.QUIT_PLUGIN_THREAD):
                # signal to exit this thread
                quitSignal = True
                continue

            print('Macenko norm plugin: received 1 image from queue')
            self.setProgressBar(0)

            print(job)
            if (job.annotations is not None) and len(job.annotations) > 0:
                if (job.annotations[0].annotationType ==
                        annotations.AnnotationType.AREA):
                    print('Found an area annotation - great!')
                    minC = job.annotations[0].minCoordinates()
                    maxC = job.annotations[0].maxCoordinates()

                    scaleX = (job.coordinates[2]) / job.currentImage.shape[1]
                    scaleY = (job.coordinates[3]) / job.currentImage.shape[0]

                    minC = np.array(
                        (max(0, int((minC.x - job.coordinates[0]) / scaleX)),
                         max(0, int((minC.y - job.coordinates[1]) / scaleY))))
                    maxC = np.array(
                        (min(job.currentImage.shape[1],
                             int((maxC.x - job.coordinates[0]) / scaleX)),
                         min(job.currentImage.shape[0],
                             int((maxC.y - job.coordinates[1]) / scaleY))))

                    rgb = np.copy(image[:, :, 0:3])
                    rgb = normalize(rgb,
                                    np.reshape(
                                        rgb[minC[1]:maxC[1],
                                            minC[0]:maxC[0], :], (-1, 3)),
                                    job=job)

            else:

                rgb = np.copy(image[:, :, 0:3])
                rgb = normalize(rgb, job=job)
            print('Stats: ', np.max(np.float32(rgb)), np.min(np.float32(rgb)),
                  np.mean(np.float32(rgb)),
                  np.float32(rgb).shape)

            self.returnImage(np.float32(rgb), job.procId)
            self.setMessage('Macenko normalization: done.')
            self.setProgressBar(-1)
class Plugin(SlideRunnerPlugin.SlideRunnerPlugin):
    version = 0.1
    shortName = 'Countdown'
    inQueue = Queue()
    outQueue = Queue()
    updateTimer = 0.5
    description = 'Count database objects down to zero'
    outputType = SlideRunnerPlugin.PluginOutputType.NO_OVERLAY
    pluginType = SlideRunnerPlugin.PluginTypes.WHOLESLIDE_PLUGIN
    configurationList = list(
        (SlideRunnerPlugin.PluginConfigurationEntry(uid=0,
                                                    name='Count down from',
                                                    initValue=300.,
                                                    minValue=0.0,
                                                    maxValue=1200.0), ))

    def __init__(self, statusQueue: Queue):
        self.statusQueue = statusQueue
        self.p = Thread(target=self.queueWorker, daemon=True)
        self.p.start()
        self.total_number = 300
        self.last_count = None

        pass

    def queueWorker(self):
        quitSignal = False
        while not quitSignal:
            job = SlideRunnerPlugin.pluginJob(self.inQueue.get())
            self.total_number = int(job.configuration[0])

            if (job.jobDescription ==
                    SlideRunnerPlugin.JobDescription.QUIT_PLUGIN_THREAD):
                # signal to exit this thread
                quitSignal = True
                continue

            database = job.openedDatabase

            annotations_count = len(database.annotations)
            if annotations_count != self.last_count:
                self.last_count = annotations_count
                if self.total_number - annotations_count == 0:
                    self.setMessage('Done. Thanks for your help :)')
                    self.showMessageBox('Done. Thanks for your help :)')
                elif self.total_number - annotations_count < 0:
                    self.setMessage(
                        'You are ambitious, thats gread, thank you')
                else:
                    self.setMessage(
                        '{0} anotations to go'.format(self.total_number -
                                                      annotations_count))
예제 #8
0
    def queueWorker(self):

        quitSignal = False
        while not quitSignal:
            job = SlideRunnerPlugin.pluginJob(self.inQueue.get())
            filename = job

            if (job.jobDescription ==
                    SlideRunnerPlugin.JobDescription.QUIT_PLUGIN_THREAD):
                # signal to exit this thread
                quitSignal = True
                continue

            self.processWholeSlide(job)
예제 #9
0
    def queueWorker(self):
        self.targetModel = -1
        self.lastCoordinates = ((None, None, None, None))
        modelInitialized = False

        oldThres = 0.0

        print('Queue worker running.')
        quitSignal = False
        while not quitSignal:
            job = SlideRunnerPlugin.pluginJob(self.inQueue.get())

            if not (modelInitialized):
                self.initializeModel()
                modelInitialized = True

            if (job.jobDescription ==
                    SlideRunnerPlugin.JobDescription.QUIT_PLUGIN_THREAD):
                # signal to exit this thread
                quitSignal = True
                continue

            print('Received request for: ', job.slideFilename, job.coordinates,
                  job.currentImage.shape)

            updateAnnos = False

            model = Models_Extensions[job.configuration[1]][1]
            if not (model == self.targetModel):
                self.targetModel = model
                self.loadModel(self.targetModel, int(job.configuration[2]))

            self.resultsStoreFilename = job.slideFilename + '_results_%s_%d.npz' % (
                Models_Extensions[job.configuration[1]][0],
                job.configuration[2])

            if (self.slideFilename is None):
                print('Processing complete slide ..')
                self.processWholeSlide(job)
                self.slideFilename = job.slideFilename

                updateAnnos = True

            if not (oldThres == job.configuration[0]):
                updateAnnos = True
                oldThres = job.configuration[0]
예제 #10
0
    def queueWorker(self):

        quitSignal = False
        oldSlide = ''
        while not quitSignal:
            job = SlideRunnerPlugin.pluginJob(self.inQueue.get())
            filename = job

            if (job.jobDescription ==
                    SlideRunnerPlugin.JobDescription.QUIT_PLUGIN_THREAD):
                # signal to exit this thread
                quitSignal = True
                continue
            if (job.slideFilename != oldSlide) or (job.trigger is not None):
                self.processWholeSlide(job)
                oldSlide = job.slideFilename
            else:
                print('Trigger:', job.trigger)
    def queueWorker(self):
        self.targetModel = ''
        self.targetEpoch = -1
        self.lastCoordinates = ((None, None, None, None))
        modelInitialized = False
        print('Queue worker running.')
        quitSignal = False
        while not quitSignal:
            job = SlideRunnerPlugin.pluginJob(self.inQueue.get())

            if not (modelInitialized):
                self.initializeModel()
                modelInitialized = True

            if (job.jobDescription ==
                    SlideRunnerPlugin.JobDescription.QUIT_PLUGIN_THREAD):
                # signal to exit this thread
                quitSignal = True
                continue

            print('Received request for: ', job.slideFilename, job.coordinates,
                  job.currentImage.shape)

            model = Models_Extensions[job.configuration[2]][1]
            if not (model == self.targetModel) and (self.targetEpoch
                                                    is not int(
                                                        job.configuration[3])):
                if (self.loadModel(model, int(job.configuration[3]))):
                    self.targetModel = model
                    self.targetEpoch = int(job.configuration[3])
                else:
                    self.targetModel = ''
                    self.targetEpoch = -1

            self.EXTENSION = Models_Extensions[job.configuration[2]][0]

            if (job.configuration[3] < 150):
                self.EXTENSION += "_epoch%d" % int(job.configuration[3])

            if (self.slideFilename is None):
                print('Processing complete slide ..')
                self.processWholeSlide(job)
                self.slideFilename = job.slideFilename
예제 #12
0
    def queueWorker(self):
        quitSignal = False
        while not quitSignal:
            job = SlideRunnerPlugin.pluginJob(self.inQueue.get())
            image = job.currentImage

            if (job.jobDescription == SlideRunnerPlugin.JobDescription.QUIT_PLUGIN_THREAD):
                # signal to exit this thread
                quitSignal=True
                continue

            print('Macenko norm plugin: received 1 image from queue')
            self.setProgressBar(0)

            rgb = np.copy(image[:,:,0:3])
            rgb = normalize(rgb)

            self.returnImage(np.float32(rgb), job.procId)
            self.setMessage('Macenko normalization: done.')
            self.setProgressBar(-1)
class Plugin(SlideRunnerPlugin.SlideRunnerPlugin):
    version = 0.1
    shortName = 'Re-Stained WSI Registration (Jiang et al.)'
    inQueue = Queue()
    outQueue = Queue()
    updateTimer=0.5
    outputType = SlideRunnerPlugin.PluginOutputType.RGB_OVERLAY
    description = 'Apply Jiang''s method for WSI co-registration'
    pluginType = SlideRunnerPlugin.PluginTypes.IMAGE_PLUGIN

    configurationList = list((
                            SlideRunnerPlugin.FilePickerConfigurationEntry(uid='file', name='Second WSI', mask='*.svs;;*.tif'),
                            SlideRunnerPlugin.PluginConfigurationEntry(uid='xoffset', name='X Offset', initValue=0, minValue=-2000, maxValue=2000.0),
                            SlideRunnerPlugin.PluginConfigurationEntry(uid='yoffset', name='Y Offset', initValue=0, minValue=-2000, maxValue=2000.0),
                            SlideRunnerPlugin.PushbuttonPluginConfigurationEntry(uid='match',name='Match')
                            ))


    def __init__(self, statusQueue:Queue):
        self.statusQueue = statusQueue
        self.p = Thread(target=self.queueWorker, daemon=True)
        self.p.start()
        
        pass

    def queueWorker(self):
        oldwsi=None
        quitSignal=False
        sl=None
        detected_offset = None
        sl_main = None
        while not quitSignal:
            job = SlideRunnerPlugin.pluginJob(self.inQueue.get())
            image = job.currentImage
            mainWSI = job.slideFilename
            
            if 'file' not in job.configuration:
                continue

            
            if (job.configuration['file'] != oldwsi) and (job.configuration['file']!='') and job.configuration['file']:
                sl = openslide.open_slide(job.configuration['file'])

            if (mainWSI):
                sl_main = openslide.open_slide(mainWSI)

            if (job.trigger is not None) and job.trigger.uid=='match':
                print('Trigger: ',job.trigger)
                self.setProgressBar(0)
                self.setMessage('Calculating optimum offset')
                tissue_detector = TissueDetector("LAB_Threshold", threshold=80) # option 1
                matcher_parameters = MatcherParameters()  # use the default parameters
                matcher = WSI_Matcher(tissue_detector, matcher_parameters)
                detected_offset = matcher.match(mainWSI, job.configuration['file'])
                self.setProgressBar(-1)
                updateConfig = list()
                updateConfig.append(SlideRunnerPlugin.PluginConfigUpdateEntry(SlideRunnerPlugin.PluginConfigurationType.SLIDER_WITH_FLOAT_VALUE, uid='xoffset', value=detected_offset[0]))
                updateConfig.append(SlideRunnerPlugin.PluginConfigUpdateEntry(SlideRunnerPlugin.PluginConfigurationType.SLIDER_WITH_FLOAT_VALUE, uid='yoffset', value=detected_offset[1]))
                self.updateConfiguration(SlideRunnerPlugin.PluginConfigUpdate(updateConfig))                

            if (sl) and (sl_main):
                self.setProgressBar(0)
                print('Reading from: ',job)

                zoomValue=job.coordinates[3]/job.currentImage.shape[0]
                print('Zoom value: ',zoomValue)
                act_level = np.argmin(np.abs(np.asarray(sl.level_downsamples)-zoomValue))
                closest_ds = sl_main.level_downsamples[np.argmin(np.abs(np.asarray(sl_main.level_downsamples)-zoomValue))]

                offset = (job.configuration['xoffset'],job.configuration['yoffset'])
                if (detected_offset is not None):
                    offset=detected_offset

                offset_scaled = [int(x/closest_ds) for x in offset]

                print('Scaled offset is: ',offset_scaled)


                imgarea_w=job.coordinates[2:4]
                size_im = (int(imgarea_w[0]/closest_ds), int(imgarea_w[1]/closest_ds))
                print('Image size: ',size_im)
                location = [int(x+y) for x,y in zip(job.coordinates[0:2],offset)]
                print('Location (original):',job.coordinates[0:2])
                print('Location (offset): ',location)
                img = sl.read_region(location=location, level=act_level, size=size_im)
                img = np.array(img.resize((job.currentImage.shape[1],job.currentImage.shape[0] )))

                self.returnImage(img, job.procId)
                self.setMessage('Align done.')
                self.setProgressBar(-1)


            if (job.jobDescription == SlideRunnerPlugin.JobDescription.QUIT_PLUGIN_THREAD):
                # signal to exit this thread
                quitSignal=True
                continue
            print('OTSU plugin: received 1 image from queue')
예제 #14
0
class Plugin(SlideRunnerPlugin.SlideRunnerPlugin):
    version = 0.0
    shortName = 'Mitosis Detection CMT'
    inQueue = Queue()
    outQueue = Queue()
    description = 'Direct Regression of Mitotic Activity'
    pluginType = SlideRunnerPlugin.PluginTypes.WHOLESLIDE_PLUGIN
    outputType = SlideRunnerPlugin.PluginOutputType.BINARY_MASK
    modelInitialized = False
    updateTimer = 0.1
    slideFilename = None

    models = list()

    for [dirname, model] in Models_Extensions:
        models.append(model)

    configurationList = list(
        (SlideRunnerPlugin.PluginConfigurationEntry(uid=0,
                                                    name='Detection Threshold',
                                                    initValue=0.3,
                                                    minValue=0.3,
                                                    maxValue=1.0),
         SlideRunnerPlugin.ComboboxPluginConfigurationEntry(uid=1,
                                                            name='Model',
                                                            options=models),
         SlideRunnerPlugin.PluginConfigurationEntry(uid=2,
                                                    name='Epoch',
                                                    initValue=150.00,
                                                    minValue=1.0,
                                                    maxValue=150.0)))

    def __init__(self, statusQueue: Queue):
        self.statusQueue = statusQueue
        self.preprocessQueue = Queue()
        self.preprocessorOutQueue = Queue()
        self.slide = None
        self.p = Thread(target=self.queueWorker, daemon=True)
        self.p.start()
        self.currentModelCheckpoint = -1
        self.pp = dict()
        for k in range(10):
            self.pp[k] = Thread(target=self.preprocessWorker, daemon=True)
            self.pp[k].start()

    def worldCoordinatesToGridcoordinates(self, x,
                                          y) -> ((int, int), (int, int)):

        tile_indices = (int(np.floor(x / TILESIZE_X)),
                        int(np.floor(y / TILESIZE_Y)))
        onTile_coordinates = (int(np.mod(x, TILESIZE_X)),
                              int(np.mod(y, TILESIZE_Y)))

        return (tile_indices, onTile_coordinates)

    def gridCoordinatesToWorldCoordinates(
        self, tile_indices: (int, int), onTile_coordinates: (int, int)
    ) -> (int, int):

        return (int(tile_indices[0] * TILESIZE_X + onTile_coordinates[0]),
                int(tile_indices[1] * TILESIZE_Y + onTile_coordinates[1]))

    def conv_conv_pool(self,
                       input_,
                       n_filters,
                       training,
                       name,
                       pool=True,
                       activation=tf.nn.relu):
        """{Conv -> BN -> RELU}x2 -> {Pool, optional}

        Args:
            input_ (4-D Tensor): (batch_size, H, W, C)
            n_filters (list): number of filters [int, int]
            training (1-D Tensor): Boolean Tensor
            name (str): name postfix
            pool (bool): If True, MaxPool2D
            activation: Activaion functions

        Returns:
            net: output of the Convolution operations
            pool (optional): output of the max pooling operations
        """
        net = input_

        with tf.variable_scope("layer{}".format(name)):
            for i, F in enumerate(n_filters):
                net = tf.layers.conv2d(net,
                                       F, (3, 3),
                                       activation=None,
                                       padding='same',
                                       name="conv_{}".format(i + 1))
                net = tf.layers.batch_normalization(net,
                                                    training=training,
                                                    name="bn_{}".format(i + 1))
                net = activation(net, name="relu{}_{}".format(name, i + 1))

            if pool is False:
                return net

            pool = tf.layers.max_pooling2d(net, (2, 2),
                                           strides=(2, 2),
                                           name="pool_{}".format(name))

            return net, pool

    def upsample_concat(self, inputA, input_B, name):
        """Upsample `inputA` and concat with `input_B`

        Args:
            input_A (4-D Tensor): (N, H, W, C)
            input_B (4-D Tensor): (N, 2*H, 2*H, C2)
            name (str): name of the concat operation

        Returns:
            output (4-D Tensor): (N, 2*H, 2*W, C + C2)
        """
        upsample = self.upsampling_2D(inputA, size=(2, 2), name=name)

        return tf.concat([upsample, input_B],
                         axis=-1,
                         name="concat_{}".format(name))

    def upsampling_2D(self, tensor, name, size=(2, 2)):
        """Upsample/Rescale `tensor` by size

        Args:
            tensor (4-D Tensor): (N, H, W, C)
            name (str): name of upsampling operations
            size (tuple, optional): (height_multiplier, width_multiplier)
                (default: (2, 2))

        Returns:
            output (4-D Tensor): (N, h_multiplier * H, w_multiplier * W, C)
        """
        H, W, _ = tensor.get_shape().as_list()[1:]

        H_multi, W_multi = size
        target_H = H * H_multi
        target_W = W * W_multi

        return tf.image.resize_nearest_neighbor(
            tensor, (target_H, target_W), name="upsample_{}".format(name))

    def loadModel(self, modelpath, epoch):
        pluginDir = os.path.dirname(os.path.realpath(__file__))
        modelpath = pluginDir + os.sep + modelpath
        modelFound = False
        for dirpath, dirnames, files in os.walk(modelpath):
            for fname in files:
                realfname, ext = os.path.splitext(fname)
                if ('_%d.ckpt' % epoch) in realfname:
                    if (modelpath + os.sep + realfname
                            is not self.currentModelCheckpoint):
                        try:
                            self.saver.restore(self.sess,
                                               modelpath + os.sep + realfname)
                            print('Restored ', realfname)
                            modelFound = True
                            self.currentModelCheckpoint = modelpath + os.sep + realfname
                        except:
                            print('Unable to load', realfname)
                            exit()
        if not modelFound:
            print('Model not found in ', modelpath)
            exit()

    def initializeModel(self):
        self.model = dict()

        self.setMessage('DirReg init.')
        self.setProgressBar(0)

        tf.reset_default_graph()
        self.netEpoch = tf.placeholder(tf.float32, name="epoch")

        self.model['X'] = tf.placeholder(
            tf.float32, shape=[None, IMAGESIZE_Y, IMAGESIZE_X, 3], name="X0")
        self.model['y'] = tf.placeholder(
            tf.float32, shape=[None, IMAGESIZE_Y, IMAGESIZE_X, 1], name="y")
        self.model['mode'] = tf.placeholder(tf.bool, name="mode")
        self.trainStep = tf.placeholder(tf.float32, name="trainStep")

        self.model['pred'] = make_net(self.model['X'], self.model['mode'])

        update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)

        summary_op = tf.summary.merge_all()

        self.sess = tf.Session()

        print('Initializing variables')
        init = tf.global_variables_initializer()
        self.sess.run(init)
        print('Done.')

        self.saver = tf.train.Saver()

    def queueWorker(self):
        self.targetModel = -1
        self.lastCoordinates = ((None, None, None, None))
        modelInitialized = False

        oldThres = 0.0

        print('Queue worker running.')
        quitSignal = False
        while not quitSignal:
            job = SlideRunnerPlugin.pluginJob(self.inQueue.get())

            if not (modelInitialized):
                self.initializeModel()
                modelInitialized = True

            if (job.jobDescription ==
                    SlideRunnerPlugin.JobDescription.QUIT_PLUGIN_THREAD):
                # signal to exit this thread
                quitSignal = True
                continue

            print('Received request for: ', job.slideFilename, job.coordinates,
                  job.currentImage.shape)

            updateAnnos = False

            model = Models_Extensions[job.configuration[1]][1]
            if not (model == self.targetModel):
                self.targetModel = model
                self.loadModel(self.targetModel, int(job.configuration[2]))

            self.resultsStoreFilename = job.slideFilename + '_results_%s_%d.npz' % (
                Models_Extensions[job.configuration[1]][0],
                job.configuration[2])

            if (self.slideFilename is None):
                print('Processing complete slide ..')
                self.processWholeSlide(job)
                self.slideFilename = job.slideFilename

                updateAnnos = True

            if not (oldThres == job.configuration[0]):
                updateAnnos = True
                oldThres = job.configuration[0]

    def preprocessWorker(self):
        while (True):
            if self.preprocessorOutQueue.qsize() < 500:
                (tile_x, tile_y, filename, coordinates,
                 tile_current) = self.preprocessQueue.get()
                sl = openslide.open_slide(filename)

                tn = sl.read_region(
                    location=(int(coordinates[0] - margin),
                              int(coordinates[1] - margin)),
                    level=0,
                    size=(int(coordinates[2] - coordinates[0] + 2 * margin),
                          int(coordinates[3] - coordinates[1] + 2 * margin)))

                X_test = np.float32(
                    cv2.cvtColor(np.array(tn), cv2.COLOR_BGRA2RGB))[:, :, ::-1]
                X_test = cv2.cvtColor(X_test, cv2.COLOR_BGR2RGB)
                #X_test = np.reshape(X_test, newshape=[1,512,512,3])

                self.preprocessorOutQueue.put(
                    (X_test, tile_x, tile_y, coordinates, tile_current))
            else:
                time.sleep(0.1)

    def processWholeSlide(self, job):
        self.setMessage('DirReg preparing ..')
        self.setProgressBar(0)

        filename = job.slideFilename
        sl = openslide.open_slide(filename)

        if os.path.isfile(self.resultsStoreFilename):
            resultsStore = np.load(self.resultsStoreFilename)
            self.tilesProcessed = resultsStore['tilesProcessed'].tolist()
            self.scores = resultsStore['scores'].tolist()
        else:
            self.tilesProcessed = list()
            self.scores = list()

        self.slide = sl
        tiles_total_x = int(np.floor(sl.dimensions[0] / TILESIZE_X))
        tiles_total_y = int(np.floor(sl.dimensions[1] / TILESIZE_Y))

        tiles_total = tiles_total_x * tiles_total_y

        tiles2process_total = tile_current = 0
        for tile_y in range(tiles_total_y):
            for tile_x in range(tiles_total_x):
                tile_current += 1

                if ([tile_x, tile_y] in self.tilesProcessed):
                    continue

                coords_p1 = self.gridCoordinatesToWorldCoordinates(
                    (tile_x, tile_y), (0, 0))
                coords_p2 = self.gridCoordinatesToWorldCoordinates(
                    (tile_x, tile_y), (TILESIZE_X, TILESIZE_Y))
                #                out_fullscale = self.processTile(sl, coordinates=(coords_p1+coords_p2))
                coordinates = coords_p1 + coords_p2
                self.preprocessQueue.put(
                    (tile_x, tile_y, filename, coordinates, tile_current))
                tiles2process_total += 1

        out_tile_current = 0
        batchsize = 20
        batchcounter = 0
        batch = np.zeros((batchsize, IMAGESIZE_Y, IMAGESIZE_X, 3))
        batchTiles = list()

        print('Images to be processed count: ', tiles2process_total)
        import time

        gt = 0
        gtf = 0
        out_tile_count = 0

        while (out_tile_count < tiles2process_total):
            t = time.time()
            (X_test, tile_x, tile_y, coordinates,
             out_tile_current) = self.preprocessorOutQueue.get()
            gt += time.time() - t

            batch[batchcounter] = X_test
            batchTiles.append([tile_x, tile_y])
            batchcounter += 1
            out_tile_count += 1

            if (batchcounter == batchsize) or (out_tile_count
                                               == tiles2process_total):
                gtf -= time.time()
                out = self.evaluateImage(batch[0:batchcounter])
                gtf += time.time()
                #                print('TensorFlow: %.2f s, Preproc: %.2f s, QSize: %d ' % (gtf, gt, self.preprocessorOutQueue.qsize()))
                for k in range(out.shape[0]):
                    self.scores.append(out[k])
                self.tilesProcessed += batchTiles
                print('Length of out:', len(self.scores),
                      len(self.tilesProcessed))
                if (len(self.scores) != len(self.tilesProcessed)):
                    print('Wrong length!')
                    sys.exit()
                batchTiles = list()
                batchcounter = 0

                self.setProgressBar(100 * out_tile_count / tiles_total)

            # split into chunks
            if (self.inQueue.qsize() > 0):  # new request pending
                self.saveState()
                return

        print('Length of out:', len(self.scores), len(self.tilesProcessed),
              out_tile_count)

        self.saveState()
        self.setProgressBar(-1)
        self.setMessage('DirReg calculation done.')

    def saveState(self):
        np.savez_compressed(self.resultsStoreFilename,
                            scores=self.scores,
                            tilesProcessed=self.tilesProcessed)

    def exceptionHandlerOnExit(self):
        if (self.slide):
            self.saveState()

    def getAnnotations(self):
        return self.annos

    def evaluateImage(self, image):
        #        if (np.all(np.mean(image, axis=(1,2,3))<200)):
        [y_act] = self.sess.run([self.model['pred']],
                                feed_dict={
                                    self.model['X']: image,
                                    self.model['mode']: False
                                })

        return y_act
class Plugin(SlideRunnerPlugin.SlideRunnerPlugin):
    version = 0.0
    shortName = 'Mitosis UNET offline (model select, downsampled)'
    inQueue = Queue()
    outQueue = Queue()
    description = 'Find mitotic figures using UNET'
    pluginType = SlideRunnerPlugin.PluginTypes.WHOLESLIDE_PLUGIN
    outputType = SlideRunnerPlugin.PluginOutputType.BINARY_MASK
    modelInitialized = False
    updateTimer = 1.0
    slideFilename = None

    models = list()

    for [dirname, model] in Models_Extensions:
        models.append(model)

    configurationList = list((
        SlideRunnerPlugin.PluginConfigurationEntry(uid=0,
                                                   name='Density Radius',
                                                   initValue=0.00,
                                                   minValue=0.0,
                                                   maxValue=255.0),
        SlideRunnerPlugin.PluginConfigurationEntry(uid=1,
                                                   name='Amplification',
                                                   initValue=4.00,
                                                   minValue=1.0,
                                                   maxValue=10.0),
        SlideRunnerPlugin.ComboboxPluginConfigurationEntry(uid=2,
                                                           name='Model',
                                                           options=models),
        SlideRunnerPlugin.PluginConfigurationEntry(uid=3,
                                                   name='Epoch',
                                                   initValue=134.00,
                                                   minValue=130.0,
                                                   maxValue=150.0),
    ))

    def __init__(self, statusQueue: Queue):
        self.statusQueue = statusQueue
        self.preprocessQueue = Queue()
        self.preprocessorOutQueue = Queue()
        self.p = Thread(target=self.queueWorker, daemon=True)
        self.p.start()
        self.EXTENSION = ''
        self.pp = dict()
        self.lastPlugin = -1
        for k in range(5):
            self.pp[k] = Thread(target=self.preprocessWorker, daemon=True)
            self.pp[k].start()

    def conv_conv_pool(self,
                       input_,
                       n_filters,
                       training,
                       name,
                       pool=True,
                       activation=tf.nn.relu):
        """{Conv -> BN -> RELU}x2 -> {Pool, optional}

        Args:
            input_ (4-D Tensor): (batch_size, H, W, C)
            n_filters (list): number of filters [int, int]
            training (1-D Tensor): Boolean Tensor
            name (str): name postfix
            pool (bool): If True, MaxPool2D
            activation: Activaion functions

        Returns:
            net: output of the Convolution operations
            pool (optional): output of the max pooling operations
        """
        net = input_

        with tf.variable_scope("layer{}".format(name)):
            for i, F in enumerate(n_filters):
                net = tf.layers.conv2d(net,
                                       F, (3, 3),
                                       activation=None,
                                       padding='same',
                                       name="conv_{}".format(i + 1))
                net = tf.layers.batch_normalization(net,
                                                    training=training,
                                                    name="bn_{}".format(i + 1))
                net = activation(net, name="relu{}_{}".format(name, i + 1))

            if pool is False:
                return net

            pool = tf.layers.max_pooling2d(net, (2, 2),
                                           strides=(2, 2),
                                           name="pool_{}".format(name))

            return net, pool

    def upsample_concat(self, inputA, input_B, name):
        """Upsample `inputA` and concat with `input_B`

        Args:
            input_A (4-D Tensor): (N, H, W, C)
            input_B (4-D Tensor): (N, 2*H, 2*H, C2)
            name (str): name of the concat operation

        Returns:
            output (4-D Tensor): (N, 2*H, 2*W, C + C2)
        """
        upsample = self.upsampling_2D(inputA, size=(2, 2), name=name)

        return tf.concat([upsample, input_B],
                         axis=-1,
                         name="concat_{}".format(name))

    def upsampling_2D(self, tensor, name, size=(2, 2)):
        """Upsample/Rescale `tensor` by size

        Args:
            tensor (4-D Tensor): (N, H, W, C)
            name (str): name of upsampling operations
            size (tuple, optional): (height_multiplier, width_multiplier)
                (default: (2, 2))

        Returns:
            output (4-D Tensor): (N, h_multiplier * H, w_multiplier * W, C)
        """
        H, W, _ = tensor.get_shape().as_list()[1:]

        H_multi, W_multi = size
        target_H = H * H_multi
        target_W = W * W_multi

        return tf.image.resize_nearest_neighbor(
            tensor, (target_H, target_W), name="upsample_{}".format(name))

    def make_unet(self, Xhinted, training):
        """Build a U-Net architecture

        Args:
            X (4-D Tensor): (N, H, W, C)
            training (1-D Tensor): Boolean Tensor is required for batchnormalization layers

        Returns:
            output (4-D Tensor): (N, H, W, C)
                Same shape as the `input` tensor

        Notes:
            U-Net: Convolutional Networks for Biomedical Image Segmentation
            https://arxiv.org/abs/1505.04597
        """
        net = Xhinted / 127.5 - 1
        net = tf.layers.conv2d(net, 4, (1, 1), name="color_space_adjust")
        conv1, pool1 = self.conv_conv_pool(net, [8, 8], training, name=1)
        conv2, pool2 = self.conv_conv_pool(pool1, [16, 16], training, name=2)
        conv3, pool3 = self.conv_conv_pool(pool2, [32, 32], training, name=3)
        conv4, pool4 = self.conv_conv_pool(pool3, [64, 64], training, name=4)
        conv5 = self.conv_conv_pool(pool4, [128, 128],
                                    training,
                                    name=5,
                                    pool=False)

        up6 = self.upsample_concat(conv5, conv4, name=6)
        conv6 = self.conv_conv_pool(up6, [64, 64],
                                    training,
                                    name=6,
                                    pool=False)

        up7 = self.upsample_concat(conv6, conv3, name=7)
        conv7 = self.conv_conv_pool(up7, [32, 32],
                                    training,
                                    name=7,
                                    pool=False)

        up8 = self.upsample_concat(conv7, conv2, name=8)
        conv8 = self.conv_conv_pool(up8, [16, 16],
                                    training,
                                    name=8,
                                    pool=False)

        up9 = self.upsample_concat(conv8, conv1, name=9)
        conv9 = self.conv_conv_pool(up9, [8, 8], training, name=9, pool=False)

        return tf.layers.conv2d(conv9,
                                1, (1, 1),
                                name='final',
                                activation=tf.nn.sigmoid,
                                padding='same')

    def initializeModel(self):
        self.model = dict()

        tf.reset_default_graph()
        self.netEpoch = tf.placeholder(tf.float32, name="epoch")

        self.model['X'] = tf.placeholder(
            tf.float32, shape=[None, IMAGESIZE_Y, IMAGESIZE_X, 3], name="X0")
        self.model['y'] = tf.placeholder(
            tf.float32, shape=[None, IMAGESIZE_Y, IMAGESIZE_X, 1], name="y")
        self.model['mode'] = tf.placeholder(tf.bool, name="mode")
        self.trainStep = tf.placeholder(tf.float32, name="trainStep")

        self.model['pred'] = self.make_unet(self.model['X'],
                                            self.model['mode'])

        pluginDir = os.path.dirname(os.path.realpath(__file__))
        update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)

        summary_op = tf.summary.merge_all()

        self.sess = tf.Session()

        print('Initializing variables')
        init = tf.global_variables_initializer()
        self.sess.run(init)
        print('Done.')
        self.currentModelCheckpoint = ''

        self.saver = tf.train.Saver()

    def loadModel(self, modelpath, epoch):
        pluginDir = os.path.dirname(os.path.realpath(__file__))
        modelpath = pluginDir + os.sep + modelpath
        modelFound = False
        for dirpath, dirnames, files in os.walk(modelpath):
            for fname in files:
                realfname, ext = os.path.splitext(fname)
                if ('_%d.ckpt' % epoch) in realfname:
                    if (modelpath + os.sep + realfname
                            is not self.currentModelCheckpoint):
                        try:
                            self.saver.restore(self.sess,
                                               modelpath + os.sep + realfname)
                            print('Restored ', realfname)
                            modelFound = True
                            self.currentModelCheckpoint = modelpath + os.sep + realfname
                        except:
                            print('Unable to load', realfname, 'in', modelpath)
                            exit()

        if not modelFound:
            print('Model not found in ', modelpath)
            self.setMessage('Model not found in' + str(modelpath))
            return False
        return True

    def queueWorker(self):
        self.targetModel = ''
        self.targetEpoch = -1
        self.lastCoordinates = ((None, None, None, None))
        modelInitialized = False
        print('Queue worker running.')
        quitSignal = False
        while not quitSignal:
            job = SlideRunnerPlugin.pluginJob(self.inQueue.get())

            if not (modelInitialized):
                self.initializeModel()
                modelInitialized = True

            if (job.jobDescription ==
                    SlideRunnerPlugin.JobDescription.QUIT_PLUGIN_THREAD):
                # signal to exit this thread
                quitSignal = True
                continue

            print('Received request for: ', job.slideFilename, job.coordinates,
                  job.currentImage.shape)

            model = Models_Extensions[job.configuration[2]][1]
            if not (model == self.targetModel) and (self.targetEpoch
                                                    is not int(
                                                        job.configuration[3])):
                if (self.loadModel(model, int(job.configuration[3]))):
                    self.targetModel = model
                    self.targetEpoch = int(job.configuration[3])
                else:
                    self.targetModel = ''
                    self.targetEpoch = -1

            self.EXTENSION = Models_Extensions[job.configuration[2]][0]

            if (job.configuration[3] < 150):
                self.EXTENSION += "_epoch%d" % int(job.configuration[3])

            if (self.slideFilename is None):
                print('Processing complete slide ..')
                self.processWholeSlide(job)
                self.slideFilename = job.slideFilename

    def preprocessWorker(self):
        while (True):
            if self.preprocessorOutQueue.qsize() < 100:
                (tile_x, tile_y, sl, coordinates,
                 tile_current) = self.preprocessQueue.get()

                try:
                    tn = sl.read_region(
                        location=(int(coordinates[0] - margin),
                                  int(coordinates[1] - margin)),
                        level=0,
                        size=(int(coordinates[2] - coordinates[0] +
                                  2 * margin),
                              int(coordinates[3] - coordinates[1] +
                                  2 * margin)))
                    X_test = np.float32(
                        cv2.cvtColor(np.array(tn),
                                     cv2.COLOR_BGRA2RGB))[:, :, ::-1]
                    self.preprocessorOutQueue.put(
                        (X_test, tile_x, tile_y, coordinates, tile_current))

                except Exception as e:
                    self.preprocessQueue.put(
                        (tile_x, tile_y, sl, coordinates, tile_current))
                    print('Did not work, message is: ', e)
                    print('Tiles: ', tile_x, tile_y)
                    print('Coordinates:')
                    print('Slide dimensions:', sl.dimensions)
                    print('Coordinates are:',
                          (int(coordinates[0] - margin), int(coordinates[1])),
                          (int(coordinates[2] - coordinates[0] + 2 * margin),
                           int(coordinates[3] - coordinates[1] + 1 * margin)))

            else:
                time.sleep(0.1)

    def worldCoordinatesToGridcoordinates(self, x,
                                          y) -> ((int, int), (int, int)):

        tile_indices = (int(np.floor(x / TILESIZE_X)),
                        int(np.floor(y / TILESIZE_Y)))
        onTile_coordinates = (int(np.mod(x, TILESIZE_X)),
                              int(np.mod(y, TILESIZE_Y)))

        return (tile_indices, onTile_coordinates)

    def gridCoordinatesToWorldCoordinates(
        self, tile_indices: (int, int), onTile_coordinates: (int, int)
    ) -> (int, int):

        return (int(tile_indices[0] * TILESIZE_X + onTile_coordinates[0]),
                int(tile_indices[1] * TILESIZE_Y + onTile_coordinates[1]))

    def processWholeSlide(self, job):
        filename = job.slideFilename
        sl = openslide.open_slide(filename)
        ds = 32
        self.imageMap = np.zeros(
            (int(sl.dimensions[1] / ds), int(sl.dimensions[0] / ds)))
        self.slide = sl
        tiles_total_x = int(np.ceil(sl.dimensions[0] / TILESIZE_X))
        tiles_total_y = int(np.ceil(sl.dimensions[1] / TILESIZE_Y))

        tiles_total = tiles_total_x * tiles_total_y

        basefilename = job.slideFilename + self.EXTENSION + '_UNET.npz'

        if not os.path.exists(basefilename) and (self.targetModel is not ''):
            tiles2process_total = tile_current = 0
            for tile_y in range(tiles_total_y):
                for tile_x in range(tiles_total_x):
                    tile_current += 1

                    coords_p1 = self.gridCoordinatesToWorldCoordinates(
                        (tile_x, tile_y), (0, 0))
                    coords_p2 = self.gridCoordinatesToWorldCoordinates(
                        (tile_x, tile_y), (TILESIZE_X, TILESIZE_Y))
                    #                out_fullscale = self.processTile(sl, coordinates=(coords_p1+coords_p2))
                    coordinates = coords_p1 + coords_p2
                    self.preprocessQueue.put(
                        (tile_x, tile_y, sl, coordinates, tile_current))
                    tiles2process_total += 1

            out_tile_current = 0
            out_tile_num = 0
            ds = 32
            print('Image size:', sl.dimensions)
            print('Images to be processed count: ', tiles2process_total)
            while (out_tile_num < tiles2process_total):
                out_tile_num += 1
                (X_test, tile_x, tile_y, coordinates,
                 out_tile_current) = self.preprocessorOutQueue.get()
                out_fullscale = self.evaluateImage(X_test)
                target_size = (int(TILESIZE_Y / ds), int(TILESIZE_X / ds))
                out_ds = cv2.resize(out_fullscale, dsize=(target_size))
                target_x = int(coordinates[0] / ds)
                target_y = int(coordinates[1] / ds)
                #print('from coord: ',coordinates, target_x, target_y)
                if (coordinates[0] + IMAGESIZE_X < sl.dimensions[0]) and (
                        coordinates[1] + IMAGESIZE_Y < sl.dimensions[1]):
                    self.imageMap[target_y:target_y + target_size[0],
                                  target_x:target_x + target_size[1]] = out_ds
                else:
                    remainsize = self.imageMap[target_y:target_y +
                                               target_size[0],
                                               target_x:target_x +
                                               target_size[1]].shape
                    self.imageMap[target_y:target_y + target_size[0],
                                  target_x:target_x +
                                  target_size[1]] = out_ds[0:remainsize[0],
                                                           0:remainsize[1]]

                self.setProgressBar(100 * out_tile_num / tiles_total)

            np.savez(basefilename, imageMap=self.imageMap)
        elif os.path.exists(basefilename):
            f = np.load(basefilename)
            self.imageMap = f['imageMap']
        else:
            self.setMessage('No cache and no model found.')

        x, y, w, h = job.coordinates
        retImg = cv2.getRectSubPix(np.float32(np.copy(self.imageMap)),
                                   (int(w / ds), int(h / ds)),
                                   center=((x - 0.5 * w) / ds,
                                           (y - 0.5 * h) / ds))
        retImg = cv2.resize(retImg,
                            dsize=(job.currentImage.shape[1],
                                   job.currentImage.shape[0]))
        self.returnImage(retImg, job.procId)

        self.setProgressBar(-1)

    def evaluateImage(self, image):
        import cv2

        overlap = 128
        margin = 64
        gridCoords_relative = list()
        # split into chunks

        X_test = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        X_test = np.reshape(X_test, newshape=[1, 512, 512, 3])

        y_est = self.sess.run([self.model['pred']],
                              feed_dict={
                                  self.model['X']: X_test,
                                  self.model['mode']: False
                              })

        y_est = np.float32(np.asarray(y_est).squeeze())
        mapOut = y_est[margin:IMAGESIZE_Y - margin,
                       margin:IMAGESIZE_X - margin]

        return mapOut
    def queueWorker(self):
        debugModule = False
        quitSignal = False
        oldFilename = ''
        oldArchive = ''
        oldSlide = ''
        oldDBfile = ''
        oldCoordinates = [-1, -1, -1, -1]
        oldThres = -1
        oldSource = -1
        self.ds = 32
        while not quitSignal:
            job = SlideRunnerPlugin.pluginJob(self.inQueue.get())
            print(job)
            print(job.configuration)

            if (job.jobDescription ==
                    SlideRunnerPlugin.JobDescription.QUIT_PLUGIN_THREAD):
                # signal to exit this thread
                quitSignal = True
                continue

            sourceChanged = job.configuration['source'] != oldSource
            if (job.configuration['source'] == 0):
                if not hasattr(job.openedDatabase, 'dbfilename'):
                    # DB not open yet
                    continue
                job.configuration['dbfile'] = job.openedDatabase.dbfilename

            dbfilechanged = job.configuration['dbfile'] != oldDBfile

            if not (sourceChanged) and (
                    job.configuration['file'] == oldArchive
            ) and (job.configuration['threshold']
                   == oldThres) and (job.slideFilename == oldSlide) and np.all(
                       job.coordinates == oldCoordinates) and not (
                           dbfilechanged):
                continue

            if not (os.path.exists(job.configuration['file'])) and (
                    job.configuration['source'] == 1):
                continue
            self.sendAnnotationLabelUpdate()

            fileChanged = job.configuration['file'] != oldArchive
            oldDBfile = job.configuration['dbfile']
            slideChanged = job.slideFilename != oldSlide
            thresChanged = job.configuration['threshold'] != oldThres

            oldArchive = job.configuration['file']
            oldThres = job.configuration['threshold']
            oldSlide = job.slideFilename
            oldSource = job.configuration['source']
            oldCoordinates = job.coordinates
            [foo, self.ext] = os.path.splitext(oldArchive)
            self.ext = self.ext.upper()
            self.slideObj = openslide.open_slide(job.slideFilename)

            if (fileChanged):
                if (self.ext == '.P') or (
                        self.ext
                        == '.BZ2'):  # Pickled format - results for many slides
                    if (self.ext == '.BZ2'):
                        self.resultsArchive = pickle.load(
                            bz2.BZ2File(oldArchive, 'rb'))
                        print('Opened bz2-compressed results container.')
                    else:
                        self.resultsArchive = pickle.load(
                            open(oldArchive, 'rb'))

            print('Sourcechanged:', sourceChanged, 'dbfilechanged:',
                  dbfilechanged, (len(job.configuration['dbfile']) > 0))
            if (sourceChanged or dbfilechanged or slideChanged) and (
                (job.configuration['source'] == 2) or
                (job.configuration['source']
                 == 0)) and (len(job.configuration['dbfile']) > 0):
                self.slideObj = openslide.open_slide(job.slideFilename)
                self.downsampledMap = np.zeros(
                    (int(self.slideObj.dimensions[1] / self.ds),
                     int(self.slideObj.dimensions[0] / self.ds)))
                self.newDB = Database()

                self.newDB.open(job.configuration['dbfile'])
                allClasses = self.newDB.getAllClasses()
                mitosisClass = -1
                for clsname, clsuid, col in allClasses:
                    if (mitosisClass
                            == -1) and ('MITO' in clsname.upper()) and (
                                'LOOK' not in clsname.upper()):
                        mitosisClass = clsuid

                pname, fname = os.path.split(job.slideFilename)
                uid = self.newDB.findSlideWithFilename(fname, pname)
                self.newDB.loadIntoMemory(uid)
                for anno in self.newDB.annotations:
                    if (self.newDB.annotations[anno].agreedClass ==
                            mitosisClass):
                        annodet = self.newDB.annotations[anno]
                        self.downsampledMap[int(annodet.y1 / self.ds),
                                            int(annodet.x1 / self.ds)] += 1

                else:
                    self.setMessage('No DB open.')

            if ((sourceChanged and job.configuration['source'] == 1) or
                (slideChanged) or
                (thresChanged)) and len(job.configuration['file']) > 0:
                pname, fname = os.path.split(job.slideFilename)
                print('Stage 6')
                if (oldFilename is not fname) or (slideChanged):
                    # process slide
                    self.annos = list()

                    if (fname not in self.resultsArchive):
                        self.setMessage('Slide ' + str(fname) +
                                        ' not found in results file.')
                        print('List of files is: ', self.resultsArchive.keys())
                        continue

                        oldFilename = fname

                    uniqueLabels = np.unique(
                        np.array(self.resultsArchive[fname])[:, 4])

                    self.annotationLabels = dict()
                    for key, label in enumerate(uniqueLabels):
                        self.annotationLabels[
                            label] = SlideRunnerPlugin.PluginAnnotationLabel(
                                0, 'Class %d' % label,
                                self.COLORS[key % len(self.COLORS)])

                    if (job.configuration['source'] == 1):
                        self.downsampledMap = np.zeros(
                            (int(self.slideObj.dimensions[1] / self.ds),
                             int(self.slideObj.dimensions[0] / self.ds)))
                    print('Downsampled image: ', self.downsampledMap.shape)

                    for idx in range(len(self.resultsArchive[fname])):
                        row = self.resultsArchive[fname][idx]
                        if (row[5] > job.configuration['threshold']):
                            myanno = annotations.rectangularAnnotation(
                                uid=idx,
                                x1=row[0],
                                x2=row[2],
                                y1=row[1],
                                y2=row[3],
                                text='%.2f' % row[5],
                                pluginAnnotationLabel=self.annotationLabels[
                                    row[4]])
                            if (job.configuration['source'] == 1):
                                self.downsampledMap[int(
                                    (row[1] + row[3]) / 2 / self.ds),
                                                    int((row[0] + row[2]) / 2 /
                                                        self.ds)] += 1
                            self.annos.append(myanno)

                    self.sendAnnotationLabelUpdate()

                elif (self.ext == '.TXT'):  # Assume MS Coco format
                    self.annos = list()

                    self.resultsArchive = np.loadtxt(
                        oldArchive,
                        dtype={
                            'names':
                            ('label', 'confidence', 'x', 'y', 'w', 'h'),
                            'formats': ('U30', 'f4', 'i4', 'i4', 'i4', 'i4')
                        },
                        skiprows=0,
                        delimiter=' ')
                    uniqueLabels = np.unique(self.resultsArchive['label'])

                    self.annotationLabels = dict()
                    for key, label in enumerate(uniqueLabels):
                        self.annotationLabels[
                            label] = SlideRunnerPlugin.PluginAnnotationLabel(
                                0, label, self.COLORS[key % len(self.COLORS)])

                    self.sendAnnotationLabelUpdate()

                    self.slideObj = openslide.open_slide(job.slideFilename)
                    self.ds = 32
                    if (job.configuration['source'] == 1):
                        self.downsampledMap = np.zeros(
                            (int(self.slideObj.dimensions[1] / self.ds),
                             int(self.slideObj.dimensions[0] / self.ds)))
                    print('Downsampled image: ', self.downsampledMap.shape)

                    for idx in range(len(self.resultsArchive)):
                        row = self.resultsArchive[idx]
                        if (row[5] > job.configuration['threshold']):
                            if (job.configuration['source'] == 1):
                                self.downsampledMap[int(
                                    (row['y'] - row['h'] / 2) / self.ds),
                                                    int((row['x'] -
                                                         row['w'] / 2) /
                                                        self.ds)] += 1

                            myanno = annotations.rectangularAnnotation(
                                uid=idx,
                                x1=row['x'],
                                x2=row['y'],
                                y1=row['x'] + row['w'],
                                y2=row['y'] + row['h'],
                                text='%.2f' % row['confidence'],
                                pluginAnnotationLabel=self.annotationLabels[
                                    row['label']])
                            self.annos.append(myanno)

            print('returning overlay...')
            A = 2.37  # mm^2
            W_hpf_microns = np.sqrt(A * 4 / 3) * 1000  # in microns
            H_hpf_microns = np.sqrt(A * 3 / 4) * 1000  # in microns

            micronsPerPixel = self.slideObj.properties[
                openslide.PROPERTY_NAME_MPP_X]

            W_hpf = int(W_hpf_microns / float(micronsPerPixel))
            H_hpf = int(H_hpf_microns / float(micronsPerPixel))

            W_x = int(W_hpf / self.ds)
            W_y = int(H_hpf / self.ds)
            kernel = np.ones((W_y, W_x), np.float32)
            mitoticCount = cv2.filter2D(self.downsampledMap, -1, kernel)

            coords_ds = np.int16(np.array(job.coordinates) / self.ds)

            centerImg = cv2.getRectSubPix(
                np.float32(mitoticCount[:, :, None]),
                patchSize=(coords_ds[2], coords_ds[3]),
                center=(coords_ds[0] + coords_ds[2] * 0.5,
                        coords_ds[1] + coords_ds[3] * 0.5))

            resized = cv2.resize(centerImg,
                                 dsize=(job.currentImage.shape[1],
                                        job.currentImage.shape[0]))

            self.returnImage(resized)

            self.updateAnnotations()
            self.setProgressBar(-1)
            self.setMessage('found %d annotations.' % len(self.annos))
class Plugin(SlideRunnerPlugin.SlideRunnerPlugin):
    version = 0.1
    shortName = 'Mitosis Heatmap'
    inQueue = Queue()
    outQueue = Queue()
    initialOpacity = 0.6
    updateTimer = 0.1
    outputType = SlideRunnerPlugin.PluginOutputType.BINARY_MASK
    description = 'Show heatmap of mitotic figures in WSI'
    pluginType = SlideRunnerPlugin.PluginTypes.WHOLESLIDE_PLUGIN
    configurationList = list(
        (SlideRunnerPlugin.FilePickerConfigurationEntry(
            uid='file', name='Result file', mask='*.p;;*.txt;;*.p.bz2'),
         SlideRunnerPlugin.FilePickerConfigurationEntry(uid='dbfile',
                                                        name='Database file',
                                                        mask='*.sqlite'),
         SlideRunnerPlugin.PluginConfigurationEntry(uid='threshold',
                                                    name='Detection threshold',
                                                    initValue=0.75,
                                                    minValue=0.0,
                                                    maxValue=1.0),
         SlideRunnerPlugin.ComboboxPluginConfigurationEntry(
             uid='source',
             name='Heatmap shows',
             options=['Primary Database', 'Results', 'SecondaryDatabase'])))

    COLORS = [[0, 128, 0, 255], [128, 0, 0, 255], [0, 0, 128, 255],
              [128, 128, 0, 255], [0, 128, 128, 255], [128, 128, 128, 255]]

    def __init__(self, statusQueue: Queue):
        self.statusQueue = statusQueue
        self.annotationLabels = {
            'Detection':
            SlideRunnerPlugin.PluginAnnotationLabel(0, 'Detection',
                                                    [0, 180, 0, 255]),
        }
        self.p = Thread(target=self.queueWorker, daemon=True)
        self.p.start()
        self.annos = []
        self.downsampledMap = np.zeros((10, 10))

        pass

    def getAnnotationUpdatePolicy():
        # This is important to tell SlideRunner that he needs to update for every change in position.
        return SlideRunnerPlugin.AnnotationUpdatePolicy.UPDATE_ON_SLIDE_CHANGE

    def queueWorker(self):
        debugModule = False
        quitSignal = False
        oldFilename = ''
        oldArchive = ''
        oldSlide = ''
        oldDBfile = ''
        oldCoordinates = [-1, -1, -1, -1]
        oldThres = -1
        oldSource = -1
        self.ds = 32
        while not quitSignal:
            job = SlideRunnerPlugin.pluginJob(self.inQueue.get())
            print(job)
            print(job.configuration)

            if (job.jobDescription ==
                    SlideRunnerPlugin.JobDescription.QUIT_PLUGIN_THREAD):
                # signal to exit this thread
                quitSignal = True
                continue

            sourceChanged = job.configuration['source'] != oldSource
            if (job.configuration['source'] == 0):
                if not hasattr(job.openedDatabase, 'dbfilename'):
                    # DB not open yet
                    continue
                job.configuration['dbfile'] = job.openedDatabase.dbfilename

            dbfilechanged = job.configuration['dbfile'] != oldDBfile

            if not (sourceChanged) and (
                    job.configuration['file'] == oldArchive
            ) and (job.configuration['threshold']
                   == oldThres) and (job.slideFilename == oldSlide) and np.all(
                       job.coordinates == oldCoordinates) and not (
                           dbfilechanged):
                continue

            if not (os.path.exists(job.configuration['file'])) and (
                    job.configuration['source'] == 1):
                continue
            self.sendAnnotationLabelUpdate()

            fileChanged = job.configuration['file'] != oldArchive
            oldDBfile = job.configuration['dbfile']
            slideChanged = job.slideFilename != oldSlide
            thresChanged = job.configuration['threshold'] != oldThres

            oldArchive = job.configuration['file']
            oldThres = job.configuration['threshold']
            oldSlide = job.slideFilename
            oldSource = job.configuration['source']
            oldCoordinates = job.coordinates
            [foo, self.ext] = os.path.splitext(oldArchive)
            self.ext = self.ext.upper()
            self.slideObj = openslide.open_slide(job.slideFilename)

            if (fileChanged):
                if (self.ext == '.P') or (
                        self.ext
                        == '.BZ2'):  # Pickled format - results for many slides
                    if (self.ext == '.BZ2'):
                        self.resultsArchive = pickle.load(
                            bz2.BZ2File(oldArchive, 'rb'))
                        print('Opened bz2-compressed results container.')
                    else:
                        self.resultsArchive = pickle.load(
                            open(oldArchive, 'rb'))

            print('Sourcechanged:', sourceChanged, 'dbfilechanged:',
                  dbfilechanged, (len(job.configuration['dbfile']) > 0))
            if (sourceChanged or dbfilechanged or slideChanged) and (
                (job.configuration['source'] == 2) or
                (job.configuration['source']
                 == 0)) and (len(job.configuration['dbfile']) > 0):
                self.slideObj = openslide.open_slide(job.slideFilename)
                self.downsampledMap = np.zeros(
                    (int(self.slideObj.dimensions[1] / self.ds),
                     int(self.slideObj.dimensions[0] / self.ds)))
                self.newDB = Database()

                self.newDB.open(job.configuration['dbfile'])
                allClasses = self.newDB.getAllClasses()
                mitosisClass = -1
                for clsname, clsuid, col in allClasses:
                    if (mitosisClass
                            == -1) and ('MITO' in clsname.upper()) and (
                                'LOOK' not in clsname.upper()):
                        mitosisClass = clsuid

                pname, fname = os.path.split(job.slideFilename)
                uid = self.newDB.findSlideWithFilename(fname, pname)
                self.newDB.loadIntoMemory(uid)
                for anno in self.newDB.annotations:
                    if (self.newDB.annotations[anno].agreedClass ==
                            mitosisClass):
                        annodet = self.newDB.annotations[anno]
                        self.downsampledMap[int(annodet.y1 / self.ds),
                                            int(annodet.x1 / self.ds)] += 1

                else:
                    self.setMessage('No DB open.')

            if ((sourceChanged and job.configuration['source'] == 1) or
                (slideChanged) or
                (thresChanged)) and len(job.configuration['file']) > 0:
                pname, fname = os.path.split(job.slideFilename)
                print('Stage 6')
                if (oldFilename is not fname) or (slideChanged):
                    # process slide
                    self.annos = list()

                    if (fname not in self.resultsArchive):
                        self.setMessage('Slide ' + str(fname) +
                                        ' not found in results file.')
                        print('List of files is: ', self.resultsArchive.keys())
                        continue

                        oldFilename = fname

                    uniqueLabels = np.unique(
                        np.array(self.resultsArchive[fname])[:, 4])

                    self.annotationLabels = dict()
                    for key, label in enumerate(uniqueLabels):
                        self.annotationLabels[
                            label] = SlideRunnerPlugin.PluginAnnotationLabel(
                                0, 'Class %d' % label,
                                self.COLORS[key % len(self.COLORS)])

                    if (job.configuration['source'] == 1):
                        self.downsampledMap = np.zeros(
                            (int(self.slideObj.dimensions[1] / self.ds),
                             int(self.slideObj.dimensions[0] / self.ds)))
                    print('Downsampled image: ', self.downsampledMap.shape)

                    for idx in range(len(self.resultsArchive[fname])):
                        row = self.resultsArchive[fname][idx]
                        if (row[5] > job.configuration['threshold']):
                            myanno = annotations.rectangularAnnotation(
                                uid=idx,
                                x1=row[0],
                                x2=row[2],
                                y1=row[1],
                                y2=row[3],
                                text='%.2f' % row[5],
                                pluginAnnotationLabel=self.annotationLabels[
                                    row[4]])
                            if (job.configuration['source'] == 1):
                                self.downsampledMap[int(
                                    (row[1] + row[3]) / 2 / self.ds),
                                                    int((row[0] + row[2]) / 2 /
                                                        self.ds)] += 1
                            self.annos.append(myanno)

                    self.sendAnnotationLabelUpdate()

                elif (self.ext == '.TXT'):  # Assume MS Coco format
                    self.annos = list()

                    self.resultsArchive = np.loadtxt(
                        oldArchive,
                        dtype={
                            'names':
                            ('label', 'confidence', 'x', 'y', 'w', 'h'),
                            'formats': ('U30', 'f4', 'i4', 'i4', 'i4', 'i4')
                        },
                        skiprows=0,
                        delimiter=' ')
                    uniqueLabels = np.unique(self.resultsArchive['label'])

                    self.annotationLabels = dict()
                    for key, label in enumerate(uniqueLabels):
                        self.annotationLabels[
                            label] = SlideRunnerPlugin.PluginAnnotationLabel(
                                0, label, self.COLORS[key % len(self.COLORS)])

                    self.sendAnnotationLabelUpdate()

                    self.slideObj = openslide.open_slide(job.slideFilename)
                    self.ds = 32
                    if (job.configuration['source'] == 1):
                        self.downsampledMap = np.zeros(
                            (int(self.slideObj.dimensions[1] / self.ds),
                             int(self.slideObj.dimensions[0] / self.ds)))
                    print('Downsampled image: ', self.downsampledMap.shape)

                    for idx in range(len(self.resultsArchive)):
                        row = self.resultsArchive[idx]
                        if (row[5] > job.configuration['threshold']):
                            if (job.configuration['source'] == 1):
                                self.downsampledMap[int(
                                    (row['y'] - row['h'] / 2) / self.ds),
                                                    int((row['x'] -
                                                         row['w'] / 2) /
                                                        self.ds)] += 1

                            myanno = annotations.rectangularAnnotation(
                                uid=idx,
                                x1=row['x'],
                                x2=row['y'],
                                y1=row['x'] + row['w'],
                                y2=row['y'] + row['h'],
                                text='%.2f' % row['confidence'],
                                pluginAnnotationLabel=self.annotationLabels[
                                    row['label']])
                            self.annos.append(myanno)

            print('returning overlay...')
            A = 2.37  # mm^2
            W_hpf_microns = np.sqrt(A * 4 / 3) * 1000  # in microns
            H_hpf_microns = np.sqrt(A * 3 / 4) * 1000  # in microns

            micronsPerPixel = self.slideObj.properties[
                openslide.PROPERTY_NAME_MPP_X]

            W_hpf = int(W_hpf_microns / float(micronsPerPixel))
            H_hpf = int(H_hpf_microns / float(micronsPerPixel))

            W_x = int(W_hpf / self.ds)
            W_y = int(H_hpf / self.ds)
            kernel = np.ones((W_y, W_x), np.float32)
            mitoticCount = cv2.filter2D(self.downsampledMap, -1, kernel)

            coords_ds = np.int16(np.array(job.coordinates) / self.ds)

            centerImg = cv2.getRectSubPix(
                np.float32(mitoticCount[:, :, None]),
                patchSize=(coords_ds[2], coords_ds[3]),
                center=(coords_ds[0] + coords_ds[2] * 0.5,
                        coords_ds[1] + coords_ds[3] * 0.5))

            resized = cv2.resize(centerImg,
                                 dsize=(job.currentImage.shape[1],
                                        job.currentImage.shape[0]))

            self.returnImage(resized)

            self.updateAnnotations()
            self.setProgressBar(-1)
            self.setMessage('found %d annotations.' % len(self.annos))

    def getAnnotations(self):
        return self.annos

    def getAnnotationLabels(self):
        # sending default annotation labels
        return [self.annotationLabels[k] for k in self.annotationLabels.keys()]
예제 #18
0
    def queueWorker(self):
        debugModule = False
        quitSignal = False
        oldFilename = ''
        oldArchive = ''
        oldSlide = ''
        oldThres = -1
        while not quitSignal:
            job = SlideRunnerPlugin.pluginJob(self.inQueue.get())
            print(job)
            print(job.configuration)

            if (job.jobDescription ==
                    SlideRunnerPlugin.JobDescription.QUIT_PLUGIN_THREAD):
                # signal to exit this thread
                quitSignal = True
                continue

            if (job.configuration['file']
                    == oldArchive) and (job.configuration['threshold']
                                        == oldThres) and (job.slideFilename
                                                          == oldSlide):
                continue

            if not (os.path.exists(job.configuration['file'])):
                continue

            print('Performing label update')
            self.sendAnnotationLabelUpdate()

            oldArchive = job.configuration['file']
            oldThres = job.configuration['threshold']
            oldSlide = job.slideFilename
            [foo, self.ext] = os.path.splitext(oldArchive)
            self.ext = self.ext.upper()

            self.annos = list()

            if (self.ext == '.P'):  # Pickled format - results for many slides
                self.resultsArchive = pickle.load(open(oldArchive, 'rb'))

                pname, fname = os.path.split(job.slideFilename)
                fnamewithfolder = pname.split(os.sep)[-1] + os.sep + fname
                if (oldFilename is not fname):
                    # process slide
                    if (fname not in self.resultsArchive) and (
                            fnamewithfolder not in self.resultsArchive):
                        self.setMessage('Slide ' + str(fname) +
                                        ' not found in results file.')
                        print('List is:', self.resultsArchive.keys())
                        continue

                    if (fnamewithfolder in self.resultsArchive):
                        fname = fnamewithfolder
                    oldFilename = fname

                uniqueLabels = np.unique(
                    np.array(self.resultsArchive[fname])[:, 4])

                self.annotationLabels = dict()
                for key, label in enumerate(uniqueLabels):
                    self.annotationLabels[
                        label] = SlideRunnerPlugin.PluginAnnotationLabel(
                            key, 'Class %d' % label,
                            self.COLORS[key % len(self.COLORS)])

                for idx in range(len(self.resultsArchive[fname])):
                    row = self.resultsArchive[fname][idx]
                    if (row[5] > job.configuration['threshold']):
                        myanno = annotations.rectangularAnnotation(
                            uid=idx,
                            x1=row[0],
                            x2=row[2],
                            y1=row[1],
                            y2=row[3],
                            text='%.2f' % row[5],
                            pluginAnnotationLabel=self.annotationLabels[
                                row[4]])
                        self.annos.append(myanno)

                self.sendAnnotationLabelUpdate()

            elif (self.ext == '.TXT'):  # Assume MS Coco format
                self.resultsArchive = np.loadtxt(
                    oldArchive,
                    dtype={
                        'names': ('label', 'confidence', 'x', 'y', 'w', 'h'),
                        'formats': ('U30', 'f4', 'i4', 'i4', 'i4', 'i4')
                    },
                    skiprows=0,
                    delimiter=' ')
                uniqueLabels = np.unique(self.resultsArchive['label'])

                self.annotationLabels = dict()
                for key, label in enumerate(uniqueLabels):
                    self.annotationLabels[
                        label] = SlideRunnerPlugin.PluginAnnotationLabel(
                            key, label, self.COLORS[key % len(self.COLORS)])

                self.sendAnnotationLabelUpdate()

                for idx in range(len(self.resultsArchive)):
                    row = self.resultsArchive[idx]
                    if (row[5] > job.configuration['threshold']):
                        myanno = annotations.rectangularAnnotation(
                            uid=idx,
                            x1=row['x'],
                            y1=row['y'],
                            x2=row['x'] + row['w'],
                            y2=row['y'] + row['h'],
                            text='%.2f' % row['confidence'],
                            pluginAnnotationLabel=self.annotationLabels[
                                row['label']])
                        self.annos.append(myanno)

            self.updateAnnotations()
            self.setProgressBar(-1)
            self.setMessage('found %d annotations.' % len(self.annos))
예제 #19
0
class Plugin(SlideRunnerPlugin.SlideRunnerPlugin):
    version = 0.1
    shortName = 'Object Detection Results'
    inQueue = Queue()
    outQueue = Queue()
    initialOpacity = 1.0
    updateTimer = 0.1
    outputType = SlideRunnerPlugin.PluginOutputType.NO_OVERLAY
    description = 'Show unpickled object detection results'
    pluginType = SlideRunnerPlugin.PluginTypes.WHOLESLIDE_PLUGIN
    configurationList = list((
        SlideRunnerPlugin.FilePickerConfigurationEntry(uid='file',
                                                       name='Result file',
                                                       mask='*.p;;*.txt'),
        SlideRunnerPlugin.PluginConfigurationEntry(uid='threshold',
                                                   name='Detection threshold',
                                                   initValue=0.75,
                                                   minValue=0.0,
                                                   maxValue=1.0),
    ))

    COLORS = [[0, 128, 0, 255], [128, 0, 0, 255], [0, 0, 128, 255],
              [128, 128, 0, 255], [0, 128, 128, 255], [128, 128, 128, 255]]

    def __init__(self, statusQueue: Queue):
        self.statusQueue = statusQueue
        self.annotationLabels = {
            'Detection':
            SlideRunnerPlugin.PluginAnnotationLabel(0, 'Detection',
                                                    [0, 180, 0, 255]),
        }
        self.p = Thread(target=self.queueWorker, daemon=True)
        self.p.start()

        pass

    def getAnnotationUpdatePolicy():
        # This is important to tell SlideRunner that he needs to update for every change in position.
        return SlideRunnerPlugin.AnnotationUpdatePolicy.UPDATE_ON_SLIDE_CHANGE

    def queueWorker(self):
        debugModule = False
        quitSignal = False
        oldFilename = ''
        oldArchive = ''
        oldSlide = ''
        oldThres = -1
        while not quitSignal:
            job = SlideRunnerPlugin.pluginJob(self.inQueue.get())
            print(job)
            print(job.configuration)

            if (job.jobDescription ==
                    SlideRunnerPlugin.JobDescription.QUIT_PLUGIN_THREAD):
                # signal to exit this thread
                quitSignal = True
                continue

            if (job.configuration['file']
                    == oldArchive) and (job.configuration['threshold']
                                        == oldThres) and (job.slideFilename
                                                          == oldSlide):
                continue

            if not (os.path.exists(job.configuration['file'])):
                continue

            print('Performing label update')
            self.sendAnnotationLabelUpdate()

            oldArchive = job.configuration['file']
            oldThres = job.configuration['threshold']
            oldSlide = job.slideFilename
            [foo, self.ext] = os.path.splitext(oldArchive)
            self.ext = self.ext.upper()

            self.annos = list()

            if (self.ext == '.P'):  # Pickled format - results for many slides
                self.resultsArchive = pickle.load(open(oldArchive, 'rb'))

                pname, fname = os.path.split(job.slideFilename)
                fnamewithfolder = pname.split(os.sep)[-1] + os.sep + fname
                if (oldFilename is not fname):
                    # process slide
                    if (fname not in self.resultsArchive) and (
                            fnamewithfolder not in self.resultsArchive):
                        self.setMessage('Slide ' + str(fname) +
                                        ' not found in results file.')
                        print('List is:', self.resultsArchive.keys())
                        continue

                    if (fnamewithfolder in self.resultsArchive):
                        fname = fnamewithfolder
                    oldFilename = fname

                uniqueLabels = np.unique(
                    np.array(self.resultsArchive[fname])[:, 4])

                self.annotationLabels = dict()
                for key, label in enumerate(uniqueLabels):
                    self.annotationLabels[
                        label] = SlideRunnerPlugin.PluginAnnotationLabel(
                            key, 'Class %d' % label,
                            self.COLORS[key % len(self.COLORS)])

                for idx in range(len(self.resultsArchive[fname])):
                    row = self.resultsArchive[fname][idx]
                    if (row[5] > job.configuration['threshold']):
                        myanno = annotations.rectangularAnnotation(
                            uid=idx,
                            x1=row[0],
                            x2=row[2],
                            y1=row[1],
                            y2=row[3],
                            text='%.2f' % row[5],
                            pluginAnnotationLabel=self.annotationLabels[
                                row[4]])
                        self.annos.append(myanno)

                self.sendAnnotationLabelUpdate()

            elif (self.ext == '.TXT'):  # Assume MS Coco format
                self.resultsArchive = np.loadtxt(
                    oldArchive,
                    dtype={
                        'names': ('label', 'confidence', 'x', 'y', 'w', 'h'),
                        'formats': ('U30', 'f4', 'i4', 'i4', 'i4', 'i4')
                    },
                    skiprows=0,
                    delimiter=' ')
                uniqueLabels = np.unique(self.resultsArchive['label'])

                self.annotationLabels = dict()
                for key, label in enumerate(uniqueLabels):
                    self.annotationLabels[
                        label] = SlideRunnerPlugin.PluginAnnotationLabel(
                            key, label, self.COLORS[key % len(self.COLORS)])

                self.sendAnnotationLabelUpdate()

                for idx in range(len(self.resultsArchive)):
                    row = self.resultsArchive[idx]
                    if (row[5] > job.configuration['threshold']):
                        myanno = annotations.rectangularAnnotation(
                            uid=idx,
                            x1=row['x'],
                            y1=row['y'],
                            x2=row['x'] + row['w'],
                            y2=row['y'] + row['h'],
                            text='%.2f' % row['confidence'],
                            pluginAnnotationLabel=self.annotationLabels[
                                row['label']])
                        self.annos.append(myanno)

            self.updateAnnotations()
            self.setProgressBar(-1)
            self.setMessage('found %d annotations.' % len(self.annos))

    def getAnnotations(self):
        return self.annos

    def getAnnotationLabels(self):
        # sending default annotation labels
        return [self.annotationLabels[k] for k in self.annotationLabels.keys()]
    def queueWorker(self):
        oldwsi=None
        quitSignal=False
        sl=None
        detected_offset = None
        sl_main = None
        while not quitSignal:
            job = SlideRunnerPlugin.pluginJob(self.inQueue.get())
            image = job.currentImage
            mainWSI = job.slideFilename
            
            if 'file' not in job.configuration:
                continue

            
            if (job.configuration['file'] != oldwsi) and (job.configuration['file']!='') and job.configuration['file']:
                sl = openslide.open_slide(job.configuration['file'])

            if (mainWSI):
                sl_main = openslide.open_slide(mainWSI)

            if (job.trigger is not None) and job.trigger.uid=='match':
                print('Trigger: ',job.trigger)
                self.setProgressBar(0)
                self.setMessage('Calculating optimum offset')
                tissue_detector = TissueDetector("LAB_Threshold", threshold=80) # option 1
                matcher_parameters = MatcherParameters()  # use the default parameters
                matcher = WSI_Matcher(tissue_detector, matcher_parameters)
                detected_offset = matcher.match(mainWSI, job.configuration['file'])
                self.setProgressBar(-1)
                updateConfig = list()
                updateConfig.append(SlideRunnerPlugin.PluginConfigUpdateEntry(SlideRunnerPlugin.PluginConfigurationType.SLIDER_WITH_FLOAT_VALUE, uid='xoffset', value=detected_offset[0]))
                updateConfig.append(SlideRunnerPlugin.PluginConfigUpdateEntry(SlideRunnerPlugin.PluginConfigurationType.SLIDER_WITH_FLOAT_VALUE, uid='yoffset', value=detected_offset[1]))
                self.updateConfiguration(SlideRunnerPlugin.PluginConfigUpdate(updateConfig))                

            if (sl) and (sl_main):
                self.setProgressBar(0)
                print('Reading from: ',job)

                zoomValue=job.coordinates[3]/job.currentImage.shape[0]
                print('Zoom value: ',zoomValue)
                act_level = np.argmin(np.abs(np.asarray(sl.level_downsamples)-zoomValue))
                closest_ds = sl_main.level_downsamples[np.argmin(np.abs(np.asarray(sl_main.level_downsamples)-zoomValue))]

                offset = (job.configuration['xoffset'],job.configuration['yoffset'])
                if (detected_offset is not None):
                    offset=detected_offset

                offset_scaled = [int(x/closest_ds) for x in offset]

                print('Scaled offset is: ',offset_scaled)


                imgarea_w=job.coordinates[2:4]
                size_im = (int(imgarea_w[0]/closest_ds), int(imgarea_w[1]/closest_ds))
                print('Image size: ',size_im)
                location = [int(x+y) for x,y in zip(job.coordinates[0:2],offset)]
                print('Location (original):',job.coordinates[0:2])
                print('Location (offset): ',location)
                img = sl.read_region(location=location, level=act_level, size=size_im)
                img = np.array(img.resize((job.currentImage.shape[1],job.currentImage.shape[0] )))

                self.returnImage(img, job.procId)
                self.setMessage('Align done.')
                self.setProgressBar(-1)


            if (job.jobDescription == SlideRunnerPlugin.JobDescription.QUIT_PLUGIN_THREAD):
                # signal to exit this thread
                quitSignal=True
                continue
            print('OTSU plugin: received 1 image from queue')
예제 #21
0
class Plugin(SlideRunnerPlugin.SlideRunnerPlugin):
    version = 0.0
    shortName = 'High Power Field Visualization'
    inQueue = Queue()
    outQueue = Queue()
    description = 'Display size of 1 HPF'
    pluginType = SlideRunnerPlugin.PluginTypes.WHOLESLIDE_PLUGIN
    outputType = SlideRunnerPlugin.PluginOutputType.NO_OVERLAY
    modelInitialized = False
    updateTimer = 0.1
    slideFilename = None
    annos = list()
    configurationList = list((
        SlideRunnerPlugin.PluginConfigurationEntry(
            uid=0,
            name='Re-center HPF',
            ctype=SlideRunnerPlugin.PluginConfigurationType.PUSHBUTTON),
        SlideRunnerPlugin.PluginConfigurationEntry(uid=1,
                                                   name='Number of HPFs',
                                                   initValue=1.00,
                                                   minValue=1.0,
                                                   maxValue=10.0),
        SlideRunnerPlugin.PluginConfigurationEntry(uid=2,
                                                   name='Size of HPF (mm2)',
                                                   initValue=0.237,
                                                   minValue=0.20,
                                                   maxValue=0.3),
    ))  #0.237

    def __init__(self, statusQueue: Queue):
        self.statusQueue = statusQueue
        self.p = Thread(target=self.queueWorker, daemon=True)
        self.p.start()

        pass

    def getAnnotationUpdatePolicy():
        # This is important to tell SlideRunner that he needs to update for every change in position.
        return SlideRunnerPlugin.AnnotationUpdatePolicy.UPDATE_ON_SLIDE_CHANGE

    def queueWorker(self):

        quitSignal = False
        oldSlide = ''
        while not quitSignal:
            job = SlideRunnerPlugin.pluginJob(self.inQueue.get())
            filename = job

            if (job.jobDescription ==
                    SlideRunnerPlugin.JobDescription.QUIT_PLUGIN_THREAD):
                # signal to exit this thread
                quitSignal = True
                continue
            if (job.slideFilename != oldSlide) or (job.trigger is not None):
                self.processWholeSlide(job)
                oldSlide = job.slideFilename
            else:
                print('Trigger:', job.trigger)

    def getAnnotations(self):
        return self.annos

    def processWholeSlide(self, job: SlideRunnerPlugin.pluginJob):

        filename = job.slideFilename
        self.slide = openslide.open_slide(filename)

        # 1 HPF = 0.237 mm^2
        A = job.configuration[2]  # mm^2
        W_hpf_microns = np.sqrt(A * 4 / 3) * 1000  # in microns
        H_hpf_microns = np.sqrt(A * 3 / 4) * 1000  # in microns

        micronsPerPixel = self.slide.properties[openslide.PROPERTY_NAME_MPP_X]

        W_hpf = int(W_hpf_microns / float(micronsPerPixel)) * np.sqrt(
            float(int(job.configuration[1])))
        H_hpf = int(H_hpf_microns / float(micronsPerPixel)) * np.sqrt(
            float(int(job.configuration[1])))

        center = (int((job.coordinates[0] + 0.5 * job.coordinates[2])),
                  int((job.coordinates[1] + 0.5 * job.coordinates[3])))

        self.annos = list()
        if (int(job.configuration[1]) == 1):
            myanno = annotations.rectangularAnnotation(0,
                                                       center[0] - W_hpf / 2,
                                                       center[1] - H_hpf / 2,
                                                       center[0] + W_hpf / 2,
                                                       center[1] + H_hpf / 2,
                                                       'High-Power Field')
        else:
            myanno = annotations.rectangularAnnotation(
                0, center[0] - W_hpf / 2, center[1] - H_hpf / 2,
                center[0] + W_hpf / 2, center[1] + H_hpf / 2,
                '%d High-Power Fields' % int(job.configuration[1]))
        self.annos.append(myanno)

        self.updateAnnotations()
예제 #22
0
class Plugin(SlideRunnerPlugin.SlideRunnerPlugin):
    version = 0.1
    shortName = 'Secondary database visualization'
    inQueue = Queue()
    outQueue = Queue()
    initialOpacity = 1.0
    updateTimer = 0.1
    outputType = SlideRunnerPlugin.PluginOutputType.RGB_IMAGE
    description = 'Visualize secondary SlideRunner database'
    pluginType = SlideRunnerPlugin.PluginTypes.WHOLESLIDE_PLUGIN
    configurationList = list(
        (SlideRunnerPlugin.FilePickerConfigurationEntry(uid='file',
                                                        name='Database file',
                                                        mask='*.sqlite'), ))

    COLORS = [[0, 128, 0, 255], [128, 0, 0, 255], [0, 0, 128, 255],
              [128, 128, 0, 255], [0, 128, 128, 255], [128, 128, 128, 255]]

    def __init__(self, statusQueue: Queue):
        self.statusQueue = statusQueue
        self.annotationLabels = {}
        self.secondaryDB = Database()
        self.p = Thread(target=self.queueWorker, daemon=True)
        self.p.start()

        pass

    def getAnnotationUpdatePolicy():
        # This is important to tell SlideRunner that he needs to update for every change in position.
        return SlideRunnerPlugin.AnnotationUpdatePolicy.UPDATE_ON_SLIDE_CHANGE

    def queueWorker(self):
        debugModule = False
        quitSignal = False
        oldFilename = ''
        oldArchive = ''
        oldSlide = ''
        oldThres = -1
        while not quitSignal:
            job = SlideRunnerPlugin.pluginJob(self.inQueue.get())
            print(job)

            if (job.jobDescription ==
                    SlideRunnerPlugin.JobDescription.QUIT_PLUGIN_THREAD):
                # signal to exit this thread
                quitSignal = True
                continue

            if (job.configuration['file'] == oldArchive) and (job.slideFilename
                                                              == oldSlide):
                continue

            if not (os.path.exists(job.configuration['file'])):
                continue
            self.sendAnnotationLabelUpdate()

            oldArchive = job.configuration['file']
            oldSlide = job.slideFilename

            self.secondaryDB.open(oldArchive)

            self.annos = list()
            self.annotationLabels = dict()

            for key, (label, annoId,
                      col) in enumerate(self.secondaryDB.getAllClasses()):
                self.annotationLabels[
                    annoId] = SlideRunnerPlugin.PluginAnnotationLabel(
                        0, '%s' % label, [*hex_to_rgb(col), 0])

            pname, fname = os.path.split(job.slideFilename)
            self.slideUID = self.secondaryDB.findSlideWithFilename(
                fname, pname)
            self.secondaryDB.loadIntoMemory(self.slideUID)
            self.annos = list()

            for annoId in self.secondaryDB.annotations.keys():
                anno = self.secondaryDB.annotations[annoId]
                anno.pluginAnnotationLabel = self.annotationLabels[
                    anno.agreedClass]
                self.annos.append(anno)
            self.sendAnnotationLabelUpdate()

            self.updateAnnotations()
            self.setProgressBar(-1)
            self.setMessage('found %d annotations.' % len(self.annos))

    def getAnnotations(self):
        return self.annos

    def getAnnotationLabels(self):
        # sending default annotation labels
        return [self.annotationLabels[k] for k in self.annotationLabels.keys()]
예제 #23
0
class Plugin(SlideRunnerPlugin.SlideRunnerPlugin):
    version = 0.1
    shortName = 'Normalize (Macenko)'
    inQueue = Queue()
    outQueue = Queue()
    initialOpacity = 1.0
    updateTimer = 0.5
    outputType = SlideRunnerPlugin.PluginOutputType.RGB_IMAGE
    description = 'H&E Image normalization (Method by Macenko)'
    pluginType = SlideRunnerPlugin.PluginTypes.IMAGE_PLUGIN
    configurationList = list(
        (SlideRunnerPlugin.ComboboxPluginConfigurationEntry(
            uid='mode',
            name='Mode',
            options=['show H&E', 'only E', 'only H'],
            selected_value=0), ))

    def __init__(self, statusQueue: Queue):
        self.statusQueue = statusQueue
        self.p = Thread(target=self.queueWorker, daemon=True)
        self.p.start()

        pass

    def queueWorker(self):
        quitSignal = False
        while not quitSignal:
            job = SlideRunnerPlugin.pluginJob(self.inQueue.get())
            image = job.currentImage

            if (job.jobDescription ==
                    SlideRunnerPlugin.JobDescription.QUIT_PLUGIN_THREAD):
                # signal to exit this thread
                quitSignal = True
                continue

            print('Macenko norm plugin: received 1 image from queue')
            self.setProgressBar(0)

            print(job)
            if (job.annotations is not None) and len(job.annotations) > 0:
                if (job.annotations[0].annotationType ==
                        annotations.AnnotationType.AREA):
                    print('Found an area annotation - great!')
                    minC = job.annotations[0].minCoordinates()
                    maxC = job.annotations[0].maxCoordinates()

                    scaleX = (job.coordinates[2]) / job.currentImage.shape[1]
                    scaleY = (job.coordinates[3]) / job.currentImage.shape[0]

                    minC = np.array(
                        (max(0, int((minC.x - job.coordinates[0]) / scaleX)),
                         max(0, int((minC.y - job.coordinates[1]) / scaleY))))
                    maxC = np.array(
                        (min(job.currentImage.shape[1],
                             int((maxC.x - job.coordinates[0]) / scaleX)),
                         min(job.currentImage.shape[0],
                             int((maxC.y - job.coordinates[1]) / scaleY))))

                    rgb = np.copy(image[:, :, 0:3])
                    rgb = normalize(rgb,
                                    np.reshape(
                                        rgb[minC[1]:maxC[1],
                                            minC[0]:maxC[0], :], (-1, 3)),
                                    job=job)

            else:

                rgb = np.copy(image[:, :, 0:3])
                rgb = normalize(rgb, job=job)
            print('Stats: ', np.max(np.float32(rgb)), np.min(np.float32(rgb)),
                  np.mean(np.float32(rgb)),
                  np.float32(rgb).shape)

            self.returnImage(np.float32(rgb), job.procId)
            self.setMessage('Macenko normalization: done.')
            self.setProgressBar(-1)