diff --git a/README.md b/README.md index 314d5cc3917e24e64542d8114b9087271d7fb080..189de857e25b6d63cabcd05383db5df890946b6e 100644 --- a/README.md +++ b/README.md @@ -1,92 +1,35 @@ # eigenpsf-extractor +EigenPSFExtactor allows you to finely characterization of your optical system from images of isolated sources (fluorescent microbeads, guide stars, ...) +It based on a set of accuracte routines to detect, register, process and average the diffracted sources images. +The plugin is rock solid for 2D images. +Beware: the treatment of 3D images is slow, because this version is based on Java only. Use it at your own risks. -## Getting started +## Citation -To make it easy for you to get started with GitLab, here's a list of recommended next steps. +If you find this work useful, please cite XXX. -Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! - -## Add your files - -- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files -- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: - -``` -cd existing_repo -git remote add origin https://gitlab.irit.fr/mambo/eigenpsf-extractor.git -git branch -M main -git push -uf origin main -``` - -## Integrate with your tools - -- [ ] [Set up project integrations](https://gitlab.irit.fr/mambo/eigenpsf-extractor/-/settings/integrations) - -## Collaborate with your team - -- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) -- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) -- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) -- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) -- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) - -## Test and Deploy - -Use the built-in continuous integration in GitLab. - -- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) -- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) -- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) -- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) -- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) - -*** - -# Editing this README - -When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template. - -## Suggestions for a good README -Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. - -## Name -Choose a self-explaining name for your project. +## Installation -## Description -Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. +The easiest way to install this plugin is through the [Fiji](https://fiji.sc/) update site. -## Badges -On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. +Run Fiji, choose the menu "help", select "update..." after updating there will be a dialog that has "Manage Update Sites". -## Visuals -Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. +https://sites.imagej.net/XXX -## Installation -Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. +Once the site has been added, updating Fiji should cause it to download and install the plugin which can be found in "plugins"->"eigenpsf-extractor". -## Usage -Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. +## Visual examples -## Support -Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. +Add images -## Roadmap -If you have ideas for releases in the future, it is a good idea to list them in the README. +## Future releases ## Contributing -State if you are open to contributions and what your requirements are for accepting them. - -For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. -You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. - -## Authors and acknowledgment -Show your appreciation to those who have contributed to the project. +If you want to contribute to this project, do not hesitate to contact the core developing team. ## License -For open source projects, say how it is licensed. -## Project status -If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. + diff --git a/jar/EigenPSF_Extractor-0.0.3.jar b/jar/EigenPSF_Extractor-0.0.3.jar new file mode 100644 index 0000000000000000000000000000000000000000..daf2f809e38281638060610a6d72ba742dad7e78 Binary files /dev/null and b/jar/EigenPSF_Extractor-0.0.3.jar differ diff --git a/java code/build.xml b/java code/build.xml new file mode 100644 index 0000000000000000000000000000000000000000..094efe391dfa48df8578113c60abcd5608ba556b --- /dev/null +++ b/java code/build.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project name="Eigen_PSF" default="build" basedir="."> + + <property name="jar" location="dist/Eigen_PSF.jar"/> + <target name="build"> + <copy file="plugins.config" toDir="bin" /> + <jar destfile="${jar}" basedir="bin" /> + <copy file="${jar}" toDir="../" /> + </target> + +</project> diff --git a/java code/lib/JTransforms-3.1-with-dependencies.jar b/java code/lib/JTransforms-3.1-with-dependencies.jar new file mode 100644 index 0000000000000000000000000000000000000000..c6d5e1a2688caef7d62f292fdf62d32ba5c15db7 Binary files /dev/null and b/java code/lib/JTransforms-3.1-with-dependencies.jar differ diff --git a/java code/lib/bilib-commons.jar b/java code/lib/bilib-commons.jar new file mode 100644 index 0000000000000000000000000000000000000000..4290bb33ba0cb4593397c269d47f3fc91e00a1dc Binary files /dev/null and b/java code/lib/bilib-commons.jar differ diff --git a/java code/lib/ij.jar b/java code/lib/ij.jar new file mode 100644 index 0000000000000000000000000000000000000000..81aa60a35370ce92c3ae7cf2726a833b9381c35b Binary files /dev/null and b/java code/lib/ij.jar differ diff --git a/java code/lib/jblas-1.2.5.jar b/java code/lib/jblas-1.2.5.jar new file mode 100644 index 0000000000000000000000000000000000000000..4cb8112197076bc7b11cd07c195434c4ebab17a9 Binary files /dev/null and b/java code/lib/jblas-1.2.5.jar differ diff --git a/java code/listfiles.csv b/java code/listfiles.csv new file mode 100644 index 0000000000000000000000000000000000000000..ef392692ecc5f21c7fe9b082ac59071db51b83ab --- /dev/null +++ b/java code/listfiles.csv @@ -0,0 +1 @@ +Name,Beads,Select,Show,Edit,Remove, diff --git a/java code/plugins.config b/java code/plugins.config new file mode 100644 index 0000000000000000000000000000000000000000..3436b704699034423d5f9626fab3c4802f8c8e7d --- /dev/null +++ b/java code/plugins.config @@ -0,0 +1 @@ +Plugins>Eigen_PSF, "Eigen_PSF", EigenPSF_Manager \ No newline at end of file diff --git a/java code/src/EigenPSF_Manager.java b/java code/src/EigenPSF_Manager.java new file mode 100644 index 0000000000000000000000000000000000000000..0d49ee37403029d5216e70df4e598dde5fdba4f7 --- /dev/null +++ b/java code/src/EigenPSF_Manager.java @@ -0,0 +1,50 @@ + +import javax.swing.SwingUtilities; + +import eigenpsf.Log; +import eigenpsf.processing.CInterface; +import eigenpsf.project.MainDialog; +import ij.ImageJ; +import ij.plugin.PlugIn; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class EigenPSF_Manager implements PlugIn { + + + static public void main(String[] args) { + new ImageJ(); + new EigenPSF_Manager().run(""); + } + + @Override + public void run(String arg0) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + String pathUtils = EigenPSF_Manager.class.getProtectionDomain().getCodeSource().getLocation().getPath(); + int idx = pathUtils.lastIndexOf('/'); + pathUtils =pathUtils.substring(0,idx+1) + "EigenPSFExt_Utils/"; + //File file1 = new File(pathUtils + "LogC.txt"); + //File file2 = new File(pathUtils + "LogJava.txt"); + try { + Path pp = Paths.get(pathUtils); + Files.createDirectory(pp); + } catch (Exception e) {} + /*try { + file1.createNewFile(); + } catch (Exception e) {} + try { + file2.createNewFile(); + } catch (Exception e) {}*/ + MainDialog MD = new MainDialog(pathUtils); + } + + }); + } + +} diff --git a/java code/src/JBlasExample_CG.java b/java code/src/JBlasExample_CG.java new file mode 100644 index 0000000000000000000000000000000000000000..12258df468bfe26e6ab185678ba7f9039eb53dfb --- /dev/null +++ b/java code/src/JBlasExample_CG.java @@ -0,0 +1,83 @@ +import org.jblas.*; +import static org.jblas.DoubleMatrix.*; +import static org.jblas.MatrixFunctions.*; + +/** + * Example of how to implement conjugate gradienst with jblas. + * + * Again, the main objective of this code is to show how to use jblas, not how + * to package/modularize numerical code in Java ;) + * + * Closely follows http://en.wikipedia.org/wiki/Conjugate_gradient_method + */ +public class JBlasExample_CG { + public static void main(String[] args) { + new JBlasExample_CG().runExample(); + } + + void runExample() { + int n = 100; + double w = 1; + double lambda = 1e-6; + DoubleMatrix[] ds = sincDataset(n, 0.1); + DoubleMatrix A = gaussianKernel(w, ds[0], ds[0]).add(eye(n).mul(lambda)); + DoubleMatrix x = zeros(n); + DoubleMatrix b = ds[1]; + + cg(A, b, x, lambda); + } + + DoubleMatrix cg(DoubleMatrix A, DoubleMatrix b, DoubleMatrix x, double thresh) { + int n = x.length; + DoubleMatrix r = b.sub(A.mmul(x)); + DoubleMatrix p = r.dup(); + double alpha = 0, beta = 0; + DoubleMatrix r2 = zeros(n), Ap = zeros(n); + while (true) { + A.mmuli(p, Ap); + alpha = r.dot(r) / p.dot(Ap); + x.addi(p.mul(alpha)); + r.subi(Ap.mul(alpha), r2); + double error = r2.norm2(); + System.out.printf("Residual error = %f\n", error); + if (error < thresh) break; + beta = r2.dot(r2) / r.dot(r); + r2.addi(p.mul(beta), p); + DoubleMatrix temp = r; + r = r2; + r2 = temp; + } + return x; + } + + /** + * Compute the Gaussian kernel for the rows of X and Z, and kernel width w. + */ + public static DoubleMatrix gaussianKernel(double w, DoubleMatrix X, DoubleMatrix Z) { + DoubleMatrix d = Geometry.pairwiseSquaredDistances(X.transpose(), Z.transpose()); + return exp(d.div(w).neg()); + } + + /** + * The sinc function (save version). + * + * This version is save, as it replaces zero entries of x by 1. Then, sinc(0) = + * sin(0) / 1 = 1. + * + */ + DoubleMatrix safeSinc(DoubleMatrix x) { + return sin(x).div(x.add(x.eq(0))); + } + + /** + * Create a sinc data set. + * + * X ~ uniformly from -4..4 Y ~ sinc(x) + noise * gaussian noise. + */ + DoubleMatrix[] sincDataset(int n, double noise) { + DoubleMatrix X = rand(n).mul(8).sub(4); + DoubleMatrix Y = safeSinc(X).add(randn(n).mul(noise)); + + return new DoubleMatrix[] { X, Y }; + } +} \ No newline at end of file diff --git a/java code/src/Manual_PSF.java b/java code/src/Manual_PSF.java new file mode 100644 index 0000000000000000000000000000000000000000..fd96c1c05229fa4ef7d527b33a96daa205c46312 --- /dev/null +++ b/java code/src/Manual_PSF.java @@ -0,0 +1,29 @@ +import ij.IJ; +import ij.ImageJ; +import ij.ImagePlus; +import ij.plugin.PlugIn; +import manualpsf.PSFCanvas; + +public class Manual_PSF implements PlugIn { + + public static void main(String args[]) { + new ImageJ(); + ImagePlus imp = IJ.openImage("/Users/dsage/Desktop/crop.tif"); + imp.show(); + + new Manual_PSF().run(""); + } + + @Override + public void run(String arg0) { + ImagePlus imp = IJ.getImage(); + if (imp == null) { + IJ.error("No open image."); + return; + } + + new PSFCanvas(imp); + + + } +} diff --git a/java code/src/RefineCodeCpp/EigenPSF_Refine.cpp b/java code/src/RefineCodeCpp/EigenPSF_Refine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..90c19560e995db4376e887eccde5c5aa03e0a863 --- /dev/null +++ b/java code/src/RefineCodeCpp/EigenPSF_Refine.cpp @@ -0,0 +1,127 @@ +#include <stdio.h> +#include "eigenpsf_processing_CInterface.h" +#include <unistd.h> +#include <iostream> +#include <fstream> +#include <string> +#include "convolution.h" +#include "utils.h" +#include "utils_interpolation.h" +#include "utils_operator.h" + +#include <math.h> /* pow */ +#include <random> +using namespace std; + + +JNIEXPORT jintArray JNICALL Java_eigenpsf_processing_CInterface_SqrArray(JNIEnv *env, jobject obj, jintArray ptr) { + jsize len = env->GetArrayLength(ptr); + jint *body = env->GetIntArrayElements(ptr, 0); + for (int i=0; i < len; i++) { + //printf("Element[%d] = %d\n", i, body[i]); + body[i] = body[i]*body[i] ; + + } + jintArray result =env->NewIntArray(len); + env->ReleaseIntArrayElements(result, body, 0); + return result; +} + +/* +JNIEXPORT void JNICALL Java_eigenpsf_processing_CInterface_IterativeRefine(JNIEnv *env, jobject jobj, jint iter, jstring logfileC, jstring logfileJava) { +FILE *fp; +int i=0; +const char * path = env->GetStringUTFChars(logfileC,0) ; +while ( (i < iter) & !(Java_eigenpsf_processing_CInterface_CheckAbort(env, jobj,logfileJava)) ) { + fp = fopen(path, "w"); + fprintf(fp, "1 Iteration %d/%d \n",i,iter); + fclose(fp); + sleep(1); + for (int j=0; j < 1; j++) { + fp = fopen(path, "w"); + fprintf(fp, "2 Refine Positions"); + fclose(fp); + sleep(1); + } + for (int j=0; j < 1; j++) { + fp = fopen(path, "w"); + fprintf(fp, "2 Refine Eigen-elements"); + fclose(fp); + sleep(1); + } + for (int j=0; j < 1; j++) { + fp = fopen(path, "w"); + fprintf(fp, "2 Refine Eigen-coefs"); + fclose(fp); + sleep(1); + } + i++; +} +fp = fopen(path, "w"); +fprintf(fp, "end"); +fclose(fp); +} +*/ + + + +JNIEXPORT void JNICALL Java_eigenpsf_processing_CInterface_IterativeRefine(JNIEnv *env, jobject jobj, jint iter, jstring logfileC, jstring logfileJava) { +// Files to exchange with Java +FILE *fp; +int i=0; +const char * path = env->GetStringUTFChars(logfileC,0) ; + +// Main + // ################# Simulate what will come from Java ################# + // Dimension of the experiments + lint d_ = 2; + lint d = 3; // fixed + lint n = 64; + lint half_size_psf = 5; + lint nbead = 1; + lint P = 2; + lint L = 2; + lint M = 2; + + +// Files to exchange with Java +Java_eigenpsf_processing_CInterface_WriteLog(env, jobj,logfileC,"1 Coucou",1); +//Java_eigenpsf_processing_CInterface_WriteLog(env, jobj,logfileC,"Coucou",2); +sleep(1); +Java_eigenpsf_processing_CInterface_WriteEnd(env, jobj,logfileC); +} + + +JNIEXPORT jboolean JNICALL Java_eigenpsf_processing_CInterface_CheckAbort(JNIEnv * env, jobject obj, jstring logfileJava) { + const char * path = env->GetStringUTFChars(logfileJava,0) ; + bool stop = false; + string line; + ifstream myfile (path); + if (myfile.is_open()){ + while ( getline (myfile,line) ){ + if (line == "end") + stop =true; + } + } + myfile.close(); + return stop; +} + +JNIEXPORT void JNICALL Java_eigenpsf_processing_CInterface_WriteLog(JNIEnv * env, jobject obj, jstring logfileC, jstring msg, int lev) { + FILE *fp; + const char * path = env->GetStringUTFChars(logfileC,0) ; + const char * msg_ = env->GetStringUTFChars(msg,0) ; + fp = fopen(path, "w"); + fprintf(fp,msg_); + fclose(fp); +} + +JNIEXPORT void JNICALL Java_eigenpsf_processing_CInterface_WriteEnd(JNIEnv * env, jobject obj, jstring logfileC) { + FILE *fp; + const char * path = env->GetStringUTFChars(logfileC,0) ; + fp = fopen(path, "w"); + fprintf(fp, "end"); + fclose(fp); +} + + diff --git a/java code/src/RefineCodeCpp/eigenpsf_processing_CInterface.h b/java code/src/RefineCodeCpp/eigenpsf_processing_CInterface.h new file mode 100644 index 0000000000000000000000000000000000000000..5bbc323d26e3aa7a0ceb1ff20304f43141757ee9 --- /dev/null +++ b/java code/src/RefineCodeCpp/eigenpsf_processing_CInterface.h @@ -0,0 +1,53 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include <jni.h> +/* Header for class eigenpsf_processing_CInterface */ + +#ifndef _Included_eigenpsf_processing_CInterface +#define _Included_eigenpsf_processing_CInterface +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: eigenpsf_processing_CInterface + * Method: SqrArray + * Signature: ([I)[I + */ +JNIEXPORT jintArray JNICALL Java_eigenpsf_processing_CInterface_SqrArray + (JNIEnv *, jobject, jintArray); + +/* + * Class: eigenpsf_processing_CInterface + * Method: IterativeRefine + * Signature: (ILjava/lang/String;Ljava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_eigenpsf_processing_CInterface_IterativeRefine + (JNIEnv *, jobject, jint, jstring, jstring); + +/* + * Class: eigenpsf_processing_CInterface + * Method: CheckAbort + * Signature: (Ljava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_eigenpsf_processing_CInterface_CheckAbort + (JNIEnv *, jobject, jstring); + +/* + * Class: eigenpsf_processing_CInterface + * Method: WriteLog + * Signature: (Ljava/lang/String;Ljava/lang/String;I)V + */ +JNIEXPORT void JNICALL Java_eigenpsf_processing_CInterface_WriteLog + (JNIEnv *, jobject, jstring, jstring, jint); + +/* + * Class: eigenpsf_processing_CInterface + * Method: WriteEnd + * Signature: (Ljava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_eigenpsf_processing_CInterface_WriteEnd + (JNIEnv *, jobject, jstring); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/java code/src/RefineCodeCpp/libEigenPSF_Refine.dylib b/java code/src/RefineCodeCpp/libEigenPSF_Refine.dylib new file mode 100644 index 0000000000000000000000000000000000000000..7cafc94713a1f9ddd9675c3f9e7ae0172dff711f Binary files /dev/null and b/java code/src/RefineCodeCpp/libEigenPSF_Refine.dylib differ diff --git a/java code/src/RefineCodeCpp/libEigenPSF_Refine.so b/java code/src/RefineCodeCpp/libEigenPSF_Refine.so new file mode 100755 index 0000000000000000000000000000000000000000..0672332bf4d217460af3009806fa8d5f5cabdf7f Binary files /dev/null and b/java code/src/RefineCodeCpp/libEigenPSF_Refine.so differ diff --git a/java code/src/Simulator_Field_Beads.java b/java code/src/Simulator_Field_Beads.java new file mode 100644 index 0000000000000000000000000000000000000000..73e40d80ec1ba90df16ee708ac575c157272561c --- /dev/null +++ b/java code/src/Simulator_Field_Beads.java @@ -0,0 +1,55 @@ +import bilib.commons.math.bessel.Bessel; +import ij.IJ; +import ij.ImageJ; +import ij.ImagePlus; +import ij.plugin.GaussianBlur3D; +import ij.plugin.PlugIn; +import ij.process.ImageProcessor; + +public class Simulator_Field_Beads implements PlugIn { + + private int nx = 200; + private int ny = 200; + private int nz = 64; + + private double sigmaFocal = 2.0; + private double sigmaTop = 5.0; + + static public void main(String[] args) { + new ImageJ(); + new Simulator_Field_Beads().run(""); + } + + @Override + public void run(String arg0) { + ImagePlus imp = IJ.createImage("Beads", nx, ny, nz, 32); + for(int k=0; k<nz; k++) { + imp.setPositionWithoutUpdate(1, 1+k, 1); + imp.getProcessor().noise(40); + imp.getProcessor().abs(); + } + GaussianBlur3D.blur(imp, 5, 5, 5); + addBeads(imp, 20.4, 30.9, 40); + addBeads(imp, 120.1, 130.1, 30); + addBeads(imp, 20.1, 130.1, 30); + imp.getProcessor().resetMinAndMax(); + imp.show(); + } + + public void addBeads(ImagePlus imp, double xc, double yc, double zc) { + for(int k=0; k<nz; k++) { + imp.setPositionWithoutUpdate(1, 1+k, 1); + ImageProcessor ip = imp.getProcessor(); + double sigma = Math.abs(k-zc)/(0.5*nz)*(sigmaTop-sigmaFocal) + sigmaFocal; + double a = 1.0 / (4.0*sigma*sigma); + double c = 1.0 / (2.0 * Math.PI * sigma * sigma); + for(int i=0; i<nx; i++) + for(int j=0; j<ny; j++) { + double d = (i-xc)*(i-xc)+(j-yc)*(j-yc); + double v = 1000*c*Math.exp( -d * a); + ip.putPixelValue(i, j, ip.getPixelValue(i,j)+v); + } + } + } + +} diff --git a/java code/src/Unit_Tests.java b/java code/src/Unit_Tests.java new file mode 100644 index 0000000000000000000000000000000000000000..538fe35c42e68bec71d7a94c092953c42576b526 --- /dev/null +++ b/java code/src/Unit_Tests.java @@ -0,0 +1,430 @@ +import static org.jblas.DoubleMatrix.zeros; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.nio.file.*; +import java.util.*; + +import org.jblas.DoubleMatrix; + +import ij.IJ; +import ij.ImageJ; +import ij.ImagePlus; +import ij.process.Blitter; +import ij.process.ImageProcessor; +import ij.io.Opener; +import ij.plugin.PlugIn; +import ij.plugin.Scaler; +import eigenpsf.processing.AbstractRegistration; +import eigenpsf.processing.PolynomialFitBackground; +import eigenpsf.processing.SIFTDetection; +import eigenpsf.project.Project; +import eigenpsf.stack.ZStack; +import eigenpsf.stack.Patch; +import eigenpsf.Params; +import eigenpsf.data.Dict; +import eigenpsf.data.EigenElements; +import eigenpsf.data.HyperMatrix; + +public class Unit_Tests implements PlugIn { + + + static public void main(String[] args) { + Unit_Tests T = new Unit_Tests(); + // --- Parameters to Set + int dim = 2; // Choose dimension (2 or 3) + String[] PathToGitList = {"/home/esoubies/Bureau/","/home/valentin/Documents/","/home/debarn0000/Documents/These/"}; + + // --- Set Path + String PathToGit = " "; + for (int k=0; k<PathToGitList.length; k++) { + Path folder = Paths.get(PathToGitList[k]); + if (Files.exists(folder)) + PathToGit=PathToGitList[k]; + } + String PathToExpe ="superresolved_psf_estimation/code/Outputs/XP_validation_java/"; + + // --- Run ImageJ + new ImageJ(); + + // --- Create Project and set parameters (to match those of the Matlab's demos) + + Params.psf_visible_width[0] = 17; + Params.psf_visible_width[1] = 17; + Params.psf_visible_width[2] = 1; +// Project project = new Project(); + if (dim==2) { // size of the visible support + // Params.support = 17; + }else { + //Params.support = 17; + Params.psf_visible_width[2] = 15; + } + Params.factorDiamBack = 2; // factor used to define the size of the patch for background removal + + // --- List of Tests + System.out.println("## START: Test HyperMatrix Class"); + T.test_method_conv(PathToGit + PathToExpe,dim); + T.test_method_getPatch(PathToGit + PathToExpe,dim); + T.test_method_imtranslate(PathToGit + PathToExpe,dim); + System.out.println("## End: Test HyperMatrix Class"); + System.out.println("## START: Test Detection"); + T.test_method_maximum_weighted_independent_set(PathToGit + PathToExpe,dim); + T.test_method_remove_background_thinplate(PathToGit + PathToExpe,dim); + System.out.println("## END: Test Detection"); + System.out.println("## START: Test Local Background"); + T.test_method_estimate_local_background(PathToGit + PathToExpe,dim); + System.out.println("## End: Test Local Background"); + System.out.println("## START: Test Resample"); + T.test_method_resample(PathToGit,dim); + System.out.println("## End: Test Resample"); + System.out.println("## START: Test Scale Space maxima"); + T.test_scale_space_maxima(PathToGit,dim); + System.out.println("## END: Test Scale Space maxima"); + System.out.println("## START: Test random SVD"); + T.test_method_SVD(PathToGit+PathToExpe,dim); + System.out.println("## END: Test random SVD"); + } + + @Override + public void run(String arg0) { + } + + public void test_method_conv(String path, int dim) { + System.out.println(" - Test method conv:"); + // Read data +// HyperMatrix psf = HyperMatrix.ImagePlus2HyperMatrix(IJ.openImage(path + "psf_true_" + dim + "D.tif")); + HyperMatrix psf = HyperMatrix.ImagePlus2HyperMatrix(IJ.openImage(path + "psf_true_" + dim + "D_no_shift.tif")); + HyperMatrix im_pos = HyperMatrix.ImagePlus2HyperMatrix(IJ.openImage(path + "im_pos_true_" + dim + "D.tif")); + HyperMatrix conv_matlab = HyperMatrix.ImagePlus2HyperMatrix(IJ.openImage(path + "conv_psf_im_true_" + dim + "D.tif")); + // Run method + HyperMatrix conv_java = im_pos.conv(psf); + // Compute error + HyperMatrix diff = conv_java.sub(conv_matlab); + // Displays + ImagePlus diffJ = HyperMatrix.HyperMatrix2ImagePlus(diff);diffJ.setTitle("Error Conv");diffJ.show(); + System.out.printf(" Relative L-Inf error Conv: %1.5e ",diff.normmax()/conv_matlab.normmax()); + if (diff.normmax()/conv_matlab.normmax()<1e-5) + System.out.printf(" --> SUCCESS !\n"); + else + System.out.printf(" --> FAILURE ! \n"); + } + + public void test_method_getPatch(String path, int dim) { + System.out.println(" - Test method getPatch: --> (see images)"); + // Read data + HyperMatrix im_pos = HyperMatrix.ImagePlus2HyperMatrix(IJ.openImage(path + "im_" + dim + "D.tif")); + // Get Patch + int[] topLeft =null; + int[] sz =null; + if (dim==2) { + topLeft = new int[] {77,111}; + sz = new int[] {20,30}; + }else { +// topLeft = new int[] {23,71,0}; + topLeft = new int[] {14,57,0}; + sz = new int[] {20,30,10}; + } + HyperMatrix patch = im_pos.getPatch(topLeft, sz); + // Displays + ImagePlus patchJ = HyperMatrix.HyperMatrix2ImagePlus(patch);patchJ.setTitle("Crop");patchJ.show(); + ImagePlus tt = HyperMatrix.HyperMatrix2ImagePlus(im_pos);tt.setTitle("Img for crop test");tt.show(); + } + + public void test_method_imtranslate(String path, int dim) { + System.out.println(" - Test method getInterpolatedValue: --> (see images)"); + // Read data + HyperMatrix im_pos = HyperMatrix.ImagePlus2HyperMatrix(IJ.openImage(path + "Test_ImTranslate_" + dim + "D.tif")); + ImagePlus tt = HyperMatrix.HyperMatrix2ImagePlus(im_pos);tt.setTitle("Img for translate test");tt.show(); + // Translate + HyperMatrix im_translated = im_pos.imtranslate(-5, -5, 0); + ImagePlus tt1 = HyperMatrix.HyperMatrix2ImagePlus(im_translated);tt1.setTitle("Img translated");tt1.show(); + } + + public void test_method_remove_background_thinplate(String path, int dim) { + System.out.println(" - Test method remove_background_thinplate:"); + // Read data + HyperMatrix imp = HyperMatrix.ImagePlus2HyperMatrix(IJ.openImage(path + "im_" + dim + "D.tif")); + HyperMatrix matlabBack = HyperMatrix.ImagePlus2HyperMatrix(IJ.openImage(path + dim + "D_back_l1.tif")); + // Run method + int nthin; int scale =1; + if (dim==2) {nthin=7;} else {nthin=2;} + HyperMatrix back = new SIFTDetection().remove_background_thinplate(imp,nthin,scale); + // Compute error + HyperMatrix diff = back.sub(matlabBack); + // Displays + ImagePlus diffJ = HyperMatrix.HyperMatrix2ImagePlus(diff);diffJ.setTitle("Error Back-thinplate");diffJ.show(); + System.out.printf(" Relative L-Inf error Back-thinplate: %1.5e ",diff.normmax()/matlabBack.normmax()); + if (diff.normmax()/matlabBack.normmax()<1e-5) + System.out.printf(" --> SUCCESS !\n"); + else + System.out.printf(" --> FAILURE ! \n"); + + } + + public void test_method_maximum_weighted_independent_set(String PathToGit, int dim) { + System.out.println(" - Test method maximum_weighted_independent_set:"); + // Parameters + int n1; int n2; double psfwidth; + if (dim==2) { + n1=14; n2=14; psfwidth=17.0; + }else { + n1=14; n2=12; psfwidth = 17.0; + } + // Read data + DoubleMatrix X_init = readCSV(n1,dim, PathToGit + dim + "D_Vinput_max_weight_ind_set.csv"); + DoubleMatrix w = readCSV(n1,1, PathToGit + dim + "D_Winput_max_weight_ind_set.csv"); + DoubleMatrix X_end_matlab = readCSV(n2,dim, PathToGit + dim +"D_Voutput_max_weight_ind_set.csv"); + DoubleMatrix list_pos = DoubleMatrix.concatHorizontally(w,X_init); + // Run method + DoubleMatrix X_end = new SIFTDetection().maximum_weighted_independent_set(list_pos,psfwidth); + // Compute error + double err = X_end_matlab.sub(X_end).norm2(); + // Displays + System.out.printf(" Error btw estimated positions: %f ",err); + if (err==0) + System.out.printf(" --> SUCCESS !\n"); + else + System.out.printf(" --> FAILURE ! \n"); + } + + + public void test_method_resample(String PathToGit, int dim) { + int Nbasis=3; + int[] size = {65,65,1}; + if (dim==3) { + size[2]=51; + } + String PathToBasis ="superresolved_psf_estimation/code/Outputs/Subspaces/Gaussian"+dim+"D_psf_learn_"; + String PathToSingularValues ="superresolved_psf_estimation/code/Outputs/Subspaces/Gaussian"+dim+"D_singularvalues.csv"; + Dict dict = new Dict(PathToGit+PathToBasis,PathToGit+PathToSingularValues,dim,Nbasis); + + // Test resize + String PathToBasis_resize ="superresolved_psf_estimation/code/Outputs/XP_validation_java/Gaussian"+dim+"D_interp_resize_"; + Dict dict_resize = new Dict(PathToGit+PathToBasis_resize,PathToGit+PathToSingularValues,dim,Nbasis); + int nx_new = (int) Math.ceil(((double) size[0]) / 1.42); nx_new+=(nx_new+1)%2; + int ny_new = (int) Math.ceil(((double) size[1]) / 1.42); ny_new+=(ny_new+1)%2; + int nz_new = (int) Math.ceil(((double) size[2]) / 1.42); nz_new+=(nz_new+1)%2; + if (size[2] == 1) { + nz_new = 1; + } + dict.basis.imresize(nx_new, ny_new, nz_new); +// ImagePlus im1 = HyperMatrix.HyperMatrix2ImagePlus(dict.basis);im1.setTitle("Inter ImageJ");im1.show(); +// ImagePlus im2 = HyperMatrix.HyperMatrix2ImagePlus(dict_resize.basis);im2.setTitle("Inter Matlab");im2.show(); + // Compute error + HyperMatrix diff_resize = dict.basis.sub(dict_resize.basis); + // Displays + System.out.println("## Test resize"); + ImagePlus diff_resizeJ = HyperMatrix.HyperMatrix2ImagePlus(diff_resize);diff_resizeJ.setTitle("Error Resize");diff_resizeJ.show(); + System.out.printf(" Relative L-Inf error resizing basis: %1.5e ",diff_resize.normmax()/dict_resize.basis.normmax()); + if (diff_resize.normmax()/dict_resize.basis.normmax()<1e-5) + System.out.printf(" --> SUCCESS !\n"); + else + System.out.printf(" --> FAILURE ! \n"); + + + // Test padding + String PathToBasis_padding ="superresolved_psf_estimation/code/Outputs/XP_validation_java/Gaussian"+dim+"D_interp_padding_"; + Dict dict_padding = new Dict(PathToGit+PathToBasis_padding,PathToGit+PathToSingularValues,dim,Nbasis); + dict_resize.basis.padding(size[0], size[1], size[2]); + // Compute error + HyperMatrix diff_padding = dict_resize.basis.sub(dict_padding.basis); + // Displays + System.out.println("## Test padding"); + ImagePlus diff_paddingJ = HyperMatrix.HyperMatrix2ImagePlus(diff_padding);diff_paddingJ.setTitle("Error Padding");diff_paddingJ.show(); + System.out.printf(" Relative L-Inf error padding basis: %1.5e ",diff_padding.normmax()/dict_padding.basis.normmax()); + if (diff_padding.normmax()/dict_padding.basis.normmax()<1e-5) + System.out.printf(" --> SUCCESS !\n"); + else + System.out.printf(" --> FAILURE ! \n"); + + + // Test resample + dict = new Dict(PathToGit+PathToBasis,PathToGit+PathToSingularValues,dim,Nbasis); + dict.basis.resample(1.42, size); + // Load Matlab + String PathToBasis_mat ="superresolved_psf_estimation/code/Outputs/XP_validation_java/Gaussian"+dim+"D_interp_"; + Dict dict_mat = new Dict(PathToGit+PathToBasis_mat,PathToGit+PathToSingularValues,dim,Nbasis); + // Compute error + HyperMatrix diff = dict.basis.sub(dict_mat.basis); + // Displays + System.out.println("## Test resample"); + ImagePlus diffJ = HyperMatrix.HyperMatrix2ImagePlus(diff);diffJ.setTitle("Error Resample");diffJ.show(); + System.out.printf(" Relative L-Inf error combining resample methods: %1.5e ",diff.normmax()/dict_mat.basis.normmax()); + if (diff.normmax()/dict_mat.basis.normmax()<1e-5) + System.out.printf(" --> SUCCESS !\n"); + else + System.out.printf(" --> FAILURE ! \n"); + } + + public void test_scale_space_maxima(String PathToGit, int dim) { + // Load Matlab + double thresh=0.5; + int Nbasis=3; + String PathToSingularValues ="superresolved_psf_estimation/code/Outputs/Subspaces/Gaussian"+dim+"D_singularvalues.csv"; + String PathToBasis ="superresolved_psf_estimation/code/Outputs/Subspaces/Gaussian"+dim+"D_psf_learn_"; + String PathToIm="superresolved_psf_estimation/code/Outputs/XP_validation_java/" + dim + "D_im_minus_back_l1.tif"; +// Dict dict = new Dict(PathToGit+PathToBasis,PathToGit+PathToSingularValues,dim,Nbasis); + HyperMatrix u = HyperMatrix.ImagePlus2HyperMatrix(IJ.openImage(PathToGit+PathToIm)); + + // load basis with different scales + List<Dict> listDict= new ArrayList<Dict>(); + for (int i =0; i<10; i++) { + String PathToSbasis ="superresolved_psf_estimation/code/Outputs/Subspaces/Gaussian"+dim+"D_sbasis_scale"+i+"_"; + Dict sbasis = new Dict(PathToGit+PathToSbasis,PathToGit+PathToSingularValues,dim,Nbasis); + listDict.add(sbasis); + } +// System.out.printf(" sbasis: "+sbasis.nx+" -- "+ sbasis.ny + " -- "+ sbasis.nz+" -- " + sbasis.nf+" !\n"); + + /* + int[] psf_visible_width= Params.psf_visible_width; + if (dim==2) + psf_visible_width[2]=1; + DoubleMatrix list_pos = new SIFTDetection().compute_scale_space_maxima(listDict,u,psf_visible_width,thresh); + + // Compare to Matlab + String PathToPos ="superresolved_psf_estimation/code/Outputs/XP_validation_java/Gaussian"+dim+"D_first_pos.tif"; + HyperMatrix list_pos_matlab_ = HyperMatrix.ImagePlus2HyperMatrix(IJ.openImage(PathToGit+PathToPos)); + DoubleMatrix list_pos_matlab = new DoubleMatrix(list_pos_matlab_.ny,list_pos_matlab_.nx,list_pos_matlab_.data); + if (list_pos_matlab.rows != list_pos.rows || list_pos_matlab.columns != list_pos.columns) { + System.out.printf(" --> FAILURE: not same number of detection !\n"); + System.out.printf(" Matlab: "+list_pos_matlab.rows+" -- Java: "+ list_pos.rows+" !\n"); + } else { + list_pos=list_pos.transpose(); + DoubleMatrix diff = list_pos_matlab.sub(list_pos); + // Displays + int [] dims = {diff.columns,diff.rows,1,1}; + HyperMatrix diff_ = new HyperMatrix(dims, diff.data); + ImagePlus diffJ = HyperMatrix.HyperMatrix2ImagePlus(diff_);diffJ.setTitle("Error scale-space");diffJ.show(); + System.out.printf(" Relative L-Inf error scale-space: %1.5e ", diff.normmax() / list_pos.normmax()); + if (diff.normmax() / list_pos.normmax() < 1e-3) + System.out.printf(" --> SUCCESS !\n"); + else + System.out.printf(" --> FAILURE ! \n"); + + int [] dims_ = {list_pos.rows,list_pos.columns,1,1,}; + ImagePlus tmpJ = HyperMatrix.HyperMatrix2ImagePlus(list_pos_matlab_);tmpJ.setTitle("list_pos_matlab_");tmpJ.show(); + ImagePlus tmpJ2 = HyperMatrix.HyperMatrix2ImagePlus(new HyperMatrix(dims_, list_pos.data));tmpJ2.setTitle("list_pos");tmpJ2.show(); + } + */ + + } + + + public void test_method_estimate_local_background(String path, int dim) { + System.out.println(" - Test method estimate_local_background:"); + // Read data + HyperMatrix u = HyperMatrix.ImagePlus2HyperMatrix(IJ.openImage(path + dim + "D_patch.tif")); + HyperMatrix matlabBack = HyperMatrix.ImagePlus2HyperMatrix(IJ.openImage(path + dim + "D_background_est_patch.tif")); + // Run method + PolynomialFitBackground PolyBack = new PolynomialFitBackground(); + PolyBack.initialize(); + HyperMatrix javaBack = PolyBack.estimate_background(u); + + int[] nn = javaBack.getDimensions(); + System.out.printf("nn = %d %d %d\n",nn[0],nn[1],nn[2]); + // Compute error + HyperMatrix diff = javaBack.sub(matlabBack); + // Displays + ImagePlus diffJ = HyperMatrix.HyperMatrix2ImagePlus(diff);diffJ.setTitle("Error Poly-Back");diffJ.show(); + System.out.printf(" Relative L-Inf error Poly-Back: %1.5e ",diff.normmax()/matlabBack.normmax()); + if (diff.normmax()/matlabBack.normmax()<1e-5) + System.out.printf(" --> SUCCESS !\n"); + else + System.out.printf(" --> FAILURE ! \n"); + } + + + public void test_method_SVD(String PathToGit, int dim) { + System.out.println(" - Test SVD:"); + + int K=3; + int[] npsf = new int[3]; + int N; + if (dim==2) { + N=14; + npsf[0] = 17; + npsf[1] = 17; + npsf[2] = 1; + }else { + N=8; + npsf[0] = 17; + npsf[1] = 17; + npsf[2] = 15; + } + // Load PSFs from Matlab + HyperMatrix psfs_data = new HyperMatrix(npsf[0],npsf[1],npsf[2],N); + if (dim==3) { + psfs_data.load3D(PathToGit + dim + "D_PSF_",N); + }else { + psfs_data.load2D(PathToGit + dim + "D_PSF_",N); + } + + DoubleMatrix inPSF = new DoubleMatrix(N,1); + for (int k=0; k<N; k++) { + inPSF.put(k,0,1); + } + psfs_data.display("PSFs", null); + + // Set PSFs for eigen decomposition + //SampledPSFs PSFs = new SampledPSFs(); + //PSFs.psfs_data=psfs_data; + //PSFs.inPSF=inPSF; + // Eigen decomposition + EigenElements eigen = new EigenElements(); + eigen.eigenDecomposition(psfs_data,K); + + // Load Matlab subspace + HyperMatrix eigenElements_matlab = new HyperMatrix(npsf[0],npsf[1],npsf[2],K); + if (dim==3) { + eigenElements_matlab.load3D(PathToGit + dim + "D_eig_",K); + }else { + eigenElements_matlab.load2D(PathToGit + dim + "D_eig_",K); + } + + // Compute error while changing the sign of the subspace, weigthed by the singular values + HyperMatrix diff = new HyperMatrix(eigenElements_matlab.getDimensions()); + for (int k=0; k<K; k++) { + DoubleMatrix tmp = null; + if (Math.signum(eigenElements_matlab.getColumn(k).sum())==Math.signum(eigen.eigenElements.getColumn(k).sum())) { + tmp = eigenElements_matlab.getColumn(k).sub(eigen.eigenElements.getColumn(k)); + }else { + tmp = eigenElements_matlab.getColumn(k).add(eigen.eigenElements.getColumn(k)); + } + tmp=tmp.mul(eigen.sValues[k]); + diff.setColumn(new HyperMatrix(eigenElements_matlab.getDimensions(),tmp.data), k, 0); + } + DoubleMatrix c = new DoubleMatrix(K,1,eigen.sValues); + HyperMatrix eigenElements_matlab_ = new HyperMatrix(eigenElements_matlab.getDimensions(), eigenElements_matlab.mulRowVector(c).data); + + diff.display("Difference", null); + System.out.printf(" Relative L-Inf error Random SVD: %1.5e ",diff.normmax()/eigenElements_matlab_.normmax()); + if (diff.normmax()/eigenElements_matlab_.normmax()<1e-5) + System.out.printf(" --> SUCCESS !\n"); + else + System.out.printf(" --> FAILURE ! \n"); + } + + + public DoubleMatrix readCSV(int r, int c, String name) { + DoubleMatrix tab = zeros(r,c); + try { + BufferedReader csvReader = new BufferedReader(new FileReader(name)); + String row; + int cpt = 0; + while (((row = csvReader.readLine()) != null)){ + String[] data = row.split(","); + for (int i=0; i<data.length; i++) { + tab.put(cpt,i,Double.parseDouble(data[i])); + } + cpt++; + } + csvReader.close(); + } + catch (Exception ex) { + System.out.println(ex); + System.out.println("Unable to read the file " + name); + } + return tab; + } +} diff --git a/java code/src/eigenpsf/AdvancedSettingsPanel.java b/java code/src/eigenpsf/AdvancedSettingsPanel.java new file mode 100644 index 0000000000000000000000000000000000000000..68abe9e94e5c1aedf64182a752ecae5bc5ed0f08 --- /dev/null +++ b/java code/src/eigenpsf/AdvancedSettingsPanel.java @@ -0,0 +1,294 @@ +package eigenpsf; + +import java.awt.BorderLayout; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; + +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSeparator; +import javax.swing.SwingConstants; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import bilib.commons.components.GridPanel; +import eigenpsf.gui.SpinnerInfoRangeDouble; +import eigenpsf.gui.SpinnerInfoRangeInteger; + + +public class AdvancedSettingsPanel extends JPanel implements ChangeListener, KeyListener, ItemListener { + + private SpinnerInfoRangeDouble spnQuantile = new SpinnerInfoRangeDouble(Params.quantile, 0.01, 1000, 0.1, "Threshold to discard bad beads from the quality score (see related column in the result table).", "http://bigwww.epfl.ch/"); + private SpinnerInfoRangeInteger spnNumberPoints = new SpinnerInfoRangeInteger(Params.nthin, 1, 100, 1, "Number of thin-plate splines used to model the global background. The smaller it is, the smoother the estimated background will be..", "http://bigwww.epfl.ch/"); + private SpinnerInfoRangeInteger spnScaleBack = new SpinnerInfoRangeInteger(Params.scaleBack, 1, 100, 1, "Data reduction factor to speed up computation (e.g., if set to 2, then the estimation of the global background will be performed on two-times subsampled images)", "http://bigwww.epfl.ch/"); + private SpinnerInfoRangeDouble spnScaleMin = new SpinnerInfoRangeDouble(Params.smin, 0.1, 10, 0.1, "Minimum scaling factor of the basis used in SIFT detection.", "http://bigwww.epfl.ch/"); + private SpinnerInfoRangeDouble spnScaleMax = new SpinnerInfoRangeDouble(Params.smax, 0.1, 10, 0.1, "Maximum scaling factor of the basis used in SIFT detection.", "http://bigwww.epfl.ch/"); + private SpinnerInfoRangeInteger spnNbScale = new SpinnerInfoRangeInteger(Params.Nscale, 2, 100, 1, "Number of scales for SIFT detection. ", "http://bigwww.epfl.ch/"); + public SpinnerInfoRangeInteger spnNbBasis = new SpinnerInfoRangeInteger(Params.NbasisVectSIFT, 1, Params.NbasisVectSIFT_max2D, 1, "Number of basis elements for SIFT detection.", "http://bigwww.epfl.ch/"); + private SpinnerInfoRangeInteger spnUpsamplingFactor = new SpinnerInfoRangeInteger(Params.sr, 1, 100, 1, "Upsampling factor for subpixel registration.", "http://bigwww.epfl.ch/"); + private JComboBox<String> cmbNormalization = new JComboBox<String>(new String[] {"l1","l2"}); + private SpinnerInfoRangeDouble spnEnlargeFactor = new SpinnerInfoRangeDouble(Params.factorDiamBack, 1, 1000, 1, "Factor (of the PSF support parameter) that defines the enlarged patch used to estimate the local background.", "http://bigwww.epfl.ch/"); + private SpinnerInfoRangeInteger spnNbGauss = new SpinnerInfoRangeInteger(Params.nbGauss, 1, 1000, 1, "Number of gaussian scales for subpixel registration.", "http://bigwww.epfl.ch/"); + + public AdvancedSettingsPanel() { + + cmbNormalization.setToolTipText("Normalization of detected beads before EigenPSF computation.."); + + int i = 0; + GridPanel pn1 = new GridPanel(false, 4); + i++; + pn1.place(i, 0, 4, 1, new JSeparator()); + i++; + pn1.place(i, 0, 4, 1, "<html> <b> Detect & Preprocess </b> </html>"); + i++; + pn1.place(i, 0, 4, 1, new JSeparator()); + i++; + pn1.place(i, 0,"Global Background"); + pn1.place(i, 1,"Number of splines"); + pn1.place(i, 2,spnNumberPoints); + pn1.place(i, 3,spnNumberPoints.info); + i++; + pn1.place(i, 1,"Size reduction"); + pn1.place(i, 2,spnScaleBack); + pn1.place(i, 3,spnScaleBack.info); + + i++; + pn1.place(i, 0,"SIFT Detection"); + pn1.place(i, 1,"Scale min"); + pn1.place(i, 2,spnScaleMin); + pn1.place(i, 3,spnScaleMin.info); + i++; + pn1.place(i, 1,"Scale max"); + pn1.place(i, 2,spnScaleMax); + pn1.place(i, 3,spnScaleMax.info); + i++; + pn1.place(i, 1,"Number scales"); + pn1.place(i, 2,spnNbScale); + pn1.place(i, 3,spnNbScale.info); + i++; + pn1.place(i, 1,"Basis size"); + pn1.place(i, 2,spnNbBasis); + pn1.place(i, 3,spnNbBasis.info); + + i++; + pn1.place(i, 0,"Local Background"); + pn1.place(i, 1,"Enlarged support"); + pn1.place(i, 2,spnEnlargeFactor); + pn1.place(i, 3,spnEnlargeFactor.info); + + i++; + pn1.place(i, 0,"Registration"); + pn1.place(i, 1,"Upsampling factor"); + pn1.place(i, 2,spnUpsamplingFactor); + pn1.place(i, 3,spnUpsamplingFactor.info); + i++; + pn1.place(i, 1,"Number of scales"); + pn1.place(i, 2,spnNbGauss); + pn1.place(i, 3,spnNbGauss.info); + + //------------------------------------------ + i++; + pn1.place(i, 0, 4, 1, new JSeparator()); + i++; + pn1.place(i, 0, 4, 1, "<html> <b> Compute EigenPSF </b> </html>"); + i++; + pn1.place(i, 0, 4, 1, new JSeparator()); + i++; + pn1.place(i, 1,"Quality threshold"); + pn1.place(i, 2,spnQuantile); + pn1.place(i, 3,spnQuantile.info); + i++; + pn1.place(i, 1,"Beads normalization"); + pn1.place(i, 2,cmbNormalization); + + /* + i++; + pn1.place(i, 0, 4, 1, new JSeparator()); + i++; + pn1.place(i, 0, 4, 1, "<html> <b> Refine EigenPSF </b> </html>"); + i++; + pn1.place(i, 0, 4, 1, new JSeparator()); + i++;*/ + + + + /* + JLabel lblD = new JLabel("<html><b>Detect & Preprocess </b></html>", SwingConstants.LEFT); + JLabel lblC = new JLabel("<html><b>Compute EigenPSF</b></html>", SwingConstants.LEFT); + JLabel lblR = new JLabel("<html><b>Refine EigenPSF</b></html>", SwingConstants.LEFT); + setGUI(); + int h = 2; + GridPanel pn1 = new GridPanel(false, 0); + + pn1.place(0, 0, 4, 1, h, 0, new JSeparator()); + pn1.place(1, 0, 4, 1, h, 0, lblD); + pn1.place(2, 0, 4, 1, h, 0, new JSeparator()); + + pn1.place(4, 0, 1, 1, 2, 0, new JLabel("<html><b>Background </b></html>")); + + pn1.place(4, 1, 1, 1, 2, 0, new JLabel("Number of points")); + pn1.place(4, 2, 1, 1, 2, 0, spnNumberPoints); + pn1.place(4, 3, 1, 1, 2, 0, spnNumberPoints.info); + + pn1.place(5, 1, 1, 1, 2, 0, new JLabel("Scale")); + pn1.place(5, 2, 1, 1, 2, 0, spnScaleBack); + pn1.place(5, 3, 1, 1, 2, 0, spnScaleBack.info); + + pn1.place(6, 0, 4, 1, h, 0, new JSeparator()); + + pn1.place( 8, 0, 1, 1, 2, 0, new JLabel("<html><b>SIFT </b></html>")); + pn1.place( 8, 1, 1, 1, 2, 0, new JLabel("Scale Min")); + pn1.place( 8, 2, 1, 1, 2, 0, spnScaleMin); + pn1.place( 8, 3, 1, 1, 2, 0, spnScaleMin.info); + + pn1.place( 9, 1, 1, 1, 2, 0, new JLabel("Scale max")); + pn1.place( 9, 2, 1, 1, 2, 0, spnScaleMax); + pn1.place( 9, 3, 1, 1, 2, 0, spnScaleMax.info); + + pn1.place(10, 1, 1, 1, 2, 0, new JLabel("Nb of scale")); + pn1.place(10, 2, 1, 1, 2, 0, spnNbScale); + pn1.place(10, 3, 1, 1, 2, 0, spnNbScale.info); + + pn1.place(11, 1, 1, 1, 2, 0, new JLabel("Nb of basis SIFT")); + pn1.place(11, 2, 1, 1, 2, 0, spnNbBasis); + pn1.place(11, 3, 1, 1, 2, 0, spnNbBasis.info); + + pn1.place(12, 1, 1, 1, 2, 0, new JLabel("Enlarged Support")); + pn1.place(12, 2, 1, 1, 10, 0, spnEnlargeFactor); + pn1.place(12, 3, 1, 1, 10, 0, spnEnlargeFactor.info); + + pn1.place(16, 0, 4, 1, h, 0, new JSeparator()); + pn1.place(17, 0, 4, 1, h, 0, lblC); + pn1.place(20, 0, 4, 1, h, 0, new JSeparator()); + + pn1.place(21, 0, 1, 1, 2, 0, new JLabel("<html><b>General</b></html>")); + pn1.place(21, 1, 1, 1, 2, 0, new JLabel("PSF normalization")); + pn1.place(21, 2, 1, 1, 2, 0, cmbNormalization); + + pn1.place(22, 1, 1, 1, 2, 0, new JLabel("Z-test Quantile")); + pn1.place(22, 2, 1, 1, 2, 0, spnQuantile); + pn1.place(22, 3, 1, 1, 2, 0, spnQuantile.info); + + pn1.place(26, 0, 1, 1, h, 0, new JSeparator()); + pn1.place(28, 0, 1, 1, 2, 0, new JLabel("<html><b>Scale-space</b></html>")); + + pn1.place(28, 1, 1, 1, 2, 0, new JLabel("Upsampling factor")); + pn1.place(28, 2, 1, 1, 2, 0, spnUpsamplingFactor); + pn1.place(28, 3, 1, 1, 2, 0, spnUpsamplingFactor.info); + + pn1.place(29, 1, 1, 1, 2, 0, new JLabel("Number of Gaussians")); + pn1.place(29, 2, 1, 1, 10, 0, spnNbGauss); + pn1.place(29, 3, 1, 1, 10, 0, spnNbGauss.info); + + pn1.place(31, 0, 4, 1, h, 0, new JSeparator()); + pn1.place(32, 0, 4, 1, h, 0, lblR); + pn1.place(33, 0, 4, 1, h, 0, new JSeparator()); +*/ + setLayout(new BorderLayout()); + add(pn1, BorderLayout.CENTER); + + spnQuantile.addChangeListener(this); + spnNumberPoints.addChangeListener(this); + spnScaleBack.addChangeListener(this); + spnScaleMin.addChangeListener(this); + spnScaleMax.addChangeListener(this); + spnNbScale.addChangeListener(this); + spnNbBasis.addChangeListener(this); + cmbNormalization.addItemListener(this); + + spnQuantile.addKeyListener(this); + spnNumberPoints.addKeyListener(this); + spnScaleBack.addKeyListener(this); + spnScaleMin.addKeyListener(this); + spnScaleMax.addKeyListener(this); + spnNbScale.addKeyListener(this); + spnNbBasis.addKeyListener(this); + + } + + @Override + public void stateChanged(ChangeEvent e) { + updateParams(); + } + + @Override + public void itemStateChanged(ItemEvent e) { + updateParams(); + } + + public void setNumberOfPoints(int npoints) { + try { + spnNumberPoints.set(npoints); + } + catch(Exception ex) {} + } + + private void updateParams() { + + try { + Params.quantile = spnQuantile.get(); + Params.nthin = spnNumberPoints.get(); + //Params.nthin2D = spnNumberPoints.get(); + //Params.nthin3D = spnNumberPoints.get(); + Params.scaleBack = spnScaleBack.get(); + if (spnScaleMin.get()<=spnScaleMax.get()) { + Params.smin = spnScaleMin.get(); + Params.smax = spnScaleMax.get(); + } + else { + Params.smax = spnScaleMin.get(); + Params.smin = spnScaleMax.get(); + } + Params.Nscale = spnNbScale.get(); + Params.NbasisVectSIFT = spnNbBasis.get(); + Params.sr = spnUpsamplingFactor.get(); + Params.normalize = (String)cmbNormalization.getSelectedItem(); + Params.factorDiamBack = spnEnlargeFactor.get(); + Params.nbGauss = spnNbGauss.get(); + } + catch(Exception ex) { + Log.write("Wrong format"); + } + } + + private void setGUI() { + try { + spnQuantile.set(Params.quantile); + spnNumberPoints.set(Params.nthin); + //spnNumberPoints.set(Params.nthin2D); + //spnNumberPoints.set(Params.nthin3D); + spnScaleBack.set(Params.scaleBack); + spnScaleMin.set(Params.smin); + spnScaleMax.set(Params.smax); + spnNbScale.set(Params.Nscale); + spnNbBasis.set(Params.NbasisVectSIFT); + spnUpsamplingFactor.set(Params.sr); + cmbNormalization.setSelectedItem(Params.normalize); + spnEnlargeFactor.set(Params.factorDiamBack); + spnNbGauss.set(Params.nbGauss); + } + catch (Exception ex) { + } + } + + @Override + public void keyTyped(KeyEvent e) { + updateParams(); + } + + @Override + public void keyPressed(KeyEvent e) { + // TODO Auto-generated method stub + + } + + @Override + public void keyReleased(KeyEvent e) { + // TODO Auto-generated method stub + + } + +} diff --git a/java code/src/eigenpsf/Constants.java b/java code/src/eigenpsf/Constants.java new file mode 100644 index 0000000000000000000000000000000000000000..533af14bfc2cfcce1ab567995d97693796f45bab --- /dev/null +++ b/java code/src/eigenpsf/Constants.java @@ -0,0 +1,11 @@ +package eigenpsf; + +public class Constants { + + static final public String title = "(c) CNRS and BIG EPFL"; + static final public String name = "Eigen PSF"; + static final public String version = "0.3"; + static final public String url = "http://bigwww.epfl.ch/"; + static final public String copyright = "(c) CNRS and BIG EPFL"; + +} diff --git a/java code/src/eigenpsf/Log.java b/java code/src/eigenpsf/Log.java new file mode 100644 index 0000000000000000000000000000000000000000..fd68a3f649201f2721e5ac06b7373a09e5796cfb --- /dev/null +++ b/java code/src/eigenpsf/Log.java @@ -0,0 +1,142 @@ +package eigenpsf; + +import java.util.Timer; +import java.util.TimerTask; + +import javax.swing.SwingUtilities; + +import bilib.commons.components.HTMLPane; +import eigenpsf.gui.WalkBar; +import eigenpsf.project.Project; + +public class Log { + + public static WalkBar status; + public static HTMLPane info; + private static Project project; + + private static Timer timer; + + public static void init(WalkBar status_,Project project_) { + status = status_; + project = project_; + } + + public static void progress(String msg, double value) { + if (status != null) + status.progress(msg, value); + } + + public static void init(HTMLPane info_) { + info = info_; + } + + public static void startProgressMessageFromC() { + timer = new Timer(); + TimerTask task = new LogScheduler(info, timer,project); + timer.schedule(task, 100, 500); + } + + public static void stopProgressMessageFromC() { + if (timer != null) { + timer.cancel(); + timer.purge(); + timer = null; + } + } + + public static void write(String message) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (info != null) info.append("p", message); + info.setCaretPosition(info.getDocument().getLength()); + } + }); + } + + public static void writeException(Exception exception) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + String msg = exception.toString(); + if (info != null) info.append("<p></p><p><span style=\"color:red\">" + msg + "</span></p>"); + info.setCaretPosition(info.getDocument().getLength()); + } + }); + } + + public static void writeTitle(String message) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (info != null) info.append("<b>" + message + "</b> <br/>"); + info.setCaretPosition(info.getDocument().getLength()); + } + }); + } + + public static void writeCenterTitle(String message) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (info != null) info.append("<center> <b>" + message + "</b> </center>"); + info.setCaretPosition(info.getDocument().getLength()); + } + }); + } + + public static void writeList1(String message) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (info != null) info.append("<em> " + message + "</em> <br />"); + info.setCaretPosition(info.getDocument().getLength()); + } + }); + } + + public static void writeList2(String message) { + if (Params.verbose==1) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (info != null) info.append("p", " " +" " + " " +" " + "- "+ message); + info.setCaretPosition(info.getDocument().getLength()); + } + }); + } + } + + public static void writeError(String message) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (info != null) info.append("<span style=\"color : red \"> Error: </span> <em>" + message + "</em> <br />"); + info.setCaretPosition(info.getDocument().getLength()); + } + }); + } + + public static void writeWarning(String message) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (info != null) info.append("<span style=\"color : orange \"> Warning: </span> <em>" + message + "</em> <br />"); + info.setCaretPosition(info.getDocument().getLength()); + } + }); + } + + public static void writeOutput(String message) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (info != null) info.append("<span style=\"color : blue\"> <em>" + " " + message + "</em> </span> <br />"); + info.setCaretPosition(info.getDocument().getLength()); + } + }); + } + + + public static void clear() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (info != null) info.clear(); + info.setCaretPosition(info.getDocument().getLength()); + } + }); + + } +} diff --git a/java code/src/eigenpsf/LogScheduler.java b/java code/src/eigenpsf/LogScheduler.java new file mode 100644 index 0000000000000000000000000000000000000000..73b0bbeb78bace3f7490bc94f37e366e8fb03d8d --- /dev/null +++ b/java code/src/eigenpsf/LogScheduler.java @@ -0,0 +1,70 @@ +package eigenpsf; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.util.Timer; +import java.util.TimerTask; +import java.util.Scanner; + +import eigenpsf.project.Project; + +import bilib.commons.components.HTMLPane; + +public class LogScheduler extends TimerTask { + private HTMLPane info; + private Timer timer; + private String old = null; + private Project project; + + public LogScheduler(HTMLPane info, Timer timer, Project project) { + this.info = info; + this.timer = timer; + this.project = project; + // Clean the LogC.txt file + try { + new File(project.pathUtils + "LogC.txt"); // Create file if does not exists + FileWriter myWriter = new FileWriter(project.pathUtils + "LogC.txt"); + myWriter.write(""); + myWriter.close(); + } catch (Exception e) { + Log.writeError("Can't write in LogC.txt "+ project.pathUtils + "LogC.txt"); + } + } + + public void run() { + + if (info == null) + return; + try { + File file = new File(project.pathUtils + "LogC.txt"); + Scanner myReader = new Scanner(file); + while (myReader.hasNextLine()) { + String data = myReader.nextLine(); + if (!data.equals(old) & !data.equals("end")) { + if (Integer.parseInt(data.substring(0,1)) ==1) { + Log.writeList1(data.substring(2)); + }else if (Integer.parseInt(data.substring(0,1)) ==2) { + Log.writeList2(data.substring(2)); + } + old=data; + } + if (data.toLowerCase().trim().equals("end")) { + // System.out.println("stop LogScheduler "); + if (timer != null) { + timer.cancel(); + timer.purge(); + timer = null; + } + } + } + myReader.close(); + } + catch(Exception ex) { + System.out.println("" + ex); + } + + } + +} \ No newline at end of file diff --git a/java code/src/eigenpsf/MaskDialog.java b/java code/src/eigenpsf/MaskDialog.java new file mode 100644 index 0000000000000000000000000000000000000000..30074a7dad889885f13c5ea15dc92a105fb37426 --- /dev/null +++ b/java code/src/eigenpsf/MaskDialog.java @@ -0,0 +1,89 @@ +package eigenpsf; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import bilib.commons.components.HTMLPane; +import eigenpsf.filemanager.IO; +import eigenpsf.stack.ZStack; +import ij.IJ; +import ij.ImagePlus; +import ij.gui.GUI; + +public class MaskDialog extends JDialog implements ActionListener { + + private HTMLPane info = new HTMLPane(500, 200); + + private JButton bnClose = new JButton("Close"); + private JButton bnSelect = new JButton("Select a mask"); + private JButton bnUnselect = new JButton("Unselect a mask"); + private JLabel lbl = new JLabel(" "); + private ZStack stack; + + public MaskDialog(ZStack stack) { + super(new JFrame(), "Mask of " + stack.getName()); + this.stack = stack; + + info.append("p", "A mask in a 2D image which a valid area defined by a non-zero value"); + info.append("p", "To create a mask, use the ROI tools of ImageJ and the command Edit > Selection > Create Mask"); + lbl.setBorder(BorderFactory.createEtchedBorder()); + + JPanel bn = new JPanel(new FlowLayout()); + bn.add(bnClose); + bn.add(bnSelect); + bn.add(bnUnselect); + + JPanel pn = new JPanel(new BorderLayout()); + pn.add(lbl, BorderLayout.NORTH); + pn.add(info.getPane(), BorderLayout.CENTER); + pn.add(bn, BorderLayout.SOUTH); + add(pn); + + bnClose.addActionListener(this); + bnSelect.addActionListener(this); + bnUnselect.addActionListener(this); + bold(stack.getMaskFilename()); + pack(); + setModal(true); + GUI.center(this); + setVisible(true); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == bnClose) { + dispose(); + } + if (e.getSource() == bnUnselect) { + stack.setMaskFilename(""); + bold(" "); + } + if (e.getSource() == bnSelect) { + String filename = IO.browseOpenFile("."); + File file = new File(filename); + if (file.exists()) { + ImagePlus imp = IJ.openImage(filename); + if (imp != null) { + imp.show(); + bold(filename); + stack.setMaskFilename(filename); + } + } + } + } + + private void bold(String text) { + lbl.setText("<html><b>" + text + "</b></html>"); + } + +} diff --git a/java code/src/eigenpsf/Params.java b/java code/src/eigenpsf/Params.java new file mode 100644 index 0000000000000000000000000000000000000000..2e83f62d5ec872e430bafc5e938d54a12081cf65 --- /dev/null +++ b/java code/src/eigenpsf/Params.java @@ -0,0 +1,184 @@ +package eigenpsf; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.StringTokenizer; +import java.lang.Math; + +import bilib.commons.table.CustomizedColumn; +import eigenpsf.processing.AbstractBackground; +import eigenpsf.processing.AbstractDetection; +import eigenpsf.processing.AbstractRegistration; +import eigenpsf.project.Project; +import eigenpsf.project.ProjectTable; +import eigenpsf.stack.ZStack; + +public class Params { + + // Handle the paths to load basis, should be done with internet link at the end. + public static String[] PathToGitList = {"/home/esoubies/Bureau/","/home/valentin/Documents/", "/Users/dsage/git/"}; + + public static int dim = 2; // dimension of the problem + public static int[] psf_visible_width = {17,17,1}; // estimated essential support of the PSF (should be forced to be odd) + public static double factorDiamBack = 2; // factor such that the local background of a bead is estimated on a patch of size factorDiamBack*support + + public static double thresholdDetection = 0.5; // threshold parameter in [0,1] + public static String PathToSingularValues ="superresolved_psf_estimation/code/Outputs/Subspaces/Gaussian"+dim+"D_singularvalues.csv"; + public static String PathToSbasis ="superresolved_psf_estimation/code/Outputs/Subspaces/Gaussian"+dim+"D_sbasis_scale"; + + public static int Nscale = 3; // number of precomputed scaled basis, set to 1 to compute it automatically + public static int NbasisVectSIFT = 3; // number of basis used to compute vectorial SIFT + public static double smin = 0.5; // minimum and maximum scaling of the basis use to detect beads + public static double smax = 1.5; + public static int NbasisVectSIFT_max2D= 0; + public static int NbasisVectSIFT_max3D= 0; + + public static int NeigenElements = 1; // number of eigen PSF to compute + + public static String normalize = "l2"; // normalization of the PSF before SVD, "l1" or "l2" + + public static double quantile = 2; // quantile for the z-test + + public static int nthin =7; // number of points use for thin plate approximation of background + //public static int nthin2D = 7; // number of points use for thin plate approximation of background + //public static int nthin3D = 2; + public static int scaleBack = 4; // scale reduction for faster background approximation + + public static int sr=2; // upsampling factor for scale space registration + public static int nbGauss=5; // number of Gaussian scales for scale space registration + + public static int verbose=1; // verbose level + + public static String detectionMethod = AbstractDetection.list().get(0).getName(); // 0: SIFT, 1: DoG + public static String backgroundMethod = AbstractBackground.list().get(0).getName(); // 0: Polynomial fit + public static String registrationMethod = AbstractRegistration.list().get(0).getName(); // 0: ScaleSpace, 1:CoG + + public static boolean load(Project project) { + String filename = project.path + File.separator + "config.csv"; + String line = ""; + try { + BufferedReader buffer = new BufferedReader(new FileReader(filename)); + while (line != null) { + StringTokenizer tokens = new StringTokenizer(line, ","); + if (tokens.countTokens() == 2) { + String key = tokens.nextToken().trim(); + String value = tokens.nextToken().trim(); + + // String + if (key.equalsIgnoreCase("detectionMethod")) + detectionMethod = value; + if (key.equalsIgnoreCase("backgroundMethod")) + backgroundMethod = value; + if (key.equalsIgnoreCase("registrationMethod")) + registrationMethod = value; + if (key.equalsIgnoreCase("PathToSingularValues")) + PathToSingularValues = value; + if (key.equalsIgnoreCase("PathToSbasis")) + PathToSbasis = value; + if (key.equalsIgnoreCase("normalize")) + normalize = value; + + // Integer + if (key.equalsIgnoreCase("dim")) + dim = Integer.parseInt(value); + if (key.equalsIgnoreCase("Nscale")) + Nscale = Integer.parseInt(value); + if (key.equalsIgnoreCase("NbasisVectSIFT")) + NbasisVectSIFT = Integer.parseInt(value); + if (key.equalsIgnoreCase("NeigenElements")) + NeigenElements = Integer.parseInt(value); + if (key.equalsIgnoreCase("nthin")) + nthin = Integer.parseInt(value); + //if (key.equalsIgnoreCase("nthin2D")) + // nthin2D = Integer.parseInt(value); + //if (key.equalsIgnoreCase("nthin3D")) + // nthin3D = Integer.parseInt(value); + if (key.equalsIgnoreCase("scaleBack")) + scaleBack = Integer.parseInt(value); + if (key.equalsIgnoreCase("psf_visible_width_0")) + psf_visible_width[0] = Integer.parseInt(value); + if (key.equalsIgnoreCase("psf_visible_width_1")) + psf_visible_width[1] = Integer.parseInt(value); + if (key.equalsIgnoreCase("psf_visible_width_2")) + psf_visible_width[2] = Integer.parseInt(value); + + // Double + if (key.equalsIgnoreCase("factorDiamBack")) + factorDiamBack = Double.parseDouble(value); + if (key.equalsIgnoreCase("thresholdDetection")) + thresholdDetection = Double.parseDouble(value); + if (key.equalsIgnoreCase("quantile")) + quantile = Double.parseDouble(value); + if (key.equalsIgnoreCase("smin")) + smin = Double.parseDouble(value); + if (key.equalsIgnoreCase("smax")) + smax = Double.parseDouble(value); + + } + } + buffer.close(); + return true; + } + catch (Exception ex) { + System.out.println("Unable to read the file " + line); + } + return false; + } + + public static void save(Project project) { + String filename = project.path + File.separator + "config.csv"; + File file = new File(filename); + try { + BufferedWriter buffer = new BufferedWriter(new FileWriter(file)); + + // String + buffer.write("detectionMethod, " + detectionMethod + "\n"); + buffer.write("backgroundMethod, " + backgroundMethod + "\n"); + buffer.write("registrationMethod, " + registrationMethod + "\n"); + buffer.write("PathToSingularValues, " + PathToSingularValues + "\n"); + buffer.write("PathToSbasis, " + PathToSbasis + "\n"); + buffer.write("normalize, " + normalize + "\n"); + + // Integer + buffer.write("dim, " + dim + "\n"); + buffer.write("Nscale, " + Nscale + "\n"); + buffer.write("NbasisVectSIFT, " + NbasisVectSIFT + "\n"); + buffer.write("NeigenElements, " + NeigenElements + "\n"); + buffer.write("nthin, " + nthin + "\n"); + //buffer.write("nthin2D, " + nthin2D + "\n"); + //buffer.write("nthin3D, " + nthin3D + "\n"); + buffer.write("scaleBack, " + scaleBack + "\n"); + + // Double + buffer.write("factorDiamBack, " + factorDiamBack + "\n"); + buffer.write("thresholdDetection, " + thresholdDetection + "\n"); + buffer.write("quantile, " + quantile + "\n"); + buffer.write("smin, " + smin + "\n"); + buffer.write("smax, " + smax + "\n"); + + // Int[] + buffer.write("psf_visible_width_0, " + psf_visible_width[0] + "\n"); + buffer.write("psf_visible_width_1, " + psf_visible_width[1] + "\n"); + buffer.write("psf_visible_width_2, " + psf_visible_width[2] + "\n"); + + buffer.close(); + + + + } + catch (IOException ex) { + System.out.println("" + ex); + } + } + +} + + diff --git a/java code/src/eigenpsf/SettingsDialog.java b/java code/src/eigenpsf/SettingsDialog.java new file mode 100644 index 0000000000000000000000000000000000000000..c392c43ce99dc2b94b66990c536a7cc69acf958c --- /dev/null +++ b/java code/src/eigenpsf/SettingsDialog.java @@ -0,0 +1,288 @@ +package eigenpsf; + +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; + +import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JSeparator; +import javax.swing.JTabbedPane; +import javax.swing.SwingConstants; +import javax.swing.border.EtchedBorder; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import bilib.commons.components.GridPanel; +import bilib.commons.components.SpinnerRangeInteger; +import eigenpsf.gui.SpinnerInfoRangeDouble; +import eigenpsf.gui.SpinnerInfoRangeInteger; +import eigenpsf.processing.AbstractDetection; +import eigenpsf.processing.AbstractRegistration; +import eigenpsf.project.Project; +import ij.IJ; +import ij.ImagePlus; +import ij.gui.GUI; +import ij.gui.Roi; + +public class SettingsDialog extends JDialog implements ActionListener, KeyListener, ChangeListener, ItemListener { + + private JComboBox<String> cmbDetection = new JComboBox<String>(); + private JComboBox<String> cmbRegistration = new JComboBox<String>(); + private JComboBox<String> cmbModality = new JComboBox<String>(new String[] {"Not Available", "Confocal", "Wide-field", "2-photons" }); + private SpinnerInfoRangeDouble spnThreshold = new SpinnerInfoRangeDouble(1, 0, 1, 0.01, "Threshold in [0,1] used for beads detection. The larger it is, the fewer beads are detected.", "http://bigwww.epfl.ch"); + private SpinnerInfoRangeInteger spnNbEigen = new SpinnerInfoRangeInteger(1, 1, 1000, 1, "Number of EigenPSF Elements to be computed.", "http://bigwww.epfl.ch"); + private SpinnerRangeInteger spnPSFXY = new SpinnerRangeInteger(Params.psf_visible_width[0], 1, 9999, 2); + private SpinnerRangeInteger spnPSFZ = new SpinnerRangeInteger(Params.psf_visible_width[2], 1, 9999, 2); + private JRadioButton bn2D = new JRadioButton("2D", true); + private JRadioButton bn3D = new JRadioButton("3D"); + + private JButton bnGetROI = new JButton("Get ROI"); + private JTabbedPane tab = new JTabbedPane(); + + private JPanel cardRegistration = new JPanel(new CardLayout()); + private AdvancedSettingsPanel advancedSettings; + + private Project project; + + public SettingsDialog(Project project_) { + + super(new JFrame(), "Settings"); + this.advancedSettings = new AdvancedSettingsPanel(); + + // ###### TEMPORARY + cmbModality.setEnabled(false); + // ################### + + this.project = project_; + + setGUI(); + + tab.addTab("General", buildGeneralSettingPanel()); + tab.addTab("Advanced", advancedSettings); + + setLayout(new BorderLayout()); + + add(tab, BorderLayout.CENTER); + + cmbDetection.setToolTipText("Select a detection method."); + cmbRegistration.setToolTipText("Select a registration method."); + cmbModality.setToolTipText("Select the imaging modality."); + bn2D.setToolTipText("Tick for 2D data"); + bn3D.setToolTipText("Tick for 3D data"); + bnGetROI.setToolTipText("Define PSF support through ROI."); + + cmbDetection.addItemListener(this); + cmbRegistration.addItemListener(this); + cmbModality.addItemListener(this); + spnThreshold.addChangeListener(this); + spnNbEigen.addChangeListener(this); + spnPSFXY.addChangeListener(this); + spnPSFZ.addChangeListener(this); + bn2D.addActionListener(this); + bn3D.addActionListener(this); + + spnThreshold.addKeyListener(this); + spnNbEigen.addKeyListener(this); + spnPSFXY.addKeyListener(this); + spnPSFZ.addKeyListener(this); + + bnGetROI.addActionListener(this); + + tab.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + pack(); + } + }); + updateInterface(); + pack(); + setModal(false); + GUI.center(this); + } + + private JPanel buildGeneralSettingPanel() { + + for (AbstractDetection detection : AbstractDetection.list()) { + cmbDetection.addItem(detection.getName()); + } + cmbDetection.setSelectedItem(Params.detectionMethod); + + for (AbstractRegistration registration : AbstractRegistration.list()) + cmbRegistration.addItem(registration.getName()); + cmbRegistration.setSelectedItem(Params.registrationMethod); + + ButtonGroup bg = new ButtonGroup(); + bg.add(bn2D); + bg.add(bn3D); + + int i = 0; + GridPanel pn = new GridPanel(false, 4); + // General + i++; + pn.place(i, 0, 4, 1, new JSeparator()); + i++; + pn.place(i, 0, 4, 1, "<html> <b> Common </b> </html>"); + i++; + pn.place(i, 0, 4, 1, new JSeparator()); + i++; + pn.place(i, 0, 4, 1, "<html><b>Modality</b></html>"); + pn.place(i, 1, cmbModality); + pn.place(i, 2, bn2D); + pn.place(i, 3, bn3D); + i++; + pn.place(i, 0, "PSF Support (XY)"); + pn.place(i, 1, spnPSFXY); + pn.place(i, 2, "px"); + pn.place(i, 3, bnGetROI); + i++; + pn.place(i, 0, "PSF Support (Z)"); + pn.place(i, 1, spnPSFZ); + pn.place(i, 2, 2, 1, "number of slices"); + i++; + pn.place(i, 0, "Eigen Components"); + pn.place(i, 1, spnNbEigen); + pn.place(i, 2, spnNbEigen.info); + + i++; + pn.place(i, 0, 4, 1, new JSeparator()); + i++; + pn.place(i, 0, 4, 1, "<html> <b> Detect & Preprocess </b> </html>"); + i++; + pn.place(i, 0, 4, 1, new JSeparator()); + i++; + pn.place(i, 0, "<html><b>Detection Threshold</b></html>"); + //pn.place(i, 1, 3, 1, cmbDetection); + //i++; + //pn.place(i, 1, "Threshold"); + pn.place(i, 1, spnThreshold); + pn.place(i, 2, spnThreshold.info); + + //i++; + //pn.place(i, 0, "<html><b>Registration</b></html>"); + //pn.place(i, 1, 3, 1, cmbRegistration); + + /*i++; + pn.place(i, 0, 4, 1, new JSeparator()); + i++; + pn.place(i, 0, 4, 1, "<html> <b> Refine EigenPSF </b> </html>"); + i++; + pn.place(i, 0, 4, 1, new JSeparator());*/ + + + pn.setBorder(BorderFactory.createCompoundBorder(new EtchedBorder(EtchedBorder.LOWERED), + BorderFactory.createEmptyBorder(8, 8, 8, 8))); + + return pn; + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == bn2D) { + advancedSettings.setNumberOfPoints(7); + advancedSettings.spnNbBasis.setLimit(1,Params.NbasisVectSIFT_max2D); + advancedSettings.spnNbBasis.setValue(Params.NbasisVectSIFT_max2D); + } + if (e.getSource() == bn3D) { + advancedSettings.setNumberOfPoints(2); + advancedSettings.spnNbBasis.setLimit(1,Params.NbasisVectSIFT_max3D); + advancedSettings.spnNbBasis.setValue(Params.NbasisVectSIFT_max3D); + } + if (e.getSource() == bnGetROI) { + try { + ImagePlus imp = IJ.getImage(); + if (imp != null) + if (imp.getRoi() != null) { + Roi roi = imp.getRoi(); + Rectangle rect = roi.getBounds(); + int size = Math.floorDiv(Math.max(rect.width, rect.height),2)*2+1; + roi = new Roi(rect.x, rect.y, size, size); + imp.setRoi(roi); + spnPSFXY.set(size); + updateParams(); + } + } catch (Exception ex) { + } + } + updateParams(); + updateInterface(); + } + + @Override + public void itemStateChanged(ItemEvent e) { + updateParams(); + updateInterface(); + } + + @Override + public void stateChanged(ChangeEvent e) { + updateParams(); + updateInterface(); + } + + private void updateInterface() { + spnPSFZ.setEnabled(bn3D.isSelected()); + CardLayout cl2 = (CardLayout) (cardRegistration.getLayout()); + cl2.show(cardRegistration, "" + cmbRegistration.getSelectedIndex()); + } + + private void updateParams() { + try { + Params.psf_visible_width[0] = spnPSFXY.get(); + Params.psf_visible_width[1] = spnPSFXY.get(); + Params.psf_visible_width[2] = spnPSFZ.get(); + Params.detectionMethod = (String) cmbDetection.getSelectedItem(); // 0: SIFT, 1: DoG + Params.registrationMethod = (String) cmbRegistration.getSelectedItem(); // 0: CoG, 1: ScaleSpace + Params.thresholdDetection = spnThreshold.get(); + Params.NeigenElements = spnNbEigen.get(); + Params.dim = bn2D.isSelected() ? 2 : 3; + } catch (Exception ex) { + } + } + + private void setGUI() { + try { + spnPSFXY.set(Params.psf_visible_width[0]); + spnPSFZ.set(Params.psf_visible_width[2]); + cmbDetection.setSelectedItem(Params.detectionMethod); // 0: SIFT, 1: DoG + cmbRegistration.setSelectedItem(Params.registrationMethod);; // 0: CoG, 1: ScaleSpace + spnThreshold.set(Params.thresholdDetection); + spnNbEigen.set(Params.NeigenElements); + bn2D.setSelected(Params.dim == 2); + bn3D.setSelected(Params.dim == 3); + } + catch (Exception ex) { + } + } + + @Override + public void keyTyped(KeyEvent e) { + updateParams(); + updateInterface(); + } + + @Override + public void keyPressed(KeyEvent e) { + // TODO Auto-generated method stub + + } + + @Override + public void keyReleased(KeyEvent e) { + // TODO Auto-generated method stub + + } + +} diff --git a/java code/src/eigenpsf/data/Convolution.java b/java code/src/eigenpsf/data/Convolution.java new file mode 100644 index 0000000000000000000000000000000000000000..fd1d6bf22b90f713557cdb8ac850ce90a2fb2cb7 --- /dev/null +++ b/java code/src/eigenpsf/data/Convolution.java @@ -0,0 +1,311 @@ +package eigenpsf.data; + +import java.awt.*; + +/** + * Convolution is the code for applying the convolution operator. + * + * @author: Simon Horne + */ +public class Convolution extends Thread { + + /** + * Default no-arg constructor. + */ + public Convolution() { + } + + /** + * Takes an image (grey-levels) and a kernel and a position, + * applies the convolution at that position and returns the + * new pixel value. + * + * @param input The 2D double array representing the image. + * @param x The x coordinate for the position of the convolution. + * @param y The y coordinate for the position of the convolution. + * @param k The 2D array representing the kernel. + * @param kernelWidth The width of the kernel. + * @param kernelHeight The height of the kernel. + * @return The new pixel value after the convolution. + */ + public static double singlePixelConvolution(double [][] input, + int x, int y, + double [][] k, + int kernelWidth, + int kernelHeight){ + double output = 0; + for(int i=0;i<kernelWidth;++i){ + for(int j=0;j<kernelHeight;++j){ + output = output + (input[x+i][y+j] * k[i][j]); + } + } + return output; + } + + public static int applyConvolution(int [][] input, + int x, int y, + double [][] k, + int kernelWidth, + int kernelHeight){ + int output = 0; + for(int i=0;i<kernelWidth;++i){ + for(int j=0;j<kernelHeight;++j){ + output = output + (int) Math.round(input[x+i][y+j] * k[i][j]); + } + } + return output; + } + + /** + * Takes a 2D array of grey-levels and a kernel and applies the convolution + * over the area of the image specified by width and height. + * + * @param input the 2D double array representing the image + * @param width the width of the image + * @param height the height of the image + * @param kernel the 2D array representing the kernel + * @param kernelWidth the width of the kernel + * @param kernelHeight the height of the kernel + * @return the 2D array representing the new image + */ + public static double [][] convolution2D(double [][] input, + int width, int height, + double [][] kernel, + int kernelWidth, + int kernelHeight){ + int smallWidth = width - kernelWidth + 1; + int smallHeight = height - kernelHeight + 1; + double [][] output = new double [smallWidth][smallHeight]; + for(int i=0;i<smallWidth;++i){ + for(int j=0;j<smallHeight;++j){ + output[i][j]=0; + } + } + for(int i=0;i<smallWidth;++i){ + for(int j=0;j<smallHeight;++j){ + output[i][j] = singlePixelConvolution(input,i,j,kernel, + kernelWidth,kernelHeight); +//if (i==32- kernelWidth + 1 && j==100- kernelHeight + 1) System.out.println("Convolve2D: "+output[i][j]); + } + } + return output; + } + + /** + * Takes a 2D array of grey-levels and a kernel, applies the convolution + * over the area of the image specified by width and height and returns + * a part of the final image. + * @param input the 2D double array representing the image + * @param width the width of the image + * @param height the height of the image + * @param kernel the 2D array representing the kernel + * @param kernelWidth the width of the kernel + * @param kernelHeight the height of the kernel + * @return the 2D array representing the new image + */ + public static double [][] convolution2DPadded(double [][] input, + int width, int height, + double [][] kernel, + int kernelWidth, + int kernelHeight){ + int smallWidth = width - kernelWidth + 1; + int smallHeight = height - kernelHeight + 1; + int top = kernelHeight/2; + int left = kernelWidth/2; + double small [][] = new double [smallWidth][smallHeight]; + small = convolution2D(input,width,height, + kernel,kernelWidth,kernelHeight); + double large [][] = new double [width][height]; + for(int j=0;j<height;++j){ + for(int i=0;i<width;++i){ + large[i][j] = 0; + } + } + for(int j=0;j<smallHeight;++j){ + for(int i=0;i<smallWidth;++i){ +//if (i+left==32 && j+top==100) System.out.println("Convolve2DP: "+small[i][j]); + large[i+left][j+top]=small[i][j]; + } + } + return large; + } + + /** + * Takes a 2D array of grey-levels and a kernel and applies the convolution + * over the area of the image specified by width and height. + * + * @param input the 2D double array representing the image + * @param width the width of the image + * @param height the height of the image + * @param kernel the 2D array representing the kernel + * @param kernelWidth the width of the kernel + * @param kernelHeight the height of the kernel + * @return the 1D array representing the new image + */ + public static double [] convolutionDouble(double [][] input, + int width, int height, + double [][] kernel, + int kernelWidth, int kernelHeight){ + int smallWidth = width - kernelWidth + 1; + int smallHeight = height - kernelHeight + 1; + double [][] small = new double [smallWidth][smallHeight]; + small = convolution2D(input,width,height,kernel,kernelWidth,kernelHeight); + double [] result = new double [smallWidth * smallHeight]; + for(int j=0;j<smallHeight;++j){ + for(int i=0;i<smallWidth;++i){ + result[j*smallWidth +i]=small[i][j]; + } + } + return result; + } + + /** + * Takes a 2D array of grey-levels and a kernel and applies the convolution + * over the area of the image specified by width and height. + * + * @param input the 2D double array representing the image + * @param width the width of the image + * @param height the height of the image + * @param kernel the 2D array representing the kernel + * @param kernelWidth the width of the kernel + * @param kernelHeight the height of the kernel + * @return the 1D array representing the new image + */ + public static double [] convolutionDoublePadded(double [][] input, + int width, int height, + double [][] kernel, + int kernelWidth, + int kernelHeight){ + double [][] result2D = new double [width][height]; + result2D = convolution2DPadded(input,width,height, + kernel,kernelWidth,kernelHeight); + double [] result = new double [width * height]; + for(int j=0;j<height;++j){ + for(int i=0;i<width;++i){ + result[j*width +i]=result2D[i][j]; +//if (i==32 && j==100) System.out.println("ConvolveDP: "+result[j*width +i]+" "+result2D[i][j]); + } + } + return result; + } + + /** + * Converts a greylevel array into a pixel array. + * @param the 1D array of greylevels. + * @return the 1D array of RGB pixels. + */ + public static int [] doublesToValidPixels (double [] greys){ + int [] result = new int [greys.length]; + int grey; + for(int i=0;i<greys.length;++i){ + if(greys[i]>255){ + grey = 255; + }else if(greys[i]<0){ + grey = 0; + }else{ + grey = (int) Math.round(greys[i]); + } + result[i] = (new Color(grey,grey,grey)).getRGB(); + } + return result; + } + + /** + * Applies the convolution2D algorithm to the input array as many as + * iterations. + * @param input the 2D double array representing the image + * @param width the width of the image + * @param height the height of the image + * @param kernel the 2D array representing the kernel + * @param kernelWidth the width of the kernel + * @param kernelHeight the height of the kernel + * @param iterations the number of iterations to apply the convolution + * @return the 2D array representing the new image + */ + public double [][] convolutionType1(double [][] input, + int width, int height, + double [][] kernel, + int kernelWidth, int kernelHeight, + int iterations){ + double [][] newInput = (double [][]) input.clone(); + double [][] output = (double [][]) input.clone(); + for(int i=0;i<iterations;++i){ + int smallWidth = width-kernelWidth+1; + int smallHeight = height-kernelHeight+1; + output = new double [smallWidth][smallHeight]; + output = convolution2D(newInput,width,height, + kernel,kernelWidth,kernelHeight); + width = smallWidth; + height = smallHeight; + newInput = (double [][]) output.clone(); + } + return output; + } + /** + * Applies the convolution2DPadded algorithm to the input array as many as + * iterations. + * @param input the 2D double array representing the image + * @param width the width of the image + * @param height the height of the image + * @param kernel the 2D array representing the kernel + * @param kernelWidth the width of the kernel + * @param kernelHeight the height of the kernel + * @param iterations the number of iterations to apply the convolution + * @return the 2D array representing the new image + */ + public double [][] convolutionType2(double [][] input, + int width, int height, + double [][] kernel, + int kernelWidth, int kernelHeight, + int iterations){ + double [][] newInput = (double [][]) input.clone(); + double [][] output = (double [][]) input.clone(); + + for(int i=0;i<iterations;++i){ + output = new double [width][height]; +//System.out.println("Iter: "+i+" conIN(50,50): "+newInput[50][50]); + output = convolution2DPadded(newInput,width,height, + kernel,kernelWidth,kernelHeight); +//System.out.println("conOUT(50,50): "+output[50][50]); + newInput = (double [][]) output.clone(); + } + return output; + } + /** + * Applies the convolution2DPadded algorithm and an offset and scale factors + * @param input the 1D int array representing the image + * @param width the width of the image + * @param height the height of the image + * @param kernel the 2D array representing the kernel + * @param kernelWidth the width of the kernel + * @param kernelHeight the height of the kernel + * @param scale the scale factor to apply + * @param offset the offset factor to apply + * @return the 1D array representing the new image + */ + public static int [] convolution_image(int [] input ,int width, int height, + double [][] kernel, + int kernelWidth,int kernelHeight, + double scale, double offset){ + double [][] input2D = new double [width][height]; + double [] output = new double [width*height]; + for(int j=0;j<height;++j){ + for(int i=0;i<width;++i){ + input2D[i][j] = (new Color(input[j*width+i])).getRed(); + } + } + output = convolutionDoublePadded(input2D,width,height, + kernel,kernelWidth,kernelHeight); + int [] outputInts = new int [width*height]; + + for(int i=0;i<outputInts.length;++i){ + outputInts[i] = (int) Math.round(output[i] * scale + offset); + if(outputInts[i]>255) outputInts[i] = 255; + if(outputInts[i]<0) outputInts[i] = 0; + int g = outputInts[i]; + outputInts[i] = (new Color(g,g,g)).getRGB(); + } + return outputInts; + } +} + diff --git a/java code/src/eigenpsf/data/Dict.java b/java code/src/eigenpsf/data/Dict.java new file mode 100644 index 0000000000000000000000000000000000000000..10f9bfc2eda2f5ebd3ba152556fe48a32dd5fa52 --- /dev/null +++ b/java code/src/eigenpsf/data/Dict.java @@ -0,0 +1,122 @@ +package eigenpsf.data; + +import org.jblas.*; + +import static org.jblas.DoubleMatrix.zeros; + +import java.io.BufferedReader; +import java.io.FileReader; + +import ij.IJ; +import ij.ImagePlus; +import ij.process.*; +import ij.ImageStack; + +// Methods +// - Dict(String PathToBasis,String PathToSingularValues) + +public class Dict { + public int Nbasis; + public HyperMatrix basis; + public DoubleMatrix S; + + public Dict(String PathToBasis,String PathToSingularValues, int dim) { + // Count number of elements + this.Nbasis = 0; + String row = ""; + try { + BufferedReader csvReader = new BufferedReader(new FileReader(PathToSingularValues)); + while (((row = csvReader.readLine()) != null)){ + this.Nbasis++; + } + csvReader.close(); + } + catch (Exception ex) { + System.out.println(ex); + System.out.println("Unable to read the file " + row); + } + + // Read singular values + this.S = zeros(this.Nbasis); + try { + BufferedReader csvReader = new BufferedReader(new FileReader(PathToSingularValues)); + int cpt = 0; + while (((row = csvReader.readLine()) != null)){ + String[] data = row.split(","); + double aa= Double.parseDouble(data[0]); + S.put(cpt,0,aa); + cpt++; + } + csvReader.close(); + } + catch (Exception ex) { + System.out.println(ex); + } + double sumS=S.sum();S.divi(sumS); + + ImagePlus tmp = IJ.openImage(PathToBasis+"0.tif"); + int[] dim_tmp=tmp.getDimensions(); + int nx=dim_tmp[0]; + int ny=dim_tmp[1]; + int nz=dim_tmp[3]; + ImageStack stack = new ImageStack(nx,ny); + for (int f = 0; f < this.Nbasis; f++) { + tmp = IJ.openImage(PathToBasis + f + ".tif"); + for (int z = 0; z < nz; z++) { + ImageProcessor ip = tmp.getStack().getProcessor(z + 1).convertToFloat(); + stack.addSlice(ip); + } + } + ImagePlus imBasis = new ImagePlus("", stack); + imBasis.setDimensions(1, nz, this.Nbasis); + this.basis = HyperMatrix.ImagePlus2HyperMatrix(imBasis); + } + + + public Dict(String PathToBasis,String PathToSingularValues, int dim, int Nbasis_) { + // Count number of elements + this.Nbasis = Nbasis_; + String row = ""; + + // Read singular values + this.S = zeros(this.Nbasis); + try { + BufferedReader csvReader = new BufferedReader(new FileReader(PathToSingularValues)); + int cpt = 0; + while (((row = csvReader.readLine()) != null) && (cpt<this.Nbasis)){ + String[] data = row.split(","); + double aa= Double.parseDouble(data[0]); + S.put(cpt,0,aa); + cpt++; + } + csvReader.close(); + } + catch (Exception ex) { + System.out.println(ex); + System.out.println("Unable to read the file " + row); + } + double sumS=S.sum();S.divi(sumS); + + + // TDOO: ecrire directement dans un HyperMatrix + ImagePlus tmp = IJ.openImage(PathToBasis+"0.tif"); + int[] dim_tmp=tmp.getDimensions(); + int nx=dim_tmp[0]; + int ny=dim_tmp[1]; + int nz=dim_tmp[3]; + ImageStack stack = new ImageStack(nx,ny); + for (int f = 0; f < this.Nbasis; f++) { + tmp = IJ.openImage(PathToBasis + f + ".tif"); +// tmp.show(); + for (int z = 0; z < nz; z++) { + ImageProcessor ip = tmp.getStack().getProcessor(z + 1).convertToFloat(); + stack.addSlice(ip); + } + } + ImagePlus imBasis = new ImagePlus("", stack); + imBasis.setDimensions(1, nz, this.Nbasis); + imBasis.setOpenAsHyperStack(true); + this.basis = HyperMatrix.ImagePlus2HyperMatrix(imBasis); + } + +} diff --git a/java code/src/eigenpsf/data/EigenElements.java b/java code/src/eigenpsf/data/EigenElements.java new file mode 100644 index 0000000000000000000000000000000000000000..9466d48a3feeda0046c1a6c20909ffab480ff575 --- /dev/null +++ b/java code/src/eigenpsf/data/EigenElements.java @@ -0,0 +1,132 @@ +package eigenpsf.data; + +import org.jblas.DoubleMatrix; +import org.jblas.MatrixFunctions; +import org.jblas.Singular; + +import eigenpsf.stack.Patches; +import eigenpsf.Log; +import eigenpsf.stack.Patch; + + +//Methods +//- EigenElements() +public class EigenElements { + public DoubleMatrix eigenCoeffs; // coefficients of the psfs on the eigen elements + public HyperMatrix eigenElements; + public double[] sValues; +/* + Constructor(s): + - EigenElements() + + Methods: + - void eigenDecomposition(SampledPSFs PSFs, int K) + - SampledPSFs ztest(SampledPSFs PSFs) + - void computeCoeffs(HyperMatrix psfs_data) +*/ + public EigenElements() { +// this.psfs_data=null; +// this.psfs_pos=null; + this.eigenElements=null; + this.eigenCoeffs=null; + this.sValues=null; + } + + public void normalize_sValues() { + if (eigenElements!=null) { + double Ssum = 0; + int K = eigenElements.nf; + for (int k=0; k<K; k++) { + Ssum += sValues[k]; + } + for (int k=0; k<K; k++) { + sValues[k]=sValues[k]/Ssum; + } + } + } + + public void eigenDecomposition(HyperMatrix psfs_data, int K) { + if ((psfs_data!=null) && (K>=1)) { + this.sValues=new double[K]; + this.eigenElements = psfs_data.randomSVD(K,sValues); + this.computeCoeffs(psfs_data); + } + } + + public HyperMatrix ztest(Patches patches, HyperMatrix psfs_data, double quantile) { + HyperMatrix new_psf_data =null; + if (this.eigenCoeffs!=null) { + //Log.write("Z-test to discard outlayers. \n"); + DoubleMatrix mu_coeffs = this.eigenCoeffs.rowMeans(); + DoubleMatrix sigma_coeffs = (this.eigenCoeffs.subColumnVector(mu_coeffs)).mmul((this.eigenCoeffs.subColumnVector(mu_coeffs)).transpose()); + sigma_coeffs=sigma_coeffs.rdiv(this.eigenCoeffs.columns); + DoubleMatrix[] eig=Singular.fullSVD(sigma_coeffs); + DoubleMatrix coeffs_ = (eig[0].transpose()).mmul(this.eigenCoeffs); + DoubleMatrix mu_coeffs_ = coeffs_.rowMeans(); + DoubleMatrix tmp = coeffs_.subColumnVector(mu_coeffs_); + DoubleMatrix sigma_est_ = MatrixFunctions.sqrt(((tmp).mul(tmp)).rowMeans()); + sigma_est_ = sigma_est_.repmat(1,tmp.columns); + tmp.divi(sigma_est_); + DoubleMatrix z_score = MatrixFunctions.abs(tmp).columnMaxs(); + DoubleMatrix bool = z_score.le(quantile); + //DoubleMatrix bool = MatrixFunctions.abs(tmp).le(sigma_est_.mul(quantile)); + //bool = bool.columnSums(); + //bool = bool.ge(this.eigenCoeffs.rows); + + // Update Sampled PSF + int Npsf = (int) bool.sum(); + new_psf_data = new HyperMatrix(psfs_data.nx,psfs_data.ny,psfs_data.nz,Npsf); + int cpt=0; + //System.out.println("" + this.eigenCoeffs.columns + " " + patches.size()); + DoubleMatrix ztest_val = new DoubleMatrix(patches.size(),1); + for (int i=0; i<this.eigenCoeffs.columns; i++) { + patches.get(i).ztest_value = z_score.get(i); + ztest_val.put(i,z_score.get(i)); + patches.get(i).select = bool.get(i) == 1; + if (bool.get(i) == 1) { + // Copy PSF + for (int x=0; x<psfs_data.nx; x++) { + for (int y=0; y<psfs_data.ny; y++) { + for (int z=0; z<psfs_data.nz; z++) { + new_psf_data.setPixel(x, y, z, cpt, psfs_data.getPixel(x, y, z, i)); + } + } + } + cpt++; + } + } + // Sort Z-test values and set the global Id + int[] sortedIdx = ztest_val.sortingPermutation(); + int idx=1; + for (int i=0; i<sortedIdx.length; i++) { + patches.get(sortedIdx[i]).glob_id=idx; + idx++; + } + } + return new_psf_data; + } + + public void computeCoeffs(HyperMatrix psfs_data) { + if (this.eigenElements!=null) { + this.eigenCoeffs = new DoubleMatrix(this.eigenElements.columns,psfs_data.nf); +// for (int n=0; n<psfs_data.nf; n++) { + this.eigenCoeffs = (this.eigenElements.transpose()).mmul(psfs_data); +// this.eigenCoeffs.putColumn(n, c); +// } + } + } + + public HyperMatrix projPatch(Patch patch) { + HyperMatrix p = null; + if (this.eigenElements!=null) { + DoubleMatrix coefs = new DoubleMatrix(this.eigenElements.columns,1); + coefs = (this.eigenElements.transpose()).mmul(patch.data.imtranslate(patch.x, patch.y, patch.z)); + p = new HyperMatrix(patch.data.nx,patch.data.ny,patch.data.nz,1,this.eigenElements.mmul(coefs).data); + } + return p; + } + +} + + + diff --git a/java code/src/eigenpsf/data/HyperMatrix.java b/java code/src/eigenpsf/data/HyperMatrix.java new file mode 100644 index 0000000000000000000000000000000000000000..6a01b8a27cbe18ca76bcee5a72312ba60665bb73 --- /dev/null +++ b/java code/src/eigenpsf/data/HyperMatrix.java @@ -0,0 +1,835 @@ +package eigenpsf.data; + +import org.jblas.*; + +//import static org.jblas.DoubleMatrix.zeros; +import org.jblas.ranges.IntervalRange; +import org.jtransforms.fft.*; + +import eigenpsf.Log; +import eigenpsf.Params; + +import java.util.ArrayList; + +//import eigenpsf.stack.Patch; +//import eigenpsf.stack.Patches; + +//import java.io.File; +//import java.text.SimpleDateFormat; +import java.util.Random; + +import ij.IJ; +//import ij.IJ; +import ij.ImagePlus; +import ij.process.*; +import ij.ImageStack; +//import ij.plugin.Scaler; +import java.lang.Math; + +// Methods +// - HyperMatrix(int nx, int ny, int nz, int nf) +// - HyperMatrix(int nx, int ny, int nz, int nf, double[] v) +// - HyperMatrix(int[] dims) +// - HyperMatrix(int[] dims, double[] v) +// - HyperMatrix ImagePlus2HyperMatrix(ImagePlus imp) +// - ImagePlus HyperMatrix2ImagePlus(HyperMatrix struct) +// - int ind2sub(int z,int f) +// - int[] sub2ind(int id) +// - double getPixel(int x,int y,int z,int f) +// - void setPixel(int x,int y,int z,int f, double v) +// - int[] getDimensions() +// - void imresize(int nx_new, int ny_new, int nz_new) +// - void padding(int nx_new, int ny_new, int nz_new) +// - void resample(double scale, int[] size) +//- HyperMatrix sub(HyperMatrix other) +//- HyperMatrix add(HyperMatrix other) +//- HyperMatrix div(HyperMatrix other) +//- HyperMatrix mul(HyperMatrix other) +//- HyperMatrix mmul(HyperMatrix other) +//- HyperMatrix abs() +//- HyperMatrix sqrt() +//- HyperMatrix pow() +//- int numel() +//- HyperMatrix columnSums_() +//- HyperMatrix rowMaxs_() +//- HyperMatrix fftshift() +//- HyperMatrix ifftshift() +// - HyperMatrix[] meshgrid(DoubleMatrix[] lin) +// - HyperMatrix localMax(int margin, double threshold) +// - void display(String title, ArrayList<ImagePlus> listOfImages) +// - HyperMatrix randomSVD(int K, double[] sValues) +// - void setColumn(HyperMatrix A, int f_this, int f_A) +// - void imtranslate(double x, double y, double z) + +public class HyperMatrix extends DoubleMatrix { + + public int nx; + public int ny; + public int nz; + public int nf; + + public HyperMatrix(int nx, int ny, int nz, int nf) { + super(nx * ny * nz, nf); + this.nx = nx; + this.ny = ny; + this.nz = nz; + this.nf = nf; + } + + public HyperMatrix(int nx, int ny, int nz, int nf, double[] v) { + super(nx * ny * nz, nf); + this.nx = nx; + this.ny = ny; + this.nz = nz; + this.nf = nf; + this.data = v; + } + + public HyperMatrix(int nx, int ny, int nz, int nf, double v) { + super(nx * ny * nz, nf); + this.nx = nx; + this.ny = ny; + this.nz = nz; + this.nf = nf; + for (int x=0; x<nx; x++) { + for (int y=0; y<ny; y++) { + for (int z=0; z<nz;z++) { + for (int f=0; f<nf; f++) { + this.data[x + nx*(y + ny*z) + f * nx * ny * nz] = v; + } + } + } + } + } + + public HyperMatrix(int[] dims, double v) { + this(dims[0], dims[1], dims[2], dims[3],v); + } + + + public HyperMatrix(int[] dims) { + this(dims[0], dims[1], dims[2], dims[3]); + } + + public HyperMatrix(int[] dims, double[] v) { + this(dims[0], dims[1], dims[2], dims[3]); + this.data = v; + } + + public HyperMatrix copy() { + HyperMatrix other = new HyperMatrix(this.getDimensions()); + for (int x=0; x<this.nx; x++) { + for (int y=0; y<this.ny; y++) { + for (int z=0; z<this.nz;z++) { + for (int f=0; f<this.nf; f++) { + other.data[x + this.nx*(y + this.ny*z) + f * this.nx * this.ny * this.nz] = this.data[x + this.nx*(y + this.ny*z) + f * this.nx * this.ny * this.nz]; + } + } + } + } + return other; + } + + public static HyperMatrix ImagePlus2HyperMatrix(ImagePlus imp) { + int[] n = imp.getDimensions(); + HyperMatrix struct = new HyperMatrix(n[0], n[1], n[3], n[2] * n[4]); + int id = 0; + for (int c = 0; c < n[2]; c++) { + for (int f = 0; f < n[4]; f++) { + for (int z = 0; z < n[3]; z++) { + ImageProcessor ip = imp.getStack().getProcessor(c + z * n[2] + f * n[2] * n[3] + 1) + .convertToFloat(); + for (int x = 0; x < n[0]; x++) { + for (int y = 0; y < n[1]; y++) { + // struct.put(struct.ind2sub(x, y, z), id, ip.getPixelValue(x, y)); + struct.data[struct.ind2sub(x, y, z) + id * n[0] * n[1] * n[3]] = ip.getPixelValue(x, y); + } + } + } + id++; + } + } + return struct; + } + + public static ImagePlus HyperMatrix2ImagePlus(HyperMatrix struct) { + if (struct == null) + return null; + + ImageStack stack = new ImageStack(struct.nx, struct.ny); + for (int f = 0; f < struct.nf; f++) { + IntervalRange c = new IntervalRange(f, f + 1); + for (int z = 0; z < struct.nz; z++) { + IntervalRange r = new IntervalRange(struct.ind2sub(0, 0, z), struct.ind2sub(0, 0, z) + struct.nx * struct.ny); + ImageProcessor ip = new FloatProcessor(struct.nx, struct.ny, struct.get(r, c).data); + stack.addSlice(ip); + } + } + ImagePlus im = new ImagePlus("", stack); + im.setDimensions(1, struct.nz, struct.nf); + im.setOpenAsHyperStack(true); +// im=im.createHyperStack("",1,struct.nz,struct.nf,im.getBitDepth()); + return im; + } + + public int ind2sub(int x, int y, int z) { + return x + this.nx * y + this.nx * this.ny * z; + } + + public int[] sub2ind(int id) { + int[] out = new int[3]; + out[0] = id % this.nx; + out[1] = ((id - out[0]) / this.nx) % this.ny; + out[2] = (id - out[0] - this.nx * out[1]) / (this.nx * this.ny); + return out; + } + + public double getPixel(int x, int y, int z, int f) { + int id = this.ind2sub(x, y, z); + return this.get(id, f); + } + + public HyperMatrix getPatch(int[] center, int[] sz) { + String padd="zero"; // "symmetric" or "zero"; + // TODO: ajouter assert si sz n'est pas impaire + // TODO: condition de bord en argument ? + int[] halfSize=new int[4]; + halfSize[0] = (int) (sz[0]-1)/2; + halfSize[1] = (int) (sz[1]-1)/2; + HyperMatrix patch = null; + if (sz.length == 3) { + halfSize[2] = (int) (sz[2]-1)/2; + halfSize[3] = 0; + patch = new HyperMatrix(sz[0],sz[1],sz[2],this.nf); + }else if (sz.length == 4){ + halfSize[2] = (int) (sz[2]-1)/2; + halfSize[3] = sz[3]; + patch = new HyperMatrix(sz[0],sz[1],sz[2],sz[3]); + } else { + halfSize[2] = 0; + halfSize[3] = 0; + patch = new HyperMatrix(sz[0],sz[1],this.nz,this.nf); + } + int[] centerExt = null; + if (center.length == 2) { + centerExt = new int[] {center[0],center[1],0,0}; + } else if (center.length == 3) { + centerExt = new int[] {center[0],center[1],center[2],0}; + } else if (center.length == 4) { + centerExt = new int[] {center[0],center[1],center[2],center[3]}; + } + int[] topLeftExt = new int[4]; + for (int i=0; i<4; i++) { + topLeftExt[i] = centerExt[i]-halfSize[i]; + } + + int[] dims = patch.getDimensions(); + int xx, yy, zz, ff; + for (int x=0; x<dims[0]; x++) { + for (int y=0; y<dims[1]; y++) { + for (int z=0; z<dims[2];z++) { + for (int f=0; f<dims[3]; f++) { + int out = 0; + // This allow to extend the image with symmetric boundaries if the patch is on the border + if (padd=="symmetric") { + xx = Math.abs(x+topLeftExt[0]); if (xx>=this.nx) {xx=2*this.nx - xx-1;}; if (xx<0) {xx=1 - xx;} + yy = Math.abs(y+topLeftExt[1]); if (yy>=this.ny) {yy=2*this.ny - yy-1;}; if (yy<0) {yy=1 - yy;} + zz = Math.abs(z+topLeftExt[2]); if (zz>=this.nz) {zz=2*this.nz - zz-1;}; if (zz<0) {zz=1 - zz;} + ff = Math.abs(f+topLeftExt[3]); if (ff>=this.nf) {ff=2*this.nf - ff-1;}; if (ff<0) {ff=1 - ff;} + } else { + xx = Math.abs(x+topLeftExt[0]); if (xx>=this.nx) {out=1;}; if (xx<0) {out=1;} + yy = Math.abs(y+topLeftExt[1]); if (yy>=this.ny) {out=1;}; if (yy<0) {out=1;} + zz = Math.abs(z+topLeftExt[2]); if (zz>=this.nz) {out=1;}; if (zz<0) {out=1;} + ff = Math.abs(f+topLeftExt[3]); if (ff>=this.nf) {out=1;}; if (ff<0) {out=1;} + } + if (out == 1) { + patch.setPixel(x, y, z, f, 0); + } else { + patch.setPixel(x, y, z, f,getPixel(xx,yy,zz,ff)); + } + } + } + } + } + return patch; + } + + public void setPixel(int x, int y, int z, int f, double v) { + int id = this.ind2sub(x, y, z); + this.put(id, f, v); + } + + public int[] getDimensions() { + int[] dims = { this.nx, this.ny, this.nz, this.nf }; + return dims; + } + + public void imresize(int nx_new, int ny_new, int nz_new) { + // TODO: faire directement sans passer par ImagePlus. + int nx_ = this.nx; + int ny_ = this.ny; + int nz_ = this.nz; + double[] data_old = this.data; + this.resize(nx_new * ny_new * nz_new, this.nf); + this.nx = nx_new; + this.ny = ny_new; + this.nz = nz_new; + for (int f = 0; f < this.nf; f++) { + ImageStack stack = new ImageStack(nx_, ny_); + for (int z = 0; z < nz_; z++) { + double[] tmp = new double[nx_ * ny_]; + for (int l = 0; l < nx_ * ny_; l++) { + tmp[l] = data_old[z * nx_ * ny_ + f * nx_ * ny_ * nz_ + l]; + } + ImageProcessor ip = new FloatProcessor(nx_, ny_, tmp); + stack.addSlice(ip); + } + ImagePlus im = new ImagePlus("", stack); + im.setDimensions(1, this.nz, 1); + ImagePlus imresized = im.resize(nx_new, ny_new, nz_new, "bicubic"); + for (int z = 0; z < nz_new; z++) { + ImageProcessor ip = imresized.getStack().getProcessor(z + 1).convertToFloat(); + for (int x = 0; x < nx_new; x++) { + for (int y = 0; y < ny_new; y++) { + this.data[this.ind2sub(x, y, z) + f * nx_new * ny_new * nz_new] = ip.getPixelValue(x, y); + } + } + } + } + } + + + public void padding(int nx_new, int ny_new, int nz_new) { +// if (nx_new < this.nx || ny_new < this.ny || nz_new < this.nz) { +// System.out.println("Wrong size in padding, nothing done"); +// return; +// } + double[] data_old = this.data; + this.resize(nx_new * ny_new * nz_new, this.nf); // resize and put each entry to 0 + int x_st = (int) Math.round(((double) nx_new - this.nx) / 2.0); + int y_st = (int) Math.round(((double) ny_new - this.ny) / 2.0); + int z_st = (int) Math.round(((double) nz_new - this.nz) / 2.0); + if ((nz_new == 1) && (this.nz == 1)) + z_st = 0; + for (int f = 0; f < this.nf; f++) { + for (int x = 0; x < this.nx; x++) { + for (int y = 0; y < this.ny; y++) { + for (int z = 0; z < this.nz; z++) { + if ((x + x_st >= 0) && (y + y_st >= 0) && (z + z_st >= 0)) { + if ((x + x_st < nx_new) && (y + y_st < ny_new) && (z + z_st < nz_new)) { + this.data[(x + x_st) + nx_new * (y + y_st + ny_new * (z + z_st)) + + f * nx_new * ny_new * nz_new] = data_old[this.ind2sub(x, y, z) + + f * this.nx * this.ny * this.nz]; + } else { +// System.out.printf(" HERE.\n" ); + } + } + } + } + } + } + this.nx = nx_new; + this.ny = ny_new; + this.nz = nz_new; + } + + public void resample(double scale, int[] size) { + int nx_new = (int) Math.ceil(((double) size[0]) * scale); + nx_new += (nx_new + 1) % 2; + int ny_new = (int) Math.ceil(((double) size[1]) * scale); + ny_new += (ny_new + 1) % 2; + int nz_new = (int) Math.ceil(((double) size[2]) * scale); + nz_new += (nz_new + 1) % 2; + if (size[2] == 1) { + nz_new = 1; + } +// System.out.printf(" Size resample : "+nx_new+" -- "+ny_new+" -- "+nz_new+".\n"); + this.imresize(nx_new, ny_new, nz_new); + this.padding(size[0], size[1], size[2]); + +// this.padding(nx_new, ny_new, nz_new); +// this.imresize(size[0], size[1], size[2]); + } + + public HyperMatrix sub(HyperMatrix other) { + DoubleMatrix tmp = super.sub(other); + return new HyperMatrix(this.getDimensions(), tmp.data); + } + + public HyperMatrix add(HyperMatrix other) { + DoubleMatrix tmp = super.add(other); + return new HyperMatrix(this.getDimensions(), tmp.data); + } + + public HyperMatrix div(HyperMatrix other) { + DoubleMatrix tmp = super.div(other); + return new HyperMatrix(this.getDimensions(), tmp.data); + } + + public HyperMatrix div(double other) { + DoubleMatrix tmp = super.div(other); + return new HyperMatrix(this.getDimensions(), tmp.data); + } + + public HyperMatrix mul(HyperMatrix other) { + DoubleMatrix tmp = super.mul(other); + return new HyperMatrix(this.getDimensions(), tmp.data); + } + + public HyperMatrix mul(double other) { + DoubleMatrix tmp = super.mul(other); + return new HyperMatrix(this.getDimensions(), tmp.data); + } + + public HyperMatrix mmul(HyperMatrix other) { + DoubleMatrix tmp = super.mmul(other); + return new HyperMatrix(this.getDimensions(), tmp.data); + } + + public HyperMatrix abs() { + DoubleMatrix tmp = (DoubleMatrix) this.copy(); + MatrixFunctions.absi(tmp); + return new HyperMatrix(this.getDimensions(), tmp.data); + } + + public HyperMatrix max(double v) { + DoubleMatrix tmp = super.max(v); + return new HyperMatrix(this.getDimensions(), tmp.data); + } + + public HyperMatrix sqrt() { + DoubleMatrix tmp = (DoubleMatrix) this.copy(); + MatrixFunctions.sqrti(tmp); + return new HyperMatrix(this.getDimensions(), tmp.data); + } + + public HyperMatrix exp() { + DoubleMatrix tmp = (DoubleMatrix) this.copy(); + MatrixFunctions.expi(tmp); + return new HyperMatrix(this.getDimensions(), tmp.data); + } + + public HyperMatrix pow(int k) { + DoubleMatrix tmp = (DoubleMatrix) this.copy(); + MatrixFunctions.powi(tmp, k); + return new HyperMatrix(this.getDimensions(), tmp.data); + } + + + public HyperMatrix columnSums_() { + DoubleMatrix tmp = ((DoubleMatrix) this).columnSums(); + int[] dims = this.getDimensions(); + int [] dims_ = {dims[0],dims[1],dims[2],1}; + return new HyperMatrix(dims_,tmp.data); + } + + public HyperMatrix rowMaxs_() { + DoubleMatrix tmp = ((DoubleMatrix) this).rowMaxs(); + int[] dims = this.getDimensions(); + int [] dims_ = {dims[0],dims[1],dims[2],1}; + return new HyperMatrix(dims_,tmp.data); + } + + public int numel() { + return this.length; + } + + public HyperMatrix normalize() { + double pond; + if (Params.normalize == "l1") { + pond = this.norm1(); + } else if (Params.normalize == "l2") { + pond = this.norm2(); + } else { + pond = 1; + } + return this.div(pond); + } + + // return DoubleMatrix of size (N,d+1), weights are in first column + public DoubleMatrix find() { + int d; + if (nz==1) { + d=2; + } + else { + d = 3; + } + DoubleMatrix tmp = this.ge(1e-8); + int Nelements = (int) tmp.columnSums().rowSums().get(0, 0); + DoubleMatrix data = zeros(Nelements, d+1); +// weights = zeros(Nelements, 1); + int cpt = 0; + // cas nf!=1 non gere + for (int x = 0; x < nx; x++) { + for (int y = 0; y < ny; y++) { + for (int z = 0; z < nz; z++) { + for (int f = 0; f < nf; f++) { + double v = this.getPixel(x, y, z, f); + if (v > 0) { + data.put(cpt, 0, v); + data.put(cpt, 1, x); + data.put(cpt, 2, y); + if (d == 3) { + data.put(cpt, 3, z); + } +// System.out.printf(" Value found: "+v+" -- (x,y,z): "+ x + " -- " + y + " -- "+z+" !\n"); + cpt++; + } + } + } + } + } + return data; + } + + public HyperMatrix fftshift() { + int shiftx = (int) Math.ceil(((double) nx) / 2); + int shifty = (int) Math.ceil(((double) ny) / 2); + int shiftz = (int) Math.ceil(((double) nz) / 2); + HyperMatrix out = new HyperMatrix(this.getDimensions()); + for (int x = 0; x < nx; x++) { + for (int y = 0; y < ny; y++) { + for (int z = 0; z < nz; z++) { + for (int f = 0; f < nf; f++) { + double v = this.getPixel(x, y, z, f); + out.setPixel((x + shiftx) % nx, (y + shifty) % ny, (z + shiftz) % nz, f, v); + } + } + } + } + return out; + } + + public HyperMatrix ifftshift() { + int shiftx = (int) Math.ceil(((double) nx) / 2); + int shifty = (int) Math.ceil(((double) ny) / 2); + int shiftz = (int) Math.ceil(((double) nz) / 2); + HyperMatrix out = new HyperMatrix(this.getDimensions()); + for (int x = 0; x < nx; x++) { + for (int y = 0; y < ny; y++) { + for (int z = 0; z < nz; z++) { + for (int f = 1; f < nf; f++) { + double v = this.getPixel(x, y, z, f); + out.setPixel((x + shiftx) % nx, (y + shifty) % ny, (z + shiftz) % nz, f, v); + } + } + } + } + return out; + } + + public HyperMatrix conv(HyperMatrix other) { + // Only works for 2D and 3D HyperMatrix + // TODO: add check input size + HyperMatrix other_ = new HyperMatrix(other.getDimensions(),other.data); + if ((other.nx != this.nx) || (other.ny != this.ny) || (other.nz != this.nz)) { + // Faire une copie ici plutot ? + other_.padding(this.nx, this.ny, this.nz); +// ImagePlus tmpJ = HyperMatrix.HyperMatrix2ImagePlus(other_);tmpJ.setTitle("other_");tmpJ.show(); + } + other_=other_.fftshift(); + int[] dims = this.getDimensions(); + ComplexDoubleMatrix otherC = new ComplexDoubleMatrix(other_); + ComplexDoubleMatrix thisC = new ComplexDoubleMatrix(this); + if (this.nz == 1) { + DoubleFFT_2D fft = new DoubleFFT_2D(this.ny, this.nx); + fft.complexForward(otherC.data); + fft.complexForward(thisC.data); + otherC.muli(thisC); + fft.complexInverse(otherC.data, true); + } else { + DoubleFFT_3D fft = new DoubleFFT_3D(this.nz, this.ny, this.nx); + fft.complexForward(otherC.data); + fft.complexForward(thisC.data); + otherC.muli(thisC); + fft.complexInverse(otherC.data, true); + } + + return new HyperMatrix(dims, otherC.real().data); + } + + public static HyperMatrix[] meshgrid(DoubleMatrix[] lin) { + // Return an array of Hypermatrix where each element contains an Hypermatrix with the linspace in one dimension + int ndim = lin.length; + int[] dims = new int[4]; + HyperMatrix[] X = new HyperMatrix[ndim]; + int id = 0; + for (int k = 0; k < 4; k++) { + if (k < ndim) + dims[k] = lin[k].length; + else + dims[k] = 1; + } + for (int k = 0; k < ndim; k++) + X[k] = new HyperMatrix(dims); + + for (int x = 0; x < dims[0]; x++) { + for (int y = 0; y < dims[1]; y++) { + for (int z = 0; z < dims[2]; z++) { + for (int f = 0; f < dims[3]; f++) { + for (int k = 0; k < ndim; k++) { + switch (k) { + case 0: + id = x; + break; + case 1: + id = y; + break; + case 2: + id = z; + break; + case 3: + id = f; + break; + } + X[k].setPixel(x, y, z, f, lin[k].get(id)); + } + } + } + } + } + return X; + } + + public HyperMatrix localMax(int margin, double threshold) { + // TODO: raise error is margin <1 + int fmin = 0; + int fmax = nf; + int zmin = margin; + int zmax = nz - margin; + int ymin = margin; + int ymax = ny - margin; + int xmin = margin; + int xmax = nx - margin; + + int z_incr = 1; + if (nz == 1) { + zmin = 0; + zmax = 1; + z_incr = 0; + } + HyperMatrix loc = new HyperMatrix(this.getDimensions()); + for (int x = xmin; x < xmax; x++) { + for (int y = ymin; y < ymax; y++) { + for (int z = zmin; z < zmax; z++) { + for (int f = fmin; f < fmax; f++) { + double v = this.getPixel(x, y, z, f); + double max_loc = -1e6; + for (int zz = z - z_incr; zz <= z + z_incr; zz++) { + for (int ff = f - 1; ff <= f + 1; ff++) { + if ((ff < 0) || (ff >= nf)) { + max_loc = Math.max(max_loc, 0); + } else { + max_loc = Math.max(max_loc, this.getPixel(x - 1, y, zz, ff)); + max_loc = Math.max(max_loc, this.getPixel(x + 1, y, zz, ff)); + max_loc = Math.max(max_loc, this.getPixel(x, y - 1, zz, ff)); + max_loc = Math.max(max_loc, this.getPixel(x, y + 1, zz, ff)); + max_loc = Math.max(max_loc, this.getPixel(x - 1, y - 1, zz, ff)); + max_loc = Math.max(max_loc, this.getPixel(x - 1, y + 1, zz, ff)); + max_loc = Math.max(max_loc, this.getPixel(x + 1, y - 1, zz, ff)); + max_loc = Math.max(max_loc, this.getPixel(x + 1, y + 1, zz, ff)); + if ((ff != f) || (zz != z)) { + max_loc = Math.max(max_loc, this.getPixel(x, y, zz, ff)); + } + } + } + } + if ((max_loc<=v) && (v>=threshold)) { + loc.setPixel(x, y, z, f, v); + } + } + } + } + } + return loc; + } + + // add image to listOfImages, usefull to automatically close the opened figures + public void display(String title, ArrayList<ImagePlus> listOfImages) { + ImagePlus bJ = HyperMatrix.HyperMatrix2ImagePlus(this); + bJ.setTitle(title); + bJ.show(); + if (listOfImages != null) + listOfImages.add(bJ); + } + + public void display(ArrayList<ImagePlus> listOfImages) { + this.display(" ", listOfImages); + } + + public HyperMatrix randomSVD(int K, double[] sValues) { + /* + * Reproduce Matlab implementation of the Random implementation proposed by A. Liutkus. + + Extremely fast computation of the truncated Singular Value Decomposition, using + randomized algorithms as described in Halko et al. 'Finding structure with randomness: + Probabilistic algorithms for constructing approximate matrix decompositions' + Input: + * this : matrix whose SVD we want + * K : number of components to keep + Output: + * U: left eigen vectors + * sValues: singular values + ------------------------------------------------------------------------------------ + Antoine Liutkus (c) Inria 2014 + */ + int P = Math.min(10*K, this.nf); + DoubleMatrix X = new DoubleMatrix(this.nf,P); + Random rnd = new Random(); + for (int x=0; x<this.nf; x++) { + for (int y=0; y<P; y++) { + X.put(x, y, rnd.nextGaussian()); + } + } + DoubleMatrix AA=new DoubleMatrix(this.nx*this.ny*this.nz,this.nf,this.data); + DoubleMatrix Y = AA.mmul(X); + DoubleMatrix[] tmp = Singular.sparseSVD(Y); + DoubleMatrix W1 = tmp[0]; + DoubleMatrix B = (W1.transpose()).mmul(AA); + DoubleMatrix[] svdB = Singular.sparseSVD(B); + DoubleMatrix U =W1.mmul(svdB[0]); + K=Math.min(K, U.columns); + + DoubleMatrix S=svdB[1]; +// DoubleMatrix V=svdB[2]; +// double Ssum = 0; +// for (int k=0; k<K; k++) { +// Ssum += S.get(k,0); +// } + for (int k=0; k<K; k++) { + sValues[k]=S.get(k,0); + } + HyperMatrix Uout = new HyperMatrix(this.nx,this.ny,this.nz,K,U.data); + return Uout; + } + + public void setColumn(HyperMatrix A, int f_this, int f_A) { + for (int x=0; x<this.nx; x++) { + for (int y=0; y<this.ny; y++) { + for (int z=0; z<this.nz; z++) { + this.setPixel(x, y, z, f_this, A.getPixel(x, y, z, f_A)); + } + } + } + } + + + // Images must be in Path+"k.tif", for k in [0,K-1] + public void load3D(String Path,int K) { + + ImagePlus tmp = IJ.openImage(Path+"0.tif"); + int[] dim_tmp=tmp.getDimensions(); + nx=dim_tmp[0]; + ny=dim_tmp[1]; + nz=dim_tmp[3]; + nf=K; + ImageStack stack = new ImageStack(nx,ny); + for (int f = 0; f < K; f++) { + tmp = IJ.openImage(Path + f + ".tif"); + for (int z = 0; z < nz; z++) { + ImageProcessor ip = tmp.getStack().getProcessor(z + 1).convertToFloat(); + stack.addSlice(ip); + } + } + ImagePlus imIJ = new ImagePlus("", stack); + imIJ.setDimensions(1, nz, K); + HyperMatrix tmp_ = HyperMatrix.ImagePlus2HyperMatrix(imIJ); + this.data = tmp_.data; + } + + // Images must be in Path+"k.tif", for k in [0,K-1] + public void load2D(String Path,int K) { + HyperMatrix PSFs_ = HyperMatrix.ImagePlus2HyperMatrix(IJ.openImage(Path +0+".tif")); + nx = PSFs_.nx; + ny = PSFs_.ny; + nz = PSFs_.nz; + nf = K; + this.data = new double[nx*ny*nz*nf]; + for (int k=0; k<K; k++) { + PSFs_ = HyperMatrix.ImagePlus2HyperMatrix(IJ.openImage(Path +k+".tif")); + this.setColumn(PSFs_, k, 0); + } + } + + public HyperMatrix imtranslate(double x, double y, double z) { + int[] dims = this.getDimensions(); + HyperMatrix translated = this.copy(); + // Make translation + for (int ix=0; ix<dims[0]; ix++) { + for (int iy=0; iy<dims[1]; iy++) { + for (int iz=0; iz<dims[2]; iz++) { + translated.data[this.ind2sub(ix, iy, iz)] = getInterpolatedValue(ix - x, iy - y, iz - z); + } + } + } + return translated; + } + + //================================================================================ + // Protected Methods (Cubic-conv interp) + //================================================================================ + protected double CubicConvolutionInterpolant(double x){ + // Evaluate cubic convolution kernel at x. + double ax=Math.abs(x); + double v; + if (ax>=2){ + v = 0.0; + } + else if ((ax<2) && (ax>=1)){ + v = -(1.0/2.0)*ax*ax*ax+(5.0/2.0)*ax*ax-4*ax+2.0; + } + else { + v = (3.0/2.0)*ax*ax*ax-(5.0/2.0)*ax*ax+1; + } + return v; + } + + protected double Interpol4(double[] V, double x){ + // Given the four nodes of interpolation, evaluate interpolated value at x. + double res = 0; + for (int i=0;i<4;++i){ + res+=CubicConvolutionInterpolant(x+1-i)*V[i]; + } + return res; + } + + protected double getInterpolatedValue(double x, double y, double z) { + // Given sampling points E in d dimension, centered in X0, evaluate the + // value at point X. The interpolation is based on cubic convolution. + + HyperMatrix V = new HyperMatrix(4,4,4,1,0.0); + int xfl = (int) Math.floor(x); + int yfl = (int) Math.floor(y); + int zfl = (int) Math.floor(z); + for (int dx=-1;dx<=2;dx++){ + for (int dy=-1;dy<=2;dy++){ + for (int dz=-1;dz<=2;dz++){ + int ix=xfl+dx; + int iy=yfl+dy; + int iz=zfl+dz; + if ((ix>=0) && (ix<this.nx) && (iy>=0) && (iy<this.ny) && (iz>=0) && (iz<this.nz)){ + V.setPixel(dx+1,dy+1,dz+1,0,this.getPixel(ix,iy,iz,0)); + } + } + } + } + + double[] vv = new double[4]; + double[] vvv = new double[4]; + double[] vals = new double[4]; + for (int jz=0;jz<4;++jz){ + for (int jy=0;jy<4;++jy){ + for (int jx=0;jx<4;++jx){ + vals[jx]=V.getPixel(jx,jy,jz,0); + } + vv[jy]=Interpol4(vals,x-xfl); + } + vvv[jz]=Interpol4(vv,y-yfl); + } + + return Interpol4(vvv,z-zfl); + } + //================================================================================ + +} diff --git a/java code/src/eigenpsf/display/ColorName.java b/java code/src/eigenpsf/display/ColorName.java new file mode 100644 index 0000000000000000000000000000000000000000..dfeac0e7778f16c824aecc3f24feb60a7b0f4427 --- /dev/null +++ b/java code/src/eigenpsf/display/ColorName.java @@ -0,0 +1,56 @@ +package eigenpsf.display; + +import java.awt.Color; +import javax.swing.JComboBox; + + +public class ColorName { + + private String[] names = { + "Maroon", "Red", "Purple", "Fuchsia", + "Green", "Lime", "Olive", "Yellow", "Navy", "Blue", + "Teal", "Aqua", "Black", "Silver", "Gray", "White" }; + + private Color[] colors = { new Color(0x80, 0x00, 0x00), new Color(0xFF, 0x00, 0x00), + new Color(0x80, 0x00, 0x80), new Color(0xFF, 0x00, 0xFF), new Color(0x00, 0x80, 0x00), + new Color(0x00, 0xFF, 0x00), new Color(0x80, 0x80, 0x00), new Color(0xFF, 0xFF, 0x00), + new Color(0x00, 0x00, 0x80), new Color(0x00, 0x00, 0xFF), new Color(0x00, 0x80, 0x80), + new Color(0x00, 0xFF, 0xFF), new Color(0x00, 0x00, 0x00), new Color(0xC0, 0xC0, 0xC0), + new Color(0x80, 0x80, 0x80), new Color(0xFF, 0xFF, 0xFF), new Color(0xFF, 0xF0, 0xFF) }; + + public String getName(Color c) { + for (int i = 0; i < names.length; i++) { + if (c.getRed() == colors[i].getRed()) + if (c.getGreen() == colors[i].getGreen()) + if (c.getBlue() == colors[i].getBlue()) + return names[i]; + } + return "unknown"; + } + + public Color getColor(String name) { + for (int i = 0; i < names.length; i++) + if (names[i].equals(name)) + return colors[i]; + return colors[0]; + } + + public JComboBox<String> createComboBox() { + JComboBox<String> cmb = new JComboBox<String>(); + for (String name : names) + cmb.addItem(name); + return cmb; + } + + public Color inverse(Color c) { + return new Color(255 - c.getRed(), 255 - c.getGreen(), 255 - c.getBlue()); + } + + public Color inverse(Color c, int opacity) { + return new Color(255 - c.getRed(), 255 - c.getGreen(), 255 - c.getBlue(), opacity); + } + + public static Color opacify(Color c, int opacity) { + return new Color(c.getRed(), c.getGreen(), c.getBlue(), opacity); + } +} diff --git a/java code/src/eigenpsf/display/Display.java b/java code/src/eigenpsf/display/Display.java new file mode 100644 index 0000000000000000000000000000000000000000..d31b0c4a3bcf9dfff2021f904c09050fde0d8b1d --- /dev/null +++ b/java code/src/eigenpsf/display/Display.java @@ -0,0 +1,17 @@ +package eigenpsf.display; + +import java.awt.Color; + +public class Display { + + static public DisplayElement ring = new DisplayElement(true, 1, 100, "4 rings", Color.cyan); + static public DisplayElement text = new DisplayElement(true, 11, 200, "ID", Color.yellow); + static public DisplayElement box = new DisplayElement(true, 1, 200, "Plain", Color.green); + static public DisplayElement boxun = new DisplayElement(true, 1, 200, "Plain", Color.red); + + static public DisplayElement ring_setting = new DisplayElement(true, 1, 100, "4 rings, 10 rings, 2 rings, 1 rings", Color.cyan); + static public DisplayElement text_setting = new DisplayElement(true, 11, 200, "ID, Distance, Maximum, Z-test, X, Y, Z", Color.yellow); + static public DisplayElement box_setting = new DisplayElement(true, 1, 200, "Plain, Dotted", Color.green); + static public DisplayElement boxun_setting = new DisplayElement(true, 1, 200, "Plain, Dotted", Color.red); + +} diff --git a/java code/src/eigenpsf/display/DisplayDialog.java b/java code/src/eigenpsf/display/DisplayDialog.java new file mode 100644 index 0000000000000000000000000000000000000000..7923738260dd7565bc400700206e9229580c32e5 --- /dev/null +++ b/java code/src/eigenpsf/display/DisplayDialog.java @@ -0,0 +1,244 @@ +package eigenpsf.display; + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.border.EtchedBorder; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import bilib.commons.components.GridPanel; +import bilib.commons.components.SpinnerRangeInteger; +import eigenpsf.Params; +import ij.gui.GUI; +import ij.gui.ImageCanvas; + +public class DisplayDialog extends JDialog implements ActionListener, ItemListener, ChangeListener { + + private SpinnerRangeInteger size = new SpinnerRangeInteger(1, 1, 300, 1); + private JCheckBox showRing = new JCheckBox("Circular rings", true); + private SpinnerRangeInteger sizeRing = new SpinnerRangeInteger(1, 1, 9999, 1); + private SpinnerRangeInteger opacityRing = new SpinnerRangeInteger(100, 0, 255, 1); + private JComboBox<String> configRing = new JComboBox<String>(); + private ColorName colorRing = new ColorName(); + + private JCheckBox showText = new JCheckBox("Value", true); + private SpinnerRangeInteger sizeText = new SpinnerRangeInteger(11, 1, 9999, 1); + private SpinnerRangeInteger opacityText = new SpinnerRangeInteger(100, 0, 255, 1); + private JComboBox<String> configText = new JComboBox<String>(); + private ColorName colorText = new ColorName(); + + private JCheckBox showBox = new JCheckBox("Box Selected", true); + private SpinnerRangeInteger sizeBox = new SpinnerRangeInteger(11, 1, 9999, 1); + private SpinnerRangeInteger opacityBox = new SpinnerRangeInteger(100, 0, 255, 1); + private JComboBox<String> configBox = new JComboBox<String>(); + private ColorName colorBox = new ColorName(); + + private JCheckBox showBoxun = new JCheckBox("Box Unselected", true); + private SpinnerRangeInteger sizeBoxun = new SpinnerRangeInteger(11, 1, 9999, 1); + private SpinnerRangeInteger opacityBoxun = new SpinnerRangeInteger(100, 0, 255, 1); + private JComboBox<String> configBoxun = new JComboBox<String>(); + private ColorName colorBoxun = new ColorName(); + + private JComboBox<String> cmbColorRing; + private JComboBox<String> cmbColorText; + private JComboBox<String> cmbColorBox; + private JComboBox<String> cmbColorBoxun; + + private JButton bnClose = new JButton("Close"); + private ImageCanvas canvas; + + public DisplayDialog(ImageCanvas canvas) { + super(new JFrame(), "Display Controller"); + + cmbColorRing = colorRing.createComboBox(); + cmbColorText = colorText.createComboBox(); + cmbColorBox = colorBox.createComboBox(); + cmbColorBoxun = colorBoxun.createComboBox(); + + this.canvas = canvas; + setLayout(new BorderLayout()); + + setParams(); + GridPanel pn = new GridPanel(false); + + pn.place(0, 0, "Size"); + pn.place(0, 1, size); + + pn.place(1, 0, "Element"); + pn.place(1, 1, "Configuration"); + pn.place(1, 2, "Stroke/Font"); + pn.place(1, 3, "Opacity"); + pn.place(1, 4, "Color"); + + pn.place(2, 0, showRing); + pn.place(2, 1, configRing); + pn.place(2, 2, sizeRing); + pn.place(2, 3, opacityRing); + pn.place(2, 4, cmbColorRing); + + pn.place(3, 0, showText); + pn.place(3, 1, configText); + pn.place(3, 2, sizeText); + pn.place(3, 3, opacityText); + pn.place(3, 4, cmbColorText); + + pn.place(4, 0, showBox); + pn.place(4, 1, configBox); + pn.place(4, 2, sizeBox); + pn.place(4, 3, opacityBox); + pn.place(4, 4, cmbColorBox); + + pn.place(5, 0, showBoxun); + pn.place(5, 1, configBoxun); + pn.place(5, 2, sizeBoxun); + pn.place(5, 3, opacityBoxun); + pn.place(5, 4, cmbColorBoxun); + + pn.setBorder(BorderFactory.createCompoundBorder(new EtchedBorder(EtchedBorder.LOWERED), + BorderFactory.createEmptyBorder(8, 8, 8, 8))); + setLayout(new BorderLayout()); + add(pn, BorderLayout.CENTER); + add(bnClose, BorderLayout.SOUTH); + + showRing.addChangeListener(this); + configRing.addActionListener(this); + sizeRing.addChangeListener(this); + opacityRing.addChangeListener(this); + cmbColorRing.addItemListener(this); + + showText.addChangeListener(this); + configText.addActionListener(this); + sizeText.addChangeListener(this); + opacityText.addChangeListener(this); + cmbColorText.addItemListener(this); + + showBox.addChangeListener(this); + configBox.addActionListener(this); + sizeBox.addChangeListener(this); + opacityBox.addChangeListener(this); + cmbColorBox.addItemListener(this); + + showBoxun.addChangeListener(this); + configBoxun.addActionListener(this); + sizeBoxun.addChangeListener(this); + opacityBoxun.addChangeListener(this); + cmbColorBoxun.addItemListener(this); + + bnClose.addActionListener(this); + + this.pack(); + this.setModal(true); + GUI.center(this); + this.setVisible(true); + repaint(); + } + + + @Override + public void stateChanged(ChangeEvent e) { + getParams(); + if (canvas!=null) + canvas.repaint(); + } + + @Override + public void itemStateChanged(ItemEvent e) { + getParams(); + if (canvas!=null) + canvas.repaint(); + } + + @Override + public void actionPerformed(ActionEvent e) { + getParams(); + if (canvas!=null) + canvas.repaint(); + if (e.getSource() == bnClose) + dispose(); + } + + private void getParams() { + Params.psf_visible_width[0] = size.get(); + Params.psf_visible_width[1] = size.get(); + + Display.ring.show = showRing.isSelected(); + Display.ring.size = sizeRing.get(); + Display.ring.opacity = opacityRing.get(); + Display.ring.config = (String)configRing.getSelectedItem(); + Display.ring.color = colorRing.getColor((String)cmbColorRing.getSelectedItem()); + + Display.text.show = showText.isSelected(); + Display.text.size = sizeText.get(); + Display.text.opacity= opacityText.get(); + Display.text.config = (String)configText.getSelectedItem(); + Display.text.color = colorText.getColor((String)cmbColorText.getSelectedItem()); + + Display.box.show = showBox.isSelected(); + Display.box.size = sizeBox.get(); + Display.box.opacity= opacityBox.get(); + Display.box.config = (String)configBox.getSelectedItem(); + Display.box.color = colorBox.getColor((String)cmbColorBox.getSelectedItem()); + + Display.boxun.show = showBoxun.isSelected(); + Display.boxun.size = sizeBoxun.get(); + Display.boxun.opacity= opacityBoxun.get(); + Display.boxun.config = (String)configBoxun.getSelectedItem(); + Display.boxun.color = colorBoxun.getColor((String)cmbColorBoxun.getSelectedItem()); + + } + + private void setParams() { + size.set(Params.psf_visible_width[0]); + String[] cr = Display.ring_setting.config.split(","); + for(String item : cr) + configRing.addItem(item.trim()); + + String[] ci = Display.text_setting.config.split(","); + for(String item : ci) + configText.addItem(item.trim()); + + String[] cb = Display.box_setting.config.split(","); + for(String item : cb) + configBox.addItem(item.trim()); + + String[] cu = Display.boxun_setting.config.split(","); + for(String item : cu) + configBoxun.addItem(item.trim()); + + configRing.setSelectedItem(Display.ring.config); + showRing.setSelected(Display.ring.show); + sizeRing.set(Display.ring.size); + opacityRing.set(Display.ring.opacity); + cmbColorRing.setSelectedItem(colorRing.getName(Display.ring.color)); + + configText.setSelectedItem(Display.text.config); + showText.setSelected(Display.text.show); + sizeText.set(Display.text.size); + opacityText.set(Display.text.opacity); + cmbColorText.setSelectedItem(colorText.getName(Display.text.color)); + + configBox.setSelectedItem(Display.box.config); + showBox.setSelected(Display.box.show); + sizeBox.set(Display.box.size); + opacityBox.set(Display.box.opacity); + cmbColorBox.setSelectedItem(colorBox.getName(Display.box.color)); + + configBoxun.setSelectedItem(Display.boxun.config); + showBoxun.setSelected(Display.boxun.show); + sizeBoxun.set(Display.boxun.size); + opacityBoxun.set(Display.boxun.opacity); + cmbColorBoxun.setSelectedItem(colorBox.getName(Display.boxun.color)); + + } + +} diff --git a/java code/src/eigenpsf/display/DisplayElement.java b/java code/src/eigenpsf/display/DisplayElement.java new file mode 100644 index 0000000000000000000000000000000000000000..cd1690ca82ffc1707b1c4149d9429563e860ad75 --- /dev/null +++ b/java code/src/eigenpsf/display/DisplayElement.java @@ -0,0 +1,25 @@ +package eigenpsf.display; + +import java.awt.Color; + +public class DisplayElement { + + public boolean show = true; + public int opacity = 100; + public int size = 1; + public String config = ""; // comma-separated + public Color color = Color.red; + + public DisplayElement(boolean show, int size, int opacity, String config, Color color) { + this.show = show; + this.size = size; + this.opacity = opacity; + this.config = config; + this.color = color; + } + + public Color getColor() { + return new Color(color.getRed(), color.getGreen(), color.getBlue(), opacity); + + } +} diff --git a/java code/src/eigenpsf/display/ProgressStatusBar.java b/java code/src/eigenpsf/display/ProgressStatusBar.java new file mode 100644 index 0000000000000000000000000000000000000000..f8b9916c2d31a9b4c89ebaf4cfbc9d076d24b8cb --- /dev/null +++ b/java code/src/eigenpsf/display/ProgressStatusBar.java @@ -0,0 +1,152 @@ +package eigenpsf.display; + +import java.awt.Dimension; + +import javax.swing.JProgressBar; +import javax.swing.JToolBar; +import javax.swing.SwingUtilities; + +/** + * This class extends the JToolbar of Java to create a status bar + * including some of the following component ProgressBar, Help Button + * About Button and Close Button + * + * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland. + * + */ +public class ProgressStatusBar extends JToolBar { + + private JProgressBar progress = new JProgressBar(); + private double chrono; + + /** + * Class SetValue in the swing thread. + */ + private static class SetValue implements Runnable { + private int value; + private JProgressBar progress; + public SetValue(JProgressBar progress, int value) { + this.progress = progress; + this.value = value; + } + public void run() { + progress.setValue(value); + } + } + + /** + * Class IncValue in the swing thread. + */ + private static class IncValue implements Runnable { + private double inc; + private JProgressBar progress; + public IncValue(JProgressBar progress, double inc) { + this.progress = progress; + this.inc = inc; + } + public void run() { + progress.setValue((int)Math.round(progress.getValue()+inc)); + } + } + + /** + * Class SetMessage in the swing thread. + */ + private static class SetMessage implements Runnable { + private String msg; + private JProgressBar progress; + public SetMessage(JProgressBar progress, String msg) { + this.progress = progress; + this.msg = msg; + } + public void run() { + progress.setString(msg); + } + } + + /** + * Constructor. + */ + public ProgressStatusBar(String initialMessage) { + super(initialMessage); + add(progress); + progress.setStringPainted(true); + progress.setString(initialMessage); + progress.setMinimum(0); + progress.setMaximum(100); + progress.setPreferredSize(new Dimension(100, 20)); + + setFloatable(false); + setRollover(true); + setBorderPainted(false); + chrono = System.currentTimeMillis(); + } + + + /** + * Set a value and a message in the progress bar. + */ + public void progress(String msg, int value) { + double elapsedTime = System.currentTimeMillis() - chrono; + String t = " [" + (elapsedTime > 3000 ? Math.round(elapsedTime/10)/100.0 + "s." : elapsedTime + "ms") + "]"; + SwingUtilities.invokeLater(new SetValue(progress, value)); + SwingUtilities.invokeLater(new SetMessage(progress, msg+t)); + } + + /** + * Set a value and a message in the progress bar. + */ + public void increment(double inc) { + SwingUtilities.invokeLater(new IncValue(progress, inc)); + } + + /** + * Set a value in the progress bar. + */ + public void setValue(int value) { + SwingUtilities.invokeLater(new SetValue(progress, value)); + } + + /** + * Set a message in the progress bar. + */ + public void setMessage(String msg) { + SwingUtilities.invokeLater(new SetMessage(progress, msg)); + } + + + /** + * Set a value and a message in the progress bar. + */ + public void progress(String msg, double value) { + progress(msg, (int)Math.round(value)); + } + + /** + * Set to 0 the progress bar. + */ + public void reset() { + chrono = System.currentTimeMillis(); + progress("Start", 0); + } + + /** + * Set to 100 the progress bar. + */ + public void finish() { + progress("End", 100); + } + + /** + * Set to 100 the progress bar with an additional message. + */ + public void finish(String msg) { + progress(msg, 100); + } + + +} + + + + diff --git a/java code/src/eigenpsf/filemanager/IO.java b/java code/src/eigenpsf/filemanager/IO.java new file mode 100644 index 0000000000000000000000000000000000000000..ef91529a0c5d9e519a27340b7665df434ee22dc5 --- /dev/null +++ b/java code/src/eigenpsf/filemanager/IO.java @@ -0,0 +1,92 @@ +package eigenpsf.filemanager; + +import java.io.File; + +import javax.swing.JFileChooser; +import javax.swing.JFrame; + +import ij.IJ; +import ij.ImagePlus; +import ij.ImageStack; + +public class IO { + + static public ImagePlus openStack(String path) { + File file = new File(path); + if (file.isDirectory()) { + String list[] = file.list(); + ImageStack stack = null; + for (String name : list) + if (!name.startsWith(".")) { + ImagePlus imp = IJ.openImage(path + File.separator + name); + if (imp != null) { + int nx = imp.getWidth(); + int ny = imp.getHeight(); + if (stack == null) + stack = new ImageStack(nx, ny); + if (stack.getWidth() == nx && stack.getHeight() == ny) + stack.addSlice(imp.getProcessor()); + } + } + return new ImagePlus(file.getName(), stack); + } + else { + return IJ.openImage(path); + } + + } + static public String browseSaveProject(String initialFilename) { + JFileChooser chooser = new JFileChooser(initialFilename); + chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + chooser.setDialogTitle("Save a project"); + int ret = chooser.showSaveDialog(new JFrame()); + if (ret == JFileChooser.APPROVE_OPTION) + return chooser.getSelectedFile().getAbsolutePath(); + else + return initialFilename; + } + + static public String browseOpen(String initialFilename) { + JFileChooser chooser = new JFileChooser(initialFilename); + chooser.setDialogTitle("Open images"); + chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); + int ret = chooser.showOpenDialog(new JFrame()); + if (ret == JFileChooser.APPROVE_OPTION) + return chooser.getSelectedFile().getAbsolutePath(); + else + return initialFilename; + } + + static public String browseOpenFile(String initialFilename) { + JFileChooser chooser = new JFileChooser(initialFilename); + chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + chooser.setDialogTitle("Open a project"); + int ret = chooser.showOpenDialog(new JFrame()); + if (ret == JFileChooser.APPROVE_OPTION) + return chooser.getSelectedFile().getAbsolutePath(); + else + return initialFilename; + } + + static public String browseOpenProject(String initialFilename) { + JFileChooser chooser = new JFileChooser(initialFilename); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + chooser.setDialogTitle("Open a project"); + int ret = chooser.showOpenDialog(new JFrame()); + if (ret == JFileChooser.APPROVE_OPTION) + return chooser.getSelectedFile().getAbsolutePath(); + else + return initialFilename; + } + + + static public String browseOpenDir(String initialFilename) { + JFileChooser chooser = new JFileChooser(initialFilename); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + int ret = chooser.showOpenDialog(new JFrame()); + if (ret == JFileChooser.APPROVE_OPTION) + return chooser.getSelectedFile().getAbsolutePath(); + else + return initialFilename; + } +} diff --git a/java code/src/eigenpsf/gui/ButtonIcon.java b/java code/src/eigenpsf/gui/ButtonIcon.java new file mode 100644 index 0000000000000000000000000000000000000000..e0a9a21a5b891acd74f546fb2ec694d0d68caba9 --- /dev/null +++ b/java code/src/eigenpsf/gui/ButtonIcon.java @@ -0,0 +1,82 @@ +/* + * VirtualSMLM + * + * Conditions of use: You are free to use this software for research or + * educational purposes. In addition, we expect you to include adequate + * citations and acknowledgments whenever you present or publish results that + * are based on it. + * + * Reference: Virtual microscope for real time interactive SMLM acquisition + * Juliette Griffie, Robin Lang, Daniel Sage + * https://github.com/juliette-griffie/Virtual-SMLM + */ + +/* + * Copyright 2019-2020 EPFL. + * + * This file is part of VirtualSMLM. + * + * VirtualSMLM is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * VirtualSMLM is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * VirtualSMLM. If not, see <http://www.gnu.org/licenses/>. + */ + +package eigenpsf.gui; + +import java.awt.Image; +import java.awt.image.RescaleOp; + +import javax.swing.BorderFactory; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.border.EtchedBorder; + +import resources.IconLoader; + +public class ButtonIcon extends JButton { + + private ImageIcon icon; + private String name; + + public ButtonIcon(String name, String tooltip) { + super(); + this.name = name; + setBorder(BorderFactory.createCompoundBorder(new EtchedBorder(EtchedBorder.LOWERED), + BorderFactory.createEmptyBorder(5, 5, 5, 5))); + + setToolTipText(tooltip); + + try { + Image image = new IconLoader().get("/resources/" +name + ".png"); + Image scaled = image.getScaledInstance(24, 24, Image.SCALE_SMOOTH); + this.icon = new ImageIcon(scaled); + this.setIcon(this.icon); + } + catch (Exception ex) { + setText(name); + } + } + + public void toggle(boolean b) { + try { + Image image = new IconLoader().get("/resources/" +name + (b ? "" : "_off") + ".png"); + Image scaled = image.getScaledInstance(24, 24, Image.SCALE_SMOOTH); + this.icon = new ImageIcon(scaled); + this.setIcon(this.icon); + } + catch (Exception ex) { + setText(name); + } + } + + public void init() { + this.setIcon(this.icon); + } +} diff --git a/java code/src/eigenpsf/gui/SpinnerInfoRangeDouble.java b/java code/src/eigenpsf/gui/SpinnerInfoRangeDouble.java new file mode 100644 index 0000000000000000000000000000000000000000..b47815fd9887a706d3f7abe60bc6854099acc28d --- /dev/null +++ b/java code/src/eigenpsf/gui/SpinnerInfoRangeDouble.java @@ -0,0 +1,27 @@ +package eigenpsf.gui; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; + +import bilib.commons.components.SpinnerRangeDouble; +import bilib.commons.utils.WebBrowser; + +public class SpinnerInfoRangeDouble extends SpinnerRangeDouble implements ActionListener { + public JButton info = new JButton("\u24D8"); + private String url; + public SpinnerInfoRangeDouble(double def, double min, double max, double inc, String tip, String url) { + super(def, min, max, inc); + this.url = url; + //setToolTipText(tip); + info.setToolTipText(tip); + info.addActionListener(this); + } + + + @Override + public void actionPerformed(ActionEvent e) { + WebBrowser.open(url); + } +} diff --git a/java code/src/eigenpsf/gui/SpinnerInfoRangeInteger.java b/java code/src/eigenpsf/gui/SpinnerInfoRangeInteger.java new file mode 100644 index 0000000000000000000000000000000000000000..8ec2d2edbeadb9939682c5d253f2d091a63f2b72 --- /dev/null +++ b/java code/src/eigenpsf/gui/SpinnerInfoRangeInteger.java @@ -0,0 +1,25 @@ +package eigenpsf.gui; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; + +import bilib.commons.components.SpinnerRangeInteger; +import bilib.commons.utils.WebBrowser; + +public class SpinnerInfoRangeInteger extends SpinnerRangeInteger implements ActionListener { + public JButton info = new JButton("\u24D8"); + private String url; + public SpinnerInfoRangeInteger(int def, int min, int max, int inc, String tip, String url) { + super(def, min, max, inc); + this.url = url; + //setToolTipText(tip); + info.setToolTipText(tip); + info.addActionListener(this); + } + @Override + public void actionPerformed(ActionEvent e) { + WebBrowser.open(url); + } +} \ No newline at end of file diff --git a/java code/src/eigenpsf/gui/WalkBar.java b/java code/src/eigenpsf/gui/WalkBar.java new file mode 100644 index 0000000000000000000000000000000000000000..09ed781062af140b7acdd5a71dadc307a22c56c8 --- /dev/null +++ b/java code/src/eigenpsf/gui/WalkBar.java @@ -0,0 +1,102 @@ +package eigenpsf.gui; + +import java.awt.Dimension; +import java.awt.Font; + +import javax.swing.JProgressBar; +import javax.swing.JToolBar; +import eigenpsf.Constants; + +/** + * This class extends the JToolbar of Java to create a status bar + * including some of the following component ProgressBar, Help Button + * About Button and Close Button + * + * @author Daniel Sage, Biomedical Imaging Group, EPFL, Lausanne, Switzerland. + * + */ +public class WalkBar extends JToolBar { + + private JProgressBar progress = new JProgressBar(); + private double chrono; + + /** + * Constructor. + */ + public WalkBar(String initialMessage, int size) { + super("Walk Bar"); + add(progress); + + + progress.setStringPainted(true); + progress.setString(initialMessage); + progress.setFont(new Font("Arial", Font.PLAIN, 10)); + progress.setMinimum(0); + progress.setMaximum(100); + progress.setPreferredSize(new Dimension(size, 20)); + + + setFloatable(false); + setRollover(true); + setBorderPainted(false); + chrono = System.currentTimeMillis(); + } + + /** + * Set a value in the progress bar. + */ + public void setValue(int value) { + progress.setValue(value); + } + + /** + * Set a message in the progress bar. + */ + public void setMessage(String msg) { + progress.setString(msg); + } + + /** + * Set a value and a message in the progress bar. + */ + public void progress(String msg, int value) { + progress.setValue(value); + double elapsedTime = System.currentTimeMillis() - chrono; + String t = " [" + (elapsedTime > 3000 ? Math.round(elapsedTime/10)/100.0 + "s." : elapsedTime + "ms") + "]"; + progress.setString(msg + t); + } + + /** + * Set a value and a message in the progress bar. + */ + public void progress(String msg, double value) { + progress(msg, (int)Math.round(value)); + } + + /** + * Set to 0 the progress bar. + */ + public void reset() { + chrono = System.currentTimeMillis(); + progress.setValue(0); + progress.setString(Constants.copyright); + } + + /** + * Set to 100 the progress bar. + */ + public void finish() { + progress("Terminated", 100); + } + + /** + * Set to 100 the progress bar with an additional message. + */ + public void finish(String msg) { + progress(msg, 100); + } +} + + + + diff --git a/java code/src/eigenpsf/lib/Loader.java b/java code/src/eigenpsf/lib/Loader.java new file mode 100644 index 0000000000000000000000000000000000000000..95a4d52ee1469700464f9feebae28b9847d1a18a --- /dev/null +++ b/java code/src/eigenpsf/lib/Loader.java @@ -0,0 +1,179 @@ +package eigenpsf.lib; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import eigenpsf.Log; +import eigenpsf.Params; +import eigenpsf.project.Project; + +public class Loader { + + /* + public static void searchCppLib(Project project) { + try { + String osname = System.getProperty("os.name"); + System.out.println("os.name " + osname); + if (osname.startsWith("Windows")) { + String osarch = System.getProperty("os.arch"); + if (osarch.contains("64")) { + System.out.println("Windos 64: not available"); + System.exit(0); + } + else { + System.out.println("Windos 32: not available"); + System.exit(0); + } + } + else if (osname.startsWith("Mac")) { + searchCppLibMac(project); + } + else { + searchCppLibLinux(project); + } + + } + catch (Exception ex) { + System.out.println(">>" + ex.toString()); + } + } + private static void searchCppLibMac(Project project) { + + try { + System.load(project.pathUtils + "libEigenPSF_Refine.dylib"); + project.setStatusCppLib(true); + } catch (java.lang.UnsatisfiedLinkError err) { + Log.writeWarning("Unable to load EigenPSF library"); + Log.writeList1("Try to download it ..."); + try { + URL url = new URL("https://www.irit.fr/~Emmanuel.Soubies/EigenPSFExt/libEigenPSF_Refine.dylib"); + try (InputStream in = url.openStream(); + BufferedInputStream bis = new BufferedInputStream(in); + FileOutputStream fos = new FileOutputStream(project.pathUtils + "libEigenPSF_Refine.dylib")) { + byte[] data = new byte[1024]; + int count; + while ((count = bis.read(data, 0, 1024)) != -1) { + fos.write(data, 0, count); + } + System.load(project.pathUtils + "libEigenPSF_Refine.dylib"); + project.setStatusCppLib(true); + Log.writeList1("... Success !"); + } catch (Exception e) { + Log.writeWarning("Unable to download the EigenPSF library (Refine method will not be accessible)"); + } + } catch (Exception e) { + Log.writeWarning("Unable to download the EigenPSF library (Refine method will not be accessible)"); + } + } + } + private static void searchCppLibLinux(Project project) { + + try { + System.load(project.pathUtils + "libEigenPSF_Refine.so"); + project.setStatusCppLib(true); + } catch (java.lang.UnsatisfiedLinkError err) { + Log.writeWarning("Unable to load EigenPSF library"); + Log.writeList1("Try to download it ..."); + try { + URL url = new URL("https://www.irit.fr/~Emmanuel.Soubies/EigenPSFExt/libEigenPSF_Refine.so"); + try (InputStream in = url.openStream(); + BufferedInputStream bis = new BufferedInputStream(in); + FileOutputStream fos = new FileOutputStream(project.pathUtils + "libEigenPSF_Refine.so")) { + byte[] data = new byte[1024]; + int count; + while ((count = bis.read(data, 0, 1024)) != -1) { + fos.write(data, 0, count); + } + System.load(project.pathUtils + "libEigenPSF_Refine.so"); + project.setStatusCppLib(true); + Log.writeList1("... Success !"); + } catch (Exception e) { + Log.writeWarning("Unable to download the EigenPSF library (Refine method will not be accessible)"); + } + } catch (Exception e) { + Log.writeWarning("Unable to download the EigenPSF library (Refine method will not be accessible)"); + } + } + }*/ + + public static void searchSIFTBasis(Project project) { + File f = new File(project.pathUtils + "SIFTbasis/"); + if (f.exists() && f.isDirectory()) { + project.setStatusSIFTBasis(true); + } else { + try { + URL url = new URL("https://www.irit.fr/~Emmanuel.Soubies/EigenPSFExt/SIFTbasis.zip"); + try (InputStream in = url.openStream(); + BufferedInputStream bis = new BufferedInputStream(in); + FileOutputStream fos = new FileOutputStream(project.pathUtils + "SIFTbasis.zip")) { + byte[] data = new byte[1024]; + int count; + while ((count = bis.read(data, 0, 1024)) != -1) { + fos.write(data, 0, count); + } + unZip(project.pathUtils + "SIFTbasis.zip",new File(project.pathUtils)); + project.setStatusSIFTBasis(true); + } catch (Exception e) { + Log.writeWarning("Unable to download"); + } + } catch (Exception e) {} + } + if (project.isSIFTbasis) { + // TODO : loop over the types of Basis + Menu in the GUI to select which ? + File directory=new File(project.pathUtils + "SIFTbasis/Gaussian/2D"); + Params.NbasisVectSIFT_max2D =directory.list().length-1; + directory=new File(project.pathUtils + "SIFTbasis/Gaussian/3D"); + Params.NbasisVectSIFT_max3D =directory.list().length-1; + } + + } + + public static void unZip(String fileZip,File destDir) throws IOException { + byte[] buffer = new byte[1024]; + ZipInputStream zis = new ZipInputStream(new FileInputStream(fileZip)); + ZipEntry zipEntry = zis.getNextEntry(); + while (zipEntry != null) { + final File newFile = newFile(destDir, zipEntry); + if (zipEntry.isDirectory()) { + if (!newFile.isDirectory() && !newFile.mkdirs()) { + throw new IOException("Failed to create directory " + newFile); + } + } else { + File parent = newFile.getParentFile(); + if (!parent.isDirectory() && !parent.mkdirs()) { + throw new IOException("Failed to create directory " + parent); + } + + final FileOutputStream fos = new FileOutputStream(newFile); + int len; + while ((len = zis.read(buffer)) > 0) { + fos.write(buffer, 0, len); + } + fos.close(); + } + zipEntry = zis.getNextEntry(); + } + zis.closeEntry(); + zis.close(); + } + + public static File newFile(File destinationDir, ZipEntry zipEntry) throws IOException { + File destFile = new File(destinationDir, zipEntry.getName()); + + String destDirPath = destinationDir.getCanonicalPath(); + String destFilePath = destFile.getCanonicalPath(); + + if (!destFilePath.startsWith(destDirPath + File.separator)) { + throw new IOException("Entry is outside of the target dir: " + zipEntry.getName()); + } + + return destFile; + } +} diff --git a/java code/src/eigenpsf/processing/AbstractBackground.java b/java code/src/eigenpsf/processing/AbstractBackground.java new file mode 100644 index 0000000000000000000000000000000000000000..aac21ddf4b770f3402ab96b0e49df69243eca24e --- /dev/null +++ b/java code/src/eigenpsf/processing/AbstractBackground.java @@ -0,0 +1,21 @@ +package eigenpsf.processing; + +import java.util.ArrayList; + +import eigenpsf.stack.Patch; +import eigenpsf.stack.ZStack; +import eigenpsf.data.HyperMatrix; + +public abstract class AbstractBackground { + + public static ArrayList<AbstractBackground> list() { + ArrayList<AbstractBackground> list = new ArrayList<AbstractBackground>(); + list.add(new PolynomialFitBackground()); + return list; + } + public abstract String getName(); + public abstract HyperMatrix process(HyperMatrix im, Patch patch); + public abstract void initialize(); + public boolean isInit = false; // To know if the instantiation of the class has been initialized + public int[] nn; // Size of the patch used to estimate the background +} diff --git a/java code/src/eigenpsf/processing/AbstractDetection.java b/java code/src/eigenpsf/processing/AbstractDetection.java new file mode 100644 index 0000000000000000000000000000000000000000..51cc581900ffbce4edde1485d09fc9ddc92649cc --- /dev/null +++ b/java code/src/eigenpsf/processing/AbstractDetection.java @@ -0,0 +1,48 @@ +package eigenpsf.processing; + +import java.io.File; +import java.util.ArrayList; + +import eigenpsf.Log; +import eigenpsf.project.Project; +import eigenpsf.stack.Patch; +import eigenpsf.stack.Patches; +import eigenpsf.stack.ZStack; +import ij.IJ; +import ij.ImagePlus; +import ij.process.ImageProcessor; + +public abstract class AbstractDetection { + + public static ArrayList<AbstractDetection> list() { + ArrayList<AbstractDetection> list = new ArrayList<AbstractDetection>(); + list.add(new SIFTDetection()); + //list.add(new DoGDetection()); + return list; + } + public abstract String getName(); + public abstract void process(ZStack stack, Project project); + public abstract String toString(); + + public void applyMask(ZStack stack) { + String filename = stack.getMaskFilename(); + File file = new File(filename); + if (!file.exists()) + return; + ImagePlus imp = IJ.openImage(filename); + if (imp == null) { + Log.writeWarning("The mask " + filename + " is not a valid image."); + return; + } + int np = stack.patches.size(); + ImageProcessor ip = imp.getProcessor(); + Patches inside = new Patches(); + for(Patch patch : stack.patches) { + if (ip.getPixelValue(patch.xp, patch.yp) != 0) + inside.add(patch); + } + stack.patches = inside; + int na = stack.patches.size(); + Log.write("Apply Mask (" + np + "->" + na + " patches)"); + } +} diff --git a/java code/src/eigenpsf/processing/AbstractRegistration.java b/java code/src/eigenpsf/processing/AbstractRegistration.java new file mode 100644 index 0000000000000000000000000000000000000000..09359d21deacd94541dcb0f7e6d55ec1a93f94f4 --- /dev/null +++ b/java code/src/eigenpsf/processing/AbstractRegistration.java @@ -0,0 +1,21 @@ +package eigenpsf.processing; + +import java.util.ArrayList; + +import eigenpsf.data.HyperMatrix; +import eigenpsf.project.Project; +import eigenpsf.stack.Patch; +import eigenpsf.stack.ZStack; + + +public abstract class AbstractRegistration { + + public static ArrayList<AbstractRegistration> list() { + ArrayList<AbstractRegistration> list = new ArrayList<AbstractRegistration>(); + list.add(new ScaleSpaceRegistration()); + //list.add(new CoGRegistration()); + return list; + } + public abstract String getName(); + public abstract double[] process(HyperMatrix patch); +} diff --git a/java code/src/eigenpsf/processing/CInterface.class b/java code/src/eigenpsf/processing/CInterface.class new file mode 100644 index 0000000000000000000000000000000000000000..fb992faa96ba668c1aa03620fcd13804a71f980f Binary files /dev/null and b/java code/src/eigenpsf/processing/CInterface.class differ diff --git a/java code/src/eigenpsf/processing/CInterface.java b/java code/src/eigenpsf/processing/CInterface.java new file mode 100644 index 0000000000000000000000000000000000000000..9c62be338fd4f5e8fc8aaf2315b44136d4ee7e52 --- /dev/null +++ b/java code/src/eigenpsf/processing/CInterface.java @@ -0,0 +1,26 @@ +package eigenpsf.processing; + +//#### TO BUILD THE C++ EigenPSF_Refine LIBRARY +//---- On Mac +//Run in folder src +// javac eigenpsf/processing/CInterface.java -h . +// mv eigenpsf_processing_CInterface.h RefineCodeCpp +// g++ -dynamiclib -I/Library/Java/JavaVirtualMachines/jdk1.8.0_71.jdk/Contents/Home/include -o RefineCodeCpp/libEigenPSF_Refine.dylib RefineCodeCpp/EigenPSF_Refine.cpp +// cp RefineCodeCpp/libEigenPSF_Refine.dylib resources/ + +//---- On Linux +// Run in folder src +// javac eigenpsf/processing/CInterface.java -h . +// mv eigenpsf_processing_CInterface.h RefineCodeCpp +// g++ -shared -fPIC -I/usr/lib/jvm/java-11-openjdk-amd64/include -I/usr/lib/jvm/java-11-openjdk-amd64/include/linux -o RefineCodeCpp/libEigenPSF_Refine.so RefineCodeCpp/EigenPSF_Refine.cpp +// cp RefineCodeCpp/libEigenPSF_Refine.so resources/ +//################################################## + +public class CInterface { + + public native int[] SqrArray(int [] a); + public native void IterativeRefine(int iter, String logfileC, String logfileJava); + public native boolean CheckAbort(String logfileJava); + public native void WriteLog(String logfileJava,String msg, int lev); + public native void WriteEnd(String logfileJava); +} diff --git a/java code/src/eigenpsf/processing/CoGRegistration.java b/java code/src/eigenpsf/processing/CoGRegistration.java new file mode 100644 index 0000000000000000000000000000000000000000..f3e19b003937dd6f9b2aff82f7f3b7889bb2d50b --- /dev/null +++ b/java code/src/eigenpsf/processing/CoGRegistration.java @@ -0,0 +1,34 @@ +package eigenpsf.processing; + +import eigenpsf.stack.Patch; +import eigenpsf.stack.ZStack; +import eigenpsf.data.HyperMatrix; +import eigenpsf.project.Project; + + +public class CoGRegistration extends AbstractRegistration{ + + @Override + public String getName() { + return "Center of Gravity"; + } + + @Override + public double[] process(HyperMatrix patch) { +// if (stack == null) +// return; +// if (stack.patches == null) +// return; + // Fake: just add a random value for the test + /*for(ZStack stack : project) { + if (stack.isSelected()) { + for(Patch patch : stack.patches) { + patch.x = patch.xp + Math.random(); + patch.y = patch.yp + Math.random(); + patch.z = patch.zp + Math.random(); + } + } + }*/ + return null; + } +} diff --git a/java code/src/eigenpsf/processing/DoGDetection.java b/java code/src/eigenpsf/processing/DoGDetection.java new file mode 100644 index 0000000000000000000000000000000000000000..9f1060fbfc4e9709c584015eb9f398581c56384e --- /dev/null +++ b/java code/src/eigenpsf/processing/DoGDetection.java @@ -0,0 +1,136 @@ +package eigenpsf.processing; + +import java.util.Collections; +import java.util.Comparator; + +import eigenpsf.Log; +import eigenpsf.Params; +import eigenpsf.stack.Patch; +import eigenpsf.stack.Patches; +import eigenpsf.stack.ZStack; +import eigenpsf.project.Project; +import ij.ImagePlus; +import ij.plugin.GaussianBlur3D; +import ij.process.Blitter; +import ij.process.ImageProcessor; + +public class DoGDetection extends AbstractDetection { + + @Override + public String getName() { + return "DoG"; + } + + @Override + public String toString() { + return "DoG(" + Params.thresholdDetection + ")"; + } + + @Override + public void process(ZStack stack, Project project) { + /* + if (stack == null) + return; + if (stack.patches == null) + return; + double sigma = Params.support * 0.25; + int margin = 1; + stack.patches.clear(); + ImagePlus mip = stack.getMIP(); + if (mip == null) + return; + ImagePlus in = new ImagePlus("", mip.getProcessor().crop()); + + ImagePlus dog = dog(in, sigma); + + Patches candidates = localMax(stack, dog, mip, margin, Params.support, Params.thresholdDetection); + stack.patches = trim(candidates, Params.support*2); + int count = 1; + for (Patch patch : stack.patches) { + patch.id = count++; + patch.ztest_value = 0; + } + Log.write("DoG detection on " + stack.getName() + " beads:" + (count-1)); + */ + } + + private ImagePlus dog(ImagePlus in, double sigma) { + ImagePlus g1 = in.duplicate(); + ImagePlus g2 = in.duplicate(); + GaussianBlur3D.blur(g1, sigma, sigma, 0); + GaussianBlur3D.blur(g2, sigma * Math.sqrt(2), sigma * Math.sqrt(2), 0); + ImageProcessor ip1 = g1.getProcessor(); + ImageProcessor ip2 = g2.getProcessor(); + ip1.copyBits(ip2, 0, 0, Blitter.SUBTRACT); + g1.setTitle("DoG of " + in.getTitle()); + g1.show(); + return g1; + } + + private Patches localMax(ZStack stack, ImagePlus dog, ImagePlus mip, int margin, int support, double threshold) { + int nx = dog.getWidth(); + int ny = dog.getHeight(); + ImageProcessor ip = dog.getProcessor(); + ImageProcessor mipip = mip.getProcessor(); + + Patches list = new Patches(); + int ymin = support + margin; + int ymax = ny - support - margin - 1; + int xmin = support + margin; + int xmax = nx - support - margin - 1; + for (int x = xmin; x < xmax; x++) { + for (int y = ymin; y < ymax; y++) { + if (mipip.getPixelValue(x, y) > threshold) { + double v = ip.getPixelValue(x, y); + if (v > ip.getPixelValue(x - 1, y + 1)) { + if (v > ip.getPixelValue(x - 1, y)) { + if (v > ip.getPixelValue(x - 1, y - 1)) { + if (v > ip.getPixelValue(x + 1, y + 1)) { + if (v > ip.getPixelValue(x + 1, y)) { + if (v > ip.getPixelValue(x + 1, y - 1)) { + if (v > ip.getPixelValue(x, y - 1)) { + if (v > ip.getPixelValue(x, y + 1)) { + double dist = (x-nx*0.5)*(x-nx*0.5) + (y-ny*0.5)*(y-ny*0.5); + dist = Math.sqrt(dist); + int id = list.size() + 1; + Patch bead = new Patch(stack, id, dist, x, y, 0, x, y, 0, v); + list.add(bead); + } + } + } + } + } + } + } + } + } + } + } + return list; + } + + public static Patches trim(Patches patches, double proximity) { + Collections.sort(patches, new Comparator<Patch>() { + @Override + public int compare(Patch b1, Patch b2) { + return (b1.max < b2.max ? 1 : -1); + + } + }); + + Patches goods = new Patches(); + + for (Patch candidate : patches) { + boolean flag = false; + for (Patch good : goods) + if (good.distance(candidate) < proximity) flag = true; + if ((flag == false)) { + goods.add(candidate); + } + + } + return goods; + } + + +} diff --git a/java code/src/eigenpsf/processing/PolynomialFitBackground.java b/java code/src/eigenpsf/processing/PolynomialFitBackground.java new file mode 100644 index 0000000000000000000000000000000000000000..e40e08676dc6b97f8ce40c411f937e9ea0605753 --- /dev/null +++ b/java code/src/eigenpsf/processing/PolynomialFitBackground.java @@ -0,0 +1,115 @@ +package eigenpsf.processing; + +import static org.jblas.DoubleMatrix.ones; + +import org.jblas.DoubleMatrix; +import org.jblas.MatrixFunctions; +import org.jblas.Solve; +import org.jblas.ranges.IntervalRange; + +import eigenpsf.Params; +import eigenpsf.data.HyperMatrix; +import eigenpsf.stack.Patch; + +public class PolynomialFitBackground extends AbstractBackground { + + public DoubleMatrix M; // Matrix containing polynomials basis + public DoubleMatrix M_; // Matrix with mask applied to rows + public DoubleMatrix MtM_; // Associated normal matrix + public int[] nn; // Size of the patch used to estimate the background + public DoubleMatrix mask; // Mask used to remove the contribution of the PSF to the patch + public boolean isInit = false; // To know if the instantiation of the class has been initialized + + @Override + public String getName() { + return "Polynomial Fit"; + } + + @Override + public HyperMatrix process(HyperMatrix im, Patch patch) { + + // Initialize if first call + if (!this.isInit) { + initialize(); + this.isInit=true; + } + + // Extract a square patch of size this.nn (take all z frames in 3D) + int[] center = {patch.xp,patch.yp,patch.zp}; + int[] sz = {this.nn[0],this.nn[1],this.nn[2]}; + HyperMatrix p = im.getPatch(center, sz); + + // Estimate the background + HyperMatrix back = estimate_background(p); + // Return the enlarged patch minus the estimated background + return p.sub(back); + } + + @Override + public void initialize() { + // This method initialize the attributes of the class + + // Compute the size of the patch used to estimate the background + this.nn = new int[3]; + this.nn[0] = (int) Math.round(((double)Params.psf_visible_width[0])*Params.factorDiamBack); + this.nn[1] = (int) Math.round(((double)Params.psf_visible_width[1])*Params.factorDiamBack); + this.nn[2] = (int) Math.round(((double)Params.psf_visible_width[2])*Params.factorDiamBack); +// this.nn = (int) Math.round(Params.support*Params.factorDiamBack); + this.nn[0] = this.nn[0] + Math.floorMod(this.nn[0]-1, 2); + this.nn[1] = this.nn[1] + Math.floorMod(this.nn[1]-1, 2); + this.nn[2] = this.nn[2] + Math.floorMod(this.nn[2]-1, 2); +// System.out.printf("nn = %d %d %d\n",nn[0],nn[1],nn[2]); + if (Params.psf_visible_width[2]==1) { + this.nn[2]=1; + } + + // Meshgrid + DoubleMatrix[] lin = new DoubleMatrix[2]; + for (int k=0; k<2; k++) + lin[k] = DoubleMatrix.linspace(1,this.nn[0],this.nn[1]); + DoubleMatrix[] X = HyperMatrix.meshgrid(lin); + + // Build Polynomials basis + this.M = ones(this.nn[0]*this.nn[1],10); + this.M = this.M.div((double) Math.max(this.nn[0],this.nn[1])); + for (int k=0; k<2; k++) { + this.M.putColumn(k+1,X[k]); + this.M.putColumn(k+3,MatrixFunctions.pow(this.M.getColumn(k+1),2)); + this.M.putColumn(k+5,MatrixFunctions.pow(this.M.getColumn(k+1),3)); + } + this.M.putColumn(7,this.M.getColumn(1).mul(this.M.getColumn(2))); + this.M.putColumn(8,this.M.getColumn(2).mul(this.M.getColumn(3))); + this.M.putColumn(9,this.M.getColumn(1).mul(this.M.getColumn(4))); + + // Build mask + double c = ((double) Math.max(this.nn[0],this.nn[1])-1.)/2.0; + int support = Math.max(Math.max(Params.psf_visible_width[0],Params.psf_visible_width[1]),Params.psf_visible_width[2]); + this.mask = MatrixFunctions.sqrt(MatrixFunctions.powi(X[0].subi(c),2).addi(MatrixFunctions.powi(X[1].subi(c),2))).gei(support/2.0); + this.M_ =this.M.getRows(this.mask); + this.MtM_ = this.M_.transpose().mmul(this.M_); + /* + int[] d = {this.nn,this.nn,1,1}; + HyperMatrix tt = new HyperMatrix(d,this.mask.data); + ImagePlus ttt = HyperMatrix.HyperMatrix2ImagePlus(tt);ttt.setTitle("Mask");ttt.show(); + */ + /* + for (int ii = 0; ii < this.MtM_.rows; ii++) { + for (int jj = 0; jj < this.MtM_.columns; jj++) { + System.out.printf("%7.0f ", this.MtM_.get(ii, jj)); + } + System.out.printf("\n"); + } + */ + } + + public HyperMatrix estimate_background(HyperMatrix u) { + int[] dims = u.getDimensions(); + DoubleMatrix b = u.dup(); + IntervalRange c = new IntervalRange(0,1); + for (int z = 0; z < u.nz; z++) { + IntervalRange r = new IntervalRange(u.ind2sub(0, 0, z),u.ind2sub(0, 0, z) + u.nx * u.ny); + b.put(r,c, this.M.mmul(Solve.solve(this.MtM_, this.M_.transpose().mmul(u.get(r,c).getRows(this.mask))))); + } + return new HyperMatrix(dims,b.data); + } +} \ No newline at end of file diff --git a/java code/src/eigenpsf/processing/Processing.java b/java code/src/eigenpsf/processing/Processing.java new file mode 100644 index 0000000000000000000000000000000000000000..c4b416ef94c306375109ed0767c79f83af7085f1 --- /dev/null +++ b/java code/src/eigenpsf/processing/Processing.java @@ -0,0 +1,202 @@ +package eigenpsf.processing; + + +import java.util.ArrayList; +import java.util.List; + +import org.jblas.DoubleMatrix; + + +import eigenpsf.Log; +//import eigenpsf.Log; +import eigenpsf.Params; +import eigenpsf.data.EigenElements; +import eigenpsf.data.HyperMatrix; +import eigenpsf.project.Project; +import eigenpsf.stack.Patch; +import eigenpsf.stack.ZStack; + +public class Processing { + + public static void detect(Project project) { + if (project == null) + return; + + //### Detect Beads + Log.writeTitle("Process global background & detect beads");Log.progress("Start Detect & Preprocess", 1); + int nbBeads=0; + int cnt = 0; + for(AbstractDetection detection : AbstractDetection.list()) { + if (Params.detectionMethod.equals(detection.getName())) { + for(ZStack stack : project) + if (stack.isSelected()) { + Log.writeList1("Process Stack " + stack.getName() + " ..."); + Log.progress("Process global background & detect: " + stack.getName(), 100*(cnt++)/project.size()); + detection.process(stack, project); + detection.applyMask(stack); + nbBeads = nbBeads + stack.patches.size(); + + } + } + } + + if (nbBeads==0) { + Log.writeError("No detected bead. You should decrease the threshold parameter. \n"); + }else { + HyperMatrix im = null; + + int[] center= {0,0,0}; + // -- Loop over the images (stacks) + for (ZStack stack : project) { + if (stack.isSelected()) { + im = stack.getHyperMatrix(); + for (Patch patch : stack.patches) { + center[0]=patch.xp; center[1]=patch.yp; center[2]=patch.zp; + patch.data = im.getPatch(center, Params.psf_visible_width); + } + } + } + Log.writeOutput(String.format("Number of detected beads: %d.\n",nbBeads)); + } + // Prune bad beads + pruneBeads(project); + + Log.progress("End Detect beads", 100); + } + + public static void preprocess(Project project) { + if (project == null) + return; + + // ### Instantiate the selected background algorithm + AbstractBackground back_meth = null; + for (AbstractBackground background : AbstractBackground.list()) { + if (Params.backgroundMethod.equals(background.getName())) + back_meth = background; + } + + // ### Instantiate the selected registration algorithm + AbstractRegistration reg_meth = null; + for (AbstractRegistration registration : AbstractRegistration.list()) { + if (Params.registrationMethod.equals(registration.getName())) { + reg_meth = registration; + } + } + + // ### Extract + Remove Background + Register patches + Log.writeTitle("Remove local background & Register"); + // -- Init parameters + HyperMatrix large_patch = null; + HyperMatrix im = null; + double[] shift; + //project.patchesForSvd = new ArrayList<HyperMatrix>(); + // -- Compute size patches + int[] size_out = new int[3]; + int[] center = new int[3]; + for (int i = 0; i < 3; i++) { + size_out[i] = (int) Math.round(((double) Params.psf_visible_width[i]) * Params.factorDiamBack); + size_out[i] = size_out[i] + Math.floorMod(size_out[i] - 1, 2); + center[i] = (int) ((size_out[i] - 1) / 2); + } + if (Params.dim == 2) { + size_out[2] = 1; + center[2] = 0; + } + + // -- Loop over the images (stacks) + for (ZStack stack : project) { + Log.writeList1("Process Stack " + stack.getName() + " ... "); + if (stack.isSelected()) { + im = stack.getHyperMatrix(); + // -- Loop over the patches + int cnt = 0; + for (Patch patch : stack.patches) { + Log.progress("Process local background & Register: " + stack.getName() + " / Patch " + cnt, 10 + (90.0*cnt++)/stack.patches.size()); + + large_patch = back_meth.process(im, patch); // Remove background (locally) + // HyperMatrix tmp2 = large_patch.getPatch(center,Params.psf_visible_width); + //tmp2.display("Before register patch",null); + shift = reg_meth.process(large_patch); // Register patch + // -- Update patch position + double xx = patch.xp + shift[0]; + double yy = patch.yp + shift[1]; + double zz = patch.zp + shift[2]; + patch.xp = (int) Math.round(xx); + patch.yp = (int) Math.round(yy); + patch.zp = (int) Math.round(zz); + patch.x = patch.xp - xx; + patch.y = patch.yp - yy; + patch.z = patch.zp - zz; + // -- Extract and store patch + int[] shift_fl = { (int) Math.round(shift[0]) + center[0], + (int) Math.round(shift[1]) + center[1], (int) Math.round(shift[2]) + center[2] }; + patch.data = large_patch.getPatch(shift_fl, Params.psf_visible_width); + //patch.data.display("After register patch",null); + // -- Preprocess patch for SVD + //project.patchesForSvd.add(large_patch.imtranslate(patch.x, patch.y, patch.z) + // .getPatch(shift_fl, Params.psf_visible_width).normalize()); + patch.data_register = large_patch.imtranslate(patch.x, patch.y, patch.z).getPatch(shift_fl, Params.psf_visible_width).normalize(); + //project.patchesForSvd.get(project.patchesForSvd.size()-1).display("After translation patch",null); + } + } + } + + // Prune bad beads + pruneBeads(project); + + Log.progress("End Preprocess patches", 100); + project.isPreprocessed = true; + } + + public static void pruneBeads(Project project) { + if (project == null) + return; + + if (project.eigen==null) + project.eigen = new EigenElements(); + + Log.writeTitle("Discard bad beads");Log.progress("Discard bad beads", 1); + + // -- Get all patches + HyperMatrix psfs_data = project.getAllPatchesHyperMatrix(); + + // -- Compute SVD + Log.writeList2("Compute SVD ... ");Log.progress("Compute SVD ... ", 30); + project.eigen.eigenDecomposition(psfs_data, 10); // Force to 10 eigenElements for the Z-test + // -- Z-Test + Log.writeList2("Z-Test to discard bad beads ... ");Log.progress("Z-Test to discard bad beads ... ",60); + psfs_data = project.eigen.ztest(project.getAllPatches(), psfs_data, Params.quantile); + // psfs_data.display("Observed PSFs after Z-test",project.getListOfOpenImages()); + Log.writeOutput(String.format("Beads kept: %d/%d.\n", psfs_data.nf, project.eigen.eigenCoeffs.columns)); + project.eigen.eigenElements = null; + } + + public static void compute(Project project) { + if (project == null) + return; + + Log.writeTitle("Compute EigenPSF");Log.progress("Compute EigenPSF", 1); + HyperMatrix psfs_data = project.getSelectedAndRegisteredPatches(); + int dims[] = psfs_data.getDimensions(); + Log.writeList1(String.format("Number of selected beads: %d.\n", dims[3])); + // -- Second SVD + Log.writeList2("Compute SVD ... ");Log.progress("Compute SVD ...", 10); + project.eigen.eigenDecomposition(psfs_data,Params.NeigenElements); + // -- Normalization + //Log.write(" - Singular values : "+ Arrays.toString(project.eigen.sValues)+" \n"); + Log.writeList2("Normalization ... ");Log.progress("Normalization ... ", 90); + project.eigen.normalize_sValues(); + Log.writeOutput("EigenPSF with " + Params.NeigenElements + " components computed.");Log.progress("End Compute EigenPSF ",100); + project.isComputed = true; + } + + public static void measure(Project project) { + System.out.println("TO DO"); + } + + public static void show(Project project) { + if (project.eigen.eigenElements != null) { + project.eigen.eigenElements.display("Eigen PSFs.", project.getListOfOpenImages()); + } + } +} diff --git a/java code/src/eigenpsf/processing/ProcessingPanel.java b/java code/src/eigenpsf/processing/ProcessingPanel.java new file mode 100644 index 0000000000000000000000000000000000000000..bd4f19ef6c135822d81bf5158d2c7f5bd2e97c23 --- /dev/null +++ b/java code/src/eigenpsf/processing/ProcessingPanel.java @@ -0,0 +1,336 @@ +package eigenpsf.processing; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.io.FileWriter; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JSplitPane; +import javax.swing.JToolBar; + +import bilib.commons.components.HTMLPane; +import eigenpsf.Log; +import eigenpsf.Params; +import eigenpsf.project.MainDialog; +import eigenpsf.project.Project; +import eigenpsf.stack.Patches; +import eigenpsf.stack.PatchesTable; +import eigenpsf.stack.ShowPatchDialog; +import eigenpsf.stack.ZStack; + +public class ProcessingPanel extends JPanel implements ActionListener, Runnable { + + private HTMLPane info = new HTMLPane(500, 200); + public PatchesTable table; + private Project project; + private MainDialog mainDialog; + + private JButton detect = new JButton("Detect beads"); + private JButton preprocess = new JButton("Preprocess patches"); + private JButton compute = new JButton("Compute EigenPSF"); + //public JButton refine = new JButton("Refine EigenPSF"); + private JButton show = new JButton("Show EigenPSF"); + private JButton patches = new JButton("Show Patches"); + + public JCheckBox verbose = new JCheckBox("Verbose", true); + public JButton clear = new JButton("Clear logs"); + + private JButton job = null; + private Thread thread = null; + + public int state = 0; // after detect = 1, after preprocess = 2, after compute = 3 + //public int stateRefine = 0; // 0=not running / 1 = running + + public ProcessingPanel(Project project, MainDialog mainDialog) { + super(new BorderLayout()); + + this.mainDialog = mainDialog; + this.project = project; + this.table = new PatchesTable(project); + verbose.setBorder(BorderFactory.createEtchedBorder()); + JToolBar tool = new JToolBar(); + tool.setFloatable(false); + tool.setBorder(BorderFactory.createEtchedBorder()); + tool.add(verbose); + tool.add(clear); + + JPanel tool1 = new JPanel(new BorderLayout()); + tool1.add(new JLabel(" ")); + tool1.add(tool); + JPanel pnTop = new JPanel(new BorderLayout()); + pnTop.add(tool1, BorderLayout.NORTH); + pnTop.add(info.getPane(), BorderLayout.CENTER); + + Font font = new Font(detect.getFont().getFamily(), Font.PLAIN, detect.getFont().getSize()+1); + JToolBar bn = new JToolBar(); + bn.setBorder(BorderFactory.createEtchedBorder()); + bn.setFloatable(false); + + detect.setFont(font); + compute.setFont(font); + preprocess.setFont(font); + //refine.setFont(font); + show.setFont(font); + patches.setFont(font); + + bn.add(detect); + bn.addSeparator(new Dimension(5, 5)); + bn.add(preprocess); + bn.addSeparator(new Dimension(5, 5)); + bn.add(compute); + bn.addSeparator(new Dimension(5, 5)); + //bn.add(refine); + //bn.addSeparator(new Dimension(5, 5)); + bn.add(show); + bn.addSeparator(new Dimension(5, 5)); + bn.addSeparator(); + bn.addSeparator(new Dimension(5, 5)); + bn.add(patches); + + JPanel pnBottom = new JPanel(new BorderLayout()); + pnBottom.add(bn, BorderLayout.NORTH); + pnBottom.add(table.getPane(600, 200), BorderLayout.CENTER); + + JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT, pnTop, pnBottom); + split.setDividerLocation(200); + add(split, BorderLayout.CENTER); + + detect.addActionListener(this); + preprocess.addActionListener(this); + compute.addActionListener(this); + //refine.addActionListener(this); + show.addActionListener(this); + verbose.addActionListener(this); + clear.addActionListener(this); + patches.addActionListener(this); + + update(); + Log.init(info); + } + + @Override + public void actionPerformed(ActionEvent e) { + Params.verbose = verbose.isSelected() ? 1 : 0; + if (e.getSource() == clear) + Log.clear(); + + if (e.getSource() == detect) { + if (thread == null) { + job = detect; + thread = new Thread(this); + thread.setPriority(Thread.MIN_PRIORITY); + thread.start(); + } + } + else if (e.getSource() == preprocess) { + if (thread == null) { + job = preprocess; + thread = new Thread(this); + thread.setPriority(Thread.MIN_PRIORITY); + thread.start(); + } + } + else if (e.getSource() == compute) { + if (thread == null) { + job = compute; + thread = new Thread(this); + thread.setPriority(Thread.MIN_PRIORITY); + thread.start(); + } + } + /*else if (e.getSource() == refine) { + if (thread == null) { + job = refine; + thread = new Thread(this); + thread.setPriority(Thread.MIN_PRIORITY); + thread.start(); + } + }*/ + else if (e.getSource() == show) { + if (thread == null) { + job = show; + thread = new Thread(this); + thread.setPriority(Thread.MIN_PRIORITY); + thread.start(); + } + } + else if (e.getSource() == patches) { + Patches patches = table.getUserSelected(); + new ShowPatchDialog(project, patches); + } + } + + @Override + public void run() { + setEnableBars(false); + detect.setEnabled(false); + preprocess.setEnabled(false); + compute.setEnabled(false); + //refine.setEnabled(false); + show.setEnabled(false); + patches.setEnabled(false); + Log.status.reset(); + try { + if (job == detect) { + if (Params.detectionMethod == AbstractDetection.list().get(0).getName() & !project.isSIFTbasis) { + Log.writeError("SIFT basis not found. Cannot use SIFT detection method."); + } + else { + if (project.getAllPatches().size() > 0) { + int ret = JOptionPane.showConfirmDialog(null, + "This will erase extracted patches and computed eigenPSF. Do you want to proceed?", + "Warning", JOptionPane.YES_NO_OPTION); + if (ret == JOptionPane.YES_OPTION) { + Log.clear(); + table.removeRows(); + Processing.detect(project); + //project.isRefined = false; + project.isComputed = false; + project.isPreprocessed = false; + state = 1; + } + } + else { + table.removeRows(); + Processing.detect(project); + state = 1; + } + } + + } + else if (job == preprocess) { + Processing.preprocess(project); + state = 2; + } + else if (job == compute) { + boolean go = true; + if (!(project.eigen.eigenElements == null)) { + String msg = "This will erase the computed EigenPSF. Do you want to proceed?"; + int ret = JOptionPane.showConfirmDialog(null, msg, "Warning", JOptionPane.YES_NO_OPTION); + if (ret == JOptionPane.NO_OPTION) { + go = false; + } + } + if (go) { + Processing.compute(project); + //project.isRefined = false; + state = 3; + } + + } + /*else if (job == refine) { + if (stateRefine == 0) { + boolean go = true; + if (project.isRefined) { + int ret = JOptionPane.showConfirmDialog(null, "This will erase the computed and refined EigenPSF. Do you want to proceed?", "Warning",JOptionPane.YES_NO_OPTION); + if (ret == JOptionPane.NO_OPTION) { + go = false; + } + } + if (go) { + // Change button text and clean LogJava.txt + //refine.setText("Interrupt"); + detect.setEnabled(false); + compute.setEnabled(false); + show.setEnabled(false); + //refine.setEnabled(true); + stateRefine = 1; + try { + new File(project.pathUtils + "LogJava.txt"); // Create file if does not exists + FileWriter myWriter = new FileWriter(project.pathUtils + "LogJava.txt"); + myWriter.write(""); + myWriter.close(); + } + catch (Exception e) { + Log.writeError("Can't write in LogJava.txt"); + } + // Run + Log.writeTitle("Refine EigenPSF"); + Log.startProgressMessageFromC(); + Refinement ref = new Refinement(project, this); + ref.start(); + state = 3; + } + } + else if (stateRefine == 1) { + try { + new File(project.pathUtils + "LogJava.txt"); // Create file if does not exists + FileWriter myWriter = new FileWriter(project.pathUtils + "LogJava.txt"); + myWriter.write("end"); + myWriter.close(); + } + catch (Exception e) { + Log.writeError("Can't write in LogJava.txt"); + } + //refine.setEnabled(false); + state = -1; // to disable all button in update + Log.stopProgressMessageFromC(); + Log.writeList1("Stopping EigenPSF refinement ..."); + } + }*/ + else if (job == show) { + Processing.show(project); + state = 3; + } + + } + catch (Exception ex) { + Log.writeException(ex); + } + update(); + thread = null; + } + + public void update() { + detect.setEnabled(false); + preprocess.setEnabled(false); + compute.setEnabled(false); + //refine.setEnabled(false); + show.setEnabled(false); + patches.setEnabled(false); + table.removeRows(); + int count = 0; + for (ZStack stack : project) + if (stack.isSelected()) { + count++; + } + if (count == 0) + return; + table.update(project); + + //if (state < 3) { + detect.setEnabled(state >= 0); + preprocess.setEnabled(state >= 1); + compute.setEnabled(state >= 1); + //refine.setEnabled(state >= 2 & project.isCppLib); + show.setEnabled(state >= 3); + setEnableBars(true); + patches.setEnabled(state >= 1); + /*} + else { + detect.setEnabled(false); + compute.setEnabled(false); + //refine.setEnabled(true & project.isCppLib); + show.setEnabled(false); + }*/ + mainDialog.table.update(project); + } + + public void setEnableBars(boolean en) { + this.mainDialog.toolbar.setEnabled(en); + this.mainDialog.bnAdd.setEnabled(en); + this.mainDialog.bnRemove.setEnabled(en); + if (!en) { + this.mainDialog.settingsDialog.setVisible(en); + } + } +} diff --git a/java code/src/eigenpsf/processing/Refinement.java b/java code/src/eigenpsf/processing/Refinement.java new file mode 100644 index 0000000000000000000000000000000000000000..7b8fa2654301c0ea6a4bfa96cc0f6a0f26147301 --- /dev/null +++ b/java code/src/eigenpsf/processing/Refinement.java @@ -0,0 +1,39 @@ +/*package eigenpsf.processing; + +import eigenpsf.Log; +import eigenpsf.project.Project; +import eigenpsf.stack.Patches; + + +public class Refinement implements Runnable { + + private Project project; + private Thread thread; + private ProcessingPanel processPanel; + + public Refinement(Project project,ProcessingPanel pro) { + this.project = project; + this.processPanel = pro; + } + + + public void start() { + thread = new Thread(this); + thread.setPriority(Thread.MIN_PRIORITY); + thread.start(); + } + + @Override + public void run() { + CInterface myjni = new CInterface(); + myjni.IterativeRefine(10,project.pathUtils + "LogC.txt",project.pathUtils + "LogJava.txt"); + Log.writeOutput("EigenPSF refined with ?? Iterations ..."); + processPanel.state=2; + processPanel.update(); + processPanel.refine.setText("Refine EigenPSF"); + processPanel.stateRefine=0; + project.isRefined=true; + } + +} +*/ diff --git a/java code/src/eigenpsf/processing/SIFTDetection.java b/java code/src/eigenpsf/processing/SIFTDetection.java new file mode 100644 index 0000000000000000000000000000000000000000..ffec69ee20e8457c2f4bea491aa0c8409725fdaf --- /dev/null +++ b/java code/src/eigenpsf/processing/SIFTDetection.java @@ -0,0 +1,402 @@ +package eigenpsf.processing; + +import static org.jblas.DoubleMatrix.ones; +import static org.jblas.DoubleMatrix.zeros; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +import org.jblas.DoubleMatrix; +import org.jblas.MatrixFunctions; +import org.jblas.Solve; + +import eigenpsf.Log; +import eigenpsf.Params; +import eigenpsf.data.Dict; +import eigenpsf.data.HyperMatrix; +import eigenpsf.stack.Patch; +import eigenpsf.stack.ZStack; +import eigenpsf.project.Project; + +public class SIFTDetection extends AbstractDetection { + + @Override + public String getName() { + return "SIFT"; + } + + @Override + public String toString() { + return "SIFT(" + Params.psf_visible_width[0] + ","+Params.psf_visible_width[1]+","+ Params.psf_visible_width[2]+")\n"; + } + + @Override + public void process(ZStack stack, Project project) { + if (stack == null) + return; + if (stack.patches == null) + return; + stack.patches.clear(); + + // Get img and dims + HyperMatrix imp = stack.getHyperMatrix(); + int[] dim = imp.getDimensions(); + + // Estimate background + Log.writeList2("Remove global background ..."); + int nthin = Params.nthin; + int scale = Params.scaleBack; + HyperMatrix back = remove_background_thinplate(imp, nthin, scale); + imp = imp.sub(back); + + // Scale space maxima + // load basis + String PathToGit = " "; + for (int k=0; k<Params.PathToGitList.length; k++) { + Path folder = Paths.get(Params.PathToGitList[k]); + if (Files.exists(folder)) + PathToGit=Params.PathToGitList[k]; + } + // -- Old version when trying to do a list of Dict with different scale to avoid recomputing the scales + /*String PathToSingularValues = PathToGit+Params.PathToSingularValues; + List<Dict> listDict= new ArrayList<Dict>(); + for (int i =0; i<Params.Nscale; i++) { + String PathToSbasis = PathToGit+Params.PathToSbasis+i+"_"; + Dict sbasis = new Dict(PathToSbasis,PathToSingularValues,Params.dim,Params.NbasisVectSIFT); + listDict.add(sbasis); + }*/ + //----------------- + String PathToSingularValues = project.pathUtils + "SIFTbasis/Gaussian/" + Params.dim + "D/singularvalues.csv"; + String PathToSbasis = project.pathUtils + "SIFTbasis/Gaussian/" + Params.dim + "D/Elem_"; + Dict sbasis = new Dict(PathToSbasis,PathToSingularValues,Params.dim,Params.NbasisVectSIFT); + + // -- Old version when trying to do a list of Dict with different scale to avoid recomputing the scales + // DoubleMatrix list_pos = new SIFTDetection().compute_scale_space_maxima(listDict,imp,Params.psf_visible_width,Params.thresholdDetection); + //----------------- + DoubleMatrix list_pos = new SIFTDetection().compute_scale_space_maxima(sbasis,imp,Params.psf_visible_width,Params.thresholdDetection); + +// // Maximum weighted independent set +// DoubleMatrix list_pos = DoubleMatrix.concatHorizontally(w, X_init); + double support_max=Math.max(Math.max(Params.psf_visible_width[0],Params.psf_visible_width[1]),Params.psf_visible_width[2]); + DoubleMatrix X_kept = maximum_weighted_independent_set(list_pos, support_max); + Log.writeList2("Prune detected beads (maximum weighted independent set computed) ... \n"); + + // Add detected patches + int d = X_kept.getColumns(); + for (int k = 0; k < X_kept.getRows(); k++) { + int id = stack.patches.getNextID(); + int xp = (int) (Math.floor(X_kept.get(k, 1))); + double x = X_kept.get(k, 1) - (double) xp; + int yp = (int) (Math.floor(X_kept.get(k, 0))); + double y = X_kept.get(k, 0) - (double) yp; + int zp = 0; + double z = 0; + if (d == 3) { + zp = (int) (Math.floor(X_kept.get(k, 2))); + z = X_kept.get(k, 2) - (double) zp; + } + + double dist = (xp-imp.nx*0.5)*(xp-imp.nx*0.5) + (yp-imp.ny*0.5)*(yp-imp.ny*0.5); + dist = Math.sqrt(dist); + + // Check if patch is not too close to the border and add it to the list + if ((xp>=(Params.psf_visible_width[0]-1)/2) && (xp<imp.nx-(Params.psf_visible_width[0]-1)/2) + && (yp>=(Params.psf_visible_width[1]-1)/2) && (yp<imp.ny-(Params.psf_visible_width[1]-1)/2) + && (zp>=(Params.psf_visible_width[2]-1)/2) && (zp<imp.nz-(Params.psf_visible_width[2]-1)/2)) { + stack.patches.add(new Patch(stack, id, dist, yp, xp, zp, y, x, z)); + } + } + } + + // Take as input a list of dictionary. If only one element, the scale are + // computed in the function, if several elements, + // it should correspond to the different scales. + // -- Old version when trying to do a list of Dict with different scale to avoid recomputing the scales + // public DoubleMatrix compute_scale_space_maxima(List<Dict> listDict, HyperMatrix u, int[] psf_visible_width, double thresh) { + //----------------- + public DoubleMatrix compute_scale_space_maxima(Dict dict, HyperMatrix u, int[] psf_visible_width, double thresh) { + // Parameters: + double smin = Params.smin; + double smax = Params.smax; + + // -- Old version when trying to do a list of Dict with different scale to avoid recomputing the scales + /*int nbins = listDict.size(); + int computescale = 0; + + // Scale space maxima + Dict dict = listDict.get(0); + if (nbins == 1) { + int psf_visible_width_max = Math.max(Math.max(psf_visible_width[0], psf_visible_width[1]), + psf_visible_width[2]); + nbins = (int) Math.max(Math.ceil(((double) psf_visible_width_max) / 3.0), Params.Nscale); + computescale = 1; + }*/ + //----------------- + int nbins = Params.Nscale; + /*if (nbins == 1) { + int psf_visible_width_max = Math.max(Math.max(psf_visible_width[0], psf_visible_width[1]), + psf_visible_width[2]); + nbins = (int) Math.max(Math.ceil(((double) psf_visible_width_max) / 3.0), Params.Nscale); + } */ + Log.writeList2("Beads detection (SIFT with " + dict.basis.nf + " basis elements and " + nbins +" scales) ... "); + + double[] scalings = new double[nbins]; + for (int x = 0; x < nbins; x++) { + scalings[x] = ((double) x) * (smax - smin) / ((double) nbins - 1) + smin; + } + if (nbins == 1) { + scalings[0] = 1; + } + int[] basis_size = new int[3]; + basis_size[0] = (int) (2.0 * Math.floor(smax * ((double) psf_visible_width[0] - 1) / 2.0) + 1.0); + basis_size[1] = (int) (2.0 * Math.floor(smax * ((double) psf_visible_width[1] - 1) / 2.0) + 1.0); + basis_size[2] = (int) (2.0 * Math.floor(smax * ((double) psf_visible_width[2] - 1) / 2.0) + 1.0); + if (psf_visible_width[2] == 1) + basis_size[2] = 1; + + HyperMatrix ss = new HyperMatrix(u.nx, u.ny, u.nz, nbins); + int[] dim_basis0 = { dict.basis.nx, dict.basis.ny, dict.basis.nz, dict.basis.nf }; + int[] dim_psf = { basis_size[0], basis_size[1], basis_size[2], 1 }; + int[] dim_im = { u.nx, u.ny, u.nz, 1 }; + for (int i = 0; i < nbins; i++) { + HyperMatrix sbasis = null; + sbasis = new HyperMatrix(dim_basis0, dict.basis.data); + sbasis.resample(scalings[i], basis_size); + // TODO: Gerer proprement les cas ou un resampling est nécessaire + /*if (computescale == 1) { + sbasis = new HyperMatrix(dim_basis0, dict.basis.data); + sbasis.resample(scalings[i], basis_size); + } else { + dict = listDict.get(i); + sbasis = dict.basis; + }*/ + HyperMatrix supp_basis = sbasis.rowMaxs_(); + supp_basis.gei(1e-8); + + // l2loc_im=conv(im**2,supp_basis); + HyperMatrix u2 = u.mul(u); + HyperMatrix l2loc_u = (u2).conv(supp_basis); + double M = l2loc_u.max(); + l2loc_u.maxi(M / 100.0); + + HyperMatrix tmp = new HyperMatrix(dim_im); + for (int j = 0; j < dict.Nbasis; j++) { + HyperMatrix ctmp = new HyperMatrix(dim_psf, sbasis.getColumn(j).data); + double basis_norm = Math.sqrt(((ctmp).mul(ctmp)).sum()); + //if (computescale == 1) { + ctmp.divi(basis_norm); + //} + ctmp = u.conv(ctmp); + ctmp = ctmp.mul(ctmp); + ctmp = ctmp.mul(dict.S.data[j]); + tmp=tmp.add(ctmp); + } + ss.putColumn(i, (tmp.div(l2loc_u)).sqrt()); + } +// HyperMatrix max_ss = ss.rowMaxs_(); +// ImagePlus tmpJ2 = HyperMatrix.HyperMatrix2ImagePlus(ss);tmpJ2.setTitle("ss");tmpJ2.show(); + HyperMatrix possible_loc = ss.localMax(1, thresh); + possible_loc = possible_loc.rowMaxs_(); + DoubleMatrix list_pos = possible_loc.find(); +// System.out.printf("size possible loc: %d -- %d -- %d -- %d ",possible_loc.nx,possible_loc.ny,possible_loc.nz,possible_loc.nf); + + return list_pos; + } + + public HyperMatrix remove_background_thinplate(HyperMatrix uin, int nthin, int scale) { + /* + * This function solves : inf_x || Bx - u ||_1 where B:R^m\to \R^n is a thin + * plate spline on a regular nthin x nthin grid The solver is the ADMM Inputs: - + * u: input image - nthin: number of points to define the thin plate - scale: + * reduction factor for speed up the process Output: - estimated background + */ + + ////// Preprocess + HyperMatrix u = uin.copy(); + int[] dims_init = u.getDimensions(); + u.imresize(Math.max(Math.floorDiv(dims_init[0], scale), 1), Math.max(Math.floorDiv(dims_init[1], scale), 1), + Math.max(Math.floorDiv(dims_init[2], scale), 1)); + int[] dims = u.getDimensions(); + int dd; + if (dims[2] == 1) { + dd = 2; + } else { + dd = 3; + } // 2D / 3D + + ////// Define the matrix B + // Meshgrid + DoubleMatrix[] lin = new DoubleMatrix[dd]; + for (int k = 0; k < dd; k++) + lin[k] = DoubleMatrix.linspace(1, dims[k], dims[k]); + HyperMatrix[] X = HyperMatrix.meshgrid(lin); + double[] normX = new double[dd]; + for (int k = 0; k < normX.length; k++) + normX[k] = X[k].norm2(); + + // Fill B + DoubleMatrix B = ones(dims[0] * dims[1] * dims[2], 1 + dd + (int) Math.pow(nthin, dd)); + double b1 = 1.0 / Math.sqrt(dims[0] * dims[1] * dims[2]); + for (int x = 0; x < dims[0]; x++) { + for (int y = 0; y < dims[1]; y++) { + for (int z = 0; z < dims[2]; z++) { + B.put(x + y * dims[0] + z * dims[0] * dims[1], 0, b1); + } + } + } + for (int k = 0; k < X.length; k++) { + for (int x = 0; x < dims[0]; x++) { + for (int y = 0; y < dims[1]; y++) { + for (int z = 0; z < dims[2]; z++) { + B.put(x + y * dims[0] + z * dims[0] * dims[1], k + 1, X[k].getPixel(x, y, z, 0) / normX[k]); + } + } + } + } + + double[] c = new double[dd]; + double nthinZ = nthin; + if (dd == 2) + nthinZ = 1; + double r = 0; + double tp = 0; + double normtp = 0; + int k = 1 + dd; + for (int jj = 0; jj < nthin; jj++) { + for (int ii = 0; ii < nthin; ii++) { + for (int ll = 0; ll < nthinZ; ll++) { + c[0] = ii * (dims[1] / (nthin - 1.0)); + c[1] = jj * (dims[0] / (nthin - 1.0)); + if (dd == 3) + c[2] = ll * (dims[2] / (nthin - 1.0)); + normtp = 0; + for (int x = 0; x < dims[0]; x++) { + for (int y = 0; y < dims[1]; y++) { + for (int z = 0; z < dims[2]; z++) { + r = 1e-15; + for (int i = 0; i < c.length; i++) { + r += Math.pow(X[i].getPixel(x, y, z, 0) - c[i], 2); + } + tp = r * Math.log(r); + normtp += tp * tp; + B.put(x + y * dims[0] + z * dims[0] * dims[1], k, tp); + } + } + } + normtp = Math.sqrt(normtp); + for (int x = 0; x < dims[0]; x++) { + for (int y = 0; y < dims[1]; y++) { + for (int z = 0; z < dims[2]; z++) { + B.put(x + y * dims[0] + z * dims[0] * dims[1], k, + B.get(x + y * dims[0] + z * dims[0] * dims[1], k) / normtp); + } + } + } + k += 1; + } + } + } + + //// L1 Estimate of the Background (ADMM) + DoubleMatrix M = B.transpose().mmul(B); + double nit = 5; + r = 0.1; + DoubleMatrix lambda = zeros(dims[0] * dims[1] * dims[2]); + DoubleMatrix Bx = zeros(dims[0] * dims[1] * dims[2]); + DoubleMatrix x = zeros(B.columns); + DoubleMatrix tmp = zeros(dims[0] * dims[1] * dims[2]); + DoubleMatrix y = u.dup(); + for (int i = 0; i < nit; i++) { + x = Solve.solve(M, B.transpose().mmul(lambda.div(r).addi(y))); + Bx = B.mmul(x); + tmp = Bx.sub(u).subi(lambda.div(r)); + y = MatrixFunctions.signum(tmp).mul(MatrixFunctions.abs(tmp).subi(1 / r).maxi(0.0)); + y.addi(u); + lambda = lambda.add(y.sub(Bx).muli(r)); + } + + HyperMatrix back = new HyperMatrix(dims, Bx.data); + back.imresize(dims_init[0], dims_init[1], dims_init[2]); + return back; + } + + public DoubleMatrix maximum_weighted_independent_set(DoubleMatrix list_pos, double dist) { + /* + * list_pos is an array [W, V], whereV is a set of vertices in R^d given as an + * array of size n x d, W is a weight vector of size nx1. Given list_pos and a + * threshold distance dist, this function uses a greedy algorithm to return a + * rough approximation of the maximum weighted independent set of the graph + * where the edges link the vertices at a distance less than dist. Inputs: - + * list_pos=[w,v] with: - v: DoubleMatrix of size (n,d), with n number of points + * and d dimension; list of points to process - w: Doubleatrix size (n,1), + * weights associated to each point - dist: minimum distance between two + * isolated points Output: - list of kept points (maximum weighted independent + * set) + */ + + // Preprocess + int n = list_pos.getRows(); + int d = list_pos.getColumns() - 1; + + int[] indw = { 0 }; + int[] indv = new int[d]; + for (int i = 0; i < d; i++) { + indv[i] = i + 1; + } + DoubleMatrix w = list_pos.getColumns(indw); + DoubleMatrix v = list_pos.getColumns(indv); + + DoubleMatrix D = zeros(n, n); + for (int i = 0; i < d; i++) { + DoubleMatrix X = v.getColumn(i).repmat(1, n); + D.addi(MatrixFunctions.pow(X.subi(X.transpose()), 2)); + } + MatrixFunctions.sqrti(D); + D.lei(dist); + + // Greedy Algo + DoubleMatrix v_kept = zeros(n, d); + int idx; + int it = 0; + while (w.norm2() > 0) { + idx = w.argmax(); + v_kept.putRow(it, v.getRow(idx)); + + int[] k = D.getColumn(idx).eq(0).findIndices(); + D = D.get(k, k); + w = w.get(k); + v = v.getRows(k); + it++; + } + + return v_kept.getRange(0, it, 0, v.columns); + } + + // TO BE REMOVED (ONLY FOR SOME TESTS) + public DoubleMatrix readCSV(int r, int c, String name) { + DoubleMatrix tab = zeros(r, c); + try { + BufferedReader csvReader = new BufferedReader(new FileReader(name)); + String row; + int cpt = 0; + while (((row = csvReader.readLine()) != null)) { + String[] data = row.split(","); + for (int i = 0; i < data.length; i++) { + tab.put(cpt, i, Double.parseDouble(data[i])); + } + cpt++; + } + csvReader.close(); + } catch (Exception ex) { +// System.out.println(ex); + System.out.println("Unable to read the file " + name); + } + return tab; + } + +} diff --git a/java code/src/eigenpsf/processing/ScaleSpaceRegistration.java b/java code/src/eigenpsf/processing/ScaleSpaceRegistration.java new file mode 100644 index 0000000000000000000000000000000000000000..bfdfb74c67cf11d31551f38a6f2578d575c6acdf --- /dev/null +++ b/java code/src/eigenpsf/processing/ScaleSpaceRegistration.java @@ -0,0 +1,133 @@ +package eigenpsf.processing; + +import eigenpsf.stack.Patch; +import eigenpsf.stack.ZStack; +import eigenpsf.data.HyperMatrix; + + +import java.util.ArrayList; +import java.util.List; + +import org.jblas.DoubleMatrix; + +import eigenpsf.Params; + +public class ScaleSpaceRegistration extends AbstractRegistration{ + + public int K =Params.nbGauss; // number of scales + public int[] nsr; + public List<HyperMatrix> g; + + public ScaleSpaceRegistration() { + + // Compute the size of the patch used to estimate the background + int[] nn = new int[3]; + nn[0] = (int) Math.round(((double)Params.psf_visible_width[0])*Params.factorDiamBack); + nn[1] = (int) Math.round(((double)Params.psf_visible_width[1])*Params.factorDiamBack); + nn[2] = (int) Math.round(((double)Params.psf_visible_width[2])*Params.factorDiamBack); + nn[0] = nn[0] + Math.floorMod(nn[0]-1, 2); + nn[1] = nn[1] + Math.floorMod(nn[1]-1, 2); + nn[2] = nn[2] + Math.floorMod(nn[2]-1, 2); + if (Params.psf_visible_width[2]==1) { + nn[2]=1; + } + + double support = Math.max(Math.max(nn[0],nn[1]),nn[2]); + double lb = 3.0/support; + double fac = Math.pow(lb,1./(double)K); + double[] sigma_list = new double[K]; + for (int k=0; k<K; k++) { + sigma_list[k]=Math.pow(fac,k); + } + int sr =Params.sr; // upsampling factor + nsr = new int[3]; + for (int k=0; k<3; k++) + nsr[k]=(nn[k]-1)*sr+1; + + // Preprocess Gaussian kernels for ScaleSpace + // Grid to define the Gaussian + DoubleMatrix[] lin; + if (Params.dim==2) { + lin = new DoubleMatrix[2]; + for (int k=0; k<2; k++) + lin[k] = DoubleMatrix.linspace(-1,1,nsr[k]).divi(2); + }else { + lin = new DoubleMatrix[3]; + for (int k=0; k<3; k++) + lin[k] = DoubleMatrix.linspace(-1,1,nsr[k]).divi(2); + } + HyperMatrix[] X = HyperMatrix.meshgrid(lin); + + // List containing the Gaussian + g = new ArrayList<HyperMatrix>(); + for (int k=0; k<K; k++) { + HyperMatrix gg = new HyperMatrix(X[0].getDimensions(),0.); + for (int j =0; j<Params.dim; j++) { + gg=gg.add(X[j].pow(2)); + } + gg=gg.div(-2*sigma_list[k]*sigma_list[k]).exp(); + g.add(gg); + } + } + + + @Override + public String getName() { + return "Scale Space"; + } + + @Override + public double[] process(HyperMatrix patch) { + + // Upsample the patch + HyperMatrix patch_sr = patch.copy(); + patch_sr.imresize(nsr[0], nsr[1], nsr[2]); + + // Loop over scales + HyperMatrix corr =null; + DoubleMatrix LocMax = null; + double[] center = {(nsr[0]-1)/2,(nsr[1]-1)/2,(nsr[2]-1)/2}; + double dist, dist_tmp; + int idx; + if (Params.dim==2) + center[2]=0; + double[] pos = {center[0],center[1],center[2]}; + for (int k=0; k<K; k++) { + // Max correlation with Gaussians + corr =patch_sr.conv(g.get(k)); + //corr.display("corr",null); + LocMax = corr.localMax(1,1e-5).find(); + // Find point closest to pos + if (LocMax.rows!=0) { + dist = 0; idx =0; + for (int j=0; j<Params.dim;j++) {dist += Math.pow(pos[j] - LocMax.get(0,j+1),2);} // Compute distance to prev pos + for (int i=1; i<LocMax.rows; i++) { + dist_tmp =0; + for (int j=0; j<Params.dim;j++) {dist_tmp += Math.pow(pos[j] - LocMax.get(i,j+1),2);}// Compute distance to prev pos + if (dist_tmp<dist) { // if better, update dist and idx + dist = dist_tmp; + idx = i; + } + } + for (int j=0;j<Params.dim;j++) + pos[j] = LocMax.get(idx,j+1); + } + } + double[] shift = new double[3]; + shift[0]=(pos[0]-center[0])/Params.sr; + shift[1]=(pos[1]-center[1])/Params.sr; + shift[2]=(pos[2]-center[2])/Params.sr; + if ((Math.abs(shift[0]) > (Params.psf_visible_width[0]-1)/2) || (Math.abs(shift[1]) > (Params.psf_visible_width[1]-1)/2) || (Math.abs(shift[2]) > (Params.psf_visible_width[2]-1)/2) ) { + shift[0]=0; + shift[1]=0; + shift[2]=0; + } + //System.out.printf("shift =[%1.5f,%1.5f,%1.5f] \n",shift[0],shift[1],shift[2]); + + return shift; + } + + + + +} diff --git a/java code/src/eigenpsf/project/DragAndDropPanel.java b/java code/src/eigenpsf/project/DragAndDropPanel.java new file mode 100644 index 0000000000000000000000000000000000000000..c5bf2a66e0ccf395a968c61ec3568871806afe88 --- /dev/null +++ b/java code/src/eigenpsf/project/DragAndDropPanel.java @@ -0,0 +1,67 @@ +package eigenpsf.project; + +import java.awt.BorderLayout; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetDropEvent; +import java.io.File; +import java.io.IOException; +import java.util.List; + +import javax.swing.JPanel; + +import bilib.commons.components.HTMLPane; + +public class DragAndDropPanel extends JPanel { + + private MainDialog main; + private Project project; + private HTMLPane info = new HTMLPane(300, 300); + + public DragAndDropPanel(MainDialog main, Project project) { + this.main = main; + this.project = project; + info.append("h1", "Drag and drop your images files"); + info.append("p", ""); + info.append("p", "- add a 2D or 3D image file"); + info.append("p", "- add list of a group of files"); + + this.setLayout(new BorderLayout()); + this.add(info.getPane(), BorderLayout.CENTER); + info.setDropTarget(new LocalDropTarget()); + } + + public class LocalDropTarget extends DropTarget { + + @Override + public void drop(DropTargetDropEvent e) { + e.acceptDrop(DnDConstants.ACTION_COPY); + e.getTransferable().getTransferDataFlavors(); + Transferable transferable = e.getTransferable(); + DataFlavor[] flavors = transferable.getTransferDataFlavors(); + for (DataFlavor flavor : flavors) { + if (flavor.isFlavorJavaFileListType()) { + try { + List<File> files = (List<File>) transferable.getTransferData(flavor); + for (File file : files) { + project.addEntry(file.getAbsolutePath()); + } + main.update(); + } + catch (UnsupportedFlavorException ex) { + ex.printStackTrace(); + } + catch (IOException ex) { + ex.printStackTrace(); + } + } + } + e.dropComplete(true); + super.drop(e); + } + } + +} diff --git a/java code/src/eigenpsf/project/MainDialog.java b/java code/src/eigenpsf/project/MainDialog.java new file mode 100644 index 0000000000000000000000000000000000000000..ea4f79349103d78500ca67eedf8795a47773260c --- /dev/null +++ b/java code/src/eigenpsf/project/MainDialog.java @@ -0,0 +1,191 @@ +package eigenpsf.project; + +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.io.File; +import java.util.ArrayList; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JSplitPane; +import javax.swing.JToolBar; + +import eigenpsf.Constants; +import eigenpsf.Log; +import eigenpsf.Params; +import eigenpsf.SettingsDialog; +import eigenpsf.filemanager.IO; +import eigenpsf.gui.WalkBar; +import eigenpsf.lib.Loader; +import eigenpsf.processing.ProcessingPanel; +import eigenpsf.stack.ZStack; +import ij.gui.GUI; + +public class MainDialog extends JDialog implements ActionListener, WindowListener { + + public JButton bnAdd = new JButton("Add Images or Stacks"); + public JButton bnRemove = new JButton("Remove Selected Files"); + public ProjectTable table; + private Project project; + public ProcessingPanel pnProcessing; + public Toolbar toolbar; + private WalkBar statusbar; + + public SettingsDialog settingsDialog = null; + + private JPanel cardsLeft = new JPanel(new CardLayout()); + + public MainDialog(String pathJar) { + super(new JFrame(), Constants.name + " " + Constants.version); + + project = new Project(pathJar); + //Loader.searchCppLib(project); + Loader.searchSIFTBasis(project); + + toolbar = new Toolbar(this); + statusbar = new WalkBar(Constants.copyright, 100); + settingsDialog = new SettingsDialog(project); + + table = new ProjectTable(project, this); + pnProcessing = new ProcessingPanel(project,this); + + JToolBar toolStack = new JToolBar("Stack"); + toolStack.setLayout(new GridLayout(1, 2)); + toolStack.setFloatable(false); + toolStack.add(bnAdd); + toolStack.add(bnRemove); + + cardsLeft.setBorder(BorderFactory.createEtchedBorder()); + cardsLeft.add(new DragAndDropPanel(this, project), "0"); + cardsLeft.add(table.getPane(300, 200), "1"); + + JPanel pn1 = new JPanel(new BorderLayout()); + pn1.add(cardsLeft, BorderLayout.CENTER); + pn1.add(toolStack, BorderLayout.SOUTH); + + JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, pn1, pnProcessing); + split.setOneTouchExpandable(true); + split.setDividerLocation(300); + JPanel main = new JPanel(new BorderLayout()); + main.add(toolbar, BorderLayout.NORTH); + main.add(split, BorderLayout.CENTER); + main.add(statusbar, BorderLayout.SOUTH); + + bnAdd.addActionListener(this); + bnRemove.addActionListener(this); + addWindowListener(this); + add(main); + pack(); + GUI.center(this); + setVisible(true); + Log.init(statusbar, project); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == toolbar.bnReset) { + int ret = JOptionPane.showConfirmDialog(null, "Do you want to clear all the project?", "Warning", JOptionPane.YES_NO_OPTION); + if (ret == JOptionPane.YES_OPTION) { + project.reset(); + table.update(project); + pnProcessing.update(); + Log.clear(); + } + } + else if (e.getSource() == toolbar.bnSetting) { + settingsDialog.setVisible(true); + } + else if (e.getSource() == toolbar.bnOpen) { + project.path = IO.browseOpenProject(project.path); + String filename = project.path + File.separator + "listfiles.csv"; + File file = new File(filename); + if (file.exists()) { + Params.load(project); + project.openCVS(filename); + Log.write(project.path + " looaded."); + } + else { + Log.write(project.path + " is empty"); + } + } + else if (e.getSource() == toolbar.bnSave) { + project.path = IO.browseSaveProject(project.path); + if (project.path != null) + project.save(project.path); + + } + + else if (e.getSource() == bnAdd) { + File file = new File(IO.browseOpen(".")); + if (file != null) + project.addEntry(file.getAbsolutePath()); + update(); + } + else if (e.getSource() == bnRemove) { + ArrayList<ZStack> stacks = table.getSelectedStack(); + for(ZStack stack : stacks) { + Log.write("Remove stack " + stack.getName()); + project.remove(stack); + } + update(); + } + } + + public void changeValues() { + table.changeValues(project); + pnProcessing.update(); + } + + public void update() { + table.update(project); + pnProcessing.update(); + CardLayout cl = (CardLayout) (cardsLeft.getLayout()); + cl.show(cardsLeft, project.size() == 0 ? "0" : "1"); + } + + @Override + public void windowOpened(WindowEvent e) { + // TODO Auto-generated method stub + } + + @Override + public void windowClosing(WindowEvent e) { + project.closeOpenImages(); + dispose(); + } + + @Override + public void windowClosed(WindowEvent e) { + project.closeOpenImages(); + dispose(); + } + + @Override + public void windowIconified(WindowEvent e) { + // TODO Auto-generated method stub + } + + @Override + public void windowDeiconified(WindowEvent e) { + // TODO Auto-generated method stub + } + + @Override + public void windowActivated(WindowEvent e) { + // TODO Auto-generated method stub + } + + @Override + public void windowDeactivated(WindowEvent e) { + // TODO Auto-generated method stub + } +} diff --git a/java code/src/eigenpsf/project/Project.java b/java code/src/eigenpsf/project/Project.java new file mode 100644 index 0000000000000000000000000000000000000000..f8286c78b07479ed4efb2493540ec0013ae5a9cb --- /dev/null +++ b/java code/src/eigenpsf/project/Project.java @@ -0,0 +1,561 @@ +package eigenpsf.project; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.StringTokenizer; + +import javax.swing.JOptionPane; + +import bilib.commons.table.CustomizedColumn; +import eigenpsf.Log; +import eigenpsf.Params; +import eigenpsf.data.EigenElements; +import eigenpsf.data.HyperMatrix; +import eigenpsf.filemanager.IO; +import eigenpsf.stack.Patch; +import eigenpsf.stack.Patches; +import eigenpsf.stack.ZStack; +import ij.IJ; +import ij.ImagePlus; +import ij.process.Blitter; +import ij.process.ImageProcessor; + +public class Project extends ArrayList<ZStack> { + + public String path = "."; + public String pathUtils = "."; + public EigenElements eigen = null; + //public ArrayList<HyperMatrix> patchesForSvd = null; // to store the registered patches used in the initial SVD + //public boolean isCppLib = false; // true if the lib for Cpp Refine has been found + public boolean isSIFTbasis = false; // true if the SIFT basis has been found + //public boolean isRefined = false; + public boolean isComputed = false; + public boolean isPreprocessed = false; + + private ArrayList<ImagePlus> listOfOpenImages = new ArrayList<ImagePlus>(); + + public Project(String pathUtils_) { + super(); + this.pathUtils = pathUtils_; + } + + public void addOpenImages(ImagePlus imp) { + listOfOpenImages.add(imp); + } + + public void reset() { + clear(); + closeOpenImages(); + } + /* + public void setStatusCppLib(boolean isCpp_) { + isCppLib = isCpp_; + } + */ + public void setStatusSIFTBasis(boolean isSIFTbasis_) { + isSIFTbasis = isSIFTbasis_; + } + + public ZStack getBeadFromID(int id) { + for (ZStack stack : this) { + if (stack.id == id) + return stack; + } + return null; + } + + public ZStack getBeadFromName(String name) { + for (ZStack stack : this) { + if (stack.getName().equals(name)) + return stack; + } + return null; + } + + public Patches getAllPatches() { + Patches all = new Patches(); + for (ZStack stack : this) + if (stack.isSelected()) + all.addAll(stack.patches); + return all; + } + + public Patches getAllPatchesSortedByGlobId() { + Patches all = new Patches(); + int nbPatches = 0; + for (ZStack stack : this) + nbPatches+=stack.patches.size(); + for (int i=1; i<=nbPatches; i++) { + if (getPatchFromGlobId(i)!=null) + all.add(getPatchFromGlobId(i)); + } + + return all; + } + + public Patches getAllPatchesSortedByGlobIdAndSelect() { + Patches all = new Patches(); + int nbPatches = 0; + for (ZStack stack : this) + nbPatches+=stack.patches.size(); + for (int i=1; i<=nbPatches; i++) { + Patch p = getPatchFromGlobId(i); + if (p!=null & p.select) + all.add(p); + } + + return all; + } +/* + public HyperMatrix getSelectedAndRegisteredPatches() { + int size_prod = Params.psf_visible_width[0] * Params.psf_visible_width[1] * Params.psf_visible_width[2]; + Patches patches = getAllPatches(); + int nbPatch = 0; + for (int i = 0; i < patches.size(); i++) { + if (patches.get(i).select) { + nbPatch += 1; + } + } + HyperMatrix psfs_data = new HyperMatrix(Params.psf_visible_width[0], Params.psf_visible_width[1], Params.psf_visible_width[2], nbPatch); + int idx = 0; + for (int i = 0; i < patchesForSvd.size(); i++) { + if (patches.get(i).select) { + for (int k = 0; k < size_prod; k++) { + int[] pos = patchesForSvd.get(i).sub2ind(k); + psfs_data.setPixel(pos[0], pos[1], pos[2], idx, patchesForSvd.get(i).getPixel(pos[0], pos[1], pos[2], 0)); + } + idx += 1; + } + } + return psfs_data; + } +*/ + public int countSelectedAndRegisteredPatches() { + int nbPatch = 0; + for (ZStack stack : this) { + if (stack.isSelected()) { + for (Patch patch : stack.patches) { + if (patch.select) { + nbPatch++; + } + } + } + } + return nbPatch; + } + + public int countAllPatches() { + int nbPatch = 0; + for (ZStack stack : this) { + if (stack.isSelected()) { + for (Patch patch : stack.patches) { + nbPatch++; + } + } + } + return nbPatch; + } + + public HyperMatrix getSelectedAndRegisteredPatches() { + int size_prod = Params.psf_visible_width[0] * Params.psf_visible_width[1] * Params.psf_visible_width[2]; + int nbPatch = countSelectedAndRegisteredPatches(); + HyperMatrix psfs_data = new HyperMatrix(Params.psf_visible_width[0], Params.psf_visible_width[1], Params.psf_visible_width[2], nbPatch); + int idx = 0; + for (ZStack stack : this) { + if (stack.isSelected()) { + for (Patch patch : stack.patches) { + if (patch.select) { + for (int k = 0; k < size_prod; k++) { + + if (this.isPreprocessed) { + int[] pos = patch.data_register.sub2ind(k); + psfs_data.setPixel(pos[0], pos[1], pos[2], idx, patch.data_register.getPixel(pos[0], pos[1], pos[2], 0)); + } + else { + int[] pos = patch.data.sub2ind(k); + psfs_data.setPixel(pos[0], pos[1], pos[2], idx, patch.data.getPixel(pos[0], pos[1], pos[2], 0)); + } + } + idx += 1; + } + } + + } + } + return psfs_data; + } + + public HyperMatrix getAllPatchesHyperMatrix() { + int size_prod = Params.psf_visible_width[0] * Params.psf_visible_width[1] * Params.psf_visible_width[2]; + int nbPatch = countAllPatches(); + HyperMatrix psfs_data = new HyperMatrix(Params.psf_visible_width[0], Params.psf_visible_width[1], Params.psf_visible_width[2], nbPatch); + int idx = 0; + for (ZStack stack : this) { + for (Patch patch : stack.patches) { + for (int k = 0; k < size_prod; k++) { + if (this.isPreprocessed) { + int[] pos = patch.data_register.sub2ind(k); + psfs_data.setPixel(pos[0], pos[1], pos[2], idx, patch.data_register.getPixel(pos[0], pos[1], pos[2], 0)); + } + else { + int[] pos = patch.data.sub2ind(k); + psfs_data.setPixel(pos[0], pos[1], pos[2], idx, patch.data.getPixel(pos[0], pos[1], pos[2], 0)); + } + } + idx += 1; + } + } + return psfs_data; + } + + + public ArrayList<ZStack> addEntry(String path) { + ArrayList<ZStack> entries = new ArrayList<ZStack>(); + File file = new File(path); + if (file == null) + return entries; + if (file.getName().startsWith(".")) + return entries; + if (file.isDirectory()) { + int ret = JOptionPane.showConfirmDialog(null, "Do you want to load all images into one stack?", "Warning", JOptionPane.YES_NO_OPTION); + if (ret == JOptionPane.YES_OPTION) { + ImagePlus imp = IO.openStack(file.getAbsolutePath()); + if (imp != null) { + ZStack stack = new ZStack(getNewID(), imp, file); + add(stack); + entries.add(stack); + return entries; + } + } + else { + String list[] = file.list(); + if (list == null) + return entries; + for (String name : list) { + String filename = path + File.separator + name; + File f = new File(filename); + if (f.exists()) + entries.addAll(addFile(f)); + } + } + } + else { + addFile(file); + } + + return entries; + } + + private ArrayList<ZStack> addFile(File file) { + ArrayList<ZStack> entries = new ArrayList<ZStack>(); + if (getBeadFromName(file.getName()) != null) + return entries; + if (file.exists()) { + String filename = file.getAbsolutePath(); + if (!filename.startsWith(".")) { + ImagePlus imp = IJ.openImage(filename); + if (imp != null) { + ZStack stack = new ZStack(getNewID(), imp, file); + add(stack); + entries.add(stack); + return entries; + } + } + } + return entries; + } + + private boolean addDirectory(File dir) { + if (!dir.exists()) + return false; + if (!dir.isDirectory()) + return false; + + String list[] = dir.list(); + for (String f : list) { + addFile(new File(dir + File.separator + f)); + } + return true; + } + + public int getNewID() { + int max = 0; + for (ZStack stack : this) { + max = Math.max(stack.id, max); + } + return max + 1; + } + + public Patch getPatch(String stackName, int idPatch) { + for (ZStack stack : this) + if (stack.getName().equals(stackName)) + for (Patch patch : stack.patches) + if (patch.id == idPatch) + return patch; + return null; + } + + public Patch getPatchFromGlobId(String stackName, int glob_idPatch) { + for (ZStack stack : this) + if (stack.getName().equals(stackName)) + for (Patch patch : stack.patches) + if (patch.glob_id == glob_idPatch) + return patch; + return null; + } + + public Patch getPatchFromGlobId(int glob_idPatch) { + for (ZStack stack : this) + for (Patch patch : stack.patches) + if (patch.glob_id == glob_idPatch) + return patch; + return null; + } + + public void setSelected(boolean b, Patches selectedPatches) { + for (Patch patch : selectedPatches) { + patch.select = b; + } + } + + /* + public void showSelectedAsSingleRaw(Patches selectedPatches) { + int sz[] = { Params.psf_visible_width[0], Params.psf_visible_width[1] }; + for (Patch patch : selectedPatches) { + HyperMatrix im = patch.stack.getHyperMatrix(); + int pos[] = { patch.xp, patch.yp }; + ImagePlus p = HyperMatrix.HyperMatrix2ImagePlus(im.getPatch(pos, sz)); + p.getProcessor().resetMinAndMax(); + p.setTitle(patch.stack.getName() + " " + patch.id); + p.show(); + listOfOpenImages.add(p); + } + } + + public void showSelectedAsAverageRaw(Patches selectedPatches) { + if (selectedPatches.size() <= 0) + return; + int sz[] = { Params.psf_visible_width[0], Params.psf_visible_width[1] }; + Patch patch = selectedPatches.get(0); + HyperMatrix im = patch.stack.getHyperMatrix(); + int pos[] = { patch.xp, patch.yp }; + ImagePlus ave = HyperMatrix.HyperMatrix2ImagePlus(im.getPatch(pos, sz)); + int nza = ave.getNSlices(); + for (int i=1; i<selectedPatches.size(); i++) { + HyperMatrix imi = selectedPatches.get(i).stack.getHyperMatrix(); + ImagePlus p = HyperMatrix.HyperMatrix2ImagePlus(imi.getPatch(pos, sz)); + int nz = p.getNSlices(); + for(int z=1; z<=Math.min(nz, nza); z++) { + ImageProcessor ip = p.getStack().getProcessor(z); + ImageProcessor ap = p.getStack().getProcessor(z); + ap.multiply(i); + ap.copyBits(ip, (sz[0] + 1) * i, 0, Blitter.ADD); + ap.multiply(1.0/(i+1)); + } + } + ave.getProcessor().resetMinAndMax(); + ave.show(); + listOfOpenImages.add(ave); + } + + + public void showSelectedAsMontageRaw(Patches selectedPatches) { + if (selectedPatches.size() <= 0) + return; + int np = selectedPatches.size(); + int nz = selectedPatches.get(0).stack.getHyperMatrix().nz; + int sz[] = { Params.psf_visible_width[0], Params.psf_visible_width[1] }; + ImagePlus imp = IJ.createHyperStack("Montage", (sz[0] + 1) * np, sz[1], 1, nz, 1, 32); + int i = 0; + for (Patch patch : selectedPatches) { + HyperMatrix im = patch.stack.getHyperMatrix(); + int pos[] = { patch.xp, patch.yp }; + ImagePlus p = HyperMatrix.HyperMatrix2ImagePlus(im.getPatch(pos, sz)); + if (p != null) { + for(int z=1; z<=Math.min(p.getNSlices(), nz); z++) { + ImageProcessor ip = p.getStack().getProcessor(z); + ImageProcessor op = imp.getStack().getProcessor(z); + op.copyBits(ip, (sz[0] + 1) * i, 0, Blitter.COPY); + } + i++; + } + } + imp.getProcessor().resetMinAndMax(); + imp.show(); + listOfOpenImages.add(imp); + } + + public void showSelectedAsStackRaw(Patches selectedPatches) { + int sz[] = { Params.psf_visible_width[0], Params.psf_visible_width[1], Params.psf_visible_width[2] }; + int np = selectedPatches.size(); + if (np == 0) + return; + + int nz = selectedPatches.get(0).stack.getHyperMatrix().nz; + ImagePlus imp = IJ.createHyperStack("Stack", sz[0], sz[1], 1, nz, np, 32); + + int i = 1; + for (Patch patch : selectedPatches) { + HyperMatrix im = patch.stack.getHyperMatrix(); + int pos[] = { patch.xp, patch.yp }; + ImagePlus p = HyperMatrix.HyperMatrix2ImagePlus(im.getPatch(pos, sz)); + if (p != null) { + for(int z=1; z<=Math.min(p.getNSlices(), nz); z++) { + ImageProcessor ip = p.getStack().getProcessor(z); + imp.setPositionWithoutUpdate(1, z, i); + ImageProcessor op = imp.getProcessor(); + op.copyBits(ip, 0, 0, Blitter.COPY); + } + } + i++; + } + imp.getProcessor().resetMinAndMax(); + imp.show(); + listOfOpenImages.add(imp); + + + //int cpt = 0; + //HyperMatrix patchList = new HyperMatrix(sz[0], sz[1], sz[2], np); + //for (Patch patch : selectedPatches) { + //HyperMatrix imp = patch.stack.getHyperMatrix(); + //int xx = patch.xp; + //int yy = patch.yp; + //int zz = patch.zp; + //int pos[] = { xx, yy, zz }; + //patchList.setColumn(imp.getPatch(pos, sz), cpt, 0); + //cpt++; + //} + //patchList.display("Selected patches.", listOfOpenImages); + + } + + + public void showSelectedAsSingleProcessed(Patches selectedPatches) { + for (Patch patch : selectedPatches) { + HyperMatrix data = patch.data_register; + + + if (data != null) { + //Patch ptrans = patch.data; //.imtranslate(patch.x, patch.y, patch.z); + ImagePlus p = HyperMatrix.HyperMatrix2ImagePlus(data); + p.getProcessor().resetMinAndMax(); + p.setTitle(patch.stack.getName() + " " + patch.id); + p.show(); + listOfOpenImages.add(p); + } + } + } + + public void showSelectedAsSingleBackground(Patches selectedPatches) { + for (Patch patch : selectedPatches) { + HyperMatrix data = patch.data_register; + // TODO + if (data != null) { + ImagePlus p = HyperMatrix.HyperMatrix2ImagePlus(data); + p.getProcessor().resetMinAndMax(); + p.setTitle(patch.stack.getName() + " " + patch.id); + p.show(); + listOfOpenImages.add(p); + } + } + } + + public void showSelectedAsMontageProcessed(Patches selectedPatches) { + for (Patch patch : selectedPatches) { + HyperMatrix data = patch.data_register; + // TODO + if (data != null) { + ImagePlus p = HyperMatrix.HyperMatrix2ImagePlus(data); + p.getProcessor().resetMinAndMax(); + p.setTitle(patch.stack.getName() + " " + patch.id); + p.show(); + listOfOpenImages.add(p); + } + } + + } + public void showSelectedAsStackProcessed(Patches selectedPatches) { + + } + */ + + public ArrayList<ImagePlus> getListOfOpenImages() { + return listOfOpenImages; + } + + public void closeOpenImages() { + for (ImagePlus imp : listOfOpenImages) + if (imp != null) + imp.close(); + listOfOpenImages.clear(); + } + + public void load(String path) { + reset(); + openCVS(path + File.separator + "listfiles.csv"); + Params.load(this); + } + + public boolean openCVS(String filename) { + String line = ""; + try { + BufferedReader buffer = new BufferedReader(new FileReader(filename)); + line = buffer.readLine(); + line = buffer.readLine(); + clear(); + while (line != null) { + StringTokenizer tokens = new StringTokenizer(line, ","); + if (tokens.countTokens() >= 4) { + int id = Integer.parseInt(tokens.nextToken().trim()); + String name = tokens.nextToken().trim(); + String path = tokens.nextToken().trim(); + String mask = tokens.nextToken().trim(); + ZStack stack = new ZStack(id, name, path); + stack.setMaskFilename(mask); + stack.updateFileInfo(); + add(stack); + } + line = buffer.readLine(); + } + buffer.close(); + return true; + } + catch (Exception ex) { + System.out.println("Unable to read the file " + line); + } + return false; + } + + public void save(String path) { + File dir = new File(path); + dir.mkdir(); + saveCVS(path + File.separator + "listfiles.csv"); + Params.save(this); + } + + private void saveCVS(String filename) { + File file = new File(filename); + try { + ArrayList<CustomizedColumn> cols = ProjectTable.getHeader(); + String s = ""; + for (int i = 0; i < cols.size(); i++) + s += cols.get(i).getHeader() + ","; + BufferedWriter buffer = new BufferedWriter(new FileWriter(file)); + buffer.write(s + "\n"); + for (ZStack stack : this) { + String p = stack.id + "," + stack.getName() + "," + stack.getPath() + "," + stack.getMaskFilename(); + buffer.write(p + "\n"); + } + buffer.close(); + } + catch (IOException ex) { + System.out.println("" + ex); + } + } + +} diff --git a/java code/src/eigenpsf/project/ProjectTable.java b/java code/src/eigenpsf/project/ProjectTable.java new file mode 100644 index 0000000000000000000000000000000000000000..833abe2f4b9d6dd39d206e4502aba3c630a3f034 --- /dev/null +++ b/java code/src/eigenpsf/project/ProjectTable.java @@ -0,0 +1,235 @@ +package eigenpsf.project; + +import java.awt.Component; +import java.awt.Font; +import java.awt.Image; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.SwingConstants; +import javax.swing.table.TableCellRenderer; + +import bilib.commons.table.CustomizedColumn; +import bilib.commons.table.CustomizedTable; +import eigenpsf.MaskDialog; +import eigenpsf.stack.StackCanvas; +import eigenpsf.stack.ZStack; +import ij.IJ; +import ij.ImagePlus; +import ij.gui.ImageWindow; +import ij.gui.StackWindow; +import resources.IconLoader; + +public class ProjectTable extends CustomizedTable implements MouseListener { + + private Project project; + private MainDialog main; + private String s = "\u2612"; + private String u = "\u2610"; + + private int SHOW = 2; + private int EDIT = 4; + private int MASK = 3; + + public static ArrayList<CustomizedColumn> getHeader() { + ArrayList<CustomizedColumn> columns = new ArrayList<CustomizedColumn>(); + columns.add(new CustomizedColumn("Name", String.class, 180, false)); + columns.add(new CustomizedColumn("Beads", Integer.class, 50, false)); + columns.add(new CustomizedColumn("Show", String.class, 30, false)); + //columns.add(new CustomizedColumn("Edit", String.class, 30, false)); + columns.add(new CustomizedColumn("Mask", String.class, 30, false)); + return columns; + } + + public ProjectTable(Project project, MainDialog main) { + super(getHeader(), false); + this.project = project; + this.main = main; + setDropTarget(new LocalDropTarget()); + setFont(new Font(getFont().getFamily(), Font.PLAIN, getFont().getSize() - 2)); + getColumnModel().getColumn(SHOW).setCellRenderer(new ClientsTableButtonRenderer()); + //getColumnModel().getColumn(EDIT).setCellRenderer(new ClientsTableButtonRenderer()); + getColumnModel().getColumn(MASK).setCellRenderer(new ClientsTableButtonRenderer()); + addMouseListener(this); + } + + public ArrayList<ZStack> getSelectedStack() { + ArrayList<ZStack> selected = new ArrayList<ZStack>(); + int rows[] = getSelectedRows(); + for (int i = 0; i < rows.length; i++) { + int ic = convertRowIndexToModel(rows[i]); + String name = (String) getModel().getValueAt(ic, 0); + ZStack stack = project.getBeadFromName(name); + if (stack != null) selected.add(stack); + } + return selected; + } + + public void changeValues(Project project) { + this.project = project; + for (int row = 0; row < this.getRowCount(); row++) { + String name = this.getCell(row, 0); + for (ZStack stack : project) { + if (name.equals(stack.getName())) { + this.setCell(row, 1, "" + stack.patches.size()); + } + } + } + } + + public void update(Project project) { + this.project = project; + int sel[] = getSelectedRows(); + removeRows(); + ArrayList<ZStack> toadd = new ArrayList<ZStack>(); + for (ZStack stack : project) { + boolean flag = false; + for (int i = 0; i < getRowCount(); i++) { + String n = getCell(i, 0); + if (n.equals(stack.getName())) flag = true; + } + if (flag == false) toadd.add(stack); + } + for (ZStack stack : toadd) { + Object[] row = new Object[] { stack.getName(), stack.patches.size() }; + this.append(row); + } + setSelectedRows(sel); + } + + public void setSelectedRows(int[] rows) { + ListSelectionModel model = getSelectionModel(); + model.clearSelection(); + for (int row : rows) { + if (row >= 0 && row < getRowCount()) model.addSelectionInterval(row, row); + } + } + + @Override + public void mouseClicked(MouseEvent e) { + int row = this.getSelectedRow(); + if (row < 0) { + return; + } + int col = this.getSelectedColumn(); + if (col < 0) { + return; + } + ZStack stack = project.getBeadFromName(getCell(row, 0)); + if (stack == null) return; + if (col == EDIT) { + stack.show(project.getListOfOpenImages()); + } + if (col == SHOW) { + ImagePlus imp = stack.getImage(); + imp.show(); + StackCanvas canvas = new StackCanvas(imp, stack, main); + /* + if (imp.getStackSize() > 1) + imp.setWindow(new StackWindow(imp, canvas)); + else + imp.setWindow(new ImageWindow(imp, canvas));*/ + } + if (col == MASK) { + new MaskDialog(stack); + } + main.update(); + } + + @Override + public void mousePressed(MouseEvent e) { + } + + @Override + public void mouseReleased(MouseEvent e) { + } + + @Override + public void mouseEntered(MouseEvent e) { + } + + @Override + public void mouseExited(MouseEvent e) { + } + + public class LocalDropTarget extends DropTarget { + + @Override + public void drop(DropTargetDropEvent e) { + e.acceptDrop(DnDConstants.ACTION_COPY); + e.getTransferable().getTransferDataFlavors(); + Transferable transferable = e.getTransferable(); + DataFlavor[] flavors = transferable.getTransferDataFlavors(); + for (DataFlavor flavor : flavors) { + if (flavor.isFlavorJavaFileListType()) { + try { + List<File> files = (List<File>) transferable.getTransferData(flavor); + for (File file : files) { + project.addEntry(file.getAbsolutePath()); + } + main.update(); + } + catch (UnsupportedFlavorException ex) { + ex.printStackTrace(); + } + catch (IOException ex) { + ex.printStackTrace(); + } + } + } + e.dropComplete(true); + super.drop(e); + } + } + + class ClientsTableButtonRenderer extends JLabel implements TableCellRenderer { + public ClientsTableButtonRenderer() { + setOpaque(false); + this.setVerticalAlignment(SwingConstants.CENTER); + this.setHorizontalAlignment(SwingConstants.CENTER); + + } + + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + ImageIcon icon = null; + String text = ""; + + if (column == MASK) text = "mask";// setText("\u232B"); + else if (column == SHOW) text = "show";// setText("\u25a2"); + else if (column == EDIT) text = "edit";// setText("edit");//setText("\u25a3"); + + String tip = ""; + if (column == MASK) tip = "Control the mask image file";// setText("\u232B"); + else if (column == SHOW) tip = "Display the image";// setText("\u25a2"); + else if (column == EDIT) tip = "Edit the patches on the images";// setText("edit");//setText("\u25a3"); + setToolTipText(tip); + + try { + Image image = new IconLoader().get("/resources/" + text + ".png"); + Image scaled = image.getScaledInstance(22, 22, Image.SCALE_SMOOTH); + icon = new ImageIcon(scaled); + setIcon(icon); + } + catch (Exception ex) { + setText(text); + } + + return this; + } + } + +} diff --git a/java code/src/eigenpsf/project/StatusBar.java b/java code/src/eigenpsf/project/StatusBar.java new file mode 100644 index 0000000000000000000000000000000000000000..4dee7155d7c6cfc581822218c0c6874d7642485d --- /dev/null +++ b/java code/src/eigenpsf/project/StatusBar.java @@ -0,0 +1,26 @@ +package eigenpsf.project; + +import java.awt.BorderLayout; + +import javax.swing.BorderFactory; +import javax.swing.JLabel; +import javax.swing.JToolBar; + +import eigenpsf.Constants; + +public class StatusBar extends JToolBar { + + private JLabel status = new JLabel(Constants.copyright); + + public StatusBar() { + super("Status"); + setLayout(new BorderLayout()); + setBorder(BorderFactory.createEtchedBorder()); + setFloatable(false); + add(status, BorderLayout.CENTER); + } + + public void write(String message) { + status.setText(message); + } +} diff --git a/java code/src/eigenpsf/project/Toolbar.java b/java code/src/eigenpsf/project/Toolbar.java new file mode 100644 index 0000000000000000000000000000000000000000..27355cae8a949501962f4d20a7aec415550d402d --- /dev/null +++ b/java code/src/eigenpsf/project/Toolbar.java @@ -0,0 +1,183 @@ +package eigenpsf.project; + + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.GridLayout; +import java.awt.Rectangle; +import java.awt.Toolkit; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSeparator; +import javax.swing.JToolBar; +import javax.swing.SwingConstants; +import javax.swing.text.DefaultCaret; + +import bilib.commons.utils.WebBrowser; +import eigenpsf.Constants; +import eigenpsf.Log; +import eigenpsf.Params; +import eigenpsf.gui.ButtonIcon; + +public class Toolbar extends JToolBar implements ActionListener { + + public ButtonIcon bnReset = new ButtonIcon("reset", "Reset EigenPSF Project"); + public ButtonIcon bnOpen = new ButtonIcon("open", "Open EigenPSF Project"); + public ButtonIcon bnSave = new ButtonIcon("save", "Save EigenPSF Project"); + public ButtonIcon bnSetting = new ButtonIcon("settings", "Settings Parameters' Processing"); + public ButtonIcon bnGit = new ButtonIcon("github", "Source Code"); + + private ButtonIcon bnHelp = new ButtonIcon("help", "Help"); + private ButtonIcon bnAbout = new ButtonIcon("about", "About"); + + public Toolbar(MainDialog main) { + this.setFloatable(false); + JPanel tool1 = new JPanel(new GridLayout(1, 7, 0, 0)); + tool1.add(bnReset); + tool1.add(bnOpen); + tool1.add(bnSave); + tool1.add(new JSeparator(SwingConstants.VERTICAL)); + tool1.add(bnSetting); + tool1.add(new JSeparator(SwingConstants.VERTICAL)); + + JPanel tool2 = new JPanel(new GridLayout(1, 4, 0, 0)); + tool2.add(bnGit); + tool2.add(bnHelp); + tool2.add(bnAbout); + setBorder(BorderFactory.createEtchedBorder()); + + setLayout(new BorderLayout()); + add(tool1, BorderLayout.WEST); + add(new JLabel(""), BorderLayout.CENTER); + add(tool2, BorderLayout.EAST); + + bnReset.addActionListener(main); + bnOpen.addActionListener(main); + bnSave.addActionListener(main); + bnSetting.addActionListener(main); + bnAbout.addActionListener(this); + bnHelp.addActionListener(this); + bnGit.addActionListener(this); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == bnAbout) + showAbout(); + + if (e.getSource() == bnHelp) + WebBrowser.open(Constants.url); + + if (e.getSource() == bnGit) + WebBrowser.open(Constants.url); + } + + public void showAbout() { + + final JFrame frame = new JFrame("About "+ Constants.name); + JEditorPane pane = new JEditorPane(); + pane.setEditable(false); + pane.setContentType("text/html; charset=ISO-8859-1"); + pane.setText("<html><head><title>" + Constants.name + "</title>" + getStyle() + "</head><body>" + + "<p class=\"name\">" + Constants.name + "</p>" + // Name + "<p class=\"vers\">" + Constants.version + "</p>" + // Version + "<p class=\"desc\">" + Constants.url + "</p><hr>" + // Description + "</html>" + ); + + final JButton bnClose = new JButton("Close"); + bnClose.addActionListener( new ActionListener() { + public void actionPerformed(ActionEvent e) { + frame.dispose(); + } + }); + pane.setCaret(new DefaultCaret()); + JScrollPane scrollPane = new JScrollPane(pane); + //helpScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); + scrollPane.setPreferredSize(new Dimension(500, 500)); + frame.getContentPane().add(scrollPane, BorderLayout.NORTH); + frame.getContentPane().add(bnClose, BorderLayout.CENTER); + + frame.pack(); + frame.setResizable(false); + frame.setVisible(true); + center(frame); + } + + + /* + * Place the window in the center of the screen. + */ + private void center(Window w) { + Dimension screenSize = new Dimension(0, 0); + boolean isWin = System.getProperty("os.name").startsWith("Windows"); + if (isWin) { // GraphicsEnvironment.getConfigurations is *very* slow on Windows + screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + } + if (GraphicsEnvironment.isHeadless()) + screenSize = new Dimension(0, 0); + else { + // Can't use Toolkit.getScreenSize() on Linux because it returns + // size of all displays rather than just the primary display. + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice[] gd = ge.getScreenDevices(); + GraphicsConfiguration[] gc = gd[0].getConfigurations(); + Rectangle bounds = gc[0].getBounds(); + if (bounds.x==0&&bounds.y==0) + screenSize = new Dimension(bounds.width, bounds.height); + else + screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + } + Dimension window = w.getSize(); + if (window.width==0) + return; + int left = screenSize.width/2-window.width/2; + int top = (screenSize.height-window.height)/4; + if (top<0) top = 0; + w.setLocation(left, top); + } + + /* + * Defines the CSS style for the help and about window. + */ + private String getStyle() { + return + "<style type=text/css>" + + "body {backgroud-color:#222277}" + + "hr {width:80% color:#333366; padding-top:7px }" + + "p, li {margin-left:10px;margin-right:10px; color:#000000; font-size:1em; font-family:Verdana,Helvetica,Arial,Geneva,Swiss,SunSans-Regular,sans-serif}" + + "p.name {color:#ffffff; font-size:1.2em; font-weight: bold; background-color: #333366; text-align:center;}" + + "p.vers {color:#333333; text-align:center;}" + + "p.desc {color:#333333; font-weight: bold; text-align:center;}" + + "p.auth {color:#333333; font-style: italic; text-align:center;}" + + "p.orga {color:#333333; text-align:center;}" + + "p.date {color:#333333; text-align:center;}" + + "p.more {color:#333333; text-align:center;}" + + "p.help {color:#000000; text-align:left;}" + + "</style>"; + } + + public void setEnabled(boolean en) { + bnReset.setEnabled(en); + bnOpen.setEnabled(en); + bnSave.setEnabled(en); + bnSetting.setEnabled(en); + bnAbout.setEnabled(en); + bnHelp.setEnabled(en); + bnGit.setEnabled(en); + } +} \ No newline at end of file diff --git a/java code/src/eigenpsf/stack/Patch.java b/java code/src/eigenpsf/stack/Patch.java new file mode 100644 index 0000000000000000000000000000000000000000..52e4a607e292a5423a848e06fabf49522b4702ea --- /dev/null +++ b/java code/src/eigenpsf/stack/Patch.java @@ -0,0 +1,73 @@ +package eigenpsf.stack; + +import java.awt.Point; +import eigenpsf.data.HyperMatrix; + +public class Patch { + + public int xp; // x - center of the bead + public int yp; // y - center of the bead + public int zp; // z - center of the bead + public double dist = 0; // distance from center + public int id; + public int glob_id=0; // Global id attributed in the order of the Z-test score (to ease user experience) + public double max; // max value of the patch + public double x; // x - shift of the bead relative to xp: xp+x is the precise position + public double y; // y - shift of the bead relative to yp: yp+x is the precise position + public double z; // z - shift of the bead relative to zp: zp+x is the precise position + public HyperMatrix data; // extracted patch + public HyperMatrix data_register; + public double ztest_value = 0; + public boolean select = true; + public ZStack stack; + + public Patch(ZStack stack, int id, double dist, int xp, int yp, int zp, double x, double y, double z, double max) { + this.stack = stack; + this.id = id; + this.dist = dist; + this.xp = xp; + this.yp = yp; + this.zp = zp; + this.x = x; + this.y = y; + this.z = z; + this.max = max; + this.data = null; + this.data_register=null; + } + + public Patch(ZStack stack, int id, double dist, int xp, int yp, int zp, double x, double y, double z) { + this.stack = stack; + this.id = id; + this.dist = dist; + this.xp = xp; + this.yp = yp; + this.zp = zp; + this.x = x; + this.y = y; + this.z = z; + this.max = -1; + this.data = null; + this.data_register=null; + } + + public void shift(Point offset) { + xp += offset.x; + yp += offset.y; + } + + public double distance(Patch pacth) { + return Math.sqrt((xp-pacth.xp)*(xp-pacth.xp) + (yp-pacth.yp)*(yp-pacth.yp)); + } + + public double getmax() { + if (max==-1 & data!=null) { + max = data.max(); + } + return max; + } + + public String toString() { + return "(id: " + id + " x:" + xp + " y:" + yp + ")"; + } +} diff --git a/java code/src/eigenpsf/stack/Patches.java b/java code/src/eigenpsf/stack/Patches.java new file mode 100644 index 0000000000000000000000000000000000000000..906f4c4a5995584455bf99f456e30aff6505f31c --- /dev/null +++ b/java code/src/eigenpsf/stack/Patches.java @@ -0,0 +1,38 @@ +package eigenpsf.stack; + +import java.util.ArrayList; + +public class Patches extends ArrayList<Patch>{ + + public int getNextID() { + int max = 0; + for(Patch patch: this) + max = Math.max(max, patch.id); + return max+1; + } + + public Patch getPatchFromID(int id) { + for(Patch patch: this) { + if (patch.id == id) + return patch; + } + return null; + } + + public Patch getPatchFromPositionXY(double x, double y, double size) { + double min = Double.MAX_VALUE; + Patch closest = null; + for(Patch patch: this) { + double d = (patch.xp-x)*(patch.xp-x) + (patch.yp-y)*(patch.yp-y); + d = Math.sqrt(d); + if (d < min) { + min = d; + closest = patch; + } + } + System.out.println(" " + min + " " + size + " " ); + if (min < size) + return closest; + return null; + } +} diff --git a/java code/src/eigenpsf/stack/PatchesTable.java b/java code/src/eigenpsf/stack/PatchesTable.java new file mode 100644 index 0000000000000000000000000000000000000000..7069f5ef8ca0120faddbff709bdf6952f042b920 --- /dev/null +++ b/java code/src/eigenpsf/stack/PatchesTable.java @@ -0,0 +1,165 @@ +package eigenpsf.stack; + +import java.awt.Color; +import java.awt.Component; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.util.ArrayList; + +import javax.swing.JTable; +import javax.swing.table.DefaultTableCellRenderer; + +import bilib.commons.table.CustomizedColumn; +import bilib.commons.table.CustomizedTable; +import bilib.commons.utils.NumFormat; +import eigenpsf.project.Project; + +public class PatchesTable extends CustomizedTable implements MouseListener { + + private PopupMenu popup; + private Project project; + + public static ArrayList<CustomizedColumn> getHeader() { + ArrayList<CustomizedColumn> columns = new ArrayList<CustomizedColumn>(); + columns.add(new CustomizedColumn("Name", String.class, 30, false)); + columns.add(new CustomizedColumn("ID", Integer.class, 6, false)); + columns.add(new CustomizedColumn("X", Double.class, 20, false)); + columns.add(new CustomizedColumn("Y", Double.class, 20, false)); + columns.add(new CustomizedColumn("Z", Double.class, 20, false)); + columns.add(new CustomizedColumn("Quality", Double.class, 20, false)); + columns.add(new CustomizedColumn("Dist", Double.class, 20, false)); + columns.add(new CustomizedColumn("Max.", String.class, 20, false)); + columns.add(new CustomizedColumn("Valid", String.class, 20, false)); + return columns; + } + + public PatchesTable(Project project) { + super(getHeader(), true); + this.project = project; + popup = new PopupMenu(project, this); + addMouseListener(this); + //getColumnModel().getColumn(0).setCellRenderer(new PatchRenderer()); + //getColumnModel().getColumn(1).setCellRenderer(new PatchRenderer()); + setRowSelectionAllowed(true); + } + + public Patches getUserSelected() { + int[] rows = getSelectedRows(); + Patches selectedPatches = new Patches(); + for(int viewRow=0; viewRow<rows.length; viewRow++) { + int row = convertRowIndexToModel(rows[viewRow]); + String stackName = ""; + int id = -1; + try { + stackName = (String)getModel().getValueAt(row, 0); + Integer p = (Integer)getModel().getValueAt(row, 1); + id = p.intValue(); + } + catch(Exception ex) { + System.out.println("Exception " + ex); + } + //Patch patch = project.getPatch(stackName, id); + Patch patch = project.getPatchFromGlobId(stackName, id); + if (patch != null) { + selectedPatches.add(patch); + } + } + return selectedPatches; + } + public void update(ZStack stack) { + int selected[] = getSelectedRows(); + removeRows(); + for (Patch patch : stack.patches) { + Object[] row = new Object[] { + stack.getName(), patch.glob_id, patch.xp, patch.yp, patch.zp, + patch.ztest_value, patch.dist, NumFormat.sci(patch.getmax()), + patch.select ? "Ok" : ""}; + insert(row); + } + int min = Integer.MAX_VALUE; + int max = -1; + for (int i=0; i<selected.length; i++) { + min = Math.min(min, selected[i]); + max = Math.max(max, selected[i]); + } + if (min > 0 && max < this.getRowCount()) + setRowSelectionInterval(min, max); + } + + public void update(Project project) { + removeRows(); + for (ZStack stack : project) { + if (stack.isSelected()) { + for (Patch patch : stack.patches) { + if (patch != null) { + Object[] row = new Object[] { + stack.getName(), patch.glob_id, patch.xp, patch.yp, patch.zp, + patch.ztest_value, patch.dist, + NumFormat.sci(patch.getmax()), + patch.select ? "Ok" : ""}; + insert(row); + } + } + } + } + } + + public void showPopup(MouseEvent me) { + if (me.isPopupTrigger()) popup.show(me.getComponent(), me.getX(), me.getY()); + } + + public void mouseClicked(MouseEvent e) { + + } + + @Override + public void mousePressed(MouseEvent e) { + if (e.isPopupTrigger()) { + popup.show(e.getComponent(), e.getX(), e.getY()); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + if (e.isPopupTrigger()) { + popup.show(e.getComponent(), e.getX(), e.getY()); + } + } + + @Override + public void mouseEntered(MouseEvent e) { + // TODO Auto-generated method stub + } + + @Override + public void mouseExited(MouseEvent e) { + // TODO Auto-generated method stub + } + + + class PatchRenderer extends DefaultTableCellRenderer { + + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + String stackName = ""; + Integer id = -1; + try { + stackName = (String)table.getModel().getValueAt(row, 0); + id = (Integer)(table.getModel().getValueAt(row, 1)); + } + catch(Exception ex) {} + Patch patch = project.getPatch(stackName, id); + + if (patch == null) + return this; + setForeground(patch.select ? Color.red : Color.gray); + if (isSelected) { + setBackground(Color.blue); + } else { + setBackground(table.getBackground()); + } + if (column == 0) setText(stackName ); + if (column == 1) setText(""+id); + return this; + } + } +} diff --git a/java code/src/eigenpsf/stack/PopupMenu.java b/java code/src/eigenpsf/stack/PopupMenu.java new file mode 100644 index 0000000000000000000000000000000000000000..2289fd45ccd2820c0d7b20db3821bcd8cea905d2 --- /dev/null +++ b/java code/src/eigenpsf/stack/PopupMenu.java @@ -0,0 +1,55 @@ +package eigenpsf.stack; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; + +import eigenpsf.Log; +import eigenpsf.project.Project; + +public class PopupMenu extends JPopupMenu implements ActionListener { + + //private JMenuItem show = new JMenuItem("Show"); + + //private JMenuItem measure = new JMenuItem("Measure"); + private JMenuItem unselect = new JMenuItem("Mark as unvalid"); + private JMenuItem select = new JMenuItem("Mark as valid"); + private Project project; + private PatchesTable table; + + public PopupMenu(Project project, PatchesTable table) { + this.project = project; + this.table = table; + add(select); + add(unselect); + //addSeparator(); + //add(show); + //show.addActionListener(this); + //measure.addActionListener(this); + unselect.addActionListener(this); + select.addActionListener(this); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (project == null) + return; + Patches selectedPatches = table.getUserSelected(); + + + if (e.getSource() == unselect) { + project.setSelected(false, selectedPatches); + } + if (e.getSource() == select) { + project.setSelected(true, selectedPatches); + } + + /*if (e.getSource() == show) { + new ShowPatchDialog(project, selectedPatches); + }*/ + table.update(project); + } + +} \ No newline at end of file diff --git a/java code/src/eigenpsf/stack/ShowPatchDialog.java b/java code/src/eigenpsf/stack/ShowPatchDialog.java new file mode 100644 index 0000000000000000000000000000000000000000..08671b5fed973226758efcfe33af6d117934bb4d --- /dev/null +++ b/java code/src/eigenpsf/stack/ShowPatchDialog.java @@ -0,0 +1,332 @@ +package eigenpsf.stack; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; + +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JTextField; + +import bilib.commons.components.GridPanel; +import eigenpsf.Log; +import eigenpsf.Params; +import eigenpsf.data.HyperMatrix; +import eigenpsf.project.Project; +import ij.IJ; +import ij.ImagePlus; +import ij.gui.GUI; +import ij.plugin.Zoom; +import ij.process.Blitter; +import ij.process.ImageProcessor; + +public class ShowPatchDialog extends JDialog implements ActionListener { + + private JButton bnCancel = new JButton("Cancel"); + private JButton bnShow = new JButton("Show"); + + private JRadioButton user = new JRadioButton("User", true); + private JRadioButton auto = new JRadioButton("Valid"); + private JRadioButton all = new JRadioButton("All"); + + private JCheckBox outputs[] = new JCheckBox[4]; + + private JRadioButton single = new JRadioButton("As Single"); + private JRadioButton stack = new JRadioButton("As Stack", true); + private JRadioButton montage = new JRadioButton("As Montage"); + private JRadioButton average = new JRadioButton("Averaged"); + + private JComboBox<String> zoom = new JComboBox<String>(new String[] { "100%", "200%", "400%", "800%", "1600%" }); + private JTextField prefix = new JTextField("", 10); + + private Project project; + private Patches userSelection; + private Patches autoSelection; + private Patches allSelection; + + private int[] sz; + + public ShowPatchDialog(Project project, Patches userSelection) { + super(new JFrame(), "Show Patch"); + this.project = project; + this.userSelection = userSelection; + this.allSelection = allSelection(); + this.autoSelection = autoSelection(); + + outputs[0] = new JCheckBox("Raw data", true); + outputs[1] = new JCheckBox("Processed"); + outputs[2] = new JCheckBox("Background"); + outputs[3] = new JCheckBox("Proj. EigenPSF"); + + if (Params.dim == 2) { + sz = new int[] { Params.psf_visible_width[0], Params.psf_visible_width[1],1 }; + } + else { + sz = new int[] { Params.psf_visible_width[0], Params.psf_visible_width[1], Params.psf_visible_width[2] }; + } + + int z = Math.max(0, Math.min(4, (int) Math.log(150.0 / sz[0]))); + zoom.setSelectedIndex(z); + ButtonGroup bg1 = new ButtonGroup(); + bg1.add(user); + bg1.add(auto); + bg1.add(all); + + user.setText("User (" + userSelection.size() + ")"); + auto.setText("Valid (" + autoSelection.size() + ")"); + all.setText("All (" + allSelection.size() + ")"); + + ButtonGroup bg2 = new ButtonGroup(); + bg2.add(single); + bg2.add(stack); + bg2.add(montage); + bg2.add(average); + + GridPanel pn = new GridPanel(""); + pn.place(2, 1, new JLabel("Selection")); + pn.place(2, 2, user); + pn.place(2, 3, auto); + pn.place(2, 4, all); + + pn.place(3, 1, new JLabel("Output")); + pn.place(3, 2, outputs[0]); + pn.place(3, 3, outputs[1]); + pn.place(3, 4, outputs[2]); + pn.place(3, 5, outputs[3]); + if (!project.isComputed) { + outputs[3].setEnabled(false); + } + if (!project.isPreprocessed) { + outputs[2].setEnabled(false); + outputs[1].setEnabled(false); + } + + pn.place(4, 1, new JLabel("Display")); + pn.place(4, 2, single); + pn.place(4, 3, stack); + pn.place(4, 4, montage); + pn.place(4, 5, average); + + pn.place(5, 1, new JLabel("Zoom")); + pn.place(5, 2, zoom); + pn.place(5, 3, new JLabel("Prefix")); + pn.place(5, 4, 2, 1, prefix); + + JPanel bn = new JPanel(new FlowLayout()); + bn.add(bnCancel); + bn.add(bnShow); + + bnCancel.addActionListener(this); + bnShow.addActionListener(this); + setLayout(new BorderLayout()); + GridPanel main = new GridPanel(false, 8); + main.place(1, 1, pn); + main.place(2, 1, bn); + add(main); + + setModal(true); + pack(); + GUI.center(this); + setVisible(true); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == bnCancel) { + dispose(); + } + if (e.getSource() == bnShow) { + + Patches sel = null; + if (all.isSelected()) { + sel= project.getAllPatchesSortedByGlobId(); + } + /* + Patches sel = new Patches(); + if (all.isSelected()) { + for (ZStack stack : project) + for (Patch patch : stack.patches) + sel.add(patch); + } + + if (auto.isSelected()) { + for (ZStack stack : project) + if (stack.isSelected()) + for (Patch patch : stack.patches) + if (patch.select) + sel.add(patch); + } + */ + if (auto.isSelected()) { + sel = project.getAllPatchesSortedByGlobIdAndSelect(); + } + + if (user.isSelected()) + sel = userSelection; + if (single.isSelected()) + showSingle(sel); + if (stack.isSelected()) + showStack(sel); + if (montage.isSelected()) + showMontage(sel); + if (average.isSelected()) + showAverage(sel); + dispose(); + } + } + + public void showSingle(ArrayList<Patch> selectedPatches) { + for (int k = 0; k < 4; k++) { + for (Patch patch : selectedPatches) + if (outputs[k].isSelected()) { + ImagePlus p = getPatchImage(patch, k); + p.setTitle(outputs[k].getText() + " " + patch.stack.getName() + " ID=" + patch.glob_id); + showFinalized(p); + } + } + } + + public void showMontage(ArrayList<Patch> selectedPatches) { + if (selectedPatches.size() <= 0) + return; + int np = selectedPatches.size(); + for (int k = 0; k < 4; k++) + if (outputs[k].isSelected()) { + ImagePlus imp = IJ.createHyperStack(outputs[k].getText(), (sz[0] + 1) * np, sz[1], 1, sz[2], 1, 32); + int i = 0; + for (Patch patch : selectedPatches) { + ImagePlus p = getPatchImage(patch, k); + if (p != null) { + for (int z = 1; z <= Math.min(p.getNSlices(), sz[2]); z++) { + ImageProcessor ip = p.getStack().getProcessor(z); + ImageProcessor op = imp.getStack().getProcessor(z); + op.copyBits(ip, (sz[0] + 1) * i, 0, Blitter.COPY); + } + i++; + } + } + showFinalized(imp); + } + } + + public void showStack(ArrayList<Patch> selectedPatches) { + int np = selectedPatches.size(); + if (np == 0) + return; + for (int k = 0; k < 4; k++) + if (outputs[k].isSelected()) { + ImagePlus imp = IJ.createHyperStack(outputs[k].getText(), sz[0], sz[1], 1, sz[2], np, 32); + int i = 1; + for (Patch patch : selectedPatches) { + ImagePlus p = getPatchImage(patch, k); + if (p != null) { + for (int z = 1; z <= Math.min(p.getNSlices(), sz[2]); z++) { + ImageProcessor ip = p.getStack().getProcessor(z); + imp.setPositionWithoutUpdate(1, z, i); + ImageProcessor op = imp.getProcessor(); + op.copyBits(ip, 0, 0, Blitter.COPY); + } + } + i++; + } + showFinalized(imp); + } + } + + public void showAverage(Patches selectedPatches) { + int np = selectedPatches.size(); + if (np == 0) + return; + for (int k = 0; k < 4; k++) + if (outputs[k].isSelected()) { + Patch patch = selectedPatches.get(0); + ImagePlus ave = getPatchImage(patch, k); + int nza = ave.getNSlices(); + for (int i = 1; i < selectedPatches.size(); i++) { + patch = selectedPatches.get(i); + ImagePlus p = getPatchImage(patch, k); + int nz = p.getNSlices(); + for (int z = 1; z <= Math.min(nz, nza); z++) { + ImageProcessor ip = p.getStack().getProcessor(z); + ImageProcessor ap = ave.getStack().getProcessor(z); + ap.multiply(i); + ap.copyBits(ip, 0, 0, Blitter.ADD); + ap.multiply(1.0 / (i + 1)); + } + } + ave.setTitle(outputs[k].getText()); + showFinalized(ave); + } + } + + private ImagePlus getPatchImage(Patch patch, int indexOutput) { + ImagePlus p = null; + int pos[]; + if (Params.dim == 2) { + pos = new int[] { patch.xp, patch.yp, 0}; + } + else { + pos = new int[] { patch.xp, patch.yp, patch.zp }; + } + + if (indexOutput == 0) { + HyperMatrix im = patch.stack.getHyperMatrix(); + p = HyperMatrix.HyperMatrix2ImagePlus(im.getPatch(pos, sz)); + } + if (indexOutput == 1) { + //p = HyperMatrix.HyperMatrix2ImagePlus(patch.data.imtranslate(patch.x, patch.y, patch.z)); + if (project.isPreprocessed) + p = HyperMatrix.HyperMatrix2ImagePlus(patch.data_register); + else + p = HyperMatrix.HyperMatrix2ImagePlus(patch.data); + } + if (indexOutput == 2) { + HyperMatrix im = patch.stack.getHyperMatrix(); + p = HyperMatrix.HyperMatrix2ImagePlus(im.getPatch(pos, sz).sub(patch.data)); + } + if (indexOutput == 3) { + p = HyperMatrix.HyperMatrix2ImagePlus(project.eigen.projPatch(patch)); + } + return p; + } + + private void showFinalized(ImagePlus imp) { + imp.setTitle(prefix.getText() + " " + imp.getTitle()); + project.addOpenImages(imp); + + imp.show(); + imp.getProcessor().resetMinAndMax(); + imp.updateAndDraw(); + + double mag = Math.pow(2, zoom.getSelectedIndex()); + //Zoom.set(imp, mag); + } + + private Patches allSelection() { + Patches sel = new Patches(); + for (ZStack stack : project) + for (Patch patch : stack.patches) + sel.add(patch); + return sel; + } + + private Patches autoSelection() { + Patches sel = new Patches(); + for (ZStack stack : project) + if (stack.isSelected()) + for (Patch patch : stack.patches) + if (patch.select) + sel.add(patch); + return sel; + } + +} diff --git a/java code/src/eigenpsf/stack/StackCanvas.java b/java code/src/eigenpsf/stack/StackCanvas.java new file mode 100644 index 0000000000000000000000000000000000000000..25345bb0f979be83221e1008ffa4733d19fafadc --- /dev/null +++ b/java code/src/eigenpsf/stack/StackCanvas.java @@ -0,0 +1,233 @@ +package eigenpsf.stack; +import java.awt.BasicStroke; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Panel; +import java.awt.Shape; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Rectangle2D; + +import javax.swing.JToolBar; + +import eigenpsf.Log; +import eigenpsf.Params; +import eigenpsf.display.Display; +import eigenpsf.display.DisplayDialog; +import eigenpsf.gui.ButtonIcon; +import eigenpsf.project.MainDialog; +import ij.ImagePlus; +import ij.gui.ImageCanvas; +import ij.gui.ImageWindow; +import ij.gui.StackWindow; + +public class StackCanvas extends ImageCanvas implements ActionListener { + + private ZStack stack; + private MainDialog main; + private int nx; + private int ny; + private ButtonIcon drawing = new ButtonIcon("drawing", "Display controls"); + //private ButtonIcon update = new ButtonIcon("update", "Update the positions and close"); + private boolean editable = true; + + public StackCanvas(ImagePlus imp, ZStack stack, MainDialog main) { + super(imp); + nx = imp.getWidth(); + ny = imp.getHeight(); + this.stack = stack; + this.main = main; + if (imp.getStackSize() > 1) + imp.setWindow(new StackWindow(imp, this)); + else + imp.setWindow(new ImageWindow(imp, this)); + + JToolBar tool = new JToolBar(); + tool.setFloatable(false); + tool.add(drawing); + // tool.addSeparator(); + // tool.add(update); + + ImageWindow win = imp.getWindow(); + Panel panel = new Panel(); + panel.setLayout(new FlowLayout(FlowLayout.LEFT)); + + panel.add(tool); + win.add(panel); + win.pack(); + + drawing.addActionListener(this); + // update.addActionListener(this); + + requestFocus(); + } + + @Override + public void paint(Graphics g1) { + super.paint(g1); + Graphics2D g = (Graphics2D) g1; + + paintRing(g); + if (stack.patches != null) { + int w = (int) ((Params.psf_visible_width[0] - 1) / 2); + int h = (int) ((Params.psf_visible_width[1] - 1) / 2); + double m = getMagnification(); + for (Patch patch : stack.patches) { + + if (patch.select && Display.box.show) { + g.setColor(Display.box.getColor()); + Shape r = new Rectangle2D.Double(screenXD(patch.xp - w), screenYD(patch.yp - h), m* w * 2, m*h * 2); + if (Display.box.config.equals("Plain")) + g.setStroke(new BasicStroke(Display.box.size)); + else g.setStroke(new BasicStroke(Display.box.size, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, + 10.0f, new float[] { 2.0f, 2.0f }, 0.0f)); + g.draw(r); + } + + if (!patch.select && Display.boxun.show) { + g.setColor(Display.boxun.getColor()); + Shape r = new Rectangle2D.Double(screenXD(patch.xp - w), screenYD(patch.yp - h), m* w * 2, m*h * 2); + if (Display.boxun.config.equals("Plain")) + g.setStroke(new BasicStroke(Display.boxun.size)); + else g.setStroke(new BasicStroke(Display.boxun.size, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, + 10.0f, new float[] { 2.0f, 2.0f }, 0.0f)); + g.draw(r); + } + + if (Display.text.show) { + g.setColor(Display.text.getColor()); + g.setFont(new Font(Font.MONOSPACED, Font.PLAIN, Display.text.size)); + int xp = screenXD(patch.xp); + int yp = screenYD(patch.yp); + if (Display.text.config.equals("ID")) + g.drawString("" + patch.glob_id, xp, yp); + if (Display.text.config.equals("Distance")) + g.drawString(nice(patch.dist), xp, yp); + if (Display.text.config.equals("Maximum")) + g.drawString(nice(patch.max), xp, yp); + if (Display.text.config.equals("Z-test")) + g.drawString(nice(patch.ztest_value), xp, yp); + if (Display.text.config.equals("X")) + g.drawString(nice(patch.xp + patch.x), xp, yp); + if (Display.text.config.equals("Y")) + g.drawString(nice(patch.yp + patch.y), xp, yp); + if (Display.text.config.equals("Z")) + g.drawString(nice(patch.zp + patch.z), xp, yp); + } + } + } + } + + private void paintRing(Graphics2D g) { + if (!Display.ring.show) + return; + g.setColor(Display.ring.getColor()); + g.setStroke(new BasicStroke(Display.ring.size)); + + double xc = screenXD(nx * 0.5); + double yc = screenYD(ny * 0.5); + double radius = getMagnification() * Math.min(nx, ny) * 0.5; + double r = radius; + g.draw(new Ellipse2D.Double(xc - r, yc - r, 2 * r, 2 * r)); + if (Display.ring.config.equalsIgnoreCase("4 rings")) { + r = radius * 0.75; + g.draw(new Ellipse2D.Double(xc - r, yc - r, 2 * r, 2 * r)); + r = radius * 0.50; + g.draw(new Ellipse2D.Double(xc - r, yc - r, 2 * r, 2 * r)); + r = radius * 0.25; + g.draw(new Ellipse2D.Double(xc - r, yc - r, 2 * r, 2 * r)); + } + else if (Display.ring.config.equalsIgnoreCase("2 rings")) { + r = radius * 0.50; + g.draw(new Ellipse2D.Double(xc - r, yc - r, 2 * r, 2 * r)); + } + else if (Display.ring.config.equalsIgnoreCase("10 rings")) { + for (double k = 0.1; k <= 0.9; k += 0.1) { + r = radius * k; + g.draw(new Ellipse2D.Double(xc - r, yc - r, 2 * r, 2 * r)); + } + } + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == drawing) { + new DisplayDialog(this); + } + + /*else if (e.getSource() == update) { + if (main != null) + main.update(); + imp.close(); + }*/ + } + + @Override + public void mousePressed(MouseEvent e) { + + int xm = offScreenX(e.getX()); + int ym = offScreenY(e.getY()); + boolean shift = (getModifiers() & ActionEvent.SHIFT_MASK) == ActionEvent.SHIFT_MASK; + boolean alt = (e.getModifiers() & ActionEvent.ALT_MASK) == ActionEvent.ALT_MASK; + + /* + if (editable & shift) { + int id = stack.patches.getNextID(); + int zp = imp.getZ(); + int nx = imp.getWidth(); + int ny = imp.getHeight(); + double max = imp.getProcessor().getPixelValue(xm, ym); + double dist = Math.sqrt((xm - nx * 0.5) * (xm - nx * 0.5) + (ym - ny * 0.5) * (ym - ny * 0.5)); + Patch p = new Patch(stack, id, dist, xm, ym, zp, 0, 0, 0, max); + stack.patches.add(p); + repaint(); + return; + + } + if (editable & alt) { + int w = (int) ((Params.psf_visible_width[0] - 1) / 2); + Patch p = stack.patches.getPatchFromPositionXY(xm, ym, w); + if (p != null) { + stack.patches.remove(p); + Log.write("Remove " + p.toString()); + } + repaint(); + return; + }*/ + if (editable & shift) { + int w = (int) ((Params.psf_visible_width[0] - 1) / 2); + Patch p = stack.patches.getPatchFromPositionXY(xm, ym, w); + if (p != null) { + stack.patches.getPatchFromID(p.id).select = !stack.patches.getPatchFromID(p.id).select; + } + repaint(); + if (main != null) + main.update(); + return; + } + super.mousePressed(e); + } + + private String nice(double a) { + double b = Math.abs(a); + String n = a < 0 ? "-" : ""; + if (a == 0) + return "0.0"; + if (b > 3000.0) + return String.format(n + "%6.3E", b); + if (b > 300.0) + return String.format(n + "%4.1f", b); + if (b > 30.0) + return String.format(n + "%3.2f", b); + if (b > 3.0) + return String.format(n + "%2.4f", b); + if (b > 0.003) + return String.format(n + "%1.4f", b); + + return String.format(n + "%6.3E", b).trim(); + } +} diff --git a/java code/src/eigenpsf/stack/ZStack.java b/java code/src/eigenpsf/stack/ZStack.java new file mode 100644 index 0000000000000000000000000000000000000000..00c9ac0adc69356a44890df93fc6188464251662 --- /dev/null +++ b/java code/src/eigenpsf/stack/ZStack.java @@ -0,0 +1,131 @@ +package eigenpsf.stack; + +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.ArrayList; + +import eigenpsf.data.HyperMatrix; +import eigenpsf.filemanager.IO; +import ij.IJ; +import ij.ImagePlus; +import ij.process.ImageProcessor; + +public class ZStack { + + public int id; + private String name; + private String path; + public String date; + public String dim; + public Patches patches; + public int[] dims; + //public HyperMatrix imp = null; + private boolean selected = true; + private String maskFilename = ""; + + public ZStack(int id, String name, String path) { + this.id = id; + this.name = name; + this.path = path; + this.patches = new Patches(); + this.dims = null; + updateFileInfo(); + } + + public ZStack(int id, ImagePlus imp, File file) { + this.id = id; + name = file.getName(); + path = file.getAbsolutePath(); + this.patches = new Patches(); + updateFileInfo(); + } + + public boolean isSelected() { + return selected; + } + + public void setMaskFilename(String maskFilename) { + this.maskFilename = maskFilename; + } + + public String getMaskFilename() { + return maskFilename; + } + + + public ImagePlus getImage() { + return HyperMatrix.HyperMatrix2ImagePlus(getHyperMatrix()); + } + + public int[] getDimensions() { + if (dims == null) { + HyperMatrix im = getHyperMatrix(); + dims = im.getDimensions(); + } + return dims; + } + + public HyperMatrix getHyperMatrix() { + return HyperMatrix.ImagePlus2HyperMatrix(IO.openStack(path)); + } + + public ImagePlus getMIP() { + ImagePlus imp = getImage(); + if (imp == null) + return null; + int nx = imp.getWidth(); + int ny = imp.getHeight(); + int nz = imp.getNSlices(); + ImagePlus mip = IJ.createImage("MIP", nx, ny, 1, 32); + ImageProcessor op = mip.getProcessor(); + for(int k=1; k<=nz; k++) { + ImageProcessor ip = imp.getProcessor(); + for(int x=0; x<nx; x++) + for(int y=0; y<ny; y++) { + double v = ip.getPixelValue(x, y); + op.putPixelValue(x, y, v); + } + } + return mip; + } + + public void updateFileInfo() { + File file = new File(path); + ImagePlus imp = IO.openStack(path); + if (imp != null) { + date = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss").format(file.lastModified()); + dim = imp.getWidth() + "x" + imp.getHeight() + "x" + imp.getNSlices(); + dim += " (" + imp.getNChannels() + ", " + imp.getNFrames() + ")"; + } + else { + date = "Not found"; + dim = "Invalid"; + } + } + + public ImagePlus show(ArrayList<ImagePlus> listOfOpenImages) { + ImagePlus imp = IO.openStack(path); + if (imp != null) { + imp.show(); + listOfOpenImages.add(imp); + } + return imp; + } + + public ImagePlus edit() { + ImagePlus imp = IO.openStack(path); + if (imp != null) + imp.show(); + return imp; + } + + + public String getName() { + return name; + } + + public String getPath() { + return path; + } + +} diff --git a/java code/src/manualpsf/ManualPSF.java b/java code/src/manualpsf/ManualPSF.java new file mode 100644 index 0000000000000000000000000000000000000000..2966ba4dfaf074457324d0ae1045e11f85e88537 --- /dev/null +++ b/java code/src/manualpsf/ManualPSF.java @@ -0,0 +1,98 @@ +package manualpsf; + +import eigenpsf.Params; +import eigenpsf.data.HyperMatrix; +import eigenpsf.stack.Patch; +import eigenpsf.stack.Patches; +import ij.ImagePlus; +import ij.plugin.Zoom; +import ij.process.Blitter; +import ij.process.ImageProcessor; + +public class ManualPSF { + + public static void showAverage(ImagePlus imp, Patches selectedPatches) { + int np = selectedPatches.size(); + if (np == 0) + return; + int[] sz = new int[] { Params.psf_visible_width[0], Params.psf_visible_width[1] }; + + Patch patch = selectedPatches.get(0); + HyperMatrix im = HyperMatrix.ImagePlus2HyperMatrix(imp); + int pos0[] = { patch.xp, patch.yp }; + HyperMatrix hm0 = im.getPatch(pos0, sz).imtranslate(patch.x, patch.y, patch.z); + ImagePlus ave = HyperMatrix.HyperMatrix2ImagePlus(hm0); + + ave.show(); + + int nza = ave.getNSlices(); + for (int i=1; i<selectedPatches.size(); i++) { + patch = selectedPatches.get(i); + int pos[] = { patch.xp, patch.yp }; + HyperMatrix hm = im.getPatch(pos, sz).imtranslate(patch.x, patch.y, patch.z); + ImagePlus p = HyperMatrix.HyperMatrix2ImagePlus(hm); + + int nz = p.getNSlices(); + for(int z=1; z<=Math.min(nz, nza); z++) { + ImageProcessor ip = p.getStack().getProcessor(z); + ImageProcessor ap = ave.getStack().getProcessor(z); + ap.multiply(i); + ap.copyBits(ip, 0, 0, Blitter.ADD); + ap.multiply(1.0/(i+1)); + } + } + ave.setTitle("Averaged PSF"); + ave.show(); + ave.getProcessor().resetMinAndMax(); + ave.updateAndDraw(); + double mag = Math.pow(2, 4); + Zoom.set(ave, mag); + } + + public static void realign(ImagePlus imp, Patches selectedPatches) { + int np = selectedPatches.size(); + if (np == 0) + return; + int[] sz = new int[] { Params.psf_visible_width[0], Params.psf_visible_width[1] }; + HyperMatrix im = HyperMatrix.ImagePlus2HyperMatrix(imp); + int hx = sz[0]/2; + int hy = sz[1]/2; + for (Patch patch : selectedPatches) { + int pos[] = { patch.xp, patch.yp }; + HyperMatrix hm = im.getPatch(pos, sz); + ImagePlus p = HyperMatrix.HyperMatrix2ImagePlus(hm); + int nz = p.getNSlices(); + double xg = 0; + double yg = 0; + double sv = 0; + int n = 0; + for(int z=1; z<=nz; z++) { + ImageProcessor ip = p.getStack().getProcessor(z); + for(int x=0; x<sz[0]; x++) + for(int y=0; y<sz[0]; y++) { + double v = ip.getPixelValue(x, y); + xg += v*(x-hx); + yg += v*(y-hy); + sv += v; + n++; + } + } + + xg = (xg/sv); + yg = (yg/sv); + System.out.println("" + xg + " " + yg); + int xi = (int)Math.round(xg); + int yi = (int)Math.round(yg); + double dx = xg - xi; + double dy = yg - yi; + patch.xp = patch.xp + xi; + patch.yp = patch.yp + yi; + patch.x = dx; + patch.y = dy; + + + + } + + } +} diff --git a/java code/src/manualpsf/PSFCanvas.java b/java code/src/manualpsf/PSFCanvas.java new file mode 100644 index 0000000000000000000000000000000000000000..595e41a8e7ae2b2f40e859b6bd2c809203a668ce --- /dev/null +++ b/java code/src/manualpsf/PSFCanvas.java @@ -0,0 +1,229 @@ +package manualpsf; + +import java.awt.BasicStroke; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Panel; +import java.awt.Shape; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Rectangle2D; + +import javax.swing.JLabel; +import javax.swing.JToolBar; + +import eigenpsf.Log; +import eigenpsf.Params; +import eigenpsf.display.Display; +import eigenpsf.display.DisplayDialog; +import eigenpsf.gui.ButtonIcon; +import eigenpsf.stack.Patch; +import eigenpsf.stack.Patches; +import ij.ImagePlus; +import ij.gui.ImageCanvas; +import ij.gui.ImageWindow; +import ij.gui.StackWindow; + +public class PSFCanvas extends ImageCanvas implements ActionListener { + + private Patches patches = new Patches(); + private int nx; + private int ny; + private ButtonIcon drawing = new ButtonIcon("drawing", "Display controls"); + private ButtonIcon realign = new ButtonIcon("target", "Realign"); + private ButtonIcon compute = new ButtonIcon("update", "Compute Average"); + + private boolean editable = true; + private JLabel info = new JLabel(" "); + + public PSFCanvas(ImagePlus imp) { + super(imp); + nx = imp.getWidth(); + ny = imp.getHeight(); + if (imp.getStackSize() > 1) + imp.setWindow(new StackWindow(imp, this)); + else + imp.setWindow(new ImageWindow(imp, this)); + + JToolBar tool = new JToolBar(); + tool.setFloatable(false); + tool.add(drawing); + tool.add(realign); + tool.add(compute); + tool.add(info); + + ImageWindow win = imp.getWindow(); + Panel panel = new Panel(); + panel.setLayout(new FlowLayout(FlowLayout.LEFT)); + + panel.add(tool); + win.add(panel); + win.pack(); + + drawing.addActionListener(this); + realign.addActionListener(this); + compute.addActionListener(this); + + requestFocus(); + } + + @Override + public void paint(Graphics g1) { + super.paint(g1); + Graphics2D g = (Graphics2D) g1; + info.setText("" + patches.size() + " patches"); + paintRing(g); + if (patches != null) { + int w = (int) ((Params.psf_visible_width[0] - 1) / 2); + int h = (int) ((Params.psf_visible_width[1] - 1) / 2); + double m = getMagnification(); + for (Patch patch : patches) { + + if (patch.select && Display.box.show) { + g.setColor(Display.box.getColor()); + Shape r = new Rectangle2D.Double(screenXD(patch.xp - w), screenYD(patch.yp - h), m* w * 2, m*h * 2); + if (Display.box.config.equals("Plain")) + g.setStroke(new BasicStroke(Display.box.size)); + else g.setStroke(new BasicStroke(Display.box.size, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, + 10.0f, new float[] { 2.0f, 2.0f }, 0.0f)); + g.draw(r); + } + + if (!patch.select && Display.boxun.show) { + g.setColor(Display.boxun.getColor()); + Shape r = new Rectangle2D.Double(screenXD(patch.xp - w), screenYD(patch.yp - h), m* w * 2, m*h * 2); + if (Display.boxun.config.equals("Plain")) + g.setStroke(new BasicStroke(Display.boxun.size)); + else g.setStroke(new BasicStroke(Display.boxun.size, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, + 10.0f, new float[] { 2.0f, 2.0f }, 0.0f)); + g.draw(r); + } + + if (Display.text.show) { + g.setColor(Display.text.getColor()); + g.setFont(new Font(Font.MONOSPACED, Font.PLAIN, Display.text.size)); + int xp = screenXD(patch.xp); + int yp = screenYD(patch.yp); + if (Display.text.config.equals("ID")) + g.drawString("" + patch.id, xp, yp); + if (Display.text.config.equals("Distance")) + g.drawString(nice(patch.dist), xp, yp); + if (Display.text.config.equals("Maximum")) + g.drawString(nice(patch.max), xp, yp); + if (Display.text.config.equals("Z-test")) + g.drawString(nice(patch.ztest_value), xp, yp); + if (Display.text.config.equals("X")) + g.drawString(nice(patch.xp + patch.x), xp, yp); + if (Display.text.config.equals("Y")) + g.drawString(nice(patch.yp + patch.y), xp, yp); + if (Display.text.config.equals("Z")) + g.drawString(nice(patch.zp + patch.z), xp, yp); + } + } + } + } + + private void paintRing(Graphics2D g) { + if (!Display.ring.show) + return; + g.setColor(Display.ring.getColor()); + g.setStroke(new BasicStroke(Display.ring.size)); + + double xc = screenXD(nx * 0.5); + double yc = screenYD(ny * 0.5); + double radius = getMagnification() * Math.min(nx, ny) * 0.5; + double r = radius; + g.draw(new Ellipse2D.Double(xc - r, yc - r, 2 * r, 2 * r)); + if (Display.ring.config.equalsIgnoreCase("4 rings")) { + r = radius * 0.75; + g.draw(new Ellipse2D.Double(xc - r, yc - r, 2 * r, 2 * r)); + r = radius * 0.50; + g.draw(new Ellipse2D.Double(xc - r, yc - r, 2 * r, 2 * r)); + r = radius * 0.25; + g.draw(new Ellipse2D.Double(xc - r, yc - r, 2 * r, 2 * r)); + } + else if (Display.ring.config.equalsIgnoreCase("2 rings")) { + r = radius * 0.50; + g.draw(new Ellipse2D.Double(xc - r, yc - r, 2 * r, 2 * r)); + } + else if (Display.ring.config.equalsIgnoreCase("10 rings")) { + for (double k = 0.1; k <= 0.9; k += 0.1) { + r = radius * k; + g.draw(new Ellipse2D.Double(xc - r, yc - r, 2 * r, 2 * r)); + } + } + } + + @Override + public void actionPerformed(ActionEvent e) { + + if (e.getSource() == drawing) { + new DisplayDialog(this); + } + + else if (e.getSource() == realign) { + ManualPSF.realign(imp, patches); + repaint(); + } + else if (e.getSource() == compute) { + + ManualPSF.showAverage(imp, patches); + } + } + + @Override + public void mousePressed(MouseEvent e) { + + int xm = offScreenX(e.getX()); + int ym = offScreenY(e.getY()); + boolean shift = (getModifiers() & ActionEvent.SHIFT_MASK) == ActionEvent.SHIFT_MASK; + boolean alt = (e.getModifiers() & ActionEvent.ALT_MASK) == ActionEvent.ALT_MASK; + + if (editable & shift) { + int id = patches.getNextID(); + int zp = imp.getZ(); + int nx = imp.getWidth(); + int ny = imp.getHeight(); + double max = imp.getProcessor().getPixelValue(xm, ym); + double dist = Math.sqrt((xm - nx * 0.5) * (xm - nx * 0.5) + (ym - ny * 0.5) * (ym - ny * 0.5)); + Patch p = new Patch(null, id, dist, xm, ym, zp, 0, 0, 0, max); + patches.add(p); + repaint(); + return; + } + if (editable & alt) { + int w = (int) ((Params.psf_visible_width[0] - 1) / 2); + Patch p = patches.getPatchFromPositionXY(xm, ym, w); + if (p != null) { + patches.remove(p); + Log.write("Remove " + p.toString()); + } + repaint(); + return; + } + super.mousePressed(e); + } + + private String nice(double a) { + double b = Math.abs(a); + String n = a < 0 ? "-" : ""; + if (a == 0) + return "0.0"; + if (b > 3000.0) + return String.format(n + "%6.3E", b); + if (b > 300.0) + return String.format(n + "%4.1f", b); + if (b > 30.0) + return String.format(n + "%3.2f", b); + if (b > 3.0) + return String.format(n + "%2.4f", b); + if (b > 0.003) + return String.format(n + "%1.4f", b); + + return String.format(n + "%6.3E", b).trim(); + } +} diff --git a/java code/src/resources/IconLoader.java b/java code/src/resources/IconLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..a2faa6e4d8111029531e7506a5839a993359ae17 --- /dev/null +++ b/java code/src/resources/IconLoader.java @@ -0,0 +1,19 @@ +package resources; + +import java.awt.Image; +import java.net.URL; + +import javax.swing.ImageIcon; + +public class IconLoader { + + public Image get(String filename) { + URL url = IconLoader.class.getResource(filename); + if (url != null) { + ImageIcon img = new ImageIcon(url, "") ; + return img.getImage(); + } + return null; + } + +} diff --git a/java code/src/resources/about.png b/java code/src/resources/about.png new file mode 100644 index 0000000000000000000000000000000000000000..2ff58285d0cfd04214a603b4811e00a23e771ae1 Binary files /dev/null and b/java code/src/resources/about.png differ diff --git a/java code/src/resources/close.png b/java code/src/resources/close.png new file mode 100644 index 0000000000000000000000000000000000000000..bddf611f819ed87d9ed7e12ad1f4b29aefb66546 Binary files /dev/null and b/java code/src/resources/close.png differ diff --git a/java code/src/resources/contrast.png b/java code/src/resources/contrast.png new file mode 100644 index 0000000000000000000000000000000000000000..6d6d6ad002f9cc4c6cbee4ece8dd5732659796fe Binary files /dev/null and b/java code/src/resources/contrast.png differ diff --git a/java code/src/resources/drawing.png b/java code/src/resources/drawing.png new file mode 100644 index 0000000000000000000000000000000000000000..50bc3d2e5818367e5af7ff3d9b8da2f67f0e0782 Binary files /dev/null and b/java code/src/resources/drawing.png differ diff --git a/java code/src/resources/edit.png b/java code/src/resources/edit.png new file mode 100644 index 0000000000000000000000000000000000000000..be67315842c49a2f974c9461bc00128ee92dff81 Binary files /dev/null and b/java code/src/resources/edit.png differ diff --git a/java code/src/resources/edit_off.png b/java code/src/resources/edit_off.png new file mode 100644 index 0000000000000000000000000000000000000000..4c74201c6eac5f3705674c959525e856b1eec3ee Binary files /dev/null and b/java code/src/resources/edit_off.png differ diff --git a/java code/src/resources/github.png b/java code/src/resources/github.png new file mode 100644 index 0000000000000000000000000000000000000000..2073b55a308b3b825a81a24d776d79376857237a Binary files /dev/null and b/java code/src/resources/github.png differ diff --git a/java code/src/resources/help.png b/java code/src/resources/help.png new file mode 100644 index 0000000000000000000000000000000000000000..64f52d8ebb14c7e5ad4315aeb14ab36a0746701b Binary files /dev/null and b/java code/src/resources/help.png differ diff --git a/java code/src/resources/imagej.png b/java code/src/resources/imagej.png new file mode 100644 index 0000000000000000000000000000000000000000..9c1d6f11e6fed572c7747c623e640af6f8ceb3fa Binary files /dev/null and b/java code/src/resources/imagej.png differ diff --git a/java code/src/resources/imagej_off.png b/java code/src/resources/imagej_off.png new file mode 100644 index 0000000000000000000000000000000000000000..c79a1a7f31dbca1bef137db1c43c05cca4accc52 Binary files /dev/null and b/java code/src/resources/imagej_off.png differ diff --git a/java code/src/resources/mask.png b/java code/src/resources/mask.png new file mode 100644 index 0000000000000000000000000000000000000000..87147aba2d5a4791a63d0194bc9896d29ef3288f Binary files /dev/null and b/java code/src/resources/mask.png differ diff --git a/java code/src/resources/new.png b/java code/src/resources/new.png new file mode 100644 index 0000000000000000000000000000000000000000..8828334843ba26b7af27aa3d73b35e74bb4b2509 Binary files /dev/null and b/java code/src/resources/new.png differ diff --git a/java code/src/resources/open.png b/java code/src/resources/open.png new file mode 100644 index 0000000000000000000000000000000000000000..2e55b15ae9b4908fc77d13cbf16a975c855fa2db Binary files /dev/null and b/java code/src/resources/open.png differ diff --git a/java code/src/resources/reset.png b/java code/src/resources/reset.png new file mode 100644 index 0000000000000000000000000000000000000000..fc528becb51e8e727e1f98c9827b57797fe5d210 Binary files /dev/null and b/java code/src/resources/reset.png differ diff --git a/java code/src/resources/save.png b/java code/src/resources/save.png new file mode 100644 index 0000000000000000000000000000000000000000..a6c0bf65005c5d2f573667f30f0b10fd80e54167 Binary files /dev/null and b/java code/src/resources/save.png differ diff --git a/java code/src/resources/select.png b/java code/src/resources/select.png new file mode 100644 index 0000000000000000000000000000000000000000..2a0df66e6dfbc376bfa70507ad993200afd540a2 Binary files /dev/null and b/java code/src/resources/select.png differ diff --git a/java code/src/resources/settings.png b/java code/src/resources/settings.png new file mode 100644 index 0000000000000000000000000000000000000000..e329c297dba3c04b5795651f380c38eaf41e96bd Binary files /dev/null and b/java code/src/resources/settings.png differ diff --git a/java code/src/resources/show.png b/java code/src/resources/show.png new file mode 100644 index 0000000000000000000000000000000000000000..3b17c2544dfe89adf77a6efbf5e24cff12a22467 Binary files /dev/null and b/java code/src/resources/show.png differ diff --git a/java code/src/resources/stop.png b/java code/src/resources/stop.png new file mode 100644 index 0000000000000000000000000000000000000000..947e13f51b97806e89abc22104b44f2d758315e5 Binary files /dev/null and b/java code/src/resources/stop.png differ diff --git a/java code/src/resources/target.png b/java code/src/resources/target.png new file mode 100644 index 0000000000000000000000000000000000000000..b42b543975f2533a9a3322a7df3fce2e3951f3f9 Binary files /dev/null and b/java code/src/resources/target.png differ diff --git a/java code/src/resources/time.png b/java code/src/resources/time.png new file mode 100644 index 0000000000000000000000000000000000000000..452e3c82c6491f98ed0c0265c8d6836705fb15c8 Binary files /dev/null and b/java code/src/resources/time.png differ diff --git a/java code/src/resources/unselect.png b/java code/src/resources/unselect.png new file mode 100644 index 0000000000000000000000000000000000000000..e3d06846e18a63d75c8b83491cad9a1dc470af25 Binary files /dev/null and b/java code/src/resources/unselect.png differ diff --git a/java code/src/resources/update.png b/java code/src/resources/update.png new file mode 100644 index 0000000000000000000000000000000000000000..1f2ec0380e7b5fd5bda5811da3d7bc7e5a4e8d4d Binary files /dev/null and b/java code/src/resources/update.png differ