From 0e34cfa98b4d696ff45b572a39c8a7e5c65a8f72 Mon Sep 17 00:00:00 2001 From: mzouink Date: Mon, 28 Nov 2022 12:01:40 -0500 Subject: [PATCH 1/6] Bump to next development cycle --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6698a4a4..e777f2dd 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ sc.fiji bigdataviewer-core - 10.4.6-SNAPSHOT + 10.4.7-SNAPSHOT BigDataViewer Core BigDataViewer core classes with minimal dependencies. From 123f2db3e6a6a1119ba936a1681a08a4fea7ef67 Mon Sep 17 00:00:00 2001 From: mzouink Date: Mon, 28 Nov 2022 12:02:26 -0500 Subject: [PATCH 2/6] integrate video producer --- src/main/java/bdv/BigDataViewer.java | 8 + src/main/java/bdv/BigDataViewerActions.java | 4 + src/main/java/bdv/tools/movie/ImagePanel.java | 49 +++ src/main/java/bdv/tools/movie/MovieFrame.java | 67 ++++ .../java/bdv/tools/movie/MovieFramePanel.java | 98 +++++ .../java/bdv/tools/movie/MovieSaveDialog.java | 136 +++++++ .../java/bdv/tools/movie/PreviewThread.java | 101 +++++ .../bdv/tools/movie/ProduceMovieDialog.java | 354 ++++++++++++++++++ src/main/java/bdv/tools/movie/VNCMovie.java | 335 +++++++++++++++++ .../AffineTransform3DJsonSerializer.java | 25 ++ .../JsonSerializerDeserializer.java | 7 + .../serilizers/MovieFramesSerializer.java | 39 ++ 12 files changed, 1223 insertions(+) create mode 100644 src/main/java/bdv/tools/movie/ImagePanel.java create mode 100644 src/main/java/bdv/tools/movie/MovieFrame.java create mode 100644 src/main/java/bdv/tools/movie/MovieFramePanel.java create mode 100644 src/main/java/bdv/tools/movie/MovieSaveDialog.java create mode 100644 src/main/java/bdv/tools/movie/PreviewThread.java create mode 100644 src/main/java/bdv/tools/movie/ProduceMovieDialog.java create mode 100644 src/main/java/bdv/tools/movie/VNCMovie.java create mode 100644 src/main/java/bdv/tools/movie/serilizers/AffineTransform3DJsonSerializer.java create mode 100644 src/main/java/bdv/tools/movie/serilizers/JsonSerializerDeserializer.java create mode 100644 src/main/java/bdv/tools/movie/serilizers/MovieFramesSerializer.java diff --git a/src/main/java/bdv/BigDataViewer.java b/src/main/java/bdv/BigDataViewer.java index 7e05d08e..16a94889 100644 --- a/src/main/java/bdv/BigDataViewer.java +++ b/src/main/java/bdv/BigDataViewer.java @@ -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; @@ -127,6 +128,8 @@ public class BigDataViewer protected final RecordMovieDialog movieDialog; + protected final ProduceMovieDialog produceMovieDialog; + protected final RecordMaxProjectionDialog movieMaxProjectDialog; protected final VisibilityAndGroupingDialog activeSourcesDialog; @@ -398,6 +401,7 @@ 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 +506,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 ); diff --git a/src/main/java/bdv/BigDataViewerActions.java b/src/main/java/bdv/BigDataViewerActions.java index 9760aa21..20bc92bf 100644 --- a/src/main/java/bdv/BigDataViewerActions.java +++ b/src/main/java/bdv/BigDataViewerActions.java @@ -61,6 +61,7 @@ public class BigDataViewerActions extends Actions public static final String LOAD_SETTINGS = "load settings"; public static final String EXPAND_CARDS = "expand and focus cards panel"; public static final String COLLAPSE_CARDS = "collapse cards panel"; + public static final String PRODUCE_MOVIE = "produce movie"; public static final String RECORD_MOVIE = "record movie"; public static final String RECORD_MAX_PROJECTION_MOVIE = "record max projection movie"; public static final String SET_BOOKMARK = "set bookmark"; @@ -78,6 +79,7 @@ public class BigDataViewerActions extends Actions public static final String[] EXPAND_CARDS_KEYS = new String[] { "P" }; public static final String[] COLLAPSE_CARDS_KEYS = new String[] { "shift P", "shift ESCAPE" }; public static final String[] RECORD_MOVIE_KEYS = new String[] { "F10" }; + public static final String[] PRODUCE_MOVIE_KEYS = new String[] { "F7" }; public static final String[] RECORD_MAX_PROJECTION_MOVIE_KEYS = new String[] { "F8" }; public static final String[] SET_BOOKMARK_KEYS = new String[] { "shift B" }; public static final String[] GO_TO_BOOKMARK_KEYS = new String[] { "B" }; @@ -108,6 +110,7 @@ public void getCommandDescriptions( final CommandDescriptions descriptions ) descriptions.add( EXPAND_CARDS, EXPAND_CARDS_KEYS, "Expand and focus the BigDataViewer card panel" ); descriptions.add( COLLAPSE_CARDS, COLLAPSE_CARDS_KEYS, "Collapse the BigDataViewer card panel" ); descriptions.add( RECORD_MOVIE, RECORD_MOVIE_KEYS, "Show the Record Movie dialog." ); + descriptions.add( PRODUCE_MOVIE, PRODUCE_MOVIE_KEYS, "Show the Produce Movie dialog." ); descriptions.add( RECORD_MAX_PROJECTION_MOVIE, RECORD_MAX_PROJECTION_MOVIE_KEYS, "Show the Record Max Projection Movie dialog." ); descriptions.add( SET_BOOKMARK, SET_BOOKMARK_KEYS, "Set a labeled bookmark at the current location." ); descriptions.add( GO_TO_BOOKMARK, GO_TO_BOOKMARK_KEYS, "Retrieve a labeled bookmark location." ); @@ -132,6 +135,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()); + } +} From e6f96ff8a9609a71bcc325fde594da41f2f99f12 Mon Sep 17 00:00:00 2001 From: mzouink Date: Mon, 28 Nov 2022 12:08:49 -0500 Subject: [PATCH 3/6] fix dependency problem --- src/main/java/bdv/tools/movie/VNCMovie.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/bdv/tools/movie/VNCMovie.java b/src/main/java/bdv/tools/movie/VNCMovie.java index 5b52e853..f012b513 100644 --- a/src/main/java/bdv/tools/movie/VNCMovie.java +++ b/src/main/java/bdv/tools/movie/VNCMovie.java @@ -31,7 +31,6 @@ 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; @@ -198,12 +197,12 @@ public static void recordMovie( /* clahe */ final BufferedImage bi = target.renderResult.getBufferedImage(); - final ColorProcessor ip = new ColorProcessor(bi); +// 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); + g2.drawImage(bi, 0, 0, null); /* scalebar */ g2.setClip(0, 0, width, height); From 8aa9e7b072b188d6e122570bb9d17838e9f0014c Mon Sep 17 00:00:00 2001 From: mzouink Date: Wed, 30 Nov 2022 21:04:02 -0500 Subject: [PATCH 4/6] optimize preview n volatile --- src/main/java/bdv/BigDataViewer.java | 2 +- .../java/bdv/tools/movie/MovieProducer.java | 207 +++++++++++ .../bdv/tools/movie/ProduceMovieDialog.java | 118 +++---- src/main/java/bdv/tools/movie/VNCMovie.java | 334 ------------------ .../bdv/tools/movie/panels/ImageFrame.java | 44 +++ .../tools/movie/{ => panels}/ImagePanel.java | 2 +- .../movie/{ => panels}/MovieFramePanel.java | 22 +- .../movie/{ => panels}/MovieSaveDialog.java | 32 +- .../MovieFrameInst.java} | 8 +- .../tools/movie/preview/PreviewRender.java | 169 +++++++++ .../movie/{ => preview}/PreviewThread.java | 8 +- .../serilizers/MovieFramesSerializer.java | 8 +- 12 files changed, 514 insertions(+), 440 deletions(-) create mode 100644 src/main/java/bdv/tools/movie/MovieProducer.java delete mode 100644 src/main/java/bdv/tools/movie/VNCMovie.java create mode 100644 src/main/java/bdv/tools/movie/panels/ImageFrame.java rename src/main/java/bdv/tools/movie/{ => panels}/ImagePanel.java (97%) rename src/main/java/bdv/tools/movie/{ => panels}/MovieFramePanel.java (82%) rename src/main/java/bdv/tools/movie/{ => panels}/MovieSaveDialog.java (84%) rename src/main/java/bdv/tools/movie/{MovieFrame.java => preview/MovieFrameInst.java} (83%) create mode 100644 src/main/java/bdv/tools/movie/preview/PreviewRender.java rename src/main/java/bdv/tools/movie/{ => preview}/PreviewThread.java (92%) diff --git a/src/main/java/bdv/BigDataViewer.java b/src/main/java/bdv/BigDataViewer.java index 16a94889..f291f2b4 100644 --- a/src/main/java/bdv/BigDataViewer.java +++ b/src/main/java/bdv/BigDataViewer.java @@ -783,7 +783,7 @@ public void collapseCardPanel() public static void main( final String[] args ) { - final String fn = "/Users/pietzsch/workspace/data/111010_weber_resave.xml"; + final String fn = "/Users/zouinkhim/Desktop/unfinishedProjects/grid-3d-stitched-h5/dataset.xml"; try { System.setProperty( "apple.laf.useScreenMenuBar", "true" ); diff --git a/src/main/java/bdv/tools/movie/MovieProducer.java b/src/main/java/bdv/tools/movie/MovieProducer.java new file mode 100644 index 00000000..b9e09a31 --- /dev/null +++ b/src/main/java/bdv/tools/movie/MovieProducer.java @@ -0,0 +1,207 @@ + +/** + * 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.cache.CacheControl; +import bdv.export.ProgressWriter; +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 net.imglib2.realtransform.AffineTransform3D; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +/** + * @author Stephan Saalfeld <saalfelds@janelia.hhmi.org> + * Code was copied from org.janelia.saalfeldlab.hotknife.VNCMovie; + * Modified and adapted by Marwan Zouinkhi + */ + +public class MovieProducer { + + public static class Target implements RenderTarget { + + public BufferedImageRenderResult renderResult = new BufferedImageRenderResult(); + + private final int width; + private final int height; + + public 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 + */ + public 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 AffineTransform3D[] transforms, + final int[] frames, + final int[] accel, + int width, + int height, + + final String dir, + ProgressWriter progressWriter) throws IOException { + + + + final ViewerState renderState = viewer.state(); + final ScaleBarOverlayRenderer scalebar = new ScaleBarOverlayRenderer(); + + int screenWidth = viewer.getDisplayComponent().getWidth(); + int screenHeight = viewer.getDisplayComponent().getHeight(); + double ratio = Math.min(width * 1.0 / screenWidth, height * 1.0 / screenHeight); + + final AffineTransform3D viewerScale = new AffineTransform3D(); + + viewerScale.set( + ratio, 0, 0, 0, + 0, ratio, 0, 0, + 0, 0, 1.0, 0); + + 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, + true, + viewer.getOptionValues().getAccumulateProjectorFactory(), + new CacheControl.Dummy()); + + int i = 0; + + for (int k = 1; k < transforms.length; ++k) { + progressWriter.setProgress((k*1.0/transforms.length)); + final SimilarityTransformAnimator animator = new SimilarityTransformAnimator( + transforms[k - 1], + transforms[k], + 0, + 0, + 0); + + for (int d = 0; d < frames[k]; ++d) { + final AffineTransform3D tkd = animator.get(accel((double) d / (double) frames[k], accel[k])); + tkd.preConcatenate(viewerScale); + viewer.state().setViewerTransform(tkd); + renderState.setViewerTransform(tkd); + renderer.requestRepaint(); + try { + renderer.paint(renderState); + } catch (final Exception e) { + e.printStackTrace(); + return; + } + + final BufferedImage bi = target.renderResult.getBufferedImage(); + + final Graphics2D g2 = bi.createGraphics(); + g2.drawImage(bi, 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)); + } + } + } + +} \ No newline at end of file diff --git a/src/main/java/bdv/tools/movie/ProduceMovieDialog.java b/src/main/java/bdv/tools/movie/ProduceMovieDialog.java index 80be063d..04dc2d3c 100644 --- a/src/main/java/bdv/tools/movie/ProduceMovieDialog.java +++ b/src/main/java/bdv/tools/movie/ProduceMovieDialog.java @@ -29,6 +29,12 @@ package bdv.tools.movie; import bdv.export.ProgressWriter; +import bdv.tools.movie.MovieProducer; +import bdv.tools.movie.panels.ImagePanel; +import bdv.tools.movie.panels.MovieFramePanel; +import bdv.tools.movie.panels.MovieSaveDialog; +import bdv.tools.movie.preview.MovieFrameInst; +import bdv.tools.movie.preview.PreviewRender; import bdv.tools.movie.serilizers.MovieFramesSerializer; import bdv.util.DelayedPackDialog; import bdv.viewer.ViewerPanel; @@ -61,7 +67,7 @@ public class ProduceMovieDialog extends DelayedPackDialog { private final static int FrameWidth = 920; private final static int DEFAULT_SLEEP = 100; private final static int DEFAULT_DOWN = 10; - private PreviewThread previewThread; + private PreviewRender previewThread; private final JTextField downsampleField; private final JTextField sleepField; @@ -72,30 +78,23 @@ public ProduceMovieDialog(final Frame owner, final ViewerPanel viewer, final Pro JPanel playerPanel = new JPanel(); downsampleField = new JTextField(String.valueOf(DEFAULT_DOWN)); + downsampleField.setPreferredSize(new Dimension(50, 20)); sleepField = new JTextField(String.valueOf(DEFAULT_SLEEP)); + sleepField.setPreferredSize(new Dimension(50, 20)); playerPanel.add(new JLabel("PREVIEW: Downsampling: 1/")); playerPanel.add(downsampleField); - playerPanel.add(new JLabel("Sleep:")); + playerPanel.add(new JLabel(" Sleep:")); playerPanel.add(sleepField); playerPanel.add(new JLabel("ms ")); JButton playButton = new JButton("▶"); - playButton.addActionListener(e -> { - startPreview(); - }); + playButton.addActionListener(e -> startPreview()); playerPanel.add(playButton); JButton pauseButton = new JButton("Stop"); - pauseButton.addActionListener(e -> { - pausePreview(); - }); - playerPanel.add(pauseButton); + pauseButton.addActionListener(e -> pausePreview()); -// JButton restartButton = new JButton("Refresh"); -// restartButton.addActionListener(e -> { -// restartPreview(); -// }); -// playerPanel.add(restartButton); + playerPanel.add(pauseButton); playerPanel.setPreferredSize(new Dimension(FrameWidth, 40)); @@ -164,7 +163,6 @@ public void actionPerformed(final ActionEvent e) { }; im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), hideKey); am.put(hideKey, hideAction); - // setResizable(false); validateButtons(); } @@ -182,17 +180,23 @@ private void restartPreview() { final int[] accel = new int[size]; for (int i = 0; i < size; i++) { - MovieFrame currentFrame = framesPanels.get(i).updateFields().getMovieFrame(); + MovieFrameInst currentFrame = framesPanels.get(i).updateFields().getMovieFrame(); transforms[i] = currentFrame.getTransform(); frames[i] = currentFrame.getFrames(); accel[i] = currentFrame.getAccel(); } - previewThread = new PreviewThread(viewer, + previewThread = new PreviewRender(viewer, transforms, frames, accel, sleep, down); + +// previewThread = new PreviewThread(viewer, +// transforms, +// frames, +// accel, +// sleep, down); } private void startPreview() { @@ -203,7 +207,7 @@ private void startPreview() { previewThread.start(); } - // TODO import + private void importSequence() { JFileChooser jfc = new JFileChooser(FileSystemView.getFileSystemView().getHomeDirectory()); jfc.setDialogTitle("Select a Json"); @@ -218,23 +222,24 @@ private void importSequence() { new Thread(() -> { try { removeAllFrames(); - List list = MovieFramesSerializer.getFrom(new File(path)); - for (MovieFrame frame : list) { + List list = MovieFramesSerializer.getFrom(new File(path)); + for (MovieFrameInst 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(); + JOptionPane.showMessageDialog(this, e.getMessage(), "ERROR", JOptionPane.ERROR_MESSAGE); + } }).run(); } -} + } private void removeAllFrames() { while (!framesPanels.isEmpty()) @@ -246,12 +251,14 @@ private void exportJson() { int returnVal = fileChooser.showSaveDialog(this); if (returnVal == JFileChooser.APPROVE_OPTION) { String path = fileChooser.getSelectedFile().getAbsolutePath(); - List list = new ArrayList<>(); + if (!path.endsWith(".json")) + path = path + ".json"; + 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); + JOptionPane.showMessageDialog(this, "File saved successfully!", "File saved", JOptionPane.PLAIN_MESSAGE); } } @@ -266,7 +273,7 @@ private void removeFrame() { repaint(); } - private void addFrame(MovieFrame movieFrame, ImagePanel imagePanel) { + private void addFrame(MovieFrameInst movieFrame, ImagePanel imagePanel) { MovieFramePanel movieFramePanel = new MovieFramePanel(movieFrame, imagePanel); framesPanels.add(movieFramePanel); mainPanel.add(movieFramePanel); @@ -285,44 +292,33 @@ private void addFrame() { repaint(); } - public void exportPNGs(int width, int height, File dir) { - + public void exportPNGs(File dir,int width, int height, ProgressWriter progressWriter) { 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); + new Thread(() -> { + 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++) { + MovieFrameInst currentFrame = framesPanels.get(i).updateFields().getMovieFrame(); + transforms[i] = currentFrame.getTransform(); + frames[i] = currentFrame.getFrames(); + accel[i] = currentFrame.getAccel(); + } - 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(); - } + try { + MovieProducer.recordMovie( + viewer, + transforms, + frames, + accel, + width, + height, + dir.getAbsolutePath(), + progressWriter); + JOptionPane.showMessageDialog(mainPanel, "All Files saved successfully!", "Files saved", JOptionPane.PLAIN_MESSAGE); + } catch (IOException e) { + e.printStackTrace(); } }).run(); } diff --git a/src/main/java/bdv/tools/movie/VNCMovie.java b/src/main/java/bdv/tools/movie/VNCMovie.java deleted file mode 100644 index f012b513..00000000 --- a/src/main/java/bdv/tools/movie/VNCMovie.java +++ /dev/null @@ -1,334 +0,0 @@ - -/** - * 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 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(bi, 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/panels/ImageFrame.java b/src/main/java/bdv/tools/movie/panels/ImageFrame.java new file mode 100644 index 00000000..c65f64f8 --- /dev/null +++ b/src/main/java/bdv/tools/movie/panels/ImageFrame.java @@ -0,0 +1,44 @@ +package bdv.tools.movie.panels; + +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; + +public class ImageFrame extends JFrame { + private BufferedImage image; + + public ImageFrame() throws HeadlessException { + super(); + setSize(new Dimension(420, 420)); + } + + @Override + public void paint(Graphics g) { + if (image != null) + g.drawImage(image, 10, 10, this); + else + super.paint(g); + } + + public static BufferedImage resize(BufferedImage img, int newW, int newH) { + Image tmp = img.getScaledInstance(newW, newH, Image.SCALE_SMOOTH); + BufferedImage dimg = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_ARGB); + + Graphics2D g2d = dimg.createGraphics(); + g2d.drawImage(tmp, 0, 0, null); + g2d.dispose(); + + return dimg; + } + + public void setImage(BufferedImage image) { + this.image = resize(image, 400, 400); + if (!isVisible()) + setVisible(true); + SwingUtilities.updateComponentTreeUI(this); + } + + public static void main(String[] args) { + new ImageFrame(); + } +} diff --git a/src/main/java/bdv/tools/movie/ImagePanel.java b/src/main/java/bdv/tools/movie/panels/ImagePanel.java similarity index 97% rename from src/main/java/bdv/tools/movie/ImagePanel.java rename to src/main/java/bdv/tools/movie/panels/ImagePanel.java index d214ab94..3580b0ac 100644 --- a/src/main/java/bdv/tools/movie/ImagePanel.java +++ b/src/main/java/bdv/tools/movie/panels/ImagePanel.java @@ -1,4 +1,4 @@ -package bdv.tools.movie; +package bdv.tools.movie.panels; import javax.swing.*; import java.awt.*; diff --git a/src/main/java/bdv/tools/movie/MovieFramePanel.java b/src/main/java/bdv/tools/movie/panels/MovieFramePanel.java similarity index 82% rename from src/main/java/bdv/tools/movie/MovieFramePanel.java rename to src/main/java/bdv/tools/movie/panels/MovieFramePanel.java index 164bbf5c..ca3441b1 100644 --- a/src/main/java/bdv/tools/movie/MovieFramePanel.java +++ b/src/main/java/bdv/tools/movie/panels/MovieFramePanel.java @@ -1,5 +1,6 @@ -package bdv.tools.movie; +package bdv.tools.movie.panels; +import bdv.tools.movie.preview.MovieFrameInst; import net.imglib2.realtransform.AffineTransform3D; import javax.swing.*; @@ -8,31 +9,26 @@ import java.awt.*; public class MovieFramePanel extends JPanel { - final private MovieFrame movieFrame; + final private MovieFrameInst 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"}; - - - + 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.movieFrame = new MovieFrameInst(position, transform); this.image = image; initView(); } - public MovieFramePanel(MovieFrame movieFrame, ImagePanel image) { + public MovieFramePanel(MovieFrameInst 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())); @@ -49,9 +45,7 @@ private void initView() { 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)); + JPanel framePanel = new JPanel(new GridLayout(1, 2)); framePanel.add(framesLabel); framePanel.add(framesField); fieldsPanel.add(framePanel); @@ -91,7 +85,7 @@ public int getFrames() { return movieFrame.getFrames(); } - public MovieFrame getMovieFrame() { + public MovieFrameInst getMovieFrame() { updateFields(); return movieFrame; } diff --git a/src/main/java/bdv/tools/movie/MovieSaveDialog.java b/src/main/java/bdv/tools/movie/panels/MovieSaveDialog.java similarity index 84% rename from src/main/java/bdv/tools/movie/MovieSaveDialog.java rename to src/main/java/bdv/tools/movie/panels/MovieSaveDialog.java index b57ae1da..6e2998ca 100644 --- a/src/main/java/bdv/tools/movie/MovieSaveDialog.java +++ b/src/main/java/bdv/tools/movie/panels/MovieSaveDialog.java @@ -1,6 +1,7 @@ -package bdv.tools.movie; +package bdv.tools.movie.panels; import bdv.export.ProgressWriter; +import bdv.tools.movie.ProduceMovieDialog; import bdv.util.DelayedPackDialog; import bdv.viewer.OverlayRenderer; import bdv.viewer.ViewerPanel; @@ -89,22 +90,19 @@ public void actionPerformed(final ActionEvent e) { } }); - 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); + recordButton.addActionListener(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(dir,width,height,progressWriter); }); final ActionMap am = getRootPane().getActionMap(); @@ -133,4 +131,4 @@ public void setCanvasSize(final int width, final int height) { spinnerWidth.setValue(width); spinnerHeight.setValue(height); } -} +} \ No newline at end of file diff --git a/src/main/java/bdv/tools/movie/MovieFrame.java b/src/main/java/bdv/tools/movie/preview/MovieFrameInst.java similarity index 83% rename from src/main/java/bdv/tools/movie/MovieFrame.java rename to src/main/java/bdv/tools/movie/preview/MovieFrameInst.java index fa9a8391..8c9bf8d3 100644 --- a/src/main/java/bdv/tools/movie/MovieFrame.java +++ b/src/main/java/bdv/tools/movie/preview/MovieFrameInst.java @@ -1,10 +1,10 @@ -package bdv.tools.movie; +package bdv.tools.movie.preview; import net.imglib2.realtransform.AffineTransform3D; import java.io.Serializable; -public class MovieFrame implements Serializable { +public class MovieFrameInst implements Serializable { private AffineTransform3D transform; private int position; @@ -14,11 +14,11 @@ public class MovieFrame implements Serializable { private final static int DEFAULT_ACCEL = 0; - public MovieFrame(int position, AffineTransform3D transform) { + public MovieFrameInst(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) { + public MovieFrameInst(int position, AffineTransform3D transform, int frames, int accel) { this.position = position; this.transform = transform; this.frames = frames; diff --git a/src/main/java/bdv/tools/movie/preview/PreviewRender.java b/src/main/java/bdv/tools/movie/preview/PreviewRender.java new file mode 100644 index 00000000..90ee5dd1 --- /dev/null +++ b/src/main/java/bdv/tools/movie/preview/PreviewRender.java @@ -0,0 +1,169 @@ +package bdv.tools.movie.preview; + + +import bdv.cache.CacheControl; +import bdv.tools.movie.MovieProducer; +import bdv.tools.movie.panels.ImageFrame; +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 net.imglib2.realtransform.AffineTransform3D; + +import java.awt.*; +import java.awt.image.BufferedImage; + +public class PreviewRender 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; + + public PreviewRender( + final ViewerPanel viewer, + final AffineTransform3D[] transforms, + final int[] frames, + final int[] accel, + final int sleep, + final int down) { + System.out.println("got "+transforms.length+" transforms"); + this.viewer = viewer; + this.transforms = transforms; + this.frames = frames; + this.accel = accel; + this.sleep = sleep; + this.down = down; + } + + public void run() { + ImageFrame preview = new ImageFrame(); + + int width = 600; + int height = 600; + + int screenWidth = viewer.getDisplayComponent().getWidth(); + int screenHeight = viewer.getDisplayComponent().getHeight(); + + double ratio = Math.min(width * 1.0 / screenWidth, height * 1.0 / screenHeight); + final AffineTransform3D viewerScale = new AffineTransform3D(); + final AffineTransform3D viewerTranslation = new AffineTransform3D(); + + viewerScale.set( + ratio, 0, 0, 0, + 0, ratio, 0, 0, + 0, 0, 1.0, 0); +// viewerTranslation.set( +// 1, 0, 0, 0.5 * screenWidth, +// 0, 1, 0, 0.5 * screenHeight, +// 0, 0, 1, 0); + + + final ViewerState renderState = viewer.state(); + final ScaleBarOverlayRenderer scalebar = new ScaleBarOverlayRenderer(); + final MultiBoxOverlayRenderer box = new MultiBoxOverlayRenderer(width, height); + + final MovieProducer.Target target = new MovieProducer.Target(width, height); + + final MultiResolutionRenderer renderer = new MultiResolutionRenderer( + target, + new PainterThread(null), + new double[]{1.0}, + 0l, + 12, + null, + true, + viewer.getOptionValues().getAccumulateProjectorFactory(), + new CacheControl.Dummy()); + + + for (int k = 1; k < transforms.length; ++k) { + final SimilarityTransformAnimator animator = new SimilarityTransformAnimator( + transforms[k - 1], + transforms[k], + 0, + 0, + 0); + int downsampledFrames = frames[k]/down; + + for (int d = 0; d < downsampledFrames; ++d) { + final AffineTransform3D tkd = animator.get(MovieProducer.accel((double) d / (double) downsampledFrames, 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 Graphics2D g2 = bi.createGraphics(); + + g2.drawImage(bi, 0, 0, null); + + /* scalebar */ + g2.setClip(0, 0, width, height); + scalebar.setViewerState(renderState); + scalebar.paint(g2); + box.setViewerState(renderState); + box.paint(g2); + + preview.setImage(bi); + try { + Thread.sleep(sleep); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + } + } + } + + 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/PreviewThread.java b/src/main/java/bdv/tools/movie/preview/PreviewThread.java similarity index 92% rename from src/main/java/bdv/tools/movie/PreviewThread.java rename to src/main/java/bdv/tools/movie/preview/PreviewThread.java index 0034b54a..0711ff67 100644 --- a/src/main/java/bdv/tools/movie/PreviewThread.java +++ b/src/main/java/bdv/tools/movie/preview/PreviewThread.java @@ -1,5 +1,6 @@ -package bdv.tools.movie; +package bdv.tools.movie.preview; +import bdv.tools.movie.MovieProducer; import bdv.viewer.Interpolation; import bdv.viewer.ViewerPanel; import bdv.viewer.animate.SimilarityTransformAnimator; @@ -7,7 +8,7 @@ import javax.swing.*; -public class PreviewThread extends VNCMovie implements Runnable { +public class PreviewThread implements Runnable { private final ViewerPanel viewer; private final AffineTransform3D[] transforms; @@ -51,8 +52,7 @@ public void run() { 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])); + final AffineTransform3D tkd = animator.get(MovieProducer.accel((double) d / (double) downFrames, accel[k])); // System.out.println(tkd.toString()); SwingUtilities.invokeLater(() -> { viewer.state().setViewerTransform(tkd); diff --git a/src/main/java/bdv/tools/movie/serilizers/MovieFramesSerializer.java b/src/main/java/bdv/tools/movie/serilizers/MovieFramesSerializer.java index 70d83456..0b428c02 100644 --- a/src/main/java/bdv/tools/movie/serilizers/MovieFramesSerializer.java +++ b/src/main/java/bdv/tools/movie/serilizers/MovieFramesSerializer.java @@ -1,6 +1,6 @@ package bdv.tools.movie.serilizers; -import bdv.tools.movie.MovieFrame; +import bdv.tools.movie.preview.MovieFrameInst; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; @@ -23,7 +23,7 @@ private static Gson getGson() { return gsonBuilder.create(); } - public static boolean save(List list, File file){ + public static boolean save(List list, File file){ try (Writer writer = new FileWriter(file)) { getGson().toJson(list, writer); } catch (IOException e) { @@ -33,7 +33,7 @@ public static boolean save(List list, File file){ return true; } - public static List getFrom(File file) throws FileNotFoundException { - return getGson().fromJson(new FileReader(file.getAbsolutePath()),new TypeToken>(){}.getType()); + public static List getFrom(File file) throws FileNotFoundException { + return getGson().fromJson(new FileReader(file.getAbsolutePath()),new TypeToken>(){}.getType()); } } From b134f8df64fc7d69bfb3cb8085dd906eec54e590 Mon Sep 17 00:00:00 2001 From: mzouink Date: Tue, 6 Dec 2022 10:22:54 -0500 Subject: [PATCH 5/6] video producer volatile --- .../java/bdv/tools/movie/MovieProducer.java | 8 +- .../tools/movie/preview/PreviewRender.java | 2 +- .../AffineTransform3DJsonSerializer.java | 1 + .../TestTransformationDifference.java | 21 ++++ .../movie/timeline/TransformationPlot.java | 116 ++++++++++++++++++ 5 files changed, 143 insertions(+), 5 deletions(-) create mode 100644 src/main/java/bdv/tools/movie/timeline/TestTransformationDifference.java create mode 100644 src/main/java/bdv/tools/movie/timeline/TransformationPlot.java diff --git a/src/main/java/bdv/tools/movie/MovieProducer.java b/src/main/java/bdv/tools/movie/MovieProducer.java index b9e09a31..f0f233c7 100644 --- a/src/main/java/bdv/tools/movie/MovieProducer.java +++ b/src/main/java/bdv/tools/movie/MovieProducer.java @@ -99,6 +99,7 @@ protected static double cos(final double x) { * 3 soft symmetric * 4 soft slow start * 5 soft slow end + * t = current frame / nb frames */ public static double accel(final double t, final int type) { @@ -125,12 +126,9 @@ public static void recordMovie( final int[] accel, int width, int height, - final String dir, ProgressWriter progressWriter) throws IOException { - - final ViewerState renderState = viewer.state(); final ScaleBarOverlayRenderer scalebar = new ScaleBarOverlayRenderer(); @@ -156,7 +154,7 @@ public static void recordMovie( 0l, 12, null, - true, + false, viewer.getOptionValues().getAccumulateProjectorFactory(), new CacheControl.Dummy()); @@ -204,4 +202,6 @@ public static void recordMovie( } } + + } \ No newline at end of file diff --git a/src/main/java/bdv/tools/movie/preview/PreviewRender.java b/src/main/java/bdv/tools/movie/preview/PreviewRender.java index 90ee5dd1..e0ae21cd 100644 --- a/src/main/java/bdv/tools/movie/preview/PreviewRender.java +++ b/src/main/java/bdv/tools/movie/preview/PreviewRender.java @@ -80,7 +80,7 @@ public void run() { 0l, 12, null, - true, + false, viewer.getOptionValues().getAccumulateProjectorFactory(), new CacheControl.Dummy()); diff --git a/src/main/java/bdv/tools/movie/serilizers/AffineTransform3DJsonSerializer.java b/src/main/java/bdv/tools/movie/serilizers/AffineTransform3DJsonSerializer.java index 803c432b..c57b7ad3 100644 --- a/src/main/java/bdv/tools/movie/serilizers/AffineTransform3DJsonSerializer.java +++ b/src/main/java/bdv/tools/movie/serilizers/AffineTransform3DJsonSerializer.java @@ -22,4 +22,5 @@ public JsonElement serialize(AffineTransform3D src, Type typeOfSrc, JsonSerializ src.toArray(data); return context.serialize(data); } + } \ No newline at end of file diff --git a/src/main/java/bdv/tools/movie/timeline/TestTransformationDifference.java b/src/main/java/bdv/tools/movie/timeline/TestTransformationDifference.java new file mode 100644 index 00000000..17bdb121 --- /dev/null +++ b/src/main/java/bdv/tools/movie/timeline/TestTransformationDifference.java @@ -0,0 +1,21 @@ +package bdv.tools.movie.timeline; + +import net.imglib2.realtransform.AffineTransform3D; + +public class TestTransformationDifference { + public static void main(String[] args) { + AffineTransform3D transform3D = new AffineTransform3D(); + transform3D.set(3,2,5,100, + 4,3,2,0, + 8,1,3,0); + AffineTransform3D transform2 = transform3D.copy(); + transform3D.scale(2.0); + transform3D.rotate(0,90); + AffineTransform3D diff = transform3D.copy(); + + diff.concatenate(transform2.inverse()); + + System.out.println(diff); + + } +} diff --git a/src/main/java/bdv/tools/movie/timeline/TransformationPlot.java b/src/main/java/bdv/tools/movie/timeline/TransformationPlot.java new file mode 100644 index 00000000..d1e573dc --- /dev/null +++ b/src/main/java/bdv/tools/movie/timeline/TransformationPlot.java @@ -0,0 +1,116 @@ +package bdv.tools.movie.timeline; + + +import bdv.tools.movie.preview.MovieFrameInst; +import bdv.tools.movie.serilizers.MovieFramesSerializer; +import bdv.viewer.animate.SimilarityTransformAnimator; +import net.imglib2.realtransform.AffineTransform3D; + +import java.awt.*; +import javax.swing.*; +import java.awt.geom.*; +import java.io.File; +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static bdv.tools.movie.MovieProducer.accel; + +//Extends JPanel class +public class TransformationPlot extends JPanel { + + private final List movieFrames; + + // int[] cord = {65, 20, 40, 80}; + int marg = 50; + + public TransformationPlot(List movieFrames) { + this.movieFrames = movieFrames; + } + + protected void paintComponent(Graphics grf) { + super.paintComponent(grf); + Graphics2D graph = (Graphics2D) grf; + + //Sets the value of a single preference for the rendering algorithms. + graph.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // get width and height + int width = getWidth(); + int height = getHeight(); + + // draw graph + graph.draw(new Line2D.Double(marg, height - marg, width - marg, height - marg)); + + + List similarity = new ArrayList<>(); + for (int k = 1; k < movieFrames.size(); ++k) { + AffineTransform3D transformsStart = movieFrames.get(k - 1).getTransform(); + AffineTransform3D last = transformsStart; + AffineTransform3D transformsEnd = movieFrames.get(k).getTransform(); + int frames = movieFrames.get(k).getFrames(); + int accel = movieFrames.get(k).getAccel(); + for (int n = 0; n < movieFrames.get(k).getFrames(); ++n) { + + final SimilarityTransformAnimator animator = new SimilarityTransformAnimator( + transformsStart, + transformsEnd, + 0, + 0, + 0); + + final AffineTransform3D tkd = animator.get(accel((double) n / (double) frames, accel)); + double sim = getSimilarity(last, tkd); + similarity.add(sim); + last = tkd; + } + } + Double max = Collections.max(similarity); + System.out.println("Max : " + max); + //find value of x and scale to plot points + double x = (double) (width - 2 * marg) / (similarity.size() - 1); + double scale = (double) (height - 2 * marg) / max; + + //set color for points + graph.setPaint(Color.RED); + + // set points to the graph + for (int i = 0; i < similarity.size(); i++) { + double x1 = marg + i * x; + double y1 = height - marg - scale * similarity.get(i); + graph.fill(new Ellipse2D.Double(x1 - 2, y1 - 2, 4, 4)); + } + } + + private double getSimilarity(AffineTransform3D last, AffineTransform3D tkd) { + + AffineTransform3D diff = tkd.copy(); + diff.concatenate(last.inverse()); + double all = 0; + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + all += diff.get(i, j); + return all; + } + + + //main() method start + public static void main(String args[]) throws FileNotFoundException { + String jsonTransformations = "/Users/zouinkhim/Desktop/test2.json"; + List movieFrames = MovieFramesSerializer.getFrom(new File(jsonTransformations)); + JFrame frame = new JFrame(); + frame.add(new TransformationPlot(movieFrames)); + frame.setSize(400, 400); + frame.setLocation(200, 200); + frame.setVisible(true); + + jsonTransformations = "/Users/zouinkhim/Desktop/test.json"; + List movieFrames2 = MovieFramesSerializer.getFrom(new File(jsonTransformations)); + JFrame frame2 = new JFrame(); + frame2.add(new TransformationPlot(movieFrames2)); + frame2.setSize(400, 400); + frame2.setLocation(200, 200); + frame2.setVisible(true); + } +} \ No newline at end of file From 63685d267ae96c4cd59f30bf82ab6f60ca649f74 Mon Sep 17 00:00:00 2001 From: mzouink Date: Tue, 6 Dec 2022 20:51:06 -0500 Subject: [PATCH 6/6] optimize serialization --- .../bdv/tools/movie/ProduceMovieDialog.java | 19 +-- .../tools/movie/preview/MovieFrameInst.java | 12 +- .../tools/movie/preview/PreviewThread.java | 2 +- .../serilizers/MovieFramesSerializer.java | 5 +- .../TestTransformationDifference.java | 21 ---- .../movie/timeline/TransformationPlot.java | 116 ------------------ 6 files changed, 23 insertions(+), 152 deletions(-) delete mode 100644 src/main/java/bdv/tools/movie/timeline/TestTransformationDifference.java delete mode 100644 src/main/java/bdv/tools/movie/timeline/TransformationPlot.java diff --git a/src/main/java/bdv/tools/movie/ProduceMovieDialog.java b/src/main/java/bdv/tools/movie/ProduceMovieDialog.java index 04dc2d3c..ee960b9d 100644 --- a/src/main/java/bdv/tools/movie/ProduceMovieDialog.java +++ b/src/main/java/bdv/tools/movie/ProduceMovieDialog.java @@ -35,6 +35,7 @@ import bdv.tools.movie.panels.MovieSaveDialog; import bdv.tools.movie.preview.MovieFrameInst; import bdv.tools.movie.preview.PreviewRender; +import bdv.tools.movie.preview.PreviewThread; import bdv.tools.movie.serilizers.MovieFramesSerializer; import bdv.util.DelayedPackDialog; import bdv.viewer.ViewerPanel; @@ -67,7 +68,7 @@ public class ProduceMovieDialog extends DelayedPackDialog { private final static int FrameWidth = 920; private final static int DEFAULT_SLEEP = 100; private final static int DEFAULT_DOWN = 10; - private PreviewRender previewThread; + private PreviewThread previewThread; private final JTextField downsampleField; private final JTextField sleepField; @@ -186,17 +187,17 @@ private void restartPreview() { accel[i] = currentFrame.getAccel(); } - previewThread = new PreviewRender(viewer, - transforms, - frames, - accel, - sleep, down); - -// previewThread = new PreviewThread(viewer, +// previewThread = new PreviewRender(viewer, // transforms, // frames, // accel, // sleep, down); + + previewThread = new PreviewThread(viewer, + transforms, + frames, + accel, + sleep, down); } private void startPreview() { @@ -292,7 +293,7 @@ private void addFrame() { repaint(); } - public void exportPNGs(File dir,int width, int height, ProgressWriter progressWriter) { + public void exportPNGs(File dir, int width, int height, ProgressWriter progressWriter) { int size = framesPanels.size(); new Thread(() -> { final AffineTransform3D[] transforms = new AffineTransform3D[size]; diff --git a/src/main/java/bdv/tools/movie/preview/MovieFrameInst.java b/src/main/java/bdv/tools/movie/preview/MovieFrameInst.java index 8c9bf8d3..f7b2f071 100644 --- a/src/main/java/bdv/tools/movie/preview/MovieFrameInst.java +++ b/src/main/java/bdv/tools/movie/preview/MovieFrameInst.java @@ -5,8 +5,8 @@ import java.io.Serializable; public class MovieFrameInst implements Serializable { - private AffineTransform3D transform; - + private volatile transient AffineTransform3D transform; + private double[] transformParams; private int position; private int frames; private int accel; @@ -21,12 +21,18 @@ public MovieFrameInst(int position, AffineTransform3D transform) { public MovieFrameInst(int position, AffineTransform3D transform, int frames, int accel) { this.position = position; this.transform = transform; + this.transformParams = transform.getRowPackedCopy(); this.frames = frames; this.accel = accel; } public AffineTransform3D getTransform() { - return transform; + if (transform == null) + { + transform = new AffineTransform3D(); + transform.set(transformParams); + } + return transform; } public void setTransform(AffineTransform3D transform) { diff --git a/src/main/java/bdv/tools/movie/preview/PreviewThread.java b/src/main/java/bdv/tools/movie/preview/PreviewThread.java index 0711ff67..2778175b 100644 --- a/src/main/java/bdv/tools/movie/preview/PreviewThread.java +++ b/src/main/java/bdv/tools/movie/preview/PreviewThread.java @@ -20,7 +20,7 @@ public class PreviewThread implements Runnable { boolean suspended = false; - PreviewThread( + public PreviewThread( final ViewerPanel viewer, final AffineTransform3D[] transforms, final int[] frames, diff --git a/src/main/java/bdv/tools/movie/serilizers/MovieFramesSerializer.java b/src/main/java/bdv/tools/movie/serilizers/MovieFramesSerializer.java index 0b428c02..510d6327 100644 --- a/src/main/java/bdv/tools/movie/serilizers/MovieFramesSerializer.java +++ b/src/main/java/bdv/tools/movie/serilizers/MovieFramesSerializer.java @@ -12,6 +12,7 @@ import java.io.FileWriter; import java.io.IOException; import java.io.Writer; +import java.util.ArrayList; import java.util.List; public class MovieFramesSerializer { @@ -33,7 +34,7 @@ public static boolean save(List list, File file){ return true; } - public static List getFrom(File file) throws FileNotFoundException { - return getGson().fromJson(new FileReader(file.getAbsolutePath()),new TypeToken>(){}.getType()); + public static ArrayList getFrom(File file) throws FileNotFoundException { + return getGson().fromJson(new FileReader(file.getAbsolutePath()),new TypeToken>(){}.getType()); } } diff --git a/src/main/java/bdv/tools/movie/timeline/TestTransformationDifference.java b/src/main/java/bdv/tools/movie/timeline/TestTransformationDifference.java deleted file mode 100644 index 17bdb121..00000000 --- a/src/main/java/bdv/tools/movie/timeline/TestTransformationDifference.java +++ /dev/null @@ -1,21 +0,0 @@ -package bdv.tools.movie.timeline; - -import net.imglib2.realtransform.AffineTransform3D; - -public class TestTransformationDifference { - public static void main(String[] args) { - AffineTransform3D transform3D = new AffineTransform3D(); - transform3D.set(3,2,5,100, - 4,3,2,0, - 8,1,3,0); - AffineTransform3D transform2 = transform3D.copy(); - transform3D.scale(2.0); - transform3D.rotate(0,90); - AffineTransform3D diff = transform3D.copy(); - - diff.concatenate(transform2.inverse()); - - System.out.println(diff); - - } -} diff --git a/src/main/java/bdv/tools/movie/timeline/TransformationPlot.java b/src/main/java/bdv/tools/movie/timeline/TransformationPlot.java deleted file mode 100644 index d1e573dc..00000000 --- a/src/main/java/bdv/tools/movie/timeline/TransformationPlot.java +++ /dev/null @@ -1,116 +0,0 @@ -package bdv.tools.movie.timeline; - - -import bdv.tools.movie.preview.MovieFrameInst; -import bdv.tools.movie.serilizers.MovieFramesSerializer; -import bdv.viewer.animate.SimilarityTransformAnimator; -import net.imglib2.realtransform.AffineTransform3D; - -import java.awt.*; -import javax.swing.*; -import java.awt.geom.*; -import java.io.File; -import java.io.FileNotFoundException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import static bdv.tools.movie.MovieProducer.accel; - -//Extends JPanel class -public class TransformationPlot extends JPanel { - - private final List movieFrames; - - // int[] cord = {65, 20, 40, 80}; - int marg = 50; - - public TransformationPlot(List movieFrames) { - this.movieFrames = movieFrames; - } - - protected void paintComponent(Graphics grf) { - super.paintComponent(grf); - Graphics2D graph = (Graphics2D) grf; - - //Sets the value of a single preference for the rendering algorithms. - graph.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - - // get width and height - int width = getWidth(); - int height = getHeight(); - - // draw graph - graph.draw(new Line2D.Double(marg, height - marg, width - marg, height - marg)); - - - List similarity = new ArrayList<>(); - for (int k = 1; k < movieFrames.size(); ++k) { - AffineTransform3D transformsStart = movieFrames.get(k - 1).getTransform(); - AffineTransform3D last = transformsStart; - AffineTransform3D transformsEnd = movieFrames.get(k).getTransform(); - int frames = movieFrames.get(k).getFrames(); - int accel = movieFrames.get(k).getAccel(); - for (int n = 0; n < movieFrames.get(k).getFrames(); ++n) { - - final SimilarityTransformAnimator animator = new SimilarityTransformAnimator( - transformsStart, - transformsEnd, - 0, - 0, - 0); - - final AffineTransform3D tkd = animator.get(accel((double) n / (double) frames, accel)); - double sim = getSimilarity(last, tkd); - similarity.add(sim); - last = tkd; - } - } - Double max = Collections.max(similarity); - System.out.println("Max : " + max); - //find value of x and scale to plot points - double x = (double) (width - 2 * marg) / (similarity.size() - 1); - double scale = (double) (height - 2 * marg) / max; - - //set color for points - graph.setPaint(Color.RED); - - // set points to the graph - for (int i = 0; i < similarity.size(); i++) { - double x1 = marg + i * x; - double y1 = height - marg - scale * similarity.get(i); - graph.fill(new Ellipse2D.Double(x1 - 2, y1 - 2, 4, 4)); - } - } - - private double getSimilarity(AffineTransform3D last, AffineTransform3D tkd) { - - AffineTransform3D diff = tkd.copy(); - diff.concatenate(last.inverse()); - double all = 0; - for (int i = 0; i < 3; i++) - for (int j = 0; j < 3; j++) - all += diff.get(i, j); - return all; - } - - - //main() method start - public static void main(String args[]) throws FileNotFoundException { - String jsonTransformations = "/Users/zouinkhim/Desktop/test2.json"; - List movieFrames = MovieFramesSerializer.getFrom(new File(jsonTransformations)); - JFrame frame = new JFrame(); - frame.add(new TransformationPlot(movieFrames)); - frame.setSize(400, 400); - frame.setLocation(200, 200); - frame.setVisible(true); - - jsonTransformations = "/Users/zouinkhim/Desktop/test.json"; - List movieFrames2 = MovieFramesSerializer.getFrom(new File(jsonTransformations)); - JFrame frame2 = new JFrame(); - frame2.add(new TransformationPlot(movieFrames2)); - frame2.setSize(400, 400); - frame2.setLocation(200, 200); - frame2.setVisible(true); - } -} \ No newline at end of file