Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Bioimage Analysis
Matlab X Server
Commits
f0ea2e08
Commit
f0ea2e08
authored
Jul 21, 2022
by
Stéphane DALLONGEVILLE
Browse files
Initial commit
parent
38cf60b5
Changes
23
Expand all
Hide whitespace changes
Inline
Side-by-side
LICENSE.md
0 → 100644
View file @
f0ea2e08
This diff is collapsed.
Click to expand it.
pom.xml
0 → 100644
View file @
f0ea2e08
<?xml version="1.0" encoding="UTF-8"?>
<project
xmlns=
"http://maven.apache.org/POM/4.0.0"
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
>
<modelVersion>
4.0.0
</modelVersion>
<!-- Inherited Icy Parent POM -->
<parent>
<groupId>
org.bioimageanalysis.icy
</groupId>
<artifactId>
parent-pom-plugin
</artifactId>
<version>
1.0.6
</version>
</parent>
<!-- Project Information -->
<artifactId>
matlab-x-server
</artifactId>
<version>
3.2.0
</version>
<packaging>
jar
</packaging>
<name>
Matlab X Server
</name>
<description>
Use Icy as an image viewer from Matlab.
</description>
<inceptionYear>
2022
</inceptionYear>
<organization>
<name>
Institut Pasteur
</name>
<url>
https://pasteur.fr
</url>
</organization>
<licenses>
<license>
<name>
GNU GPLv3
</name>
<url>
https://www.gnu.org/licenses/gpl-3.0.en.html
</url>
<distribution>
repo
</distribution>
</license>
</licenses>
<developers>
<developer>
<id>
ylemontag
</id>
<name>
Yoann Le Montagner
</name>
<url>
https://icy.bioimageanalysis.org/author/ylemontag/
</url>
<roles>
<role>
developer
</role>
<role>
maintainer
</role>
</roles>
</developer>
</developers>
<!-- Project properties -->
<properties>
</properties>
<!-- Project build configuration -->
<build>
<plugins>
</plugins>
</build>
<!-- List of project's dependencies -->
<dependencies>
<!-- The core of Icy -->
<dependency>
<groupId>
org.bioimageanalysis.icy
</groupId>
<artifactId>
icy-kernel
</artifactId>
</dependency>
<dependency>
<groupId>
org.bioimageanalysis.icy
</groupId>
<artifactId>
matlab-communicator
</artifactId>
<version>
1.0.5
</version>
</dependency>
</dependencies>
<!-- Icy Maven repository (to find parent POM) -->
<repositories>
<repository>
<id>
icy
</id>
<name>
Icy's Nexus
</name>
<url>
https://icy-nexus.pasteur.fr/repository/Icy/
</url>
</repository>
</repositories>
</project>
src/main/java/plugins/ylemontag/matlabxserver/MatlabXServerDeamon.java
0 → 100644
View file @
f0ea2e08
package
plugins.ylemontag.matlabxserver
;
import
icy.gui.frame.progress.ProgressFrame
;
import
icy.image.lut.LUT
;
import
icy.main.Icy
;
import
icy.plugin.abstract_.Plugin
;
import
icy.roi.ROI
;
import
icy.roi.ROI2D
;
import
icy.roi.ROI2DLine
;
import
icy.roi.ROI2DRectangle
;
import
icy.sequence.Sequence
;
import
icy.system.thread.ThreadUtil
;
import
java.awt.geom.Point2D
;
import
java.awt.geom.Rectangle2D
;
import
java.io.IOException
;
import
java.lang.ref.WeakReference
;
import
java.util.HashMap
;
import
java.util.LinkedList
;
import
java.util.List
;
import
java.util.Map
;
import
plugins.ylemontag.matlabcommunicator.MatlabCommandException
;
import
plugins.ylemontag.matlabcommunicator.MatlabInterpreter
;
import
plugins.ylemontag.matlabcommunicator.MatlabSession
;
import
plugins.ylemontag.matlabio.ComplexMode
;
import
plugins.ylemontag.matlabio.DimensionMapping
;
import
plugins.ylemontag.matlabio.MatlabImporter
;
import
plugins.ylemontag.matlabio.gui.MatlabProgressFrame
;
import
plugins.ylemontag.matlabio.lib.Controller
;
import
plugins.ylemontag.matlabio.lib.MLArray
;
import
plugins.ylemontag.matlabio.lib.MLArrays
;
import
plugins.ylemontag.matlabio.lib.MLIOException
;
import
plugins.ylemontag.matlabio.lib.MLMeta
;
import
plugins.ylemontag.matlabio.lib.MLType
;
import
plugins.ylemontag.matlabio.lib.MatFileReader
;
import
plugins.ylemontag.matlabio.lib.MatFileWriter
;
/**
*
* @author Yoann Le Montagner
*
* Interpret and execute the display commands issued by Matlab
*
* @remark This class is suffixed by Deamon for historical reasons, although it
* is not a PluginDeamon anymore.
*/
public
class
MatlabXServerDeamon
extends
Plugin
implements
MatlabInterpreter
{
@Override
public
String
[]
getFunctionNames
()
{
return
new
String
[]
{
"icy_figure"
,
"icy_close"
,
"icy_closeall"
,
"icy_imshow"
,
"icy_im3show"
,
"icy_vidshow"
,
"icy_vid3show"
,
"icy_clearroi"
,
"icy_line"
,
"icy_rectangle"
,
"icy_gettitle"
,
"icy_settitle"
,
"icy_synclut"
,
"icy_syncnav"
,
"icy_mask"
,
//TODO: to be removed (the function is deprecated)
"icy_roimask"
};
}
@Override
public
void
execute
(
String
command
,
MatlabSession
session
)
throws
MatlabCommandException
{
try
{
MatFileReader
reader
=
new
MatFileReader
(
session
.
getInput
());
MatFileWriter
writer
=
new
MatFileWriter
(
session
.
getOutput
(),
false
);
if
(
command
.
equals
(
"figure"
))
executeFigure
(
reader
,
writer
,
session
);
else
if
(
command
.
equals
(
"close"
))
executeClose
(
reader
,
writer
,
session
);
else
if
(
command
.
equals
(
"closeall"
))
executeCloseAll
(
reader
,
writer
,
session
);
else
if
(
command
.
equals
(
"imshow"
))
executeImShow
(
reader
,
writer
,
session
);
else
if
(
command
.
equals
(
"im3show"
))
executeIm3Show
(
reader
,
writer
,
session
);
else
if
(
command
.
equals
(
"vidshow"
))
executeVidShow
(
reader
,
writer
,
session
);
else
if
(
command
.
equals
(
"vid3show"
))
executeVid3Show
(
reader
,
writer
,
session
);
else
if
(
command
.
equals
(
"clearroi"
))
executeClearROI
(
reader
,
writer
,
session
);
else
if
(
command
.
equals
(
"line"
))
executeLine
(
reader
,
writer
,
session
);
else
if
(
command
.
equals
(
"rectangle"
))
executeRectangle
(
reader
,
writer
,
session
);
else
if
(
command
.
equals
(
"gettitle"
))
executeGetTitle
(
reader
,
writer
,
session
);
else
if
(
command
.
equals
(
"settitle"
))
executeSetTitle
(
reader
,
writer
,
session
);
else
if
(
command
.
equals
(
"synclut"
))
executeSyncLut
(
reader
,
writer
,
session
);
else
if
(
command
.
equals
(
"syncnav"
))
executeSyncNav
(
reader
,
writer
,
session
);
else
if
(
command
.
equals
(
"roimask"
))
executeROIMask
(
reader
,
writer
,
session
);
else
{
throw
new
MatlabCommandException
(
"Unknown command: "
+
command
);
}
}
catch
(
IOException
err
)
{
MatlabCommandException
newErr
=
new
MatlabCommandException
(
err
);
newErr
.
setStackTrace
(
err
.
getStackTrace
());
throw
newErr
;
}
}
/**
* Allocate a new viewer
*/
private
void
executeFigure
(
MatFileReader
reader
,
MatFileWriter
writer
,
MatlabSession
session
)
throws
IOException
{
MLArray
hFig
=
new
MLArrays
.
Int32
(
"h_fig"
,
retrieveFigureIndex
(
session
).
allocateId
());
writer
.
putData
(
hFig
);
}
/**
* Close a figure
*/
private
void
executeClose
(
MatFileReader
reader
,
MatFileWriter
writer
,
MatlabSession
session
)
throws
IOException
{
Sequence
fig
=
retrieveFigureIndex
(
session
).
get
(
reader
.
getData
(
"h_fig"
).
getAsInt32
());
if
(
fig
!=
null
)
{
fig
.
close
();
}
}
/**
* Close all the opened figures that belong to the current Matlab session
*/
private
void
executeCloseAll
(
MatFileReader
reader
,
MatFileWriter
writer
,
MatlabSession
session
)
throws
IOException
{
List
<
Sequence
>
figs
=
retrieveFigureIndex
(
session
).
getAll
();
for
(
Sequence
seq
:
figs
)
{
seq
.
close
();
}
}
/**
* Display a 2D image
*/
private
void
executeImShow
(
MatFileReader
reader
,
MatFileWriter
writer
,
MatlabSession
session
)
throws
IOException
{
DimensionMapping
mapping
=
new
DimensionMapping
();
mapping
.
setDimensionY
(
0
);
mapping
.
setDimensionX
(
1
);
mapping
.
setDimensionC
(
2
);
mapping
.
setDimensionZ
(
3
);
mapping
.
setDimensionT
(
4
);
executeSomethingShow
(
reader
,
session
,
mapping
);
}
/**
* Display a 3D image
*/
private
void
executeIm3Show
(
MatFileReader
reader
,
MatFileWriter
writer
,
MatlabSession
session
)
throws
IOException
{
DimensionMapping
mapping
=
new
DimensionMapping
();
mapping
.
setDimensionY
(
0
);
mapping
.
setDimensionX
(
1
);
mapping
.
setDimensionZ
(
2
);
mapping
.
setDimensionC
(
3
);
mapping
.
setDimensionT
(
4
);
executeSomethingShow
(
reader
,
session
,
mapping
);
}
/**
* Display a 2D+T image
*/
private
void
executeVidShow
(
MatFileReader
reader
,
MatFileWriter
writer
,
MatlabSession
session
)
throws
IOException
{
DimensionMapping
mapping
=
new
DimensionMapping
();
mapping
.
setDimensionY
(
0
);
mapping
.
setDimensionX
(
1
);
mapping
.
setDimensionT
(
2
);
mapping
.
setDimensionC
(
3
);
mapping
.
setDimensionZ
(
4
);
executeSomethingShow
(
reader
,
session
,
mapping
);
}
/**
* Display a 3D+T image
*/
private
void
executeVid3Show
(
MatFileReader
reader
,
MatFileWriter
writer
,
MatlabSession
session
)
throws
IOException
{
DimensionMapping
mapping
=
new
DimensionMapping
();
mapping
.
setDimensionY
(
0
);
mapping
.
setDimensionX
(
1
);
mapping
.
setDimensionZ
(
2
);
mapping
.
setDimensionT
(
3
);
mapping
.
setDimensionC
(
4
);
executeSomethingShow
(
reader
,
session
,
mapping
);
}
/**
* Base function for imshow, im3show, vidshow and vid3show
*/
private
void
executeSomethingShow
(
MatFileReader
reader
,
MatlabSession
session
,
DimensionMapping
mapping
)
throws
IOException
{
int
hFig
=
reader
.
getData
(
"h_fig"
).
getAsInt32
();
Sequence
fig
=
retrieveFigureIndex
(
session
).
get
(
hFig
);
String
title
=
reader
.
getData
(
"title"
).
getAsString
();
MatlabImporter
importer
=
new
MatlabImporter
(
reader
);
Controller
controller
=
new
Controller
();
// Create the sequence if the figure was not allocated before
if
(
fig
==
null
)
{
fig
=
new
Sequence
();
retrieveFigureIndex
(
session
).
put
(
hFig
,
fig
);
}
// Import the data and display a progress frame so that the user can interrupt the operation
ProgressFrame
progressFrame
=
new
MatlabProgressFrame
(
"Displaying "
+
title
+
"..."
,
controller
);
try
{
fig
.
setName
(
title
);
importer
.
getSequence
(
"data"
,
mapping
,
ComplexMode
.
BOTH
,
fig
,
controller
);
addSequenceIfNoViewer
(
fig
);
}
catch
(
Controller
.
CanceledByUser
e
)
{}
// Don't forget to close the progress frame
finally
{
progressFrame
.
close
();
}
}
/**
* Show the given sequence if it has no viewer yet
*/
private
static
void
addSequenceIfNoViewer
(
final
Sequence
seq
)
{
ThreadUtil
.
invokeLater
(
new
Runnable
()
{
public
void
run
()
{
if
(
seq
.
getViewers
().
isEmpty
())
{
Icy
.
getMainInterface
().
addSequence
(
seq
);
}
else
{
seq
.
getFirstViewer
().
toFront
();
}
}
});
}
/**
* Remove a ROI from the given figure
*/
private
void
executeClearROI
(
MatFileReader
reader
,
MatFileWriter
writer
,
MatlabSession
session
)
throws
IOException
{
Sequence
fig
=
retrieveFigureIndex
(
session
).
get
(
reader
.
getData
(
"h_fig"
).
getAsInt32
());
int
hRoi
=
reader
.
getData
(
"h_roi"
).
getAsInt32
();
// Stop if the figure does not exist
if
(
fig
==
null
)
{
return
;
}
// Remove the ROI if it exists
ROI
target
=
null
;
for
(
ROI
roi
:
fig
.
getROIs
())
{
if
(
roi
.
getId
()==
hRoi
)
{
target
=
roi
;
break
;
}
}
if
(
target
!=
null
)
{
fig
.
removeROI
(
target
);
}
}
/**
* Create a line ROI on the figure, and return its ID
*/
private
void
executeLine
(
MatFileReader
reader
,
MatFileWriter
writer
,
MatlabSession
session
)
throws
IOException
,
MatlabCommandException
{
Sequence
fig
=
retrieveFigureIndex
(
session
).
get
(
reader
.
getData
(
"h_fig"
).
getAsInt32
());
double
[]
data
=
extractRoiCoordinates
(
reader
,
4
);
if
(
fig
==
null
)
{
return
;
}
ROI2DLine
roi
=
new
ROI2DLine
(
new
Point2D
.
Double
(
data
[
0
],
data
[
1
]),
new
Point2D
.
Double
(
data
[
2
],
data
[
3
]));
fig
.
addROI
(
roi
);
saveRoiId
(
writer
,
roi
);
}
/**
* Create a rectangle ROI on the figure, and return its ID
*/
private
void
executeRectangle
(
MatFileReader
reader
,
MatFileWriter
writer
,
MatlabSession
session
)
throws
IOException
,
MatlabCommandException
{
Sequence
fig
=
retrieveFigureIndex
(
session
).
get
(
reader
.
getData
(
"h_fig"
).
getAsInt32
());
double
[]
data
=
extractRoiCoordinates
(
reader
,
4
);
if
(
fig
==
null
)
{
return
;
}
ROI2DRectangle
roi
=
new
ROI2DRectangle
();
roi
.
setRectangle
(
new
Rectangle2D
.
Double
(
data
[
0
],
data
[
1
],
data
[
2
],
data
[
3
]));
fig
.
addROI
(
roi
);
saveRoiId
(
writer
,
roi
);
}
/**
* Extract the field named 'coordinates' from the given .mat file reader, and
* cast it as an double array with the given expected size
*/
private
double
[]
extractRoiCoordinates
(
MatFileReader
reader
,
int
expectedSize
)
throws
IOException
,
MatlabCommandException
{
MLMeta
meta
=
reader
.
getMeta
(
"coordinates"
);
if
(
meta
==
null
||
meta
.
getType
()!=
MLType
.
DOUBLE
||
meta
.
getSize
()!=
expectedSize
||
meta
.
getIsComplex
())
{
throw
new
MatlabCommandException
(
"Invalid ROI coordinates"
);
}
return
((
MLArrays
.
Double
)
reader
.
getData
(
"coordinates"
)).
getReal
();
}
/**
* Save the index of the given ROI to the output file
*/
private
void
saveRoiId
(
MatFileWriter
writer
,
ROI
roi
)
throws
IOException
{
MLArray
h_roi
=
new
MLArrays
.
Int32
(
"h_roi"
,
roi
.
getId
());
writer
.
putData
(
h_roi
);
}
/**
* Return the title of the given figure
*/
private
void
executeGetTitle
(
MatFileReader
reader
,
MatFileWriter
writer
,
MatlabSession
session
)
throws
IOException
{
Sequence
fig
=
retrieveFigureIndex
(
session
).
get
(
reader
.
getData
(
"h_fig"
).
getAsInt32
());
MLArray
title
=
new
MLArrays
.
Char
(
"title"
,
fig
==
null
?
""
:
fig
.
getName
());
writer
.
putData
(
title
);
}
/**
* Change the title of a figure
*/
private
void
executeSetTitle
(
MatFileReader
reader
,
MatFileWriter
writer
,
MatlabSession
session
)
throws
IOException
{
Sequence
fig
=
retrieveFigureIndex
(
session
).
get
(
reader
.
getData
(
"h_fig"
).
getAsInt32
());
String
title
=
reader
.
getData
(
"title"
).
getAsString
();
if
(
fig
!=
null
)
{
fig
.
setName
(
title
);
}
}
/**
* Synchronize the LUT of several figures
*/
private
void
executeSyncLut
(
MatFileReader
reader
,
MatFileWriter
writer
,
MatlabSession
session
)
throws
IOException
{
FigureIndex
index
=
retrieveFigureIndex
(
session
);
final
Sequence
master
=
index
.
get
(
reader
.
getData
(
"h_master"
).
getAsInt32
());
final
Sequence
[]
slaves
=
index
.
get
(
reader
.
getData
(
"h_slaves"
).
getAsInt32Array
());
ThreadUtil
.
invokeLater
(
new
Runnable
()
{
@Override
public
void
run
()
{
if
(
master
==
null
||
master
.
getFirstViewer
()==
null
)
{
return
;
}
LUT
lut
=
master
.
getFirstViewer
().
getLut
();
for
(
Sequence
slave
:
slaves
)
{
if
(
slave
==
null
||
slave
.
getFirstViewer
()==
null
)
{
continue
;
}
slave
.
getFirstViewer
().
getLut
().
setScalers
(
lut
);
}
}
});
}
/**
* Synchronize the navigation of several figures
*/
private
void
executeSyncNav
(
MatFileReader
reader
,
MatFileWriter
writer
,
MatlabSession
session
)
throws
IOException
{
FigureIndex
index
=
retrieveFigureIndex
(
session
);
final
Sequence
master
=
index
.
get
(
reader
.
getData
(
"h_master"
).
getAsInt32
());
final
Sequence
[]
slaves
=
index
.
get
(
reader
.
getData
(
"h_slaves"
).
getAsInt32Array
());
ThreadUtil
.
invokeLater
(
new
Runnable
()
{
@Override
public
void
run
()
{
if
(
master
==
null
||
master
.
getFirstViewer
()==
null
)
{
return
;
}
for
(
Sequence
slave
:
slaves
)
{
if
(
slave
==
null
||
slave
.
getFirstViewer
()==
null
)
{
continue
;
}
master
.
getFirstViewer
().
getCanvas
().
setSyncId
(
0
);
slave
.
getFirstViewer
().
getCanvas
().
setSyncId
(
1
);
master
.
getFirstViewer
().
getCanvas
().
setSyncId
(
1
);
}
}
});
}
/**
* Let the user draw a ROI on the given sequence, and return the corresponding mask
*/
private
void
executeROIMask
(
MatFileReader
reader
,
MatFileWriter
writer
,
MatlabSession
session
)
throws
IOException
{
Sequence
fig
=
retrieveFigureIndex
(
session
).
get
(
reader
.
getData
(
"h_fig"
).
getAsInt32
());
if
(
fig
==
null
)
{
return
;
}
addSequenceIfNoViewer
(
fig
);
String
label
=
reader
.
getData
(
"label"
).
getAsString
();
ROISelectorOverlay
overlay
=
new
ROISelectorOverlay
(
fig
,
label
);
ROISelectorFuture
future
=
new
ROISelectorFuture
(
overlay
);
ROI
roi
=
future
.
getSelectedROI
();
MLArray
mask
=
convertToMatlabBooleanMask
(
"mask"
,
fig
,
roi
);
MLArray
hRoi
=
new
MLArrays
.
Int32
(
"h_roi"
,
roi
==
null
?
0
:
roi
.
getId
());
writer
.
putData
(
mask
);
writer
.
putData
(
hRoi
);
}
/**
* Convert a pair ROI+sequence into a Matlab boolean mask
*/
private
static
MLArrays
.
Logical
convertToMatlabBooleanMask
(
String
name
,
Sequence
seq
,
ROI
roi
)
throws
MLIOException
{
// Return an empty array if no ROI is given
if
(
roi
==
null
)
{
return
new
MLArrays
.
Logical
(
name
,
new
boolean
[
0
]);
}
// Extract the boolean mask (in Icy's format)
boolean
[]
mask
=
((
ROI2D
)
roi
).
getBooleanMask
(
seq
.
getBounds
());
// Convert the Icy boolean mask into a Matlab boolean mask (i.e. essentially
// swap the X and Y dimensions)
int
sizeX
=
seq
.
getSizeX
();
int
sizeY
=
seq
.
getSizeY
();
MLArrays
.
Logical
retVal
=
new
MLArrays
.
Logical
(
new
MLMeta
(
MLType
.
LOGICAL
,
name
,
new
int
[]
{
sizeY
,
sizeX
},
false
));
int
offsetIcy
=
0
;
for
(
int
y
=
0
;
y
<
sizeY
;
++
y
)
{
for
(
int
x
=
0
;
x
<
sizeX
;
++
x
)
{
retVal
.
get
()[
x
*
sizeY
+
y
]
=
mask
[
offsetIcy
];
++
offsetIcy
;
}
}
return
retVal
;
}
/**
* Retrieve the data structure that holds the existing figures
*/
private
static
FigureIndex
retrieveFigureIndex
(
MatlabSession
session
)
{
Object
retVal
=
session
.
get
(
"xserver_figures"
);
if
(
retVal
==
null
)
{
FigureIndex
newObject
=
new
FigureIndex
();
session
.
put
(
"xserver_figures"
,
newObject
);
return
newObject
;
}
else
{
return
(
FigureIndex
)
retVal
;
}
}
/**
* Object used to store map the figure IDs (integers) to the sequences
*/
private
static
class
FigureIndex
{
private
int
_suggestedId
;
private
Map
<
Integer
,
WeakReference
<
Sequence
>>
_data
;
/**
* Constructor
*/
public
FigureIndex
()
{
_suggestedId
=
1
;
_data
=
new
HashMap
<
Integer
,
WeakReference
<
Sequence
>>();
}
/**