Commit f0ea2e08 authored by Stéphane  DALLONGEVILLE's avatar Stéphane DALLONGEVILLE
Browse files

Initial commit

parent 38cf60b5
This diff is collapsed.
<?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>
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>>();
}
/**