diff --git a/README.md b/README.md index f2b36a75..52890b83 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,32 @@ # bigdataviewer-core [![](https://api.github.com/bigdataviewer/bigdataviewer-core/actions/workflows/build-main.yml/badge.svg)](https://github.com/bigdataviewer/bigdataviewer-core/actions/workflows/build-main.yml) + +[![Join the chat at https://gitter.im/bigdataviewer/bigdataviewer-core](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/bigdataviewer/bigdataviewer-core?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ImgLib2-based viewer for registered SPIM stacks and more +--- +Feature added in this fork: **Interactive Video producing using Spline interpolation** + +### Install: +`$ ./install.sh` + +### Run: +`$ ./bdv INPUT_PATH` + +### How to use: + +1- **Video producer panel** can be opened by clicking `F7` or via `Tools -> Produce Movie` +![](img/1.png) + +2- By opening Video Producer you get this panel +![](img/2.png) +- `+` To add current frame +- `-` To delete last frame +- Preview with a frame down-sampling and sleep time between frames +- Accel: you select the interpolation: `Slow start` , `slow end` , `Symmetric` ... +- Export: Can be in `Json` or `PNG sequence` +- Import saved `Json` +======= [![developer chat](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg)](https://imagesc.zulipchat.com/#narrow/stream/327326-BigDataViewer) ImgLib2-based viewer for registered SPIM stacks and more + diff --git a/img/1.png b/img/1.png new file mode 100644 index 00000000..5a26e3f0 Binary files /dev/null and b/img/1.png differ diff --git a/img/2.png b/img/2.png new file mode 100644 index 00000000..00e91ab0 Binary files /dev/null and b/img/2.png differ diff --git a/install.sh b/install.sh new file mode 100755 index 00000000..46d32ceb --- /dev/null +++ b/install.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +# This script is shamelessly adapted from https://github.com/saalfeldlab/n5-utils, thanks @axtimwalde & co! + +VERSION="10.3.2-SNAPSHOT" +INSTALL_DIR=${1:-$(pwd)} + +echo "" +echo "Installing into $INSTALL_DIR" + +# check for operating system +if [[ "$OSTYPE" == "linux-gnu" ]]; then + echo "Assuming on Linux operating system" + MEM=$(cat /proc/meminfo | grep MemTotal | sed s/^MemTotal:\\\s*\\\|\\\s\\+[^\\\s]*$//g) + MEMGB=$(($MEM/1024/1024)) + MEM=$((($MEMGB/5)*4)) +elif [[ "$OSTYPE" == "darwin"* ]]; then + echo "Assuming on MacOS X operating system" + # sysctl returns total hardware memory size in bytes + MEM=$(sysctl hw.memsize | grep hw.memsize | sed s/hw.memsize://g) + MEMGB=$(($MEM/1024/1024/1024)) + MEM=$((($MEMGB/5)*4)) +else + echo "ERROR - Operating system (arg2) must be either linux or osx - EXITING (on windows please run as a normal Java class from e.g. Eclipse)" + exit +fi + +echo "Available memory:" $MEMGB "GB, setting Java memory limit to" $MEM "GB" + +mvn clean install +mvn -Dmdep.outputFile=cp.txt -Dmdep.includeScope=runtime dependency:build-classpath + +echo "" +echo "Installing 'bdv' command into" $INSTALL_DIR + +echo '#!/bin/bash' > bdv +echo '' >> bdv +echo "JAR=\$HOME/.m2/repository/sc/fiji/bigdataviewer-core/${VERSION}/bigdataviewer-core-${VERSION}.jar" >> bdv +echo 'java \' >> bdv +echo " -Xmx${MEM}g \\" >> bdv +echo ' -XX:+UseConcMarkSweepGC \' >> bdv +echo -n ' -cp $JAR:' >> bdv +echo -n $(cat cp.txt) >> bdv +echo ' \' >> bdv +echo ' bdv.BigDataViewer "$@"' >> bdv + + +chmod a+x bdv + +if [ $(pwd) == "$INSTALL_DIR" ]; then + echo "Installation directory equals current directory, we are done." +else + echo "Creating directory $INSTALL_DIR and moving files..." + mkdir -p $INSTALL_DIR + mv bdv $INSTALL_DIR/ +fi + +rm cp.txt + +echo "Installation finished." diff --git a/pom.xml b/pom.xml index 6698a4a4..164700af 100644 --- a/pom.xml +++ b/pom.xml @@ -149,6 +149,10 @@ + + net.imagej + ij + net.imglib2 imglib2 @@ -197,6 +201,11 @@ com.miglayout miglayout-swing + + com.google.code.gson + gson + 2.8.5 + com.formdev flatlaf diff --git a/src/main/java/bdv/BigDataViewer.java b/src/main/java/bdv/BigDataViewer.java index 7e05d08e..919eb4a4 100644 --- a/src/main/java/bdv/BigDataViewer.java +++ b/src/main/java/bdv/BigDataViewer.java @@ -6,13 +6,13 @@ * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -29,6 +29,7 @@ package bdv; import bdv.tools.PreferencesDialog; +import bdv.tools.movie.ProduceMovieDialog; import bdv.ui.UIUtils; import bdv.ui.keymap.Keymap; import bdv.ui.keymap.KeymapManager; @@ -107,6 +108,7 @@ import mpicbg.spim.data.sequence.Channel; import org.scijava.ui.behaviour.util.Actions; + public class BigDataViewer { public static String configDir = ProjectDirectories.from( "sc", "fiji", "bigdataviewer" ).configDir; @@ -127,6 +129,8 @@ public class BigDataViewer protected final RecordMovieDialog movieDialog; + protected final ProduceMovieDialog produceMovieDialog; + protected final RecordMaxProjectionDialog movieMaxProjectDialog; protected final VisibilityAndGroupingDialog activeSourcesDialog; @@ -398,6 +402,8 @@ public BigDataViewer( cropDialog = ( spimData == null ) ? null : new CropDialog( viewerFrame, viewer, spimData.getSequenceDescription() ); movieDialog = new RecordMovieDialog( viewerFrame, viewer, progressWriter ); + + produceMovieDialog = new ProduceMovieDialog( viewerFrame, viewer, progressWriter ); // this is just to get updates of window size: viewer.getDisplay().overlays().add( movieDialog ); @@ -502,6 +508,10 @@ public boolean accept( final File f ) miMovie.setText( "Record Movie" ); menu.add( miMovie ); + final JMenuItem miMovieProducer = new JMenuItem( actionMap.get( BigDataViewerActions.PRODUCE_MOVIE ) ); + miMovieProducer.setText( "Produce Movie" ); + menu.add( miMovieProducer ); + final JMenuItem miMaxProjectMovie = new JMenuItem( actionMap.get( BigDataViewerActions.RECORD_MAX_PROJECTION_MOVIE ) ); miMaxProjectMovie.setText( "Record Max-Projection Movie" ); menu.add( miMaxProjectMovie ); @@ -775,9 +785,38 @@ public void collapseCardPanel() public static void main( final String[] args ) { - final String fn = "/Users/pietzsch/workspace/data/111010_weber_resave.xml"; +// final String fn = "http://tomancak-mac-17.mpi-cbg.de:8080/openspim/"; +// final String fn = "/Users/Pietzsch/Desktop/openspim/datasetHDF.xml"; +// final String fn = "/Users/pietzsch/workspace/data/111010_weber_full.xml"; +// final String fn = "/Users/Pietzsch/Desktop/spimrec2/dataset.xml"; +// final String fn = "/Users/pietzsch/Desktop/HisYFP-SPIM/dataset.xml"; +// final String fn = "/Users/Pietzsch/Desktop/bdv example/drosophila 2.xml"; +// final String fn = "/Users/pietzsch/Desktop/data/clusterValia/140219-1/valia-140219-1.xml"; +// final String fn = "/Users/Pietzsch/Desktop/data/catmaid.xml"; +// final String fn = "src/main/resources/openconnectome-bock11-neariso.xml"; +// final String fn = "/home/saalfeld/catmaid.xml"; +// final String fn = "/home/saalfeld/catmaid-fafb00-v9.xml"; +// final String fn = "/home/saalfeld/catmaid-fafb00-sample_A_cutout_3k.xml"; +// final String fn = "/home/saalfeld/catmaid-thorsten.xml"; +// final String fn = "/home/saalfeld/knossos-example.xml"; +// final String fn = "/Users/Pietzsch/Desktop/data/catmaid-confocal.xml"; +// final String fn = "/Users/pietzsch/desktop/data/BDV130418A325/BDV130418A325_NoTempReg.xml"; +// final String fn = "/Users/pietzsch/Desktop/data/valia2/valia.xml"; +// final String fn = "/Users/pietzsch/workspace/data/fast fly/111010_weber/combined.xml"; +// final String fn = "/Users/pietzsch/workspace/data/mette/mette.xml"; +// final String fn = "/Users/tobias/Desktop/openspim.xml"; +// final String fn = "/Users/pietzsch/Desktop/data/fibsem.xml"; +// final String fn = "/Users/pietzsch/Desktop/data/fibsem-remote.xml"; +// final String fn = "/Users/pietzsch/Desktop/url-valia.xml"; +// final String fn = "/Users/pietzsch/Desktop/data/clusterValia/140219-1/valia-140219-1.xml"; +// final String fn = "/Users/pietzsch/workspace/data/111010_weber_full.xml"; +// final String fn = "/Volumes/projects/tomancak_lightsheet/Mette/ZeissZ1SPIM/Maritigrella/021013_McH2BsGFP_CAAX-mCherry/11-use/hdf5/021013_McH2BsGFP_CAAX-mCherry-11-use.xml"; + +// final String fn = "/Users/Marwan/Downloads/drosophila_his-yfp/dataset.xml"; + try { + final String fn = args[0]; System.setProperty( "apple.laf.useScreenMenuBar", "true" ); // System.setProperty( "apple.awt.application.appearance", "system" ); UIUtils.installFlatLafInfos(); @@ -785,10 +824,13 @@ public static void main( final String[] args ) System.out.println( "reading config files from \"" + configDir + "\"" ); final BigDataViewer bdv = open( fn, new File( fn ).getName(), new ProgressWriterConsole(), ViewerOptions.options() ); +// PanelSnapshot.showPanel(bdv.getViewer()); // DumpInputConfig.writeToYaml( System.getProperty( "user.home" ) + "/.bdv/bdvkeyconfig.yaml", bdv.getViewerFrame() ); } catch ( final Exception e ) { + if (args.length==0) + System.out.println("Please specify the input"); e.printStackTrace(); } } diff --git a/src/main/java/bdv/BigDataViewerActions.java b/src/main/java/bdv/BigDataViewerActions.java index 9760aa21..e0fba54f 100644 --- a/src/main/java/bdv/BigDataViewerActions.java +++ b/src/main/java/bdv/BigDataViewerActions.java @@ -6,13 +6,13 @@ * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -62,6 +62,7 @@ public class BigDataViewerActions extends Actions public static final String EXPAND_CARDS = "expand and focus cards panel"; public static final String COLLAPSE_CARDS = "collapse cards panel"; public static final String RECORD_MOVIE = "record movie"; + public static final String PRODUCE_MOVIE = "produce movie"; public static final String RECORD_MAX_PROJECTION_MOVIE = "record max projection movie"; public static final String SET_BOOKMARK = "set bookmark"; public static final String GO_TO_BOOKMARK = "go to bookmark"; @@ -72,6 +73,7 @@ public class BigDataViewerActions extends Actions public static final String[] VISIBILITY_AND_GROUPING_KEYS = new String[] { "F6" }; public static final String[] SHOW_HELP_KEYS = new String[] { "F1", "H" }; public static final String[] CROP_KEYS = new String[] { "F9" }; + public static final String[] PRODUCE_MOVIE_KEYS = new String[] { "F7" }; public static final String[] MANUAL_TRANSFORM_KEYS = new String[] { "T" }; public static final String[] SAVE_SETTINGS_KEYS = new String[] { "F11" }; public static final String[] LOAD_SETTINGS_KEYS = new String[] { "F12" }; @@ -132,6 +134,7 @@ public static void install( final Actions actions, final BigDataViewer bdv ) toggleDialogAction( actions, bdv.helpDialog, SHOW_HELP, SHOW_HELP_KEYS ); toggleDialogAction( actions, bdv.cropDialog, CROP, CROP_KEYS ); toggleDialogAction( actions, bdv.movieDialog, RECORD_MOVIE, RECORD_MOVIE_KEYS ); + toggleDialogAction( actions, bdv.produceMovieDialog, PRODUCE_MOVIE, PRODUCE_MOVIE_KEYS ); toggleDialogAction( actions, bdv.movieMaxProjectDialog, RECORD_MAX_PROJECTION_MOVIE, RECORD_MAX_PROJECTION_MOVIE_KEYS ); toggleDialogAction( actions, bdv.preferencesDialog, PREFERENCES_DIALOG, PREFERENCES_DIALOG_KEYS ); bookmarks( actions, bdv.bookmarkEditor ); diff --git a/src/main/java/bdv/tools/movie/ImagePanel.java b/src/main/java/bdv/tools/movie/ImagePanel.java new file mode 100644 index 00000000..d214ab94 --- /dev/null +++ b/src/main/java/bdv/tools/movie/ImagePanel.java @@ -0,0 +1,49 @@ +package bdv.tools.movie; + +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; + +public class ImagePanel extends JPanel { + private final BufferedImage image; + + public ImagePanel(BufferedImage image) { + super(); + setPreferredSize(new Dimension(90, 90)); + this.image = scale(image, 90, 90); + } + + public static ImagePanel snapshotOf(JPanel panel) { + return new ImagePanel(takeSnapShot(panel)); + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + g.drawImage(image, 0, 0, this); + } + + public static BufferedImage scale(BufferedImage src, int w, int h) { + BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); + int x, y; + int ww = src.getWidth(); + int hh = src.getHeight(); + int[] ys = new int[h]; + for (y = 0; y < h; y++) + ys[y] = y * hh / h; + for (x = 0; x < w; x++) { + int newX = x * ww / w; + for (y = 0; y < h; y++) { + int col = src.getRGB(newX, ys[y]); + img.setRGB(x, y, col); + } + } + return img; + } + + public static BufferedImage takeSnapShot(JPanel panel){ + BufferedImage bufImage = new BufferedImage(panel.getSize().width, panel.getSize().height,BufferedImage.TYPE_INT_RGB); + panel.paint(bufImage.createGraphics()); + return bufImage; + } +} diff --git a/src/main/java/bdv/tools/movie/MovieFrame.java b/src/main/java/bdv/tools/movie/MovieFrame.java new file mode 100644 index 00000000..fa9a8391 --- /dev/null +++ b/src/main/java/bdv/tools/movie/MovieFrame.java @@ -0,0 +1,67 @@ +package bdv.tools.movie; + +import net.imglib2.realtransform.AffineTransform3D; + +import java.io.Serializable; + +public class MovieFrame implements Serializable { + private AffineTransform3D transform; + + private int position; + private int frames; + private int accel; + private final static int DEFAULT_FRAMES = 120; + private final static int DEFAULT_ACCEL = 0; + + + public MovieFrame(int position, AffineTransform3D transform) { + this(position, transform, ((position == 0) ? 0 : DEFAULT_FRAMES), ((position == 0) ? 0 : DEFAULT_ACCEL)); + } + + public MovieFrame(int position, AffineTransform3D transform, int frames, int accel) { + this.position = position; + this.transform = transform; + this.frames = frames; + this.accel = accel; + } + + public AffineTransform3D getTransform() { + return transform; + } + + public void setTransform(AffineTransform3D transform) { + this.transform = transform; + } + + public int getPosition() { + return position; + } + + public void setPosition(int position) { + this.position = position; + } + + public int getFrames() { + return frames; + } + + public void setFrames(int frames) { + this.frames = frames; + } + + public int getAccel() { + return accel; + } + + public void setAccel(int accel) { + this.accel = accel; + } + + public static int getDefaultFrames() { + return DEFAULT_FRAMES; + } + + public static int getDefaultAccel() { + return DEFAULT_ACCEL; + } +} diff --git a/src/main/java/bdv/tools/movie/MovieFramePanel.java b/src/main/java/bdv/tools/movie/MovieFramePanel.java new file mode 100644 index 00000000..164bbf5c --- /dev/null +++ b/src/main/java/bdv/tools/movie/MovieFramePanel.java @@ -0,0 +1,98 @@ +package bdv.tools.movie; + +import net.imglib2.realtransform.AffineTransform3D; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.TitledBorder; +import java.awt.*; + +public class MovieFramePanel extends JPanel { + final private MovieFrame movieFrame; + final private ImagePanel image; + private JTextField framesField; + private JComboBox accelField; + + private final String[] ACCELS = new String[]{"symmetric","slow start","slow end","soft symmetric","soft slow start","soft slow end"}; + + + + + public MovieFramePanel(AffineTransform3D transform, ImagePanel image, int position) { + super(); + this.movieFrame = new MovieFrame(position, transform); + this.image = image; + initView(); + } + + public MovieFramePanel(MovieFrame movieFrame, ImagePanel image) { + super(); + this.movieFrame = movieFrame; + this.image = image; + initView(); + } + + + private void initView() { + Border blackline = BorderFactory.createLineBorder(Color.lightGray); + TitledBorder title = BorderFactory.createTitledBorder(blackline, String.valueOf(movieFrame.getPosition())); + title.setTitleJustification(TitledBorder.CENTER); + setBorder(title); + setPreferredSize(new Dimension(140, 180)); + if (image != null) + add(image); + JPanel fieldsPanel = new JPanel(new GridLayout(2, 1)); + framesField = new JTextField(String.valueOf(movieFrame.getFrames())); + framesField.setFont(new Font("Serif", Font.PLAIN, 9)); + accelField = new JComboBox<>(ACCELS); + accelField.setSelectedIndex(movieFrame.getAccel()); + accelField.setFont(new Font("Serif", Font.PLAIN, 9)); + JLabel framesLabel = new JLabel("Frames: "); + framesLabel.setFont(new Font("Serif", Font.PLAIN, 8)); +// JLabel accelLabel = new JLabel("Accel: "); +// accelLabel.setFont(new Font("Serif", Font.PLAIN, 8)); + JPanel framePanel = new JPanel(new GridLayout(1,2)); + framePanel.add(framesLabel); + framePanel.add(framesField); + fieldsPanel.add(framePanel); + fieldsPanel.add(accelField); + add(fieldsPanel); + } + + public ImagePanel getImage() { + return image; + } + + public MovieFramePanel updateFields() { + try { + int accel = accelField.getSelectedIndex(); + movieFrame.setAccel(accel); + } catch (Exception e) { + e.printStackTrace(); + System.out.println("Invalid value :" + accelField.getSelectedItem()); + } + try { + int frames = Integer.valueOf(framesField.getText()); + movieFrame.setFrames(frames); + } catch (Exception e) { + e.printStackTrace(); + System.out.println("Invalid value :" + framesField.getText()); + } + return this; + } + + public int getAccel() { + updateFields(); + return movieFrame.getAccel(); + } + + public int getFrames() { + updateFields(); + return movieFrame.getFrames(); + } + + public MovieFrame getMovieFrame() { + updateFields(); + return movieFrame; + } +} diff --git a/src/main/java/bdv/tools/movie/MovieSaveDialog.java b/src/main/java/bdv/tools/movie/MovieSaveDialog.java new file mode 100644 index 00000000..b57ae1da --- /dev/null +++ b/src/main/java/bdv/tools/movie/MovieSaveDialog.java @@ -0,0 +1,136 @@ +package bdv.tools.movie; + +import bdv.export.ProgressWriter; +import bdv.util.DelayedPackDialog; +import bdv.viewer.OverlayRenderer; +import bdv.viewer.ViewerPanel; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.io.File; + +public class MovieSaveDialog extends DelayedPackDialog implements OverlayRenderer { + private static final long serialVersionUID = 1L; + + private final ViewerPanel viewer; + + private final ProgressWriter progressWriter; + + private final JTextField pathTextField; + + private final JSpinner spinnerWidth; + + private final JSpinner spinnerHeight; + + public MovieSaveDialog(final Frame owner, final ViewerPanel viewer, final ProgressWriter progressWriter, ProduceMovieDialog produceMovieDialog) { + super(owner, "record movie", false); + this.viewer = viewer; + this.progressWriter = progressWriter; + + final JPanel boxes = new JPanel(); + getContentPane().add(boxes, BorderLayout.NORTH); + boxes.setLayout(new BoxLayout(boxes, BoxLayout.PAGE_AXIS)); + + final JPanel saveAsPanel = new JPanel(); + saveAsPanel.setLayout(new BorderLayout(0, 0)); + boxes.add(saveAsPanel); + + saveAsPanel.add(new JLabel("save to"), BorderLayout.WEST); + + pathTextField = new JTextField("./record/"); + saveAsPanel.add(pathTextField, BorderLayout.CENTER); + pathTextField.setColumns(20); + + final JButton browseButton = new JButton("Browse"); + saveAsPanel.add(browseButton, BorderLayout.EAST); + + final JPanel typePanel = new JPanel(); + boxes.add(typePanel); + + typePanel.add(new JLabel("Type:")); + + final JPanel widthPanel = new JPanel(); + boxes.add(widthPanel); + widthPanel.add(new JLabel("width")); + spinnerWidth = new JSpinner(); + spinnerWidth.setModel(new SpinnerNumberModel(800, 10, 5000, 1)); + widthPanel.add(spinnerWidth); + + final JPanel heightPanel = new JPanel(); + boxes.add(heightPanel); + heightPanel.add(new JLabel("height")); + spinnerHeight = new JSpinner(); + spinnerHeight.setModel(new SpinnerNumberModel(600, 10, 5000, 1)); + heightPanel.add(spinnerHeight); + + final JPanel buttonsPanel = new JPanel(); + boxes.add(buttonsPanel); + buttonsPanel.setLayout(new BorderLayout(0, 0)); + + final JButton recordButton = new JButton("Record"); + buttonsPanel.add(recordButton, BorderLayout.EAST); + + final JFileChooser fileChooser = new JFileChooser(); + fileChooser.setMultiSelectionEnabled(false); + fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + + browseButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + fileChooser.setSelectedFile(new File(pathTextField.getText())); + final int returnVal = fileChooser.showSaveDialog(null); + if (returnVal == JFileChooser.APPROVE_OPTION) { + final File file = fileChooser.getSelectedFile(); + pathTextField.setText(file.getAbsolutePath()); + } + } + }); + + recordButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + final String dirname = pathTextField.getText(); + final File dir = new File(dirname); + if (!dir.exists()) + dir.mkdirs(); + if (!dir.exists() || !dir.isDirectory()) { + System.err.println("Invalid export directory " + dirname); + return; + } + setVisible(false); + final int width = (Integer) spinnerWidth.getValue(); + final int height = (Integer) spinnerHeight.getValue(); + produceMovieDialog.exportPNGs(width,height,dir); + } + }); + + final ActionMap am = getRootPane().getActionMap(); + final InputMap im = getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + final Object hideKey = new Object(); + final Action hideAction = new AbstractAction() { + @Override + public void actionPerformed(final ActionEvent e) { + setVisible(false); + } + + private static final long serialVersionUID = 1L; + }; + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), hideKey); + am.put(hideKey, hideAction); + + pack(); + } + + @Override + public void drawOverlays(final Graphics g) { + } + + @Override + public void setCanvasSize(final int width, final int height) { + spinnerWidth.setValue(width); + spinnerHeight.setValue(height); + } +} diff --git a/src/main/java/bdv/tools/movie/PreviewThread.java b/src/main/java/bdv/tools/movie/PreviewThread.java new file mode 100644 index 00000000..0034b54a --- /dev/null +++ b/src/main/java/bdv/tools/movie/PreviewThread.java @@ -0,0 +1,101 @@ +package bdv.tools.movie; + +import bdv.viewer.Interpolation; +import bdv.viewer.ViewerPanel; +import bdv.viewer.animate.SimilarityTransformAnimator; +import net.imglib2.realtransform.AffineTransform3D; + +import javax.swing.*; + +public class PreviewThread extends VNCMovie implements Runnable { + + private final ViewerPanel viewer; + private final AffineTransform3D[] transforms; + private final int[] frames; + private final int[] accel; + private final int sleep; + private final int down; + public Thread t; + + boolean suspended = false; + + PreviewThread( + final ViewerPanel viewer, + final AffineTransform3D[] transforms, + final int[] frames, + final int[] accel, + final int sleep, + final int down) { + this.viewer = viewer; + this.transforms = transforms; + this.frames = frames; + this.accel = accel; + this.sleep = sleep; + this.down = down; + } + + public void run() { + try { + viewer.setInterpolation(Interpolation.NLINEAR); + for (int k = 1; k < transforms.length; ++k) { + synchronized (this) { + if (suspended) { + wait(); + } + } + final SimilarityTransformAnimator animator = new SimilarityTransformAnimator( + transforms[k - 1], + transforms[k], + 0, + 0, + 0); + int downFrames = frames[k] / down; + for (int d = 0; d < downFrames; ++d) { +// System.out.println(k + "-" + d); + final AffineTransform3D tkd = animator.get(accel((double) d / (double) downFrames, accel[k])); +// System.out.println(tkd.toString()); + SwingUtilities.invokeLater(() -> { + viewer.state().setViewerTransform(tkd); + viewer.repaint(); + }); + Thread.sleep(sleep); + } + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + this.start(); + } + + public boolean getStatus() { + return t == null; + } + + public boolean isDone(){ + return (t.getState()==Thread.State.TERMINATED); + } + + public void start() { + if (getStatus()) { + t = new Thread(this); + t.start(); + } + } + + public void suspend() { + t.stop(); + suspended = true; + } + + public boolean isSuspended() { + return suspended; + } + + synchronized void resume() { + suspended = false; + t.resume(); + this.notify(); + } + +} + diff --git a/src/main/java/bdv/tools/movie/ProduceMovieDialog.java b/src/main/java/bdv/tools/movie/ProduceMovieDialog.java new file mode 100644 index 00000000..80be063d --- /dev/null +++ b/src/main/java/bdv/tools/movie/ProduceMovieDialog.java @@ -0,0 +1,354 @@ +/* + * #%L + * BigDataViewer core classes with minimal dependencies. + * %% + * Copyright (C) 2012 - 2022 BigDataViewer developers. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package bdv.tools.movie; + +import bdv.export.ProgressWriter; +import bdv.tools.movie.serilizers.MovieFramesSerializer; +import bdv.util.DelayedPackDialog; +import bdv.viewer.ViewerPanel; +import net.imglib2.realtransform.AffineTransform3D; + +import javax.swing.*; +import javax.swing.border.TitledBorder; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.filechooser.FileSystemView; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class ProduceMovieDialog extends DelayedPackDialog { + + private final List framesPanels; + private final ViewerPanel viewer; + + private final ProgressWriter progressWriter; + private final JPanel mainPanel; + private final JButton removeButton; + private final MovieSaveDialog saveDialog; + private final JButton exportJsonButton; + private final JButton exportPNGsButton; + private final static int FrameWidth = 920; + private final static int DEFAULT_SLEEP = 100; + private final static int DEFAULT_DOWN = 10; + private PreviewThread previewThread; + private final JTextField downsampleField; + private final JTextField sleepField; + + public ProduceMovieDialog(final Frame owner, final ViewerPanel viewer, final ProgressWriter progressWriter) { + super(owner, "produce movie", false); + setLayout(new FlowLayout()); + setSize(new Dimension(FrameWidth, 340)); + JPanel playerPanel = new JPanel(); + + downsampleField = new JTextField(String.valueOf(DEFAULT_DOWN)); + sleepField = new JTextField(String.valueOf(DEFAULT_SLEEP)); + playerPanel.add(new JLabel("PREVIEW: Downsampling: 1/")); + playerPanel.add(downsampleField); + playerPanel.add(new JLabel("Sleep:")); + playerPanel.add(sleepField); + playerPanel.add(new JLabel("ms ")); + + JButton playButton = new JButton("▶"); + playButton.addActionListener(e -> { + startPreview(); + }); + playerPanel.add(playButton); + + JButton pauseButton = new JButton("Stop"); + pauseButton.addActionListener(e -> { + pausePreview(); + }); + playerPanel.add(pauseButton); + +// JButton restartButton = new JButton("Refresh"); +// restartButton.addActionListener(e -> { +// restartPreview(); +// }); +// playerPanel.add(restartButton); + + playerPanel.setPreferredSize(new Dimension(FrameWidth, 40)); + + JPanel makerPanel = new JPanel(new FlowLayout()); + makerPanel.setPreferredSize(new Dimension(FrameWidth, 280)); + + this.saveDialog = new MovieSaveDialog(owner, viewer, progressWriter, this); + this.viewer = viewer; + this.progressWriter = progressWriter; + framesPanels = new ArrayList<>(); + final JPanel controlPanel = new JPanel(new FlowLayout()); + controlPanel.setPreferredSize(new Dimension(50, 200)); + + JButton addButton = new JButton("+"); + addButton.setPreferredSize(new Dimension(50, 30)); + addButton.addActionListener(e -> addFrame()); + controlPanel.add(addButton); + + removeButton = new JButton("-"); + removeButton.setPreferredSize(new Dimension(50, 30)); + removeButton.addActionListener(e -> removeFrame()); + controlPanel.add(removeButton); + + makerPanel.add(controlPanel); + + this.mainPanel = new JPanel(); + + JScrollPane scrollMain = new JScrollPane(mainPanel); + scrollMain.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + scrollMain.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); + TitledBorder title = BorderFactory.createTitledBorder("Frames: "); + scrollMain.setBorder(title); + scrollMain.setPreferredSize(new Dimension(750, 240)); + makerPanel.add(scrollMain); + + final JPanel exportPanel = new JPanel(new FlowLayout()); + exportPanel.setPreferredSize(new Dimension(100, 200)); + + this.exportJsonButton = new JButton("Export Json"); + exportJsonButton.addActionListener(e -> exportJson()); + exportPanel.add(exportJsonButton); + + this.exportPNGsButton = new JButton("Generate PNGs"); + exportPNGsButton.addActionListener(e -> showSavePNGsDialog()); + exportPanel.add(exportPNGsButton); + + JButton importButton = new JButton("Import"); + importButton.addActionListener(e -> importSequence()); + exportPanel.add(importButton); + + makerPanel.add(exportPanel); + + add(playerPanel); + add(makerPanel); + + final ActionMap am = getRootPane().getActionMap(); + final InputMap im = getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + final Object hideKey = new Object(); + final Action hideAction = new AbstractAction() { + @Override + public void actionPerformed(final ActionEvent e) { + setVisible(false); + } + + private static final long serialVersionUID = 1L; + }; + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), hideKey); + am.put(hideKey, hideAction); + // setResizable(false); + validateButtons(); + } + + private void pausePreview() { + if (previewThread != null) + previewThread.suspend(); + } + + private void restartPreview() { + int sleep = Integer.valueOf(sleepField.getText()); + int down = Integer.valueOf(downsampleField.getText()); + int size = framesPanels.size(); + final AffineTransform3D[] transforms = new AffineTransform3D[size]; + final int[] frames = new int[size]; + final int[] accel = new int[size]; + + for (int i = 0; i < size; i++) { + MovieFrame currentFrame = framesPanels.get(i).updateFields().getMovieFrame(); + transforms[i] = currentFrame.getTransform(); + frames[i] = currentFrame.getFrames(); + accel[i] = currentFrame.getAccel(); + } + + previewThread = new PreviewThread(viewer, + transforms, + frames, + accel, + sleep, down); + } + + private void startPreview() { + if (previewThread != null) { + previewThread.suspend(); + } + restartPreview(); + previewThread.start(); + } + + // TODO import + private void importSequence() { + JFileChooser jfc = new JFileChooser(FileSystemView.getFileSystemView().getHomeDirectory()); + jfc.setDialogTitle("Select a Json"); + jfc.setAcceptAllFileFilterUsed(false); + FileNameExtensionFilter filter = new FileNameExtensionFilter("JSON File", "json"); + jfc.addChoosableFileFilter(filter); + + int returnValue = jfc.showOpenDialog(null); + if (returnValue == JFileChooser.APPROVE_OPTION) { + System.out.println(jfc.getSelectedFile().getPath()); + String path = jfc.getSelectedFile().getPath(); + new Thread(() -> { + try { + removeAllFrames(); + List list = MovieFramesSerializer.getFrom(new File(path)); + for (MovieFrame frame : list) { + AffineTransform3D currentTransform = frame.getTransform().copy(); + viewer.state().setViewerTransform(currentTransform); + Thread.sleep(50); + ImagePanel imagePanel = ImagePanel.snapshotOf(viewer); + addFrame(frame, imagePanel); + } + JOptionPane.showMessageDialog(this, "File imported successfully!", "File imported", JOptionPane.INFORMATION_MESSAGE); + + + } catch (FileNotFoundException | InterruptedException e) { + e.printStackTrace(); + } + }).run(); + } +} + + private void removeAllFrames() { + while (!framesPanels.isEmpty()) + removeFrame(); + } + + private void exportJson() { + JFileChooser fileChooser = new JFileChooser(); + int returnVal = fileChooser.showSaveDialog(this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + String path = fileChooser.getSelectedFile().getAbsolutePath(); + List list = new ArrayList<>(); + for (MovieFramePanel panels : framesPanels) + list.add(panels.getMovieFrame()); + MovieFramesSerializer.save(list, new File(path)); + + JOptionPane.showMessageDialog(this, "File saved successfully!", "File saved", JOptionPane.INFORMATION_MESSAGE); + } + } + + private void showSavePNGsDialog() { + saveDialog.setVisible(true); + } + + private void removeFrame() { + mainPanel.remove(framesPanels.remove(framesPanels.size() - 1)); + validateButtons(); + revalidate(); + repaint(); + } + + private void addFrame(MovieFrame movieFrame, ImagePanel imagePanel) { + MovieFramePanel movieFramePanel = new MovieFramePanel(movieFrame, imagePanel); + framesPanels.add(movieFramePanel); + mainPanel.add(movieFramePanel); + validateButtons(); + revalidate(); + repaint(); + } + + private void addFrame() { + AffineTransform3D currentTransform = viewer.state().getViewerTransform(); + MovieFramePanel movieFramePanel = new MovieFramePanel(currentTransform, ImagePanel.snapshotOf(viewer), framesPanels.size()); + framesPanels.add(movieFramePanel); + mainPanel.add(movieFramePanel); + validateButtons(); + revalidate(); + repaint(); + } + + public void exportPNGs(int width, int height, File dir) { + + int size = framesPanels.size(); + new Thread(new Runnable() { + @Override + public void run() { + final AffineTransform3D[] transforms = new AffineTransform3D[size]; + final int[] frames = new int[size]; + final int[] accel = new int[size]; + + for (int i = 0; i < size; i++) { + MovieFrame currentFrame = framesPanels.get(i).updateFields().getMovieFrame(); + transforms[i] = currentFrame.getTransform(); + frames[i] = currentFrame.getFrames(); + accel[i] = currentFrame.getAccel(); + } + + AffineTransform3D viewerScale = new AffineTransform3D(); + viewerScale.set( + 1.0, 0, 0, 0, + 0, 1.0, 0, 0, + 0, 0, 1.0, 0); + + try { + VNCMovie.recordMovie( + viewer, + width, + height, + transforms, + viewerScale, + frames, + accel, + 1, + dir.getAbsolutePath()); + JOptionPane.showMessageDialog(mainPanel, "All Files saved successfully!", "Files saved", JOptionPane.INFORMATION_MESSAGE); + } catch (IOException e) { + e.printStackTrace(); + } + } + }).run(); + } + + private void validateButtons() { + if (framesPanels.size() == 2 && !exportPNGsButton.isEnabled()) { + exportJsonButton.setEnabled(true); + exportPNGsButton.setEnabled(true); + exportJsonButton.revalidate(); + exportPNGsButton.revalidate(); + } + if (framesPanels.size() < 2 && exportPNGsButton.isEnabled()) { + exportJsonButton.setEnabled(false); + exportPNGsButton.setEnabled(false); + exportJsonButton.revalidate(); + exportPNGsButton.revalidate(); + } + if (framesPanels.size() > 0) { + if (!removeButton.isEnabled()) + removeButton.setEnabled(true); + removeButton.revalidate(); + removeButton.repaint(); + } else if (removeButton.isEnabled()) { + removeButton.setEnabled(false); + removeButton.revalidate(); + removeButton.repaint(); + } + } +} diff --git a/src/main/java/bdv/tools/movie/VNCMovie.java b/src/main/java/bdv/tools/movie/VNCMovie.java new file mode 100644 index 00000000..5b52e853 --- /dev/null +++ b/src/main/java/bdv/tools/movie/VNCMovie.java @@ -0,0 +1,335 @@ + +/** + * License: GPL + *

+ * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License 2 + * as published by the Free Software Foundation. + *

+ * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +package bdv.tools.movie; + +import bdv.BigDataViewer; +import bdv.cache.CacheControl; +import bdv.export.ProgressWriterConsole; +import bdv.viewer.Interpolation; +import bdv.viewer.ViewerOptions; +import bdv.viewer.ViewerPanel; +import bdv.viewer.ViewerState; +import bdv.viewer.animate.SimilarityTransformAnimator; +import bdv.viewer.overlay.MultiBoxOverlayRenderer; +import bdv.viewer.overlay.ScaleBarOverlayRenderer; +import bdv.viewer.render.MultiResolutionRenderer; +import bdv.viewer.render.PainterThread; +import bdv.viewer.render.RenderTarget; +import bdv.viewer.render.awt.BufferedImageRenderResult; +import ij.process.ColorProcessor; +import mpicbg.spim.data.SpimDataException; +import net.imglib2.realtransform.AffineTransform3D; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.concurrent.ExecutionException; + +/** + * @author Stephan Saalfeld <saalfelds@janelia.hhmi.org> + * Code was copied from org.janelia.saalfeldlab.hotknife.VNCMovie; + * Modified and adapted by Marwan Zouinkhi + */ + +public class VNCMovie { + + public static class Target implements RenderTarget { + + public BufferedImageRenderResult renderResult = new BufferedImageRenderResult(); + + private final int width; + private final int height; + + Target(final int width, final int height) { + this.width = width; + this.height = height; + } + + @Override + public BufferedImageRenderResult getReusableRenderResult() { + return renderResult; + } + + @Override + public BufferedImageRenderResult createRenderResult() { + return new BufferedImageRenderResult(); + } + + @Override + public void setRenderResult(final BufferedImageRenderResult renderResult) { + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + } + + /** + * Cosine shape of linear [0,1] + */ + protected static double cos(final double x) { + + return 0.5 - 0.5 * Math.cos(Math.PI * x); + } + + /** + * Acceleration function for a t in [0,1]: + *

+ * types + * 0 symmetric + * 1 slow start + * 2 slow end + * 3 soft symmetric + * 4 soft slow start + * 5 soft slow end + */ + protected static double accel(final double t, final int type) { + + switch (type) { + case 1: // slow start + return cos(t * t); + case 2: // slow end + return 1.0 - cos(Math.pow(1.0 - t, 2)); + case 3: // soft symmetric + return cos(cos(t)); + case 4: // soft slow start + return cos(cos(t * t)); + case 5: // soft slow end + return 1.0 - cos(cos(Math.pow(1.0 - t, 2))); + default: // symmetric + return cos(t); + } + } + + public static void recordMovie( + final ViewerPanel viewer, + final int width, + final int height, + final AffineTransform3D[] transforms, + final AffineTransform3D viewerScale, + final int[] frames, + final int[] accel, + final int firstTransformIndex, + final String dir) throws IOException { + + viewer.setInterpolation(Interpolation.NLINEAR); + viewer.setCanvasSize(width, height); + + final AffineTransform3D viewerTranslation = new AffineTransform3D(); + viewerTranslation.set( + 1, 0, 0, 0.5 * width, + 0, 1, 0, 0.5 * height, + 0, 0, 1, 0); + + final ViewerState renderState = viewer.state(); + final ScaleBarOverlayRenderer scalebar = new ScaleBarOverlayRenderer(); + final MultiBoxOverlayRenderer box = new MultiBoxOverlayRenderer(width, height); + + final Target target = new Target(width, height); + + final MultiResolutionRenderer renderer = new MultiResolutionRenderer( + target, + new PainterThread(null), + new double[]{1.0}, + 0l, + 12, + null, + false, + viewer.getOptionValues().getAccumulateProjectorFactory(), + new CacheControl.Dummy()); + + /* count i up to firstFrame */ + + int cX = 0; + int cY = 0; + + /* Removed to fix transformation */ + // cX = width / 2; + // cY = height / 2; + int i = 0; + for (int k = 0; k < firstTransformIndex; ++k) + i += frames[k]; + + for (int k = firstTransformIndex; k < transforms.length; ++k) { + final SimilarityTransformAnimator animator = new SimilarityTransformAnimator( + transforms[k - 1], + transforms[k], + cX, + cY, + 0); + + for (int d = 0; d < frames[k]; ++d) { + final AffineTransform3D tkd = animator.get(accel((double) d / (double) frames[k], accel[k])); + tkd.preConcatenate(viewerTranslation.inverse()); + tkd.preConcatenate(viewerScale); + tkd.preConcatenate(viewerTranslation); + viewer.state().setViewerTransform(tkd); + renderState.setViewerTransform(tkd); + renderer.requestRepaint(); + try { + renderer.paint(renderState); + } catch (final Exception e) { + e.printStackTrace(); + return; + } + + /* clahe */ + final BufferedImage bi = target.renderResult.getBufferedImage(); + final ColorProcessor ip = new ColorProcessor(bi); +// final ImagePlus imp = new ImagePlus("", ip); +// Flat.getFastInstance().run(imp, 128, 256, 1.5f, null, false); + + final Graphics2D g2 = bi.createGraphics(); + g2.drawImage(ip.createImage(), 0, 0, null); + + /* scalebar */ + g2.setClip(0, 0, width, height); + scalebar.setViewerState(renderState); + scalebar.paint(g2); + box.setViewerState(renderState); + box.paint(g2); + + /* save image */ + ImageIO.write(bi, "png", new File(String.format("%s/img-%04d.png", dir, i++))); + + System.out.println(String.format("%s/img-%04d.png", dir, i)); + } + } + } + + public static final void main(final String... args) throws IOException, InterruptedException, ExecutionException, SpimDataException { + + final String fn = "/Users/Marwan/Downloads/drosophila_his-yfp/dataset.xml"; + + System.setProperty("apple.laf.useScreenMenuBar", "true"); + + final BigDataViewer bdv = BigDataViewer.open(fn, new File(fn).getName(), new ProgressWriterConsole(), ViewerOptions.options()); + /* some parameters */ + final int screenWidth = 1280; + final int screenHeight = 720; + final String outDir = "/Users/Marwan/Desktop/Viewer/generatedvideo"; + + + final AffineTransform3D viewerScale = new AffineTransform3D(); + viewerScale.set( + 1.0, 0, 0, 0, + 0, 1.0, 0, 0, + 0, 0, 1.0, 0); + + +// final Window frame = SwingUtilities.getWindowAncestor(bdv.getViewerFrame().getViewerPanel()); +// frame.setSize(screenWidth, screenHeight); + +// Thread.sleep(1000); + + /* animate */ + final AffineTransform3D[] transforms = new AffineTransform3D[14]; + final int[] frames = new int[transforms.length]; + final int[] accel = new int[transforms.length]; + + transforms[0] = new AffineTransform3D(); + transforms[0].set(3.205689255981331E-4, 0.005954925593038874, 0.011687199528399559, -632.8995892966425, -0.0036154940367559957, 0.01127792679169835, -0.00564722110622439, -0.759428258980563, -0.012608726125403544, -0.00308249323337054, 0.0019164542864629447, 295.710839505524); + frames[0] = 0; + accel[0] = 0; + + transforms[1] = new AffineTransform3D(); + transforms[1].set(3.205689255981331E-4, 0.005954925593038874, 0.011687199528399559, -632.8995892966425, -0.0036154940367559957, 0.01127792679169835, -0.00564722110622439, -0.759428258980563, -0.012608726125403544, -0.00308249323337054, 0.0019164542864629447, 555.7108395055241); + frames[1] = 120; + accel[1] = 0; + + transforms[2] = new AffineTransform3D(); + transforms[2].set(3.205689255981331E-4, 0.005954925593038874, 0.011687199528399559, -632.8995892966425, -0.0036154940367559957, 0.01127792679169835, -0.00564722110622439, -0.759428258980563, -0.012608726125403544, -0.00308249323337054, 0.0019164542864629447, 75.71083950552406); + frames[2] = 240; + accel[2] = 0; + + transforms[3] = new AffineTransform3D(); + transforms[3].set(3.205689255981456E-4, 0.0059549255930388565, 0.011687199528399543, -632.8995892966416, -0.012608726125403527, -0.003082493233370528, 0.0019164542864629568, 295.7108395055235, 0.0036154940367559988, -0.011277926791698303, 0.005647221106224375, 230.75942825898073); + frames[3] = 180; + accel[3] = 3; + + transforms[4] = new AffineTransform3D(); + transforms[4].set(3.205689255981456E-4, 0.0059549255930388565, 0.011687199528399543, -632.8995892966416, -0.012608726125403527, -0.003082493233370528, 0.0019164542864629568, 295.7108395055235, 0.0036154940367559988, -0.011277926791698303, 0.005647221106224375, -179.24057174101927); + frames[4] = 240; + accel[4] = 0; + + transforms[5] = new AffineTransform3D(); + transforms[5].set(0.019311094248977183, 0.3587251295747136, 0.7040377078920287, -38125.915031351644, -0.759550533829473, -0.18568960556730266, 0.11544733876088775, 17813.641423541798, 0.21779761082624102, -0.6793830899273354, 0.3401889900077849, 45.74801084399742); + frames[5] = 480; + accel[5] = 3; + + transforms[6] = new AffineTransform3D(); + transforms[6].set(0.002612435770058029, 0.04852891027494824, 0.09524334912633023, -5124.0108529147265, -0.10275321316128334, -0.025120387351311363, 0.015617900956230078, 2409.8579526280787, 0.02946400974588925, -0.09190803290666587, 0.04602131161590296, -1115.265726010176); + frames[6] = 480; + accel[6] = 3; + + transforms[7] = new AffineTransform3D(); + transforms[7].set(-2.211895033480208E-15, -2.284330309165575E-15, 2.097155715487181, -35014.06691782091, -2.0579552334826206E-17, 2.097155715487181, 2.284330309165575E-15, -25463.86077773481, -2.097155715487181, -2.0579552334823722E-17, -2.211895033480207E-15, 50824.96035364583); + frames[7] = 480; + accel[7] = 3; + + transforms[8] = new AffineTransform3D(); + transforms[8].set(0.6192958954651673, -4.2768680290629657E-32, -3.4377828110064176E-17, -15028.751663860457, 3.7308848764166316E-32, 0.6192958954651673, 1.2288609967644243E-31, -7499.817143398536, 3.4377828110063714E-17, -1.0972935981519313E-31, 0.6192958954651673, -10322.844953809492); + frames[8] = 60; + accel[8] = 3; + + transforms[9] = new AffineTransform3D(); + transforms[9].set(0.6192958954651673, -4.2768680290629657E-32, -3.4377828110064176E-17, -15028.751663860457, 3.7308848764166316E-32, 0.6192958954651673, 1.2288609967644243E-31, -7499.817143398536, 3.4377828110063714E-17, -1.0972935981519313E-31, 0.6192958954651673, -10357.844953809492); + frames[9] = 60; + accel[9] = 3; + + transforms[10] = new AffineTransform3D(); + transforms[10].set(0.6192958954651673, -4.2768680290629657E-32, -3.4377828110064176E-17, -15028.751663860457, 3.7308848764166316E-32, 0.6192958954651673, 1.2288609967644243E-31, -7499.817143398536, 3.4377828110063714E-17, -1.0972935981519313E-31, 0.6192958954651673, -10322.844953809492); + frames[10] = 60; + accel[10] = 3; + + transforms[11] = new AffineTransform3D(); + transforms[11].set(0.02264346057538643, 0.00553571497306888, -0.003441676553876729, -558.2168887335088, -0.006492907837603556, 0.02025353617328589, -0.010141597748058744, -84.30166893338213, 5.756957329615456E-4, 0.010694190797249918, 0.02098853120655918, -124.37851098566614); + frames[11] = 480; + accel[11] = 3; + + transforms[12] = new AffineTransform3D(); + transforms[12].set(0.12282535881319635, 0.012078550390662496, -0.009523313252987673, -2682.6420581847337, -0.015081057749620071, 0.10963820290448946, -0.05544979534201524, 197.056798178377, 0.0030243300095934987, 0.056180305506280205, 0.11026005778911437, -11362.499097287542); + frames[12] = 960; + accel[12] = 3; + + transforms[13] = new AffineTransform3D(); + transforms[13].set(3.205689255981331E-4, 0.005954925593038874, 0.011687199528399559, -632.8995892966425, -0.0036154940367559957, 0.01127792679169835, -0.00564722110622439, -0.759428258980563, -0.012608726125403544, -0.00308249323337054, 0.0019164542864629447, 295.710839505524); + frames[13] = 480; + accel[13] = 3; + + recordMovie( + bdv.getViewerFrame().getViewerPanel(), + screenWidth, + screenHeight, + transforms, + viewerScale, + frames, + accel, + 5, + outDir); + + } +} \ No newline at end of file diff --git a/src/main/java/bdv/tools/movie/serilizers/AffineTransform3DJsonSerializer.java b/src/main/java/bdv/tools/movie/serilizers/AffineTransform3DJsonSerializer.java new file mode 100644 index 00000000..803c432b --- /dev/null +++ b/src/main/java/bdv/tools/movie/serilizers/AffineTransform3DJsonSerializer.java @@ -0,0 +1,25 @@ +package bdv.tools.movie.serilizers; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import net.imglib2.realtransform.AffineTransform3D; + +import java.lang.reflect.Type; + +public class AffineTransform3DJsonSerializer implements JsonSerializerDeserializer { + + public AffineTransform3D deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + double[] data = context.deserialize(json, double[].class); + AffineTransform3D t = new AffineTransform3D(); + t.set(data); + return t; + } + + public JsonElement serialize(AffineTransform3D src, Type typeOfSrc, JsonSerializationContext context) { + double[] data = new double[12]; + src.toArray(data); + return context.serialize(data); + } +} \ No newline at end of file diff --git a/src/main/java/bdv/tools/movie/serilizers/JsonSerializerDeserializer.java b/src/main/java/bdv/tools/movie/serilizers/JsonSerializerDeserializer.java new file mode 100644 index 00000000..aaa78465 --- /dev/null +++ b/src/main/java/bdv/tools/movie/serilizers/JsonSerializerDeserializer.java @@ -0,0 +1,7 @@ +package bdv.tools.movie.serilizers; + +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonSerializer; + +public interface JsonSerializerDeserializer extends JsonSerializer, JsonDeserializer { +} \ No newline at end of file diff --git a/src/main/java/bdv/tools/movie/serilizers/MovieFramesSerializer.java b/src/main/java/bdv/tools/movie/serilizers/MovieFramesSerializer.java new file mode 100644 index 00000000..70d83456 --- /dev/null +++ b/src/main/java/bdv/tools/movie/serilizers/MovieFramesSerializer.java @@ -0,0 +1,39 @@ +package bdv.tools.movie.serilizers; + +import bdv.tools.movie.MovieFrame; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import net.imglib2.realtransform.AffineTransform3D; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.util.List; + +public class MovieFramesSerializer { + + private static Gson getGson() { + GsonBuilder gsonBuilder = new GsonBuilder(); + gsonBuilder.serializeNulls().serializeSpecialFloatingPointValues(); + gsonBuilder.registerTypeAdapter(AffineTransform3D.class, new AffineTransform3DJsonSerializer()); + return gsonBuilder.create(); + } + + public static boolean save(List list, File file){ + try (Writer writer = new FileWriter(file)) { + getGson().toJson(list, writer); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + return true; + } + + public static List getFrom(File file) throws FileNotFoundException { + return getGson().fromJson(new FileReader(file.getAbsolutePath()),new TypeToken>(){}.getType()); + } +}