def addPredictionsToImages(conn, prediction, dsId, commentImages, tagSet): """ Add a comment to the dataset containing the prediction results. @param commentImages If true add comment to individual images as well as the dataset @param tagSet If provided then tag images with the predicted label """ message = "" dsComment = "" tagMap = {} if tagSet: for tag in tagSet.listTagsInTagset(): tagMap[tag.getValue()] = tag for r in prediction.individual_results: c = formatPredResult(r) imId = long(r.source_file) if commentImages: message += WndcharmStorage.addCommentTo(conn, c, "Image", imId) im = conn.getObject("Image", imId) dsComment += im.getName() + " " + c + "\n" if tagMap: tag = tagMap[r.predicted_class_name]._obj message += WndcharmStorage.addTagTo(conn, tag, "Image", imId) message += WndcharmStorage.addCommentTo(conn, dsComment, "Dataset", dsId) return message
def loadClassifier(ctb, project): tidF = WndcharmStorage.getAttachedTableFile(ctb.tcF, project) tidW = WndcharmStorage.getAttachedTableFile(ctb.tcW, project) tidL = WndcharmStorage.getAttachedTableFile(ctb.tcL, project) if tidF is None or tidW is None or tidL is None: raise Exception("Incomplete set of classifier tables: %s" % (tidF, tidW, tidL)) ctb.openTables(tidF, tidW, tidL) version = unwrap(ctb.versiontag.getTextValue()) cls = ctb.loadClassifierTables() # ids,trainClassIds,featureMatrix,featureNames,weights,classIds,classNames trainFts = wndcharm.FeatureSet.FeatureSet_Discrete() # if cls['classIds'] != sorted(cls['classIds']): if cls["classIds"] != range(len(cls["classIds"])): raise Exception("Incorrectly ordered class IDs") trainFts.classnames_list = cls["classNames"] # Objects should be in order of increasing class ID, this makes rebuilding # data_list much faster nclasses = len(cls["classNames"]) classCounts = [0] * nclasses cprev = -1 for c in cls["trainClassIds"]: if c < cprev: raise Exception("Incorrectly ordered training class feature data") cprev = c classCounts[c] += 1 trainFts.classsizes_list = classCounts trainFts.feature_vector_version = version classFts = [[] for n in xrange(len(cls["classNames"]))] p = 0 for i in xrange(nclasses): classFts[i] = numpy.array(cls["featureMatrix"][p : (p + classCounts[i])]) p += classCounts[i] trainFts.data_list = classFts trainFts.num_images = sum(classCounts) trainFts.num_features = len(cls["featureNames"]) trainFts.num_classes = nclasses trainFts.featurenames_list = cls["featureNames"] trainFts.imagenames_list = [str(i) for i in cls["ids"]] tmp = trainFts.ContiguousDataMatrix() weights = wndcharm.FeatureSet.FisherFeatureWeights( data_dict={"names": cls["featureNames"], "values": cls["weights"]} ) return (trainFts, weights)
def predict(client, scriptParams): message = "" # for params with default values, we can get the value directly projectId = scriptParams["Training_Project_ID"] dataType = scriptParams["Data_Type"] predictIds = scriptParams["IDs"] commentImages = scriptParams["Comment_Images"] tagImages = scriptParams["Tag_Images"] contextName = scriptParams["Context_Name"] tableNameIn = "/Wndcharm/" + contextName + WndcharmStorage.SMALLFEATURES_TABLE tableNameF = "/Wndcharm/" + contextName + WndcharmStorage.CLASS_FEATURES_TABLE tableNameW = "/Wndcharm/" + contextName + WndcharmStorage.CLASS_WEIGHTS_TABLE tableNameL = "/Wndcharm/" + contextName + WndcharmStorage.CLASS_LABELS_TABLE message += "tableNameIn:" + tableNameIn + "\n" message += "tableNameF:" + tableNameF + "\n" message += "tableNameW:" + tableNameW + "\n" message += "tableNameL:" + tableNameL + "\n" ftb = WndcharmStorage.FeatureTable(client, tableNameIn) ctb = WndcharmStorage.ClassifierTables(client, tableNameF, tableNameW, tableNameL) try: message += "Loading classifier\n" trainProject = ftb.conn.getObject("Project", projectId) trainFts, weights = loadClassifier(ctb, trainProject) classifierName = WndcharmStorage.CLASSIFIER_WNDCHARM_NAMESPACE tagSet = WndcharmStorage.getClassifierTagSet(classifierName, trainProject.getName(), trainProject) # Predict message += "Predicting\n" predDatasets = WndcharmStorage.datasetGenerator(ftb.conn, dataType, predictIds) for ds in predDatasets: message += "Predicting dataset id:%d\n" % ds.getId() pred, msg = predictDataset(ftb, trainFts, ds, weights) message += msg message += addPredictionsToImages(ftb.conn, pred, ds.getId(), commentImages, tagSet) except: print message raise finally: ftb.close() ctb.close() return message
def processImages(client, scriptParams): message = '' # for params with default values, we can get the value directly dataType = scriptParams['Data_Type'] ids = scriptParams['IDs'] contextName = scriptParams['Context_Name'] tableName = '/Wndcharm/' + contextName + '/SmallFeatureSet.h5' message += 'tableName:' + tableName + '\n' ftb = WndcharmStorage.FeatureTable(client, tableName) try: # Get the datasets objects, logMessage = script_utils.getObjects(ftb.conn, scriptParams) message += logMessage if not objects: return message datasets = WndcharmStorage.datasetGenerator(ftb.conn, dataType, ids) for ds in datasets: message += 'Processing dataset id:%d\n' % ds.getId() msg = countCompleted(ftb, ds) message += msg except: print message raise finally: ftb.close() return message
def addToFeatureSet(ftb, ds, fts, classId): message = "" tid = WndcharmStorage.getAttachedTableFile(ftb.tc, ds) if tid: if not ftb.openTable(tid): return message + "\nERROR: Table not opened" version = unwrap(ftb.versiontag.getTextValue()) message += "Opened table id:%d version:%s\n" % (tid, version) else: message += "ERROR: Table not found for Dataset id:%d" % ds.getId() return message # fts = wndcharm.FeatureSet.FeatureSet_Discrete({'num_images': 0}) for image in ds.listChildren(): imId = image.getId() message += "\tProcessing features for image id:%d\n" % imId # message += extractFeatures(tc, d, im = image) + '\n' sig = wndcharm.FeatureSet.Signatures() (sig.names, sig.values) = ftb.loadFeatures(imId) # sig.source_file = image.getName() sig.source_file = str(imId) sig.version = version fts.AddSignature(sig, classId) fts.classnames_list[classId] = ds.getName() return message
def crossValidate(ftb, project, featureThreshold, imagesOnly, numSplits): message = '' fullSet = FeatureSet_Discrete() fullSet.source_path = project.getName() classId = 0 for ds in project.listChildren(): message += 'Processing dataset id:%d\n' % ds.getId() message += addToFeatureSet(ftb, ds, fullSet, classId, imagesOnly) classId += 1 tmp = fullSet.ContiguousDataMatrix() experiment = DiscreteClassificationExperimentResult(training_set=fullSet) for i in range(numSplits): trainSet, testSet = fullSet.Split() trainSet.Normalize() testSet.Normalize(trainSet) weights = FisherFeatureWeights.NewFromFeatureSet(trainSet) nFeatures = ceil(len(weights.names) * featureThreshold) message += 'Selecting top %d features\n' % nFeatures weights = weights.Threshold(nFeatures) trainSet = reduceFeatures(trainSet, weights) reducedTestSet = reduceFeatures(testSet, weights) reducedTrainSet = reduceFeatures(trainSet, weights) batchResult = DiscreteBatchClassificationResult.New( reducedTrainSet, reducedTestSet, weights, batch_number=i) experiment.individual_results.append(batchResult) out = StringIO() experiment.Print(output_stream=out) experiment.PerSampleStatistics(output_stream=out) pid = project.getId() WndcharmStorage.addTextFileAnnotationTo( ftb.conn, out.getvalue(), 'Project', pid, 'Wndcharm_Cross_Validation_Results.txt', 'Wndcharm Cross Validation Results for Project:%d' % pid) message += 'Attached cross-validation results\n' #return experiment return message
def removeAnnotations(conn, obj, rmTables, rmComments, unlinkTags, rmTagsets): """ Remove annotations that are in one of the Wndcharm namespaces """ message = '' rmIds = [] for ann in obj.listAnnotations(): if ann.getNs() == WndcharmStorage.WNDCHARM_NAMESPACE: if rmTables and isinstance(ann, FileAnnotationWrapper): # Need to remove version annotation on the OriginalFile # otherwise delete will fail WndcharmStorage.unlinkAnnotations(conn, ann.getFile()) message += ('Checking for annotations on file id:%d\n' % ann.getFile().getId()) rmIds.append(ann.getId()) if rmComments and isinstance(ann, CommentAnnotationWrapper): rmIds.append(ann.getId()) deleteObjects(conn, 'Annotation', rmIds) message += 'Removed annotations:%s from %s id:%d\n' % \ (rmIds, obj.OMERO_CLASS, obj.getId()) if unlinkTags: message += removeTagAnnotations(conn, obj) if rmTagsets: message += removeTags(conn, obj) try: # Keep recursing until listChildren not implemented for ch in obj.listChildren(): message += removeAnnotations( conn, ch, rmTables, rmComments, unlinkTags, rmTagsets) except NotImplementedError: pass return message
def createWeights(ftb, ctb, project, featureThreshold, imagesOnly): # Build the classifier (basically a set of weights) message = '' trainFts = wndcharm.FeatureSet.FeatureSet_Discrete() classId = 0 for ds in project.listChildren(): message += 'Processing dataset id:%d\n' % ds.getId() message += addToFeatureSet(ftb, ds, trainFts, classId, imagesOnly) classId += 1 tmp = trainFts.ContiguousDataMatrix() weights = wndcharm.FeatureSet.FisherFeatureWeights.NewFromFeatureSet(trainFts) if featureThreshold < 1.0: nFeatures = ceil(len(weights.names) * featureThreshold) message += 'Selecting top %d features\n' % nFeatures weights = weights.Threshold(nFeatures) trainFts = reduceFeatures(trainFts, weights) version = trainFts.feature_vector_version # Save the features, weights and classes to tables # TODO:Delete existing tables #if getProjectTableFile(tcOutF, tcF.tableName, proj): ctb.createClassifierTables(weights.names, version) message += 'Created classifier tables ids: %d %d %d version:%s\n' % ( ctb.tcF.tableId, ctb.tcW.tableId, ctb.tcL.tableId, version) # We've (ab)used imagenames_list to hold the image ids ids = [long(a) for b in trainFts.imagenames_list for a in b] classIds = [a for b in [[i] * len(z) for i, z in izip(xrange( len(trainFts.imagenames_list)), trainFts.imagenames_list)] for a in b] featureMatrix = trainFts.data_matrix featureNames = weights.names featureWeights = weights.values classNames = trainFts.classnames_list ctb.saveClassifierTables(ids, classIds, featureMatrix, featureNames, featureWeights, classNames) WndcharmStorage.addFileAnnotationTo(ctb.tcF, project) WndcharmStorage.addFileAnnotationTo(ctb.tcW, project) WndcharmStorage.addFileAnnotationTo(ctb.tcL, project) message += 'Saved classifier\n' classifierName = WndcharmStorage.CLASSIFIER_WNDCHARM_NAMESPACE ns = WndcharmStorage.createClassifierTagSet( ctb.tcL.conn, classifierName, project.getName(), classNames, project) message += 'Created tagset: %s\n' % ns return trainFts, weights, message
def processImages(client, scriptParams): message = '' # for params with default values, we can get the value directly dataType = scriptParams['Data_Type'] ids = scriptParams['IDs'] contextName = scriptParams['Context_Name'] newOnly = scriptParams['New_Images_Only'] tableName = '/Wndcharm/' + contextName + '/SmallFeatureSet.h5' message += 'tableName:' + tableName + '\n' ftb = WndcharmStorage.FeatureTable(client, tableName) try: nimages = 0 # Get the datasets objects, logMessage = script_utils.getObjects(ftb.conn, scriptParams) message += logMessage if not objects: return message datasets = list(WndcharmStorage.datasetGenerator( ftb.conn, dataType, ids)) good, chNames, msg = checkChannels(datasets) message += msg if not good: raise omero.ServerError( 'Channel check failed, ' + 'all images must have the same channels: %s' % message) for d in datasets: message += 'Processing dataset id:%d\n' % d.getId() for image in d.listChildren(): message += 'Processing image id:%d\n' % image.getId() msg = extractFeatures(ftb, d, newOnly, chNames, im=image) message += msg + '\n' except: print message raise finally: ftb.close() return message
def addToFeatureSet(ftb, ds, fts, classId, imagesOnly): message = '' tid = WndcharmStorage.getAttachedTableFile(ftb.tc, ds) if tid: if not ftb.openTable(tid): return message + '\nERROR: Table not opened' version = unwrap(ftb.versiontag.getTextValue()) message += 'Opened table id:%d version:%s\n' % (tid, version) else: message += 'ERROR: Table not found for Dataset id:%d' % ds.getId() return message #fts = wndcharm.FeatureSet.FeatureSet_Discrete({'num_images': 0}) if imagesOnly: for image in ds.listChildren(): imId = image.getId() message += '\tProcessing features for image id:%d\n' % imId sig = wndcharm.FeatureSet.Signatures() (sig.names, sig.values) = ftb.loadFeatures(imId) sig.source_file = str(imId) sig.version = version fts.AddSignature(sig, classId) else: names, values, ids = ftb.bulkLoadFeatures() message += '\tProcessing all features for dataset id:%d\n' % ds.getId() for imId, vals in izip(ids, values): sig = wndcharm.FeatureSet.Signatures() sig.names = names sig.values = vals sig.source_file = str(imId) sig.version = version fts.AddSignature(sig, classId) fts.classnames_list[classId] = ds.getName() return message
def countCompleted(ftb, ds): message = '' tc = ftb.tc imIds = [im.getId() for im in ds.listChildren()] tid = WndcharmStorage.getAttachedTableFile(ftb.tc, ds) if tid is None: message += 'Image feature status PRESENT:%d ABSENT:%d\n' % \ (0, len(imIds)) return message if not ftb.openTable(tid): message += 'ERROR: Table not opened\n' message += 'Image feature status UNKNOWN:%d\n' % len(imIds) return message message += 'Opened table id:%d\n' % tid d = tc.chunkedRead([0], 0, tc.table.getNumberOfRows(), 100) ftImIds = d.columns[0].values matchedIds = set(imIds).intersection(ftImIds) message += 'Image feature status PRESENT:%d ABSENT:%d\n' % \ (len(matchedIds), len(imIds) - len(matchedIds)) return message
def extractFeatures(ftb, ds, newOnly, chNames, imageId = None, im = None): message = '' tc = ftb.tc # dataset must be explicitly provided because an image can be linked to # multiple datasets in which case im.getDataset() doesn't work if not im: if not imageId: #raise Exception('No input image') raise omero.ServerError('No input image') im = ftb.conn.getObject('Image', imageId) if not im: return 'Image id:%d not found\n' % imageId else: imageId = im.getId() tid = WndcharmStorage.getAttachedTableFile(ftb.tc, ds) if tid: if not ftb.openTable(tid): return message + '\nERROR: Table not opened\n' version = unwrap(ftb.versiontag.getTextValue()) # version seems to be in unicode message += 'Opened table id:%d version:%s\n' % (tid, str(version)) if newOnly and ftb.tableContainsId(imageId): return message + 'Image id:%d features already in table' % imageId # FIXME: default is convert multichannel to greyscale unless user input # Calculate features for an image channel # Optionally prepend the channel label to each feature name and combine ftall = None for c in xrange( len( chNames ) ): wndcharm_matrix = PyImageMatrix() wndcharm_matrix.allocate( im.getSizeX(), im.getSizeY() ) numpy_matrix = wndcharm_matrix.as_ndarray() numpy_matrix[:] = im.getPrimaryPixels().getPlane(theZ=0,theC=c,theT=0) feature_plan = wndcharm.StdFeatureComputationPlans.getFeatureSet(); options = "" # This is where you can tell wnd-charm to normalize pixel intensities, # take ROIs etc. ... leave blank for now. ft = Signatures.NewFromFeatureComputationPlan( wndcharm_matrix, feature_plan, options ) ft.names = [WndcharmStorage.insert_channel_name( n, chNames[c]) for n in ft.names] ft.source_path = im.getName() if not ftall: ftall = ft else: ftall.names += ft.names ftall.values += ft.values # Save the features to a table if not tid: ftb.createTable(ftall.names, ft.version) version = unwrap(ftb.versiontag.getTextValue()) message += 'Created new table id:%d version:%s\n' % ( ftb.tc.tableId, version) message += WndcharmStorage.addFileAnnotationTo(tc, ds) if version != ft.version: return message + 'Incompatible version: Stored=%s Calculated=%s' % ( version, ft.version) ftb.saveFeatures(imageId, ftall) return message + 'Extracted features from Image id:%d\n' % imageId