...
 
Commits (36)
......@@ -5,10 +5,11 @@ Configuring your lever server with your own auth0 account
1. create a new auth0 application
type regular web application
2. update the file leverjs/config.js with the details from your auth0 application -- modify the domain and audience fields with the information from your auth0 application. you can change the name, logo, color scheme of your login page as you desire. set the defaultPI field here as well.
2. update the files ./leverjs/clientConfig.js and ./config.js with the details from your auth0 application -- modify the domain and audience fields with the information from your auth0 application. you can change the name, logo, color scheme of your login page as you desire. set the defaultPI field here as well.
you can ask the git repo to not track your changes to the config.js file, using:
git update-index --skip-worktree ./leverjs/config.js
you can ask the git repo to not track your changes to the config.js and leverjs/clientConfig.js file, using:
git update-index --skip-worktree ./leverjs/clientConfig.js
(repeat for config.js)
3. next, enable google login
https://auth0.com/docs/connections/social/google
......@@ -18,7 +19,7 @@ Configuring your lever server with your own auth0 account
a. go to the user settings on the auth0 page. Under "App Metadata", set
{
"verified": "true",
"verified" : "true",
"pi": piNameHere_this_should_match_defaultPI_in_leverjs\config.js
}
......
// config.js leverjs server configuration settings
//
// you should be able to edit this file and have updates take effect even if server is
// running
//
// you can keep git from updating this file by issuing the following:
// git update-index --skip-worktree ./leverjs/config.js
var config={};
......@@ -6,19 +10,16 @@ var config={};
config.jwksUri=`https://leverjs.auth0.com/.well-known/jwks.json`;
config.aud= '{https://leverjs.auth0.com/api/v2/}';
config.issuer= `https://leverjs.auth0.com/`;
// case insensitive
// pi is case insensitive
config.defaultPI= 'cohen';
config.login={};
// for auth0 login widget
config.login.title="leverjs: login required";
config.login.audience='https://leverjs.auth0.com/api/v2/';
config.login.appID='A31IUGxTl9ZsqhByitVa4r3ABJFNWSND';
config.login.domain='leverjs.auth0.com';
// other pi( case insensitive) matches portions of server paths with pi field (set on auth0.com user property)
// here, steve will be authorized to edit any url that contains '/path/To/StevesStuff'
// important -- if you have somewhere else in your leverjs folder tree with that path, steve will have
// access there as well. you the man steve!
config.otherPI=[ ['/path/To/StevesStuff/','steve'], ];
config.debugMatlab=false;
config.logVerbose=false;
if (('undefined'===typeof IS_NODE) || (IS_NODE)) {
module.exports = config;
}
\ No newline at end of file
......@@ -5,6 +5,7 @@ rm -rf leverjsExt
cp -r leverjs leverjsExt
cp -r j2l/node_modules leverjsExt
cp server.js leverjsExt
cp config.js leverjsExt
cp package.json leverjsExt
mkdir bin
cp -rf prebuilt/*.app bin/
......
......@@ -7,6 +7,7 @@ xcopy /Y /I /Q /S leverjs leverjsExt
xcopy /Q /Y /I /S j2l leverjsExt
copy server.js leverjsExt
copy package.json leverjsExt
copy config.js leverjsExt
mkdir bin
copy prebuilt\*.json bin\
copy prebuilt\*.exe bin\
\ No newline at end of file
......@@ -72,7 +72,7 @@ function initiateReseg(time)
var sqlCmd = "INSERT INTO tblCommands VALUES ('matlab','resegFrame',0," + (time) + ",'"
+ WindowSize + "','','')";
ljsLog.log('initiating reseg:' + sqlCmd);
ljsLog.logVerbose('initiating reseg:' + sqlCmd);
gDB.exec(sqlCmd);
} // initiateReseg
......@@ -491,12 +491,12 @@ function startTrackandAnalyzeProcesses()
// to start your own matlab, change 0 to 1 below...then run matlabPollDB.m from leverjs/matlab folder
const path=require('path');
const fs=require('fs');
var packagePath=path.join(process.cwd(),'package.json');
var packageInfo = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
var configPath=path.join(process.cwd(),'config.js');
delete require.cache[configPath]
config=require(configPath);
if (0 || true===packageInfo.noLaunchMATLAB)
ljsLog.log('NOT LAUNCHING MATLAB -- START YER OWN!');
if (true===config.debugMatlab)
ljsLog.warn('NOT LAUNCHING MATLAB -- START YER OWN!');
else
launchMatlab();
......
......@@ -76,6 +76,11 @@
</div>
</div>
<div id='sbAlgorithms' style='display:none;'>
<br>
<div> <button class='sbButton' id="btnReplaceAlgorithms" tabindex="1" onclick="expCommand('replaceAlgorithmURL',false)">
replace algorithms on selected datasets from url</button>
<input type=textbox id="tbReplaceAlgorithmsURL"/>
</div>
<br><button class='sbButton' id="btnDeleteAllSegmentations" tabindex="1" onclick="expCommand('deleteAlgorithmAll',false)">
Delete all algorithms from selected datasets</button>
<h2>Available algorithms</h2>
......@@ -196,7 +201,7 @@
<script src="lineage.js" type="text/javascript"> </script>
<script src="algorithms.js"></script>
<script src='edit.js'> </script>
<script src='config.js'></script>
<script src='clientConfig.js'></script>
<script src='login.js'> </script>
<script src='sidebar.js'> </script>
<script src='lever.js'></script>
......
......@@ -94,7 +94,7 @@ function pauseAll(bKill)
} // pauseAll
function expCommand(command,bAll)
function expCommand(command,bAll,param)
{
if (command==='eraseSelected') {
var result = window.confirm('WARNING - THERE IS NO WAY TO UNDO THIS! Press cancel or all the cells get it!');
......@@ -105,22 +105,35 @@ function expCommand(command,bAll)
var btnPause=document.getElementById('btnPause');
btnPause.style.display="";
}
var url;
if ('replaceAlgorithmURL'===command) {
url=document.getElementById('tbReplaceAlgorithmsURL').value;
if (url=='')
return;
}
// get the file list (if needed)
var leverFileList=[];
var fileList=document.getElementById('fileDiv');
for (var n=0;n<fileList.children.length;n++) {
if (document.getElementById('checkbox_'+n).checked || bAll===true) {
var leverFile=document.getElementById('fileDiv_'+n).leverFile;
if ('replaceAlgorithmURL'===command){
var fqLeverFile=gServerURL+leverFile;
if (fqLeverFile===url)
continue;
}
leverFileList.push(leverFile);
}
}
var leverExpCommand={command:command,leverFileList:leverFileList};
if (command==='applyAlgorithmAll') {
var selectAlgorithm=document.getElementById('selectSegmentationAlgorithm');
var algInfo=gSegInfo[selectSegmentationAlgorithm.selectedIndex];
leverExpCommand.algInfo=algInfo;
}
if ('replaceAlgorithmURL'===command) {
leverExpCommand.URL=url;
}
if (!IS_NODE) {
// send it to server
var cmd=gServerURL + "experimentCommand";
......@@ -141,7 +154,6 @@ function expCommand(command,bAll)
}
} // expCommand
// ------------------------ start process Q stuff
......@@ -777,9 +789,14 @@ function refreshExperiments()
var cbID='checkbox_'+n;
var linkClone ='<i id="clone_'+n+'" class="far fa-clone"></i> ';
var linkPreview='<i id="image_'+n+'" class="far fa-image"></i> ';
var clipboardURL='&nbsp <a title="click to copy url" href="javascript:copyAlgorithmURL(\''+leverFile+'\')"<i class="far fa-clipboard"></i></a> ';
if (IS_NODE) {
// can't copy URL in node -- clipboard navigator not available plus need a server running...
clipboardURL='';
}
newDiv.innerHTML='<div class="fileDiv_name"><input type="checkbox" id="'+cbID+'"></input> '
+(n+1)+'/'+(flist.length)+') &nbsp '+linkPreview+'&nbsp'
+linkClone+linkImageWindow +'</div>';
+linkClone+linkImageWindow +clipboardURL+' </div>';
newDiv.style.marginBottom='10px'
newDiv.style.marginTop='10px';
fileDiv.appendChild(newDiv);
......
......@@ -171,6 +171,17 @@
<table id='editList' ></table>
</div>
<div id='sbAlgorithms' style='display:none'>
<br>
<div>
<button class='sbButton' id="btnCopyAlgURL" tabindex="0" onclick="copyAlgorithmURL()">
copy algorithms url</button>
</div>
<div>
<button class='sbButton' id="btnReplaceAlgorithms" tabindex="0" onclick="replaceAlgorithms()">
replace algorithm set from url</button>
<input type=textbox style='width:20em' id="tbReplaceAlgorithmsURL"/>
</div>
<div id="currentSegmentationStatus"></div>
<select id="selectSegmentationAlgorithm"> </select>
<table id='segmentationTable' > </table>
......@@ -277,7 +288,7 @@
<script src="MouseHandler.js"></script>
<script src="ImageWindowBuilder.js"></script>
<script src="config.js"></script>
<script src="clientConfig.js"></script>
<script src="LoadHulls.js"></script>
<script src="LoadImage.js"></script>
......
......@@ -146,6 +146,7 @@ window.onload = function () {
g_db_path=g_db_path.substring(1);
if (IS_NODE) {
document.getElementById('sbtabAuthorization').style.display='none';
document.getElementById('btnCopyAlgURL').disabled=true;
setRootDir();
if (null===g_db_path || g_db_path.length<2) {
// we weren't opened by experiment window (no incoming path)
......
......@@ -272,6 +272,56 @@ function removeAlgorithm(rowid,algName)
}
} // remove the ith row of the tblAlgorithms
function copyAlgorithmURL(leverFile)
{
if (undefined===navigator || undefined===navigator.clipboard) {
console.log('copyURL: clipboard not available')
return;
}
if (undefined==gServerURL) {
// no server here -- can't use this as source
return;
}
if (undefined==leverFile) {
leverFile=g_db_path;
}
navigator.clipboard.writeText(gServerURL+leverFile).then(function() {
// console.log('Async: Copying to clipboard was successful!');
}, function(err) {
console.error('Async: Could not copy text: ', err);
});
} // copyAlgorithmURL
function replaceAlgorithms()
{
if (undefined==g_db_path || null==g_db_path) {
return;
}
url=document.getElementById('tbReplaceAlgorithmsURL').value;
if (url=='')
return;
var leverExpCommand={command:'replaceAlgorithmURL',leverFileList:[g_db_path],URL:url};
if (!IS_NODE) {
// send it to server
var cmd=gServerURL + "experimentCommand";
var body=JSON.stringify(leverExpCommand);
editSendServerCommand(cmd,function(){
// on complete...
document.activeElement.blur();
updateAlgorithms();
},replaceAlgorithms,[],false,body);
}
else {
const experimentHelper=require('./experimentHelper.js');
// run it locally
experimentHelper.experimentExecCmd([g_db_path],leverExpCommand,function(){
document.activeElement.blur();
updateAlgorithms();
});
}
} // replaceAlgorithms
if (('undefined'===typeof IS_NODE) || (IS_NODE)) {
module.exports.getNumberOfCellsInDB=getNumberOfCellsInDB;
......
// clientConfig.js leverjs web page config settings
// right now this is used only for settings on the auth0 login widget
// see auth0 config.md
// you can keep git from updating this file by issuing the following:
// git update-index --skip-worktree ./leverjs/clientConfig.js
var clientConfig={};
clientConfig.login={};
// for auth0 login widget
clientConfig.login.title="leverjs: login required";
clientConfig.login.audience='https://leverjs.auth0.com/api/v2/';
clientConfig.login.appID='A31IUGxTl9ZsqhByitVa4r3ABJFNWSND';
clientConfig.login.domain='leverjs.auth0.com';
if (('undefined'===typeof IS_NODE) || (IS_NODE)) {
module.exports = clientConfig;
}
\ No newline at end of file
......@@ -11,11 +11,14 @@ function optimize(dbFile,bQuiet=false)
var cmd;
cmd='PRAGMA optimize';
lDB.exec(cmd,function(){
cmd='PRAGMA wal_checkpoint(truncate)';
cmd='PRAGMA wal_checkpoint(full)';
lDB.exec(cmd,function(){
lDB.close();
if (!bQuiet)
console.log('completed optimizing file '+dbFile);
lDB.close(function (error) {
if (null!=error)
console.error(error.message);
if (!bQuiet)
console.log('completed checkpoint of file '+dbFile);
});
})
});
......
......@@ -12,6 +12,8 @@ function eraseAll(lDB)
lDB.run(sqlCmd);
sqlCmd = "DELETE from tblDistCC;" ;
lDB.run(sqlCmd);
sqlCmd = "DELETE from tblCosts;" ;
lDB.run(sqlCmd);
sqlCmd = "DELETE from tblEditList" ;
lDB.run(sqlCmd);
sqlCmd = "DELETE from tblZombies" ;
......
......@@ -52,6 +52,74 @@ function deleteAlgorithm(leverFile,fnCallback)
} // deleteAlgorithmAll
function replaceAlgorithms(leverFile,dbAlgorithms)
{
var lDB = openDB(leverFile);
if (null===lDB) {
console.log('replaceAlgorithms db not found : '+leverFile)
return;
}
var cmd='DELETE FROM tblAlgorithms';
lDB.exec(cmd,function(){
for (var n=0;n<dbAlgorithms.length;n++) {
cmd="insert into tblAlgorithms(fqn,type,jsAlgorithmInfo) VALUES ("+
"'"+dbAlgorithms[n].fqn+"','"+dbAlgorithms[n].type+"','"+dbAlgorithms[n].jsAlgorithmInfo+"')";
lDB.exec(cmd);
}
});
} // replaceAlgorithms
function replaceAlgorithmURL(leverFile,URL,fnCallback)
{
var http;
if (URL.indexOf('https')==0)
http=require('https');
else if (URL.indexOf('http')==0)
http=require('http');
else {
console.log('replaceAlgorithmsURL :: requires leverjs server mode : '+URL);
return;
}
http.get(URL+'/algorithms', (res) => {
const { statusCode } = res;
const contentType = res.headers['content-type'];
let error;
if (statusCode !== 200) {
error = new Error('replaceAlgorithmURL Request Failed.\n' +
`Status Code: ${statusCode}`);
} else if (!/^application\/json/.test(contentType)) {
error = new Error('replaceAlgorithmURL Invalid content-type.\n' +
`Expected application/json but received ${contentType}`);
}
if (error) {
console.error(error.message);
// consume response data to free up memory
res.resume();
return;
}
res.setEncoding('utf8');
let rawData = '';
res.on('data', (chunk) => { rawData += chunk; });
res.on('end', () => {
try {
const algorithms = JSON.parse(rawData);
replaceAlgorithms(leverFile,algorithms.dbAlgorithms);
} catch (e) {
console.error('replaceAlgorithmURL ' + e.message);
}
});
}).on('error', (e) => {
console.error(`replaceAlgorithmURL error: ${e.message}`);
});
fnCallback();
} // replaceAlgorithmURL
// called when we're running in node (e.g. electron) to run the supplied command on the leverFileList
function experimentExecCmd(leverFileList,leverExpCommand,fnCallback)
{
......@@ -84,7 +152,16 @@ function experimentExecCmd(leverFileList,leverExpCommand,fnCallback)
}
});
break;
case 'replaceAlgorithmURL':
replaceAlgorithmURL(leverFile,leverExpCommand.URL,function(){
nComplete++;
if (leverFileList.length===nComplete) {
// all done...
if (undefined!==fnCallback)
fnCallback();
}
});
break;
case 'resegAll':
var leverCommand={leverFile:leverFile,time:1,command:'resegAll',params:[1]};
process.addToQ(leverCommand);
......
// ljsLog
// leverjs debug logging
function getPackageVerbose(){
function getConfigVerbose(){
// clear the require cache so we reload config
const path=require('path');
const fs=require('fs');
var packagePath=path.join(process.cwd(),'package.json');
if (!fs.existsSync(packagePath)) {
return false;
};
var packageInfo = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
return packageInfo.logVerbose;
} // getPackageVerbose
var configPath=path.join(process.cwd(),'config.js');
delete require.cache[configPath]
config=require(configPath);
return config.logVerbose;
} // getConfigVerbose
class ljsLog {
......@@ -31,7 +29,7 @@ class ljsLog {
});
}
this.verbose=getPackageVerbose();
this.verbose=getConfigVerbose();
this.refreshTime=Date.now();
}
......@@ -49,7 +47,7 @@ class ljsLog {
logVerbose(strLog) {
if (Date.now()-this.refreshTime > 10e3) {
this.verbose=getPackageVerbose();
this.verbose=getConfigVerbose();
this.refreshTime=Date.now();
}
if (true==this.verbose)
......
......@@ -6,7 +6,7 @@ var options = {
primaryColor: '#00bbff'
},
languageDictionary: {
title: config.login.title
title: clientConfig.login.title
},
auth: {
redirect: false,
......@@ -14,7 +14,7 @@ var options = {
responseType: 'token',
params: {
scope: 'user_metadata openid profile',
audience: config.login.audience
audience: clientConfig.login.audience
}
},
additionalSignUpFields: [{
......@@ -26,7 +26,7 @@ function authorize(fnCallback)
{
if (undefined===lock)
lock = new Auth0Lock(config.login.appID, config.login.domain,options);
lock = new Auth0Lock(clientConfig.login.appID, clientConfig.login.domain,options);
// Listening for the authenticated event
lock.on("authenticated", function(authResult) {
......@@ -50,7 +50,7 @@ function authorize(fnCallback)
function logout()
{
if (undefined===lock)
lock = new Auth0Lock('A31IUGxTl9ZsqhByitVa4r3ABJFNWSND', 'leverjs.auth0.com',options);
lock = new Auth0Lock(clientConfig.login.appID, clientConfig.login.domain,options);
localStorage.removeItem('profile');
localStorage.removeItem('accessToken');
......
......@@ -427,9 +427,12 @@ function pauseOrKillAll(bKill,leverFile)
continue;
if (bKill) {
gCommandMap.delete(inProcessQ[i].ID);
if (null!==inProcessQ[i].childProc)
inProcessQ[i].childProc.kill();
inProcessQ[i].childProc=null;
inProcessQ[i].leverFile=null;
}
else {
// pause gets put right into Q
......
fref='\path\to\leverFile.LEVER';
targetFolder='\path\to\filesToReplaceAlgorithmsOn';
flist=dir(fullfile(targetFolder,'*.LEVER'));
for ff=1:length(flist)
if flist(ff).name==fref
continue
end
strDB=fullfile(flist(ff).folder,flist(ff).name);
conn = database(strDB, '','', 'org.sqlite.JDBC', 'jdbc:sqlite:');
exec(conn,['ATTACH "' fnameRef '" as dbRef']);
exec(conn,'DELETE from tblAlgorithms');
exec(conn,'INSERT into tblAlgorithms select * from dbRef.tblAlgorithms');
close(conn);
end
\ No newline at end of file
function tidExtFamilies=getExtFamilies(conn)
tidExtFamilies=[];
cmd='select trackID from uiExtFamilies';
Q=fetch(conn,cmd);
if isempty(Q)
return
end
tidExtFamilies=cell2mat(Q);
cmdLineage=['SELECT parent.trackID AS trackID,c1.trackID as tchild1, c2.trackID as tchild2' ...
' FROM tblFamilies INNER JOIN tblCells as parent ON parent.cellID = tblFamilies.cellID_parent' ...
' INNER JOIN tblCells as c1 ON c1.cellID = tblFamilies.cellID_child1 INNER JOIN tblCells as c2' ...
' ON c2.cellID = tblFamilies.cellID_child2'];
Qlineage=fetch(conn,cmdLineage);
familyList=cell2mat(Qlineage);
idIntersect=intersect(tidExtFamilies,familyList);
while ~isempty(idIntersect)
for i=1:length(idIntersect)
[r,c]=find(familyList==idIntersect(i));
if isempty(r)
continue
end
tidExtFamilies=union(tidExtFamilies,familyList(r,:));
familyList(r,:)=[];
end
idIntersect=intersect(tidExtFamilies,familyList);
end
\ No newline at end of file
......@@ -76,10 +76,10 @@ for nChannel=1:numberOfChannels
pts1=Read.getPoints(iQ1{j,3},CONSTANTS);
pts1=double(pts1);
% size gate - if you're > 10x the size, no go
if (min([size(pts0,1),size(pts1,1)])/max([size(pts0,1),size(pts1,1)])) <0.1
continue
end
% % size gate - if you're > 10x the size, no go
% if (min([size(pts0,1),size(pts1,1)])/max([size(pts0,1),size(pts1,1)])) <0.1
% continue
% end
MIN_MAHAL_POINTS=15;
if size(pts0,1)<MIN_MAHAL_POINTS && size(pts1,1)<MIN_MAHAL_POINTS
d12com=norm(mean(pts0)-mean(pts1));
......
function [Cells,nestedCells]=FrameSegment_texture(conn, t,CONSTANTS,args)
% args.minimumRadius_um=8;
% args.minimumRadius_um=3;
% args.ensemble_minimumRadius_um=8;
% args.draw=true;
% args.denoise=false;
Cells=[];
nestedCells=[];
......@@ -21,6 +23,8 @@ segParams.storeEnsemble=true;
segParams.alpha=1.0;
segParams.denoise=true;
segParams.bCytoplasmic=false;
segParams.bCircular=false;
segParams.brightLevels=-1;
if nargin==4
segParams=Segment.getParams(segParams,args);
end
......@@ -45,9 +49,7 @@ min_radius_pixels = segParams.minimumRadius_um ./ resolution_um;
chan=segParams.channels(1); % default channel
[im,qLog,imLog]=Segment.getImages(CONSTANTS,t,chan,min_radius_pixels,segParams.denoise);
if segParams.isPhase
qLog=max(qLog(:))-qLog;
end
for idChannel=2:length(segParams.channels)
[~,channel_qLog]=Segment.getImages(CONSTANTS,t,segParams.channels(idChannel),min_radius_pixels,segParams.denoise);
qLog=qLog+channel_qLog;
......@@ -96,34 +98,10 @@ if DRAW
drawnow
end
lm=segParams.alpha*multithresh(im,1);
bwIntensity=imquantize(im,lm);
bwIntensity=logical(bwIntensity>1);
if segParams.isPhase
nLevels=2;
lm=segParams.alpha*multithresh(imTexture,nLevels);
q=imquantize(imTexture,lm);
bw=logical(q>nLevels);
else
n0=1;
if is3D(im)
ratioThresh=30;
else
ratioThresh=10;
end
for nLevels=n0:20
lm=segParams.alpha*multithresh(imTexture,nLevels);
q=imquantize(imTexture,lm);
bw=logical(q>nLevels);
if length(find(bw))/length(find(bwIntensity))<ratioThresh
break;
end
end
end
[bw,bwIntensity,nLevels]=thresholdImages(im,imTexture,segParams);
fprintf(1,'frameSegmentTexture: min_radius_pixels=%s nLevels detected at %d\n',mat2str(min_radius_pixels,3),nLevels);
bw=bw|imfill(bw,'holes');
if segParams.wellRadius>0
% mask all points at r>radius
center=size(im)/2;
......@@ -135,22 +113,10 @@ if segParams.wellRadius>0
bw(~bwMask)=0;
end
if segParams.isPhase
se=strel('square',2*floor(min_radius_pixels/2)+1);
bw=imopen(bw,se);
bw=imclose(bw,se);
% bwTexture=imfill(bwTexture,'holes');
bw=bwareaopen(bw,min_area_pixels);
% halo clean up
bwd=bwdist(~bw);
bw(bwd<1.5*min_radius_pixels)=0;
else
if ~segParams.bCytoplasmic
if ~segParams.bCytoplasmic
bw=removeOuterRadius(bw,CONSTANTS,min_radius_pixels);
end
end
qLog(~bw)=0;
% bw2 is all the pixels in our foreground image. after we segReduce, some
......@@ -175,6 +141,7 @@ if isempty(bw)
end
preShakeL=bwlabeln(bw);
bwdCells=bwdist(~bw);
[L,num]=Segment.allocateShake(bw,bw2,qLog,im);
[L2]=bwlabeln(bw2); % used to mark original cc for segCC field
preShakeL=L;
......@@ -209,6 +176,10 @@ for n=1:num
newCell.idxPreShake=find(preShakeL==n);
if bEnsemble
newCell=setEnsembleFeatures(newCell,n,rp,rpPre);
if ~segParams.bCircular
newCell.ensemble.circularity=0;
newCell.ensemble.preCircularity=0;
end
end
if DRAW && ~is3D(im)
cmap=hsv(255);
......@@ -231,13 +202,14 @@ for i=1:length(Cells)
end
Cells(i).nfg=length(find(bwIntensity(idx)));
Cells(i).ncc=length(find([Cells.segCC]==Cells(i).segCC));
if DRAW & 0==Cells(i).nfg
Cells(i).maxd=max(bwdCells(idx));
if DRAW && ( (0==Cells(i).nfg && Cells(i).ncc>1)|| Cells(i).maxd<min_radius_pixels/2)
plot(Cells(i).surface(:,1),Cells(i).surface(:,2),'color','k','linewidth',3);
end
end
if ~isempty(Cells)
idxParasite=find(0==[Cells.nfg] & 1<[Cells.ncc]);
idxParasite=find( (0==[Cells.nfg] & 1<[Cells.ncc]) | ([Cells.maxd]<min(min_radius_pixels)/2));
Cells(idxParasite)=[];
end
......@@ -251,13 +223,68 @@ if DRAW
title([num2str(tElapsed,'%0.0f') 'seconds']);
drawnow
end
4;
function [bw,bwIntensity,nLevels]=thresholdImages(im,imTexture,segParams)
if length(segParams.alpha)>1
alpha=segParams.alpha(1);
alphaIntensity=segParams.alpha(2);
else
alpha=segParams.alpha;
alphaIntensity=segParams.alpha;
end
% first, threshold bwIntensity
if segParams.brightLevels>=0
nLevels=segParams.brightLevels;
else
nLevels=1;
end
lm=alphaIntensity*multithresh(im,nLevels);
bwIntensity=imquantize(im,lm);
bwIntensity=logical(bwIntensity>nLevels);
if segParams.isPhase
if segParams.brightLevels>=0
nLevels=segParams.brightLevels;
else
nLevels=2;
end
lm=alpha*multithresh(imTexture,nLevels);
q=imquantize(imTexture,lm);
bw=logical(q>nLevels);
else
if segParams.brightLevels>=0
n0=segParams.brightLevels;
else
n0=1;
end
if is3D(im)
ratioThresh=30;
else
ratioThresh=10;
end
for nLevels=n0:20
lm=alpha*multithresh(imTexture,nLevels);
q=imquantize(imTexture,lm);
bw=logical(q>nLevels);
if length(find(bw))/length(find(bwIntensity))<ratioThresh
break;
end
end
end
function Cells=getGradientDef(Cells,imLog,bPhase)
if is3D(imLog)
if bPhase
return
end
if is3D(imLog)
return
end
gradientDir=-1; % gradient decreases towards centroid
bw=logical(0*imLog);
for i=1:length(Cells)
idx=sub2ind(size(imLog),Cells(i).pts(:,2),Cells(i).pts(:,1));
......@@ -269,11 +296,6 @@ end
% transform for each individual cell
bwd=bwdist(~bw);
if bPhase
gradientDir=1;
else
gradientDir=-1;
end
for i=1:length(Cells)
idx=sub2ind(size(imLog),Cells(i).pts(:,2),Cells(i).pts(:,1));
p=corrcoef(bwd(idx),imLog(idx));
......
......@@ -38,6 +38,7 @@ rgRadius=[rEnd:rStep:rStart];
resolution_um=CONSTANTS.imageData.PixelPhysicalSize; % um per pixel
% ask for a parpool somewhere between 2 and nCores
p=getPool(segParams.nCores);
p=gcp('nocreate');
if isempty(p)
p=parpool([2,segParams.nCores]);
......@@ -180,6 +181,26 @@ fprintf(1,' elapsed time=%2.3f\n',tElapsed);
4;
function p=getPool(nCores);
p=gcp('nocreate');
if ~isempty(p)
return;
end
nrep=0;
MAX_REPS=5;
while isempty(p) && nrep<MAX_REPS
try
p=parpool([2,nCores]);
break;
catch
if nrep<MAX_REPS-1
tpause=30+60*rand();
pause(tpause);
end
nrep=nrep+1;
end
end
function segCC=getEnsembleCC(nc,nEnsemble)
prec=ceil(log10(nEnsemble+1));
segCC=[ num2str(nc.segCC) '.' num2str(nc.iRadius,['%0' num2str(prec) 'd'])];
......@@ -237,6 +258,9 @@ cd=cell.ensemble.area/cell.ensemble.convexArea;
cdPS=cell.ensemble.preArea/cell.ensemble.preConvexArea;
score=min(cd,cdPS);
% circularity
score=score+min(cell.ensemble.preCircularity,cell.ensemble.circularity);
if isfield(cell,'ensemble') && isfield(cell.ensemble,'gdef')
gdef=min(cell.ensemble.gdef,cell.ensemble.gdefPre);
score = score+gdef;
......
function [im,qLog,imLogRaw]=getImages(CONSTANTS,t,channel,min_radius_pixels,bFilter)
if ~exist('bFilter','var')
bFilter=true;
bFilter=false;
end
global USE_CUDA
im = MicroscopeData.Reader('imageData',CONSTANTS.imageData, 'chanList',channel,'timeRange',[t t], 'outType','single','prompt',false);
szFilter=round(size(im)/3);
im=denoise(im,bFilter);
if 1==nargout
return
end
if is3D(im)
% 3D
if bFilter
imx = medfilt3(im)-imgaussfilt3(im,szFilter);
im=mat2gray(imx);
else
im=mat2gray(im);
end
if 1==nargout
return
end
if USE_CUDA
if USE_CUDA
% AC 2 2019 apparently HIP LOG is broken in 3D.
logRadius=min_radius_pixels.*0.75;
imLog=HIP.LoG(im,logRadius,1);
% imLog=HIP.LoG(im,logRadius,1);
imd1=HIP.Cuda.Gaussian(im,sqrt(2).*logRadius,1,[]);
imd2=HIP.Cuda.Gaussian(im,logRadius./sqrt(2),1,[]);
imLog=imd1-imd2;
else
% use difference of gaussian approximation
logRadius=min_radius_pixels;
......@@ -31,20 +32,6 @@ if is3D(im)
imLogRaw=imLog;
else
% 2D
if bFilter
imlp=imgaussfilt(im,szFilter);
imx = medfilt2(im)-imlp;
im=mat2gray(imx);
else
im=mat2gray(im);
end
% ACK! clamping the denoised images to >=0 seems a good idea, but it
% breaks transmitted light images...is it worth it? does it help? ACK!
% imx=max(imx,0); % AC turned off 7 22 18 -- kills phase / transmitted light
if 1==nargout
return
end
if USE_CUDA
logRadius=0.5.*min_radius_pixels;
logRadius(3)=0;
......@@ -65,6 +52,35 @@ imLog=mat2gray(imLog);
lm=multithresh(imLog,15);
qLog=imquantize(imLog,lm);
function im=denoise(im,bFilter)
if false==bFilter
im=mat2gray(im);
return
end
global USE_CUDA
szFilter=size(im)./3;
if is3D(im)
% 3D
if USE_CUDA
imx = medfilt3(im)-HIP.Cuda.Gaussian(im,szFilter,1,[]);
else
imx = medfilt3(im)-imgaussfilt3(im,szFilter);
end
else
% 2D
if USE_CUDA
szFilter(3)=0;
imx = medfilt3(im)-HIP.Cuda.Gaussian(im,szFilter,1,[]);
else
imx=medfilt2(im)-imgaussfilt(im,szFilter);
end
end
imx=max(imx,0);
im=mat2gray(imx);
function b3D=is3D(im)
......
......@@ -29,7 +29,7 @@ function psCell=prepareCell(psCell,cell)
end
% this last value is either trackID or iRadius
if isfield(cell,'trackID')
psCell.setInt(9,trackID);
psCell.setInt(9,cell.trackID);
else
psCell.setInt(9,-1);
end
......
function hipCheckSetRegistry()
if ~ispc() || HIP.Cuda.DeviceCount<=0
if ismac()
fprintf(1,' HIP not available for mac. Use CPU alternatives\n')
return
end
if ispc() && HIP.Cuda.DeviceCount<=0
fprintf(1,' no cuda compatible device found. cuda support not enabled\n');
return
end
if ~ispc()
if HIP.Cuda.DeviceCount>0
fprintf(1,'found Cuda device!\n');
else
fprintf(1,'no Cuda device found\n');
end
return
end
try
valnames = winqueryreg('HKEY_LOCAL_MACHINE','SYSTEM\CurrentControlSet\Control\GraphicsDrivers\','TdrDelay');
fprintf(1,' HIP cuda support enabled in registry. Well done!\n');
......
......@@ -10,15 +10,20 @@
"minimumRadius_um":"4",
"useCuda":"false",
"note_alpha":" - smaller alpha values lower segmentation threshold",
"alpha":1.0,
"alpha":"1.0",
"denoise":"true",
"note_2":" - nCores: for ensemble segment only",
"nCores":4,
"isPhase":false,
"note_brightLevels":" - brightLevels is the base number of foreground groups, e.g. {2} for {bg,noise,fg}. leave blank for default",
"brightLevels":"",
"wellRadius":-1,
"note_2":" - bCytoplasmic to true for objects with fine processes to preserve",
"bCytoplasmic":false,
"note_3":" - parameters below are for ensemble segment only",
"nCores":4,
"storeEnsemble":"true",
"note_3":" - bCytoplasmic to true for objects with fine processes to preserve",
"bCytoplasmic":false
"note_4":"bCircular adds a bit more weight to circular segmentations (ensemble only)",
"bCircular":"false"
},
"function":"+Segment.FrameSegment_texture.m",
"commandHost":"matlab"
......
......@@ -64,6 +64,7 @@
"from": "leverjsExt/",
"filter": [
"package.json",
"config.js",
"server.js"
]
},
......@@ -98,7 +99,5 @@
]
}
]
},
"noLaunchMATLAB": false,
"logVerbose":false
}
}
This source diff could not be displayed because it is stored in LFS. You can view the blob instead.
No preview for this file type
No preview for this file type
......@@ -65,6 +65,14 @@ git clone --depth=1 https://git-bioimage.coe.drexel.edu/opensource/leverjs.git
git clone --depth=1 https://git-bioimage.coe.drexel.edu/opensource/leverUtilities.git
[Optional but recommended -- clone the hydra image processor. for windows/ubuntu machines (ubuntu support pending)
this provides gpu accelerated (NVIDIA Cuda) image processing. Improved filtering over the matlab implementations
provides more accurate results in general, so this is strongly recommended. Mac support not available yet
git clone --depth=1 https://git@git-bioimage.coe.drexel.edu:opensource/hydra-image-processor.git
note that after cloning you will need to register the tdrDelay.reg file on windows, and then restart your pc.]
cd leverjs
npm update
......@@ -120,7 +128,7 @@ window stuff.
b. it is possible to open the .LEVER file directly using e.g. the DB Browser sqlite viewer. Note that the .LEVER file format is likely to change significantly at any time, and use this power for debugging purposes only.
c. to debug matlab process, edit package.json and set 'noLaunchMatlab' to true. start the image window renderer program, pick your algorithms and select reseg all (or next). then start matlab. run matlabpollDB('\path\to\leverFile.LEVER'). Or, just run matlabPollDB and it usually finds the right LEVER file automagically (from lever.state tidbit left by CmdLoop.js).
c. to debug matlab process, edit config.js and set 'debugMatlab' to true. start the image window renderer program, pick your algorithms and select reseg all (or next). then start matlab. run matlabpollDB('\path\to\leverFile.LEVER'). Or, just run matlabPollDB and it usually finds the right LEVER file automagically (from lever.state tidbit left by CmdLoop.js).
<h1>scripting and image capture for visualization</h1>
see the scripting tab in the electron (stand-alone) image window for more details...there is now a matlab/+MovieMaker folder with two examples.
......@@ -130,7 +138,7 @@ security is designed into leverjs at every level, from the firewall, to the ngin
Alternatively, to access the api functionality, you can use auth0 for use authentication. you will need
to create an auth0.com account and application key (more details TBD). Alternatively, if you start the lever server with no security (node server.js --secure=false ...) you can then use your firewall to limit
incoming connections on the server port to localhost only (easiest but not very secure).
incoming connections on the server port to localhost only (easiest but not very secure). See auth0_config.md for more detail.
You can also generate your own keys for programmatic authentication (run node.exe leverjs\keyGen.js, see matlab\+Client for examples).
......@@ -48,7 +48,7 @@ else
gPI=gPI.toLowerCase();
ljsLog.log('gPI set to : '+gPI);
const config=require('./leverjs/config');
const config=require('./config.js');
// kick off a dbupdate
function initRoot()
......@@ -117,7 +117,7 @@ function checkLWT(req,res,next)
const fs=require('fs');
const path=require('path');
var token=req.headers.authorization;
if (false===bSecure) {
return next();
}
......@@ -131,20 +131,40 @@ function checkLWT(req,res,next)
// then this originates via auth0
// first, make sure jwt is valid
checkJwt(req,res,function(){
// check config override
// reload config -- allows dynamic reconfig of access control
const path=require('path');
var configPath=path.join(process.cwd(),'config.js');
delete require.cache[configPath]
var config=require(configPath);
var pi;
if (undefined!=req.user) {
// next check authorization (i.e. pi matches)
if (undefined===req.user) {
return res.status(401).send('invalid user');
pi=req.user['https://leverjs.net/pi'];
}
var pi=req.user['https://leverjs.net/pi'];
if (pi!=null && pi!=undefined)
pi=pi.toLowerCase();
if (pi!==config.defaultPI && pi!==gPI) {
res.status(403).send('leverServer: access not granted -- server pi not set');
ljsLog.log('user not authorized:')
ljsLog.log(req.user);
if (undefined===req.user || null==pi || undefined==pi) {
return res.status(401).send('invalid user');
}
else
pi=pi.toLowerCase();
if (pi===gPI || pi===config.defaultPI.toLowerCase()) {
next();
return;
}
// convert *'s to /'s in path
var targetPath=req.path.replace(/\*/g,'\/');;
for (var i=0;i<config.otherPI.length;i++) {
if (targetPath.indexOf(config.otherPI[i][0])>=0 &&
pi===config.otherPI[i][1].toLocaleLowerCase()) {
next();
return;
}
}
// else if we get here then the authorization failed
var userID=req.user['https://leverjs.net/email'];
res.status(403).send('leverServer: authorization denied for user='+userID
+' with pi='+pi);
ljsLog.warn('user '+userID+' not authorized: pi='+pi);
});
return;
}
......