├── .mailmap ├── src └── main │ ├── resources │ └── fiji.png │ └── java │ └── bdv │ ├── ij │ ├── export │ │ ├── tiles │ │ │ ├── TileImgLoader.java │ │ │ ├── LegacyTileImgLoader.java │ │ │ └── CellVoyagerDataExporter.java │ │ ├── imgloader │ │ │ ├── StackImageLoader.java │ │ │ ├── LegacyStackImageLoader.java │ │ │ ├── HuiskenImageLoader.java │ │ │ ├── FusionImageLoader.java │ │ │ └── ImagePlusImgLoader.java │ │ ├── ViewSetupWrapper.java │ │ ├── FixAbsolutePathsInHdf5Partitions.java │ │ ├── FusionResult.java │ │ ├── Scripting.java │ │ ├── SpimRegistrationSequence.java │ │ └── SetupAggregator.java │ ├── util │ │ ├── ProgressWriterIJ.java │ │ └── PluginHelper.java │ ├── BigDataViewerPlugIn.java │ ├── OpenImarisPlugIn.java │ ├── CreateXmlForImarisPlugIn.java │ ├── BigDataBrowserPlugIn.java │ ├── ExportCellVoyagerPlugIn.java │ ├── ImportPlugIn.java │ ├── OpenImagePlusPlugIn.java │ └── ExportImagePlusPlugIn.java │ └── img │ ├── imagestack │ └── ImageStackImageLoader.java │ └── virtualstack │ └── VirtualStackImageLoader.java ├── .github ├── build.sh ├── setup.sh └── workflows │ ├── build-pr.yml │ └── build-main.yml ├── .gitignore ├── README.md └── pom.xml /.mailmap: -------------------------------------------------------------------------------- 1 | Tobias Pietzsch 2 | -------------------------------------------------------------------------------- /src/main/resources/fiji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigdataviewer/bigdataviewer_fiji/HEAD/src/main/resources/fiji.png -------------------------------------------------------------------------------- /.github/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | curl -fsLO https://raw.githubusercontent.com/scijava/scijava-scripts/master/ci-build.sh 3 | sh ci-build.sh 4 | -------------------------------------------------------------------------------- /.github/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | curl -fsLO https://raw.githubusercontent.com/scijava/scijava-scripts/master/ci-setup-github-actions.sh 3 | sh ci-setup-github-actions.sh 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | **/..classpath 3 | **/.project 4 | **/.settings/ 5 | **/target/ 6 | *.orig 7 | *.iml 8 | .classpath 9 | .project 10 | .settings 11 | /target 12 | /samples 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![](https://github.com/bigdataviewer/bigdataviewer_fiji/actions/workflows/build-main.yml/badge.svg)](https://github.com/bigdataviewer/bigdataviewer_fiji/actions/workflows/build-main.yml) 2 | 3 | # bigdataviewer_fiji 4 | Fiji plugins for starting BigDataViewer and exporting data. 5 | -------------------------------------------------------------------------------- /.github/workflows/build-pr.yml: -------------------------------------------------------------------------------- 1 | name: build PR 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Set up Java 15 | uses: actions/setup-java@v3 16 | with: 17 | java-version: '8' 18 | distribution: 'zulu' 19 | cache: 'maven' 20 | - name: Set up CI environment 21 | run: .github/setup.sh 22 | - name: Execute the build 23 | run: .github/build.sh 24 | -------------------------------------------------------------------------------- /.github/workflows/build-main.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | tags: 8 | - "*-[0-9]+.*" 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Set up Java 17 | uses: actions/setup-java@v3 18 | with: 19 | java-version: '8' 20 | distribution: 'zulu' 21 | cache: 'maven' 22 | - name: Set up CI environment 23 | run: .github/setup.sh 24 | - name: Execute the build 25 | run: .github/build.sh 26 | env: 27 | GPG_KEY_NAME: ${{ secrets.GPG_KEY_NAME }} 28 | GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} 29 | MAVEN_USER: ${{ secrets.MAVEN_USER }} 30 | MAVEN_PASS: ${{ secrets.MAVEN_PASS }} 31 | OSSRH_PASS: ${{ secrets.OSSRH_PASS }} 32 | SIGNING_ASC: ${{ secrets.SIGNING_ASC }} 33 | -------------------------------------------------------------------------------- /src/main/java/bdv/ij/export/tiles/TileImgLoader.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.ij.export.tiles; 23 | 24 | import java.io.File; 25 | import java.util.List; 26 | 27 | import mpicbg.spim.data.legacy.LegacyBasicImgLoaderWrapper; 28 | import net.imglib2.type.numeric.integer.UnsignedShortType; 29 | import bdv.ij.export.tiles.CellVoyagerDataExporter.ChannelInfo; 30 | 31 | public class TileImgLoader extends LegacyBasicImgLoaderWrapper< UnsignedShortType, LegacyTileImgLoader > 32 | { 33 | public TileImgLoader( final File imageIndexFile, final List< ChannelInfo > channelInfos ) 34 | { 35 | super( new LegacyTileImgLoader( imageIndexFile, channelInfos ) ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/bdv/ij/util/ProgressWriterIJ.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.ij.util; 23 | 24 | import ij.IJ; 25 | import ij.io.LogStream; 26 | 27 | import java.io.PrintStream; 28 | 29 | import bdv.export.ProgressWriter; 30 | 31 | public class ProgressWriterIJ implements ProgressWriter 32 | { 33 | protected final PrintStream out; 34 | 35 | protected final PrintStream err; 36 | 37 | public ProgressWriterIJ() 38 | { 39 | out = new LogStream(); 40 | err = new LogStream(); 41 | } 42 | 43 | @Override 44 | public PrintStream out() 45 | { 46 | return out; 47 | } 48 | 49 | @Override 50 | public PrintStream err() 51 | { 52 | return err; 53 | } 54 | 55 | @Override 56 | public void setProgress( final double completionRatio ) 57 | { 58 | IJ.showProgress( completionRatio ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/bdv/ij/export/imgloader/StackImageLoader.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.ij.export.imgloader; 23 | 24 | import java.util.HashMap; 25 | 26 | import mpicbg.spim.data.legacy.LegacyBasicImgLoaderWrapper; 27 | import mpicbg.spim.data.sequence.ImgLoader; 28 | import mpicbg.spim.data.sequence.ViewDescription; 29 | import mpicbg.spim.data.sequence.ViewId; 30 | import net.imglib2.type.numeric.integer.UnsignedShortType; 31 | 32 | 33 | /** 34 | * This {@link ImgLoader} loads images that represent a 3D stack in a single 35 | * file, for example in tif, lsm, or czi format. It is constructed with a list 36 | * of image filenames and the number of setups (e.g. angles). Then, to laod the 37 | * image for a given {@link ViewDescription}, its index in the filename list is computed as 38 | * view.getSetupIndex() + numViewSetups * view.getTimepointIndex(). 39 | * 40 | * @author Tobias Pietzsch <tobias.pietzsch@gmail.com> 41 | */ 42 | @Deprecated 43 | public class StackImageLoader extends LegacyBasicImgLoaderWrapper< UnsignedShortType, LegacyStackImageLoader > 44 | { 45 | public StackImageLoader( final HashMap< ViewId, String > filenames, final boolean useImageJOpener ) 46 | { 47 | super( new LegacyStackImageLoader( filenames, useImageJOpener ) ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/bdv/ij/export/ViewSetupWrapper.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.ij.export; 23 | 24 | import mpicbg.spim.data.generic.sequence.AbstractSequenceDescription; 25 | import mpicbg.spim.data.generic.sequence.BasicViewSetup; 26 | import mpicbg.spim.data.sequence.ImgLoader; 27 | import mpicbg.spim.data.sequence.SequenceDescription; 28 | import mpicbg.spim.data.sequence.ViewSetup; 29 | 30 | /** 31 | * A copy of a {@link ViewSetup} with another id. 32 | * Stores the {@link ViewSetup setup}'s original id and {@link SequenceDescription}. 33 | * For example, this can be used to access the original {@link ImgLoader}. 34 | * 35 | * @author Tobias Pietzsch <tobias.pietzsch@gmail.com> 36 | */ 37 | @Deprecated 38 | public class ViewSetupWrapper extends BasicViewSetup 39 | { 40 | private final AbstractSequenceDescription< ?, ?, ? > sourceSequence; 41 | 42 | private final int sourceSetupId; 43 | 44 | public ViewSetupWrapper( final int id, final AbstractSequenceDescription< ?, ?, ? > sourceSequence, final BasicViewSetup sourceSetup ) 45 | { 46 | super( id, sourceSetup.getName(), sourceSetup.getSize(), sourceSetup.getVoxelSize() ); 47 | this.sourceSequence = sourceSequence; 48 | this.sourceSetupId = sourceSetup.getId(); 49 | } 50 | 51 | public AbstractSequenceDescription< ?, ?, ? > getSourceSequence() 52 | { 53 | return sourceSequence; 54 | } 55 | 56 | public int getSourceSetupId() 57 | { 58 | return sourceSetupId; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/bdv/ij/export/FixAbsolutePathsInHdf5Partitions.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.ij.export; 23 | 24 | import java.io.File; 25 | import java.io.IOException; 26 | import java.util.ArrayList; 27 | import java.util.HashMap; 28 | 29 | import bdv.export.ExportMipmapInfo; 30 | import bdv.export.WriteSequenceToHdf5; 31 | import bdv.img.MipmapInfo; 32 | import bdv.img.hdf5.Hdf5ImageLoader; 33 | import bdv.img.hdf5.Partition; 34 | import bdv.img.hdf5.Util; 35 | import bdv.spimdata.SequenceDescriptionMinimal; 36 | import bdv.spimdata.SpimDataMinimal; 37 | import bdv.spimdata.XmlIoSpimDataMinimal; 38 | import mpicbg.spim.data.SpimDataException; 39 | import mpicbg.spim.data.generic.sequence.BasicViewSetup; 40 | 41 | /** 42 | * Older versions of multi-partition hdf5 export had a bug that caused absolute 43 | * paths to be used in the master hdf5 to link into the partitions. This may 44 | * cause problems if the data is moved around. {@link #fix(String)} can be used 45 | * to "repair" the master hdf5 and convert the absolute paths to relative paths. 46 | * Call {@link #fix(String)} with the path to the xml file of the dataset. A 47 | * fixed version of the master hdf5 with "FIXED" appended to the filename will 48 | * be written (the original master hdf5 will not be overwritten). To actually 49 | * use the fixed version rename it (remove the "FIXED" postfix). 50 | * 51 | * @author Tobias Pietzsch <tobias.pietzsch@gmail.com> 52 | */ 53 | @Deprecated 54 | public class FixAbsolutePathsInHdf5Partitions 55 | { 56 | public static void fix( final String xmlFilename ) throws SpimDataException, IOException 57 | { 58 | final XmlIoSpimDataMinimal spimDataIo = new XmlIoSpimDataMinimal(); 59 | final SpimDataMinimal spimData = spimDataIo.load( xmlFilename ); 60 | final SequenceDescriptionMinimal seq = spimData.getSequenceDescription(); 61 | final Hdf5ImageLoader il = ( Hdf5ImageLoader) seq.getImgLoader(); 62 | final String outfn = il.getHdf5File().getCanonicalPath() + "FIXED"; 63 | final HashMap< Integer, ExportMipmapInfo > perSetupMipmapInfo = new HashMap<>(); 64 | for ( final BasicViewSetup setup : seq.getViewSetupsOrdered() ) 65 | { 66 | final int setupId = setup.getId(); 67 | final MipmapInfo info = il.getSetupImgLoader( setupId ).getMipmapInfo(); 68 | perSetupMipmapInfo.put( setupId, new ExportMipmapInfo( 69 | Util.castToInts( info.getResolutions() ), 70 | info.getSubdivisions() ) ); 71 | } 72 | final ArrayList< Partition > partitions = il.getPartitions(); 73 | WriteSequenceToHdf5.writeHdf5PartitionLinkFile( seq, perSetupMipmapInfo, partitions, new File( outfn ) ); 74 | 75 | System.out.println( "fixed hdf5 master file written to " + outfn ); 76 | System.out.println( "rename it to " + il.getHdf5File().getCanonicalPath() + " to use it." ); 77 | } 78 | // public static void main( final String[] args ) throws Exception 79 | // { 80 | // fix( "/Users/pietzsch/Desktop/data/valia2/valia.xml" ); 81 | // } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/bdv/ij/export/imgloader/LegacyStackImageLoader.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.ij.export.imgloader; 23 | 24 | import java.util.HashMap; 25 | 26 | import org.scijava.Context; 27 | import org.scijava.app.AppService; 28 | import org.scijava.app.StatusService; 29 | 30 | import ij.ImagePlus; 31 | import io.scif.SCIFIOService; 32 | import io.scif.img.ImgIOException; 33 | import io.scif.img.ImgOpener; 34 | import mpicbg.spim.data.legacy.LegacyBasicImgLoader; 35 | import mpicbg.spim.data.sequence.ImgLoader; 36 | import mpicbg.spim.data.sequence.ViewDescription; 37 | import mpicbg.spim.data.sequence.ViewId; 38 | import net.imglib2.RandomAccessibleInterval; 39 | import net.imglib2.converter.Converter; 40 | import net.imglib2.converter.Converters; 41 | import net.imglib2.img.ImgView; 42 | import net.imglib2.img.array.ArrayImgFactory; 43 | import net.imglib2.img.display.imagej.ImageJFunctions; 44 | import net.imglib2.meta.ImgPlus; 45 | import net.imglib2.type.numeric.integer.UnsignedByteType; 46 | import net.imglib2.type.numeric.integer.UnsignedShortType; 47 | 48 | 49 | /** 50 | * This {@link ImgLoader} loads images that represent a 3D stack in a single 51 | * file, for example in tif, lsm, or czi format. It is constructed with a list 52 | * of image filenames and the number of setups (e.g. angles). Then, to laod the 53 | * image for a given {@link ViewDescription}, its index in the filename list is computed as 54 | * view.getSetupIndex() + numViewSetups * view.getTimepointIndex(). 55 | * 56 | * @author Tobias Pietzsch <tobias.pietzsch@gmail.com> 57 | */ 58 | @Deprecated 59 | public class LegacyStackImageLoader implements LegacyBasicImgLoader< UnsignedShortType > 60 | { 61 | private final ImgOpener opener; 62 | 63 | private final ArrayImgFactory< UnsignedShortType > factory; 64 | 65 | private final UnsignedShortType type; 66 | 67 | final HashMap< ViewId, String > filenames; 68 | 69 | private boolean useImageJOpener; 70 | 71 | public LegacyStackImageLoader( final HashMap< ViewId, String > filenames, final boolean useImageJOpener ) 72 | { 73 | this.filenames = filenames; 74 | this.useImageJOpener = useImageJOpener; 75 | opener = useImageJOpener ? null : new ImgOpener( new Context( SCIFIOService.class, AppService.class, StatusService.class ) ); 76 | factory = new ArrayImgFactory<>(); 77 | type = new UnsignedShortType(); 78 | } 79 | 80 | @Override 81 | public RandomAccessibleInterval< UnsignedShortType > getImage( final ViewId view ) 82 | { 83 | final String fn = filenames.get( view ); 84 | if ( useImageJOpener ) 85 | { 86 | final ImagePlus imp = new ImagePlus( fn ); 87 | if ( imp.getType() == ImagePlus.GRAY16 ) 88 | return new ImgPlus<>( ImageJFunctions.wrapShort( imp ) ); 89 | else if ( imp.getType() == ImagePlus.GRAY8 ) 90 | { 91 | System.out.println( "wrapping" ); 92 | return new ImgPlus<>( 93 | new ImgView<>( 94 | Converters.convert( 95 | ( RandomAccessibleInterval ) ImageJFunctions.wrapByte( imp ), 96 | new Converter< UnsignedByteType, UnsignedShortType >() { 97 | @Override 98 | public void convert( final UnsignedByteType input, final UnsignedShortType output ) 99 | { 100 | output.set( input.get() ); 101 | } 102 | }, 103 | new UnsignedShortType() 104 | ), null ) ); 105 | } 106 | else 107 | useImageJOpener = false; 108 | } 109 | 110 | try 111 | { 112 | return opener.openImg( fn, factory, type ); 113 | } 114 | catch ( final ImgIOException e ) 115 | { 116 | throw new RuntimeException( e ); 117 | } 118 | } 119 | 120 | @Override 121 | public UnsignedShortType getImageType() 122 | { 123 | return type; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/bdv/ij/BigDataViewerPlugIn.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.ij; 23 | 24 | import java.awt.FileDialog; 25 | import java.awt.Frame; 26 | import java.io.File; 27 | import java.io.FilenameFilter; 28 | import java.util.Locale; 29 | import java.util.concurrent.atomic.AtomicBoolean; 30 | 31 | import javax.swing.JFileChooser; 32 | import javax.swing.filechooser.FileFilter; 33 | 34 | import org.scijava.command.Command; 35 | import org.scijava.plugin.Plugin; 36 | 37 | import bdv.BigDataViewer; 38 | import bdv.ij.util.ProgressWriterIJ; 39 | import bdv.viewer.ViewerOptions; 40 | import ij.Prefs; 41 | 42 | @Plugin(type = Command.class, menuPath = "Plugins>BigDataViewer>Open XML/HDF5") 43 | public class BigDataViewerPlugIn implements Command 44 | { 45 | static String lastDatasetPath = "./export.xml"; 46 | 47 | @Override 48 | public void run() 49 | { 50 | if ( ij.Prefs.setIJMenuBar ) 51 | System.setProperty( "apple.laf.useScreenMenuBar", "true" ); 52 | 53 | File file = null; 54 | 55 | if ( Prefs.useJFileChooser ) 56 | { 57 | final JFileChooser fileChooser = new JFileChooser(); 58 | fileChooser.setSelectedFile( new File( lastDatasetPath ) ); 59 | fileChooser.setFileFilter( new FileFilter() 60 | { 61 | @Override 62 | public String getDescription() 63 | { 64 | return "xml files"; 65 | } 66 | 67 | @Override 68 | public boolean accept( final File f ) 69 | { 70 | if ( f.isDirectory() ) 71 | return true; 72 | if ( f.isFile() ) 73 | { 74 | final String s = f.getName(); 75 | final int i = s.lastIndexOf('.'); 76 | if (i > 0 && i < s.length() - 1) { 77 | final String ext = s.substring(i+1).toLowerCase(); 78 | return ext.equals( "xml" ); 79 | } 80 | } 81 | return false; 82 | } 83 | } ); 84 | 85 | final int returnVal = fileChooser.showOpenDialog( null ); 86 | if ( returnVal == JFileChooser.APPROVE_OPTION ) 87 | file = fileChooser.getSelectedFile(); 88 | } 89 | else // use FileDialog 90 | { 91 | final FileDialog fd = new FileDialog( ( Frame ) null, "Open", FileDialog.LOAD ); 92 | fd.setDirectory( new File( lastDatasetPath ).getParent() ); 93 | fd.setFile( new File( lastDatasetPath ).getName() ); 94 | final AtomicBoolean workedWithFilenameFilter = new AtomicBoolean( false ); 95 | fd.setFilenameFilter( new FilenameFilter() 96 | { 97 | private boolean firstTime = true; 98 | 99 | @Override 100 | public boolean accept( final File dir, final String name ) 101 | { 102 | if ( firstTime ) 103 | { 104 | workedWithFilenameFilter.set( true ); 105 | firstTime = false; 106 | } 107 | 108 | final int i = name.lastIndexOf( '.' ); 109 | if ( i > 0 && i < name.length() - 1 ) 110 | { 111 | final String ext = name.substring( i + 1 ).toLowerCase(); 112 | return ext.equals( "xml" ); 113 | } 114 | return false; 115 | } 116 | } ); 117 | fd.setVisible( true ); 118 | if ( isMac() && !workedWithFilenameFilter.get() ) 119 | { 120 | fd.setFilenameFilter( null ); 121 | fd.setVisible( true ); 122 | } 123 | final String filename = fd.getFile(); 124 | if ( filename != null ) 125 | { 126 | file = new File( fd.getDirectory() + filename ); 127 | } 128 | } 129 | 130 | if ( file != null ) 131 | { 132 | try 133 | { 134 | lastDatasetPath = file.getAbsolutePath(); 135 | BigDataViewer.open( file.getAbsolutePath(), file.getName(), new ProgressWriterIJ(), ViewerOptions.options() ); 136 | } 137 | catch ( final Exception e ) 138 | { 139 | throw new RuntimeException( e ); 140 | } 141 | } 142 | } 143 | 144 | private boolean isMac() 145 | { 146 | final String OS = System.getProperty( "os.name", "generic" ).toLowerCase( Locale.ENGLISH ); 147 | return ( OS.indexOf( "mac" ) >= 0 ) || ( OS.indexOf( "darwin" ) >= 0 ); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/main/java/bdv/ij/OpenImarisPlugIn.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.ij; 23 | 24 | import java.awt.FileDialog; 25 | import java.awt.Frame; 26 | import java.io.File; 27 | import java.io.FilenameFilter; 28 | import java.io.IOException; 29 | import java.util.concurrent.atomic.AtomicBoolean; 30 | 31 | import javax.swing.JFileChooser; 32 | import javax.swing.filechooser.FileFilter; 33 | 34 | import org.scijava.command.Command; 35 | import org.scijava.plugin.Plugin; 36 | 37 | import bdv.BigDataViewer; 38 | import bdv.ij.util.ProgressWriterIJ; 39 | import bdv.img.imaris.Imaris; 40 | import bdv.spimdata.SpimDataMinimal; 41 | import bdv.viewer.ViewerOptions; 42 | import ij.ImageJ; 43 | import ij.Prefs; 44 | 45 | @Plugin(type = Command.class, 46 | menuPath = "Plugins>BigDataViewer>Open Imaris") 47 | public class OpenImarisPlugIn implements Command 48 | { 49 | static String lastDatasetPath = ""; 50 | 51 | public static void main( final String[] args ) 52 | { 53 | ImageJ.main( args ); 54 | new OpenImarisPlugIn().run(); 55 | } 56 | @Override 57 | public void run() 58 | { 59 | if ( ij.Prefs.setIJMenuBar ) 60 | System.setProperty( "apple.laf.useScreenMenuBar", "true" ); 61 | 62 | File file = null; 63 | 64 | if ( Prefs.useJFileChooser ) 65 | { 66 | final JFileChooser fileChooser = new JFileChooser(); 67 | fileChooser.setSelectedFile( new File( lastDatasetPath ) ); 68 | fileChooser.setFileFilter( new FileFilter() 69 | { 70 | @Override 71 | public String getDescription() 72 | { 73 | return "ims files"; 74 | } 75 | 76 | @Override 77 | public boolean accept( final File f ) 78 | { 79 | if ( f.isDirectory() ) 80 | return true; 81 | if ( f.isFile() ) 82 | { 83 | final String s = f.getName(); 84 | final int i = s.lastIndexOf('.'); 85 | if (i > 0 && i < s.length() - 1) { 86 | final String ext = s.substring(i+1).toLowerCase(); 87 | return ext.equals( "ims" ); 88 | } 89 | } 90 | return false; 91 | } 92 | } ); 93 | 94 | final int returnVal = fileChooser.showOpenDialog( null ); 95 | if ( returnVal == JFileChooser.APPROVE_OPTION ) 96 | file = fileChooser.getSelectedFile(); 97 | } 98 | else // use FileDialog 99 | { 100 | final FileDialog fd = new FileDialog( ( Frame ) null, "Open", FileDialog.LOAD ); 101 | fd.setDirectory( new File( lastDatasetPath ).getParent() ); 102 | fd.setFile( new File( lastDatasetPath ).getName() ); 103 | final AtomicBoolean workedWithFilenameFilter = new AtomicBoolean( false ); 104 | fd.setFilenameFilter( new FilenameFilter() 105 | { 106 | private boolean firstTime = true; 107 | 108 | @Override 109 | public boolean accept( final File dir, final String name ) 110 | { 111 | if ( firstTime ) 112 | { 113 | workedWithFilenameFilter.set( true ); 114 | firstTime = false; 115 | } 116 | 117 | final int i = name.lastIndexOf( '.' ); 118 | if ( i > 0 && i < name.length() - 1 ) 119 | { 120 | final String ext = name.substring( i + 1 ).toLowerCase(); 121 | return ext.equals( "ims" ); 122 | } 123 | return false; 124 | } 125 | } ); 126 | fd.setVisible( true ); 127 | if ( !workedWithFilenameFilter.get() ) 128 | { 129 | fd.setFilenameFilter( null ); 130 | fd.setVisible( true ); 131 | } 132 | final String filename = fd.getFile(); 133 | if ( filename != null ) 134 | { 135 | file = new File( fd.getDirectory() + filename ); 136 | } 137 | } 138 | 139 | if ( file != null ) 140 | { 141 | try 142 | { 143 | lastDatasetPath = file.getAbsolutePath(); 144 | final SpimDataMinimal spimData = Imaris.openIms( file.getAbsolutePath() ); 145 | BigDataViewer.open( spimData, file.getName(), new ProgressWriterIJ(), ViewerOptions.options() ); 146 | } 147 | catch ( final IOException e ) 148 | { 149 | throw new RuntimeException( e ); 150 | } 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/main/java/bdv/ij/CreateXmlForImarisPlugIn.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.ij; 23 | 24 | import java.awt.FileDialog; 25 | import java.awt.Frame; 26 | import java.io.File; 27 | import java.io.FilenameFilter; 28 | import java.io.IOException; 29 | import java.util.concurrent.atomic.AtomicBoolean; 30 | 31 | import javax.swing.JFileChooser; 32 | import javax.swing.filechooser.FileFilter; 33 | 34 | import org.scijava.command.Command; 35 | import org.scijava.plugin.Plugin; 36 | 37 | import bdv.BigDataViewer; 38 | import bdv.ij.util.ProgressWriterIJ; 39 | import bdv.img.imaris.Imaris; 40 | import bdv.spimdata.SpimDataMinimal; 41 | import bdv.spimdata.XmlIoSpimDataMinimal; 42 | import bdv.viewer.ViewerOptions; 43 | import ij.IJ; 44 | import ij.ImageJ; 45 | import ij.Prefs; 46 | import mpicbg.spim.data.SpimDataException; 47 | 48 | @Plugin(type = Command.class, 49 | menuPath = "Plugins>BigDataViewer>Create XML for Imaris file") 50 | public class CreateXmlForImarisPlugIn implements Command 51 | { 52 | static String lastDatasetPath = ""; 53 | 54 | public static void main( final String[] args ) 55 | { 56 | ImageJ.main( args ); 57 | new CreateXmlForImarisPlugIn().run(); 58 | } 59 | @Override 60 | public void run() 61 | { 62 | if ( Prefs.setIJMenuBar ) 63 | System.setProperty( "apple.laf.useScreenMenuBar", "true" ); 64 | 65 | File file = null; 66 | 67 | if ( Prefs.useJFileChooser ) 68 | { 69 | final JFileChooser fileChooser = new JFileChooser(); 70 | fileChooser.setSelectedFile( new File( lastDatasetPath ) ); 71 | fileChooser.setFileFilter( new FileFilter() 72 | { 73 | @Override 74 | public String getDescription() 75 | { 76 | return "ims files"; 77 | } 78 | 79 | @Override 80 | public boolean accept( final File f ) 81 | { 82 | if ( f.isDirectory() ) 83 | return true; 84 | if ( f.isFile() ) 85 | { 86 | final String s = f.getName(); 87 | final int i = s.lastIndexOf('.'); 88 | if (i > 0 && i < s.length() - 1) { 89 | final String ext = s.substring(i+1).toLowerCase(); 90 | return ext.equals( "ims" ); 91 | } 92 | } 93 | return false; 94 | } 95 | } ); 96 | 97 | final int returnVal = fileChooser.showOpenDialog( null ); 98 | if ( returnVal == JFileChooser.APPROVE_OPTION ) 99 | file = fileChooser.getSelectedFile(); 100 | } 101 | else // use FileDialog 102 | { 103 | final FileDialog fd = new FileDialog( ( Frame ) null, "Open", FileDialog.LOAD ); 104 | fd.setDirectory( new File( lastDatasetPath ).getParent() ); 105 | fd.setFile( new File( lastDatasetPath ).getName() ); 106 | final AtomicBoolean workedWithFilenameFilter = new AtomicBoolean( false ); 107 | fd.setFilenameFilter( new FilenameFilter() 108 | { 109 | private boolean firstTime = true; 110 | 111 | @Override 112 | public boolean accept( final File dir, final String name ) 113 | { 114 | if ( firstTime ) 115 | { 116 | workedWithFilenameFilter.set( true ); 117 | firstTime = false; 118 | } 119 | 120 | final int i = name.lastIndexOf( '.' ); 121 | if ( i > 0 && i < name.length() - 1 ) 122 | { 123 | final String ext = name.substring( i + 1 ).toLowerCase(); 124 | return ext.equals( "ims" ); 125 | } 126 | return false; 127 | } 128 | } ); 129 | fd.setVisible( true ); 130 | if ( !workedWithFilenameFilter.get() ) 131 | { 132 | fd.setFilenameFilter( null ); 133 | fd.setVisible( true ); 134 | } 135 | final String filename = fd.getFile(); 136 | if ( filename != null ) 137 | { 138 | file = new File( fd.getDirectory() + filename ); 139 | } 140 | } 141 | 142 | if ( file != null ) 143 | { 144 | try 145 | { 146 | final String imsFilename = file.getAbsolutePath(); 147 | lastDatasetPath = imsFilename; 148 | final SpimDataMinimal spimData = Imaris.openIms( imsFilename ); 149 | final String xmlFilename = xmlFilename( imsFilename ); 150 | new XmlIoSpimDataMinimal().save( spimData, xmlFilename ); 151 | IJ.showMessage( "created " + xmlFilename ); 152 | } 153 | catch ( final IOException | SpimDataException e ) 154 | { 155 | throw new RuntimeException( e ); 156 | } 157 | } 158 | } 159 | 160 | private static String xmlFilename( String s ) 161 | { 162 | String f = s.endsWith( ".ims" ) ? s.substring( 0, s.length() - 4 ) : s; 163 | if ( !new File( f + ".xml" ).exists() ) 164 | return f + ".xml"; 165 | 166 | int i = 2; 167 | while ( new File( f + i + ".xml" ).exists() ) 168 | ++i; 169 | return f + i + ".xml"; 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/main/java/bdv/ij/export/imgloader/HuiskenImageLoader.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.ij.export.imgloader; 23 | 24 | import java.io.File; 25 | import java.util.HashMap; 26 | import java.util.Map.Entry; 27 | 28 | import ij.ImagePlus; 29 | import mpicbg.spim.data.generic.sequence.BasicImgLoader; 30 | import mpicbg.spim.data.generic.sequence.BasicSetupImgLoader; 31 | import mpicbg.spim.data.generic.sequence.ImgLoaderHint; 32 | import mpicbg.spim.data.sequence.ImgLoader; 33 | import mpicbg.spim.data.sequence.ViewSetup; 34 | import net.imglib2.img.Img; 35 | import net.imglib2.img.display.imagej.ImageJFunctions; 36 | import net.imglib2.meta.Axes; 37 | import net.imglib2.meta.AxisType; 38 | import net.imglib2.meta.ImgPlus; 39 | import net.imglib2.type.numeric.integer.UnsignedShortType; 40 | import spimopener.SPIMExperiment; 41 | 42 | /** 43 | * This {@link ImgLoader} implementation uses Benjamin Schmid's 44 | * spimopener 45 | * to load images in Jan Husiken's format. 46 | * 47 | * @author Tobias Pietzsch <tobias.pietzsch@gmail.com> 48 | */ 49 | @Deprecated 50 | public class HuiskenImageLoader implements BasicImgLoader 51 | { 52 | private final File expFile; 53 | 54 | private SPIMExperiment exp; 55 | 56 | private boolean hasAlternatingIllumination; 57 | 58 | private final HashMap< Integer, SetupLoader > setupIdToSetupImgLoader; 59 | 60 | public HuiskenImageLoader( final File file, final HashMap< Integer, ViewSetup > setups ) 61 | { 62 | expFile = file; 63 | exp = null; 64 | setupIdToSetupImgLoader = new HashMap<>(); 65 | for ( final Entry< Integer, ViewSetup > entry : setups.entrySet() ) 66 | setupIdToSetupImgLoader.put( entry.getKey(), new SetupLoader( entry.getValue() ) ); 67 | } 68 | 69 | private synchronized void ensureExpIsOpen() 70 | { 71 | if ( exp == null ) 72 | { 73 | exp = new SPIMExperiment( expFile.getAbsolutePath() ); 74 | hasAlternatingIllumination = exp.d < ( exp.planeEnd + 1 - exp.planeStart ); 75 | } 76 | } 77 | 78 | final static private String basenameFormatString = "t%05d-a%03d-c%03d-i%01d"; 79 | 80 | private static String getBasename( final int timepoint, final int angle, final int channel, final int illumination ) 81 | { 82 | return String.format( basenameFormatString, timepoint, angle, channel, illumination ); 83 | } 84 | 85 | @Override 86 | public BasicSetupImgLoader< ? > getSetupImgLoader( final int setupId ) 87 | { 88 | return setupIdToSetupImgLoader.get( setupId ); 89 | } 90 | 91 | public class SetupLoader implements BasicSetupImgLoader< UnsignedShortType > 92 | { 93 | private final UnsignedShortType type; 94 | 95 | private final ViewSetup setup; 96 | 97 | public SetupLoader( final ViewSetup setup ) 98 | { 99 | type = new UnsignedShortType(); 100 | this.setup = setup; 101 | } 102 | 103 | @Override 104 | public ImgPlus< UnsignedShortType > getImage( final int timepointId, final ImgLoaderHint... hints ) 105 | { 106 | ensureExpIsOpen(); 107 | 108 | final int channel = setup.getChannel().getId(); 109 | final int illumination = setup.getIllumination().getId(); 110 | final int angle = setup.getAngle().getId(); 111 | 112 | final ImagePlus imp = getImagePlus( timepointId ); 113 | final Img< UnsignedShortType > img = ImageJFunctions.wrapShort( imp ); 114 | 115 | final String name = getBasename( timepointId, angle, channel, illumination ); 116 | 117 | final AxisType[] axes = new AxisType[] { Axes.X, Axes.Y, Axes.Z }; 118 | 119 | final float zStretching = ( float ) ( exp.pd / exp.pw ); 120 | final double[] calibration = new double[] { 1, 1, zStretching }; 121 | 122 | return new ImgPlus<>( img, name, axes, calibration ); 123 | } 124 | 125 | @Override 126 | public UnsignedShortType getImageType() 127 | { 128 | return type; 129 | } 130 | 131 | private ImagePlus getImagePlus( final int timepointId ) 132 | { 133 | final int channel = setup.getChannel().getId(); 134 | final int illumination = setup.getIllumination().getId(); 135 | final int angle = setup.getAngle().getId(); 136 | 137 | final int s = exp.sampleStart; 138 | final int r = exp.regionStart; 139 | final int f = exp.frameStart; 140 | final int zMin = exp.planeStart; 141 | final int zMax = exp.planeEnd; 142 | final int xMin = 0; 143 | final int xMax = exp.w - 1; 144 | final int yMin = 0; 145 | final int yMax = exp.h - 1; 146 | 147 | ImagePlus imp; 148 | if ( hasAlternatingIllumination ) 149 | { 150 | final int zStep = 2; 151 | if ( illumination == 0 ) 152 | imp = exp.openNotProjected( s, timepointId, timepointId, r, angle, channel, zMin, zMax - 1, zStep, f, f, yMin, yMax, xMin, xMax, SPIMExperiment.X, SPIMExperiment.Y, SPIMExperiment.Z, false ); 153 | else 154 | imp = exp.openNotProjected( s, timepointId, timepointId, r, angle, channel, zMin + 1, zMax, zStep, f, f, yMin, yMax, xMin, xMax, SPIMExperiment.X, SPIMExperiment.Y, SPIMExperiment.Z, false ); 155 | } 156 | else 157 | { 158 | imp = exp.openNotProjected( s, timepointId, timepointId, r, angle, channel, zMin, zMax, f, f, yMin, yMax, xMin, xMax, SPIMExperiment.X, SPIMExperiment.Y, SPIMExperiment.Z, false ); 159 | } 160 | 161 | return imp; 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/main/java/bdv/img/imagestack/ImageStackImageLoader.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.img.imagestack; 23 | 24 | import java.util.ArrayList; 25 | import java.util.HashMap; 26 | import java.util.function.Function; 27 | 28 | import net.imglib2.RandomAccessibleInterval; 29 | import net.imglib2.img.basictypeaccess.array.ArrayDataAccess; 30 | import net.imglib2.img.basictypeaccess.array.ByteArray; 31 | import net.imglib2.img.basictypeaccess.array.FloatArray; 32 | import net.imglib2.img.basictypeaccess.array.IntArray; 33 | import net.imglib2.img.basictypeaccess.array.ShortArray; 34 | import net.imglib2.img.planar.PlanarImg; 35 | import net.imglib2.type.NativeType; 36 | import net.imglib2.type.NativeTypeFactory; 37 | import net.imglib2.type.numeric.ARGBType; 38 | import net.imglib2.type.numeric.NumericType; 39 | import net.imglib2.type.numeric.integer.UnsignedByteType; 40 | import net.imglib2.type.numeric.integer.UnsignedShortType; 41 | import net.imglib2.type.numeric.real.FloatType; 42 | 43 | import ij.ImagePlus; 44 | import mpicbg.spim.data.generic.sequence.BasicImgLoader; 45 | import mpicbg.spim.data.generic.sequence.BasicSetupImgLoader; 46 | import mpicbg.spim.data.generic.sequence.ImgLoaderHint; 47 | import mpicbg.spim.data.generic.sequence.TypedBasicImgLoader; 48 | 49 | public class ImageStackImageLoader< T extends NumericType< T > & NativeType< T >, A extends ArrayDataAccess< A > > implements BasicImgLoader, TypedBasicImgLoader< T > 50 | { 51 | public static ImageStackImageLoader< UnsignedByteType, ByteArray > createUnsignedByteInstance( final ImagePlus imp ) 52 | { 53 | return createUnsignedByteInstance( imp, 0 ); 54 | } 55 | 56 | public static ImageStackImageLoader< UnsignedByteType, ByteArray > createUnsignedByteInstance( final ImagePlus imp, int offset ) 57 | { 58 | return new ImageStackImageLoader<>( new UnsignedByteType(), imp, array -> new ByteArray( ( byte[] ) array ), offset ); 59 | } 60 | 61 | public static ImageStackImageLoader< UnsignedShortType, ShortArray > createUnsignedShortInstance( final ImagePlus imp ) 62 | { 63 | return createUnsignedShortInstance( imp, 0 ); 64 | } 65 | 66 | public static ImageStackImageLoader< UnsignedShortType, ShortArray > createUnsignedShortInstance( final ImagePlus imp, int offset ) 67 | { 68 | return new ImageStackImageLoader<>( new UnsignedShortType(), imp, array -> new ShortArray( ( short[] ) array ), offset ); 69 | } 70 | 71 | public static ImageStackImageLoader< FloatType, FloatArray > createFloatInstance( final ImagePlus imp ) 72 | { 73 | return createFloatInstance( imp, 0 ); 74 | } 75 | 76 | public static ImageStackImageLoader< FloatType, FloatArray > createFloatInstance( final ImagePlus imp, int offset ) 77 | { 78 | return new ImageStackImageLoader<>( new FloatType(), imp, array -> new FloatArray( ( float[] ) array ), offset ); 79 | } 80 | 81 | public static ImageStackImageLoader< ARGBType, IntArray > createARGBInstance( final ImagePlus imp ) 82 | { 83 | return createARGBInstance( imp, 0 ); 84 | } 85 | 86 | public static ImageStackImageLoader< ARGBType, IntArray > createARGBInstance( final ImagePlus imp, int offset ) 87 | { 88 | return new ImageStackImageLoader<>( new ARGBType(), imp, array -> new IntArray( ( int[] ) array ), offset ); 89 | } 90 | 91 | private final T type; 92 | 93 | private final ImagePlus imp; 94 | 95 | private final long[] dim; 96 | 97 | private final HashMap< Integer, SetupImgLoader > setupImgLoaders; 98 | 99 | private final Function< Object, A > wrapPixels; 100 | 101 | public ImageStackImageLoader( final T type, final ImagePlus imp, final Function< Object, A > wrapPixels, int setup_id_offset ) 102 | { 103 | this.type = type; 104 | this.imp = imp; 105 | this.wrapPixels = wrapPixels; 106 | this.dim = new long[] { imp.getWidth(), imp.getHeight(), imp.getNSlices() }; 107 | final int numSetups = imp.getNChannels(); 108 | setupImgLoaders = new HashMap<>(); 109 | for ( int c = 0; c < numSetups; ++c ) 110 | setupImgLoaders.put( (setup_id_offset + c), new SetupImgLoader( c ) ); 111 | } 112 | 113 | public ImageStackImageLoader( final T type, final ImagePlus imp, final Function< Object, A > wrapPixels ) 114 | { 115 | this( type, imp, wrapPixels, 0 ); 116 | } 117 | 118 | public class SetupImgLoader implements BasicSetupImgLoader< T > 119 | { 120 | private final int channel; 121 | 122 | public SetupImgLoader( final int channel ) 123 | { 124 | this.channel = channel + 1; 125 | } 126 | 127 | @Override 128 | public RandomAccessibleInterval< T > getImage( final int timepointId, final ImgLoaderHint... hints ) 129 | { 130 | final int frame = timepointId + 1; 131 | final ArrayList< A > slices = new ArrayList<>(); 132 | for ( int slice = 1; slice <= dim[ 2 ]; ++slice ) 133 | slices.add( wrapPixels.apply( imp.getStack().getPixels( imp.getStackIndex( channel, slice, frame ) ) ) ); 134 | final PlanarImg< T, A > img = new PlanarImg<>( slices, dim, type.getEntitiesPerPixel() ); 135 | @SuppressWarnings( "unchecked" ) 136 | final NativeTypeFactory< T, ? super A > typeFactory = ( NativeTypeFactory< T, ? super A > ) type.getNativeTypeFactory(); 137 | img.setLinkedType( typeFactory.createLinkedType( img ) ); 138 | return img; 139 | } 140 | 141 | @Override 142 | public T getImageType() 143 | { 144 | return type; 145 | } 146 | } 147 | 148 | @Override 149 | public SetupImgLoader getSetupImgLoader( final int setupId ) 150 | { 151 | return setupImgLoaders.get( setupId ); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/main/java/bdv/ij/export/FusionResult.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.ij.export; 23 | 24 | import java.util.ArrayList; 25 | import java.util.Arrays; 26 | import java.util.HashMap; 27 | import java.util.List; 28 | import java.util.Map; 29 | 30 | import bdv.ij.export.imgloader.FusionImageLoader; 31 | import bdv.spimdata.SequenceDescriptionMinimal; 32 | import mpicbg.spim.data.generic.base.Entity; 33 | import mpicbg.spim.data.generic.sequence.BasicViewSetup; 34 | import mpicbg.spim.data.registration.ViewRegistration; 35 | import mpicbg.spim.data.registration.ViewRegistrations; 36 | import mpicbg.spim.data.sequence.Channel; 37 | import mpicbg.spim.data.sequence.ImgLoader; 38 | import mpicbg.spim.data.sequence.TimePoint; 39 | import mpicbg.spim.data.sequence.TimePoints; 40 | import mpicbg.spim.data.sequence.VoxelDimensions; 41 | import net.imglib2.Dimensions; 42 | import net.imglib2.realtransform.AffineTransform3D; 43 | 44 | @Deprecated 45 | public class FusionResult 46 | { 47 | private final SequenceDescriptionMinimal desc; 48 | 49 | private final ViewRegistrations regs; 50 | 51 | public static FusionResult create( 52 | final SpimRegistrationSequence spimseq, 53 | final String filepath, 54 | final String filepattern, 55 | final int numSlices, 56 | final double sliceValueMin, 57 | final double sliceValueMax, 58 | final Map< Integer, AffineTransform3D > perTimePointFusionTransforms ) 59 | { 60 | // add one fused ViewSetup per channel in the SpimRegistrationSequence 61 | final List< Integer > channels = new ArrayList<>(); 62 | for ( final BasicViewSetup setup : spimseq.getSequenceDescription().getViewSetupsOrdered() ) 63 | { 64 | final int channel = setup.getAttribute( Channel.class ).getId(); 65 | if ( ! channels.contains( channel ) ) 66 | channels.add( channel ); 67 | } 68 | final TimePoints timepoints = spimseq.getSequenceDescription().getTimePoints(); 69 | return new FusionResult( filepath, filepattern, channels, timepoints, numSlices, sliceValueMin, sliceValueMax, perTimePointFusionTransforms ); 70 | } 71 | 72 | public FusionResult( 73 | final String filepath, 74 | final String filepattern, 75 | final TimePoints timepoints, 76 | final int numSlices, 77 | final double sliceValueMin, 78 | final double sliceValueMax, 79 | final Map< Integer, AffineTransform3D > perTimePointFusionTransforms ) 80 | { 81 | final HashMap< Integer, Integer > setupIdToChannelId = new HashMap<>(); 82 | setupIdToChannelId.put( 0, 0 ); 83 | final ImgLoader fusionLoader = new FusionImageLoader<>( filepath +"/" + filepattern, setupIdToChannelId, numSlices, new FusionImageLoader.Gray32ImagePlusLoader(), sliceValueMin, sliceValueMax ); 84 | final int setupId = 0; 85 | final String name = "fused"; 86 | final int timepointId = timepoints.getTimePointsOrdered().get( 0 ).getId(); 87 | final Dimensions size = fusionLoader.getSetupImgLoader( setupId ).getImageSize( timepointId ); 88 | final VoxelDimensions voxelSize = fusionLoader.getSetupImgLoader( setupId ).getVoxelSize( timepointId ); 89 | final BasicViewSetup setup = new BasicViewSetup( setupId, name, size, voxelSize ); 90 | desc = new SequenceDescriptionMinimal( timepoints, Entity.idMap( Arrays.asList( setup ) ), fusionLoader, null ); 91 | final ArrayList< ViewRegistration > registrations = new ArrayList<>(); 92 | for ( final TimePoint timepoint : timepoints.getTimePointsOrdered() ) 93 | registrations.add( new ViewRegistration( timepoint.getId(), 0, perTimePointFusionTransforms.get( timepoint.getId() ) ) ); 94 | regs = new ViewRegistrations( registrations ); 95 | } 96 | 97 | public FusionResult( 98 | final String filepath, 99 | final String filepattern, 100 | final List< Integer > channels, 101 | final TimePoints timepoints, 102 | final int numSlices, 103 | final double sliceValueMin, 104 | final double sliceValueMax, 105 | final Map< Integer, AffineTransform3D > perTimePointFusionTransforms ) 106 | { 107 | final HashMap< Integer, Integer > setupIdToChannelId = new HashMap<>(); 108 | for ( int setupId = 0; setupId < channels.size(); ++setupId ) 109 | { 110 | setupIdToChannelId.put( setupId, channels.get( setupId ) ); 111 | } 112 | final ImgLoader fusionLoader = new FusionImageLoader<>( filepath +"/" + filepattern, setupIdToChannelId, numSlices, new FusionImageLoader.Gray32ImagePlusLoader(), sliceValueMin, sliceValueMax ); 113 | final ArrayList< BasicViewSetup > setups = new ArrayList<>(); 114 | for ( int setupId = 0; setupId < channels.size(); ++setupId ) 115 | { 116 | final String name = "fused c " + channels.get( setupId ); 117 | final int timepointId = timepoints.getTimePointsOrdered().get( 0 ).getId(); 118 | final Dimensions size = fusionLoader.getSetupImgLoader( setupId ).getImageSize( timepointId ); 119 | final VoxelDimensions voxelSize = fusionLoader.getSetupImgLoader( setupId ).getVoxelSize( timepointId ); 120 | final BasicViewSetup setup = new BasicViewSetup( setupId, name, size, voxelSize ); 121 | setup.setAttribute( new Channel( channels.get( setupId ) ) ); 122 | setups.add( setup ); 123 | } 124 | desc = new SequenceDescriptionMinimal( timepoints, Entity.idMap( setups ), fusionLoader, null ); 125 | final ArrayList< ViewRegistration > registrations = new ArrayList<>(); 126 | for ( final TimePoint timepoint : timepoints.getTimePointsOrdered() ) 127 | for ( final BasicViewSetup setup : setups ) 128 | registrations.add( new ViewRegistration( timepoint.getId(), setup.getId(), perTimePointFusionTransforms.get( timepoint.getId() ) ) ); 129 | regs = new ViewRegistrations( registrations ); 130 | } 131 | 132 | public SequenceDescriptionMinimal getSequenceDescription() 133 | { 134 | return desc; 135 | } 136 | 137 | public ViewRegistrations getViewRegistrations() 138 | { 139 | return regs; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | org.scijava 7 | pom-scijava 8 | 38.0.1 9 | 10 | 11 | 12 | sc.fiji 13 | bigdataviewer_fiji 14 | 6.4.3-SNAPSHOT 15 | 16 | BigDataViewer Fiji 17 | Fiji plugins for starting BigDataViewer and exporting data. 18 | https://imagej.net/BigDataViewer 19 | 2014 20 | 21 | BigDataViewer 22 | https://imagej.net/BigDataViewer 23 | 24 | 25 | 26 | GNU General Public License v3+ 27 | https://www.gnu.org/licenses/gpl.html 28 | repo 29 | 30 | 31 | 32 | 33 | 34 | tpietzsch 35 | Tobias Pietzsch 36 | https://imagej.net/User:Pietzsch 37 | 38 | founder 39 | lead 40 | developer 41 | debugger 42 | reviewer 43 | support 44 | maintainer 45 | 46 | 47 | 48 | ctrueden 49 | Curtis Rueden 50 | https://imagej.net/User:Rueden 51 | 52 | maintainer 53 | 54 | 55 | 56 | 57 | 58 | Stephan Preibisch 59 | https://imagej.net/User:StephanP 60 | StephanPreibisch 61 | 62 | 63 | HongKee Moon 64 | https://imagej.net/User:Moon 65 | hkmoon 66 | 67 | 68 | John Bogovic 69 | https://imagej.net/User:Bogovic 70 | bogovicj 71 | 72 | 73 | 74 | 75 | 76 | Image.sc Forum 77 | https://forum.image.sc/tag/bigdataviewer 78 | 79 | 80 | 81 | 82 | scm:git:https://github.com/bigdataviewer/bigdataviewer_fiji 83 | scm:git:git@github.com:bigdataviewer/bigdataviewer_fiji 84 | HEAD 85 | https://github.com/bigdataviewer/bigdataviewer_fiji 86 | 87 | 88 | GitHub Issues 89 | https://github.com/bigdataviewer/bigdataviewer_fiji/issues 90 | 91 | 92 | GitHub Actions 93 | https://github.com/bigdataviewer/bigdataviewer_fiji/actions 94 | 95 | 96 | 97 | bdv.ij 98 | gpl_v3 99 | BigDataViewer developers. 100 | 101 | 7.1.0 102 | 1.0.0-beta-18 103 | 2.0.2 104 | 10.6.0 105 | 106 | 107 | sign,deploy-to-scijava 108 | 109 | 1.1.11 110 | 111 | 112 | 113 | 114 | scijava.public 115 | https://maven.scijava.org/content/groups/public 116 | 117 | 118 | 119 | 120 | 121 | sc.fiji 122 | bigdataviewer-core 123 | 124 | 125 | net.imagej 126 | imagej-deprecated 127 | 128 | 129 | net.imglib2 130 | imglib2 131 | 132 | 133 | net.imglib2 134 | imglib2-cache 135 | 136 | 137 | net.imglib2 138 | imglib2-ij 139 | 140 | 141 | io.scif 142 | scifio 143 | 144 | 145 | sc.fiji 146 | SPIM_Registration 147 | 148 | 149 | 150 | org.janelia.saalfeldlab 151 | n5 152 | 153 | 154 | org.apache.commons 155 | commons-compress 156 | 157 | 158 | org.jdom 159 | jdom2 160 | 161 | 162 | commons-lang 163 | commons-lang 164 | 165 | 166 | net.imagej 167 | ij 168 | 169 | 170 | sc.fiji 171 | fiji-lib 172 | 173 | 174 | sc.fiji 175 | spim_data 176 | 177 | 178 | org.scijava 179 | scijava-common 180 | 181 | 182 | sc.fiji 183 | legacy-imglib1 184 | 185 | 186 | net.imglib2 187 | imglib2-algorithm 188 | 189 | 190 | sc.fiji 191 | SPIM_Opener 192 | 193 | 194 | mpicbg 195 | mpicbg 196 | 197 | 198 | net.imglib2 199 | imglib2-realtransform 200 | 201 | 202 | com.google.code.gson 203 | gson 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /src/main/java/bdv/ij/util/PluginHelper.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.ij.util; 23 | 24 | import java.awt.Button; 25 | import java.awt.FileDialog; 26 | import java.awt.FlowLayout; 27 | import java.awt.Frame; 28 | import java.awt.GridBagConstraints; 29 | import java.awt.GridBagLayout; 30 | import java.awt.Panel; 31 | import java.awt.TextField; 32 | import java.awt.event.ActionEvent; 33 | import java.awt.event.ActionListener; 34 | import java.io.File; 35 | import java.io.FilenameFilter; 36 | import java.io.IOException; 37 | import java.util.ArrayList; 38 | import java.util.regex.Matcher; 39 | import java.util.regex.Pattern; 40 | 41 | import javax.swing.JFileChooser; 42 | import javax.swing.filechooser.FileFilter; 43 | 44 | import fiji.util.gui.GenericDialogPlus; 45 | import ij.Prefs; 46 | 47 | public class PluginHelper 48 | { 49 | public static void addSaveAsFileField( final GenericDialogPlus dialog, final String label, final String defaultPath, final int columns) { 50 | dialog.addStringField( label, defaultPath, columns ); 51 | 52 | final TextField text = ( TextField ) dialog.getStringFields().lastElement(); 53 | final GridBagLayout layout = ( GridBagLayout ) dialog.getLayout(); 54 | final GridBagConstraints constraints = layout.getConstraints( text ); 55 | 56 | final Button button = new Button( "Browse..." ); 57 | final ChooseXmlFileListener listener = new ChooseXmlFileListener( text ); 58 | button.addActionListener( listener ); 59 | button.addKeyListener( dialog ); 60 | 61 | final Panel panel = new Panel(); 62 | panel.setLayout( new FlowLayout( FlowLayout.LEFT, 0, 0 ) ); 63 | panel.add( text ); 64 | panel.add( button ); 65 | 66 | layout.setConstraints( panel, constraints ); 67 | dialog.add( panel ); 68 | } 69 | 70 | public static boolean useFileDialog = true; 71 | 72 | public static class ChooseXmlFileListener implements ActionListener 73 | { 74 | TextField text; 75 | 76 | public ChooseXmlFileListener( final TextField text ) 77 | { 78 | this.text = text; 79 | } 80 | 81 | @Override 82 | public void actionPerformed( final ActionEvent e ) 83 | { 84 | File directory = new File( text.getText() ); 85 | final String fn = directory.getName(); 86 | while ( directory != null && !directory.exists() ) 87 | directory = directory.getParentFile(); 88 | 89 | if ( Prefs.useJFileChooser ) 90 | { 91 | final JFileChooser fc = new JFileChooser( directory ); 92 | fc.setSelectedFile( new File( fn ) ); 93 | fc.setFileFilter( new FileFilter() 94 | { 95 | @Override 96 | public String getDescription() 97 | { 98 | return "xml files"; 99 | } 100 | 101 | @Override 102 | public boolean accept( final File f ) 103 | { 104 | if ( f.isDirectory() ) 105 | return true; 106 | if ( f.isFile() ) 107 | { 108 | final String s = f.getName(); 109 | final int i = s.lastIndexOf( '.' ); 110 | if ( i > 0 && i < s.length() - 1 ) 111 | { 112 | final String ext = s.substring( i + 1 ).toLowerCase(); 113 | return ext.equals( "xml" ); 114 | } 115 | } 116 | return false; 117 | } 118 | } ); 119 | 120 | fc.setFileSelectionMode( JFileChooser.FILES_ONLY ); 121 | 122 | final int returnVal = fc.showSaveDialog( null ); 123 | if ( returnVal == JFileChooser.APPROVE_OPTION ) 124 | { 125 | String f = fc.getSelectedFile().getAbsolutePath(); 126 | if ( !f.endsWith( ".xml" ) ) 127 | f += ".xml"; 128 | text.setText( f ); 129 | } 130 | } 131 | else // use FileDialog 132 | { 133 | final FileDialog fd = new FileDialog( ( Frame ) null, "Save", FileDialog.SAVE ); 134 | fd.setDirectory( directory.getAbsolutePath() ); 135 | fd.setFile( fn ); 136 | fd.setFilenameFilter( new FilenameFilter() 137 | { 138 | @Override 139 | public boolean accept( final File dir, final String name ) 140 | { 141 | final int i = name.lastIndexOf( '.' ); 142 | if ( i > 0 && i < name.length() - 1 ) 143 | { 144 | final String ext = name.substring( i + 1 ).toLowerCase(); 145 | return ext.equals( "xml" ); 146 | } 147 | return false; 148 | } 149 | } ); 150 | fd.setVisible( true ); 151 | final String filename = fd.getFile(); 152 | if ( filename != null ) 153 | { 154 | String f = new File( fd.getDirectory() + filename ).getAbsolutePath(); 155 | if ( !f.endsWith( ".xml" ) ) 156 | f += ".xml"; 157 | text.setText( f ); 158 | } 159 | } 160 | } 161 | } 162 | 163 | public static int[][] parseResolutionsString( final String s ) 164 | { 165 | final String regex = "\\{\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\}"; 166 | final Pattern pattern = Pattern.compile( regex ); 167 | final Matcher matcher = pattern.matcher( s ); 168 | 169 | final ArrayList< int[] > tmp = new ArrayList<>(); 170 | while ( matcher.find() ) 171 | { 172 | final int[] resolution = new int[] { Integer.parseInt( matcher.group( 1 ) ), Integer.parseInt( matcher.group( 2 ) ), Integer.parseInt( matcher.group( 3 ) ) }; 173 | tmp.add( resolution ); 174 | } 175 | final int[][] resolutions = new int[ tmp.size() ][]; 176 | for ( int i = 0; i < resolutions.length; ++i ) 177 | resolutions[ i ] = tmp.get( i ); 178 | 179 | return resolutions; 180 | } 181 | 182 | public static File createNewPartitionFile( final String baseFilename ) throws IOException 183 | { 184 | File hdf5File = new File( String.format( "%s.h5", baseFilename ) ); 185 | if ( ! hdf5File.exists() ) 186 | if ( hdf5File.createNewFile() ) 187 | return hdf5File; 188 | 189 | for ( int i = 0; i < Integer.MAX_VALUE; ++i ) 190 | { 191 | hdf5File = new File( String.format( "%s-%d.h5", baseFilename, i ) ); 192 | if ( ! hdf5File.exists() ) 193 | if ( hdf5File.createNewFile() ) 194 | return hdf5File; 195 | } 196 | 197 | throw new RuntimeException( "could not generate new partition filename" ); 198 | } 199 | 200 | public static int numThreads() 201 | { 202 | return Math.max( 1, Prefs.getThreads() ); 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/main/java/bdv/ij/export/tiles/LegacyTileImgLoader.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.ij.export.tiles; 23 | 24 | import java.io.File; 25 | import java.io.IOException; 26 | import java.util.HashMap; 27 | import java.util.Iterator; 28 | import java.util.List; 29 | import java.util.Map; 30 | import java.util.TreeMap; 31 | 32 | import org.jdom2.DataConversionException; 33 | import org.jdom2.Document; 34 | import org.jdom2.Element; 35 | import org.jdom2.JDOMException; 36 | import org.jdom2.Namespace; 37 | import org.jdom2.input.SAXBuilder; 38 | 39 | import bdv.ij.export.tiles.CellVoyagerDataExporter.ChannelInfo; 40 | import ij.ImagePlus; 41 | import mpicbg.spim.data.legacy.LegacyBasicImgLoader; 42 | import mpicbg.spim.data.sequence.ViewId; 43 | import net.imglib2.Cursor; 44 | import net.imglib2.RandomAccessibleInterval; 45 | import net.imglib2.img.Img; 46 | import net.imglib2.img.array.ArrayImg; 47 | import net.imglib2.img.array.ArrayImgs; 48 | import net.imglib2.img.array.ArrayRandomAccess; 49 | import net.imglib2.img.basictypeaccess.array.ShortArray; 50 | import net.imglib2.img.display.imagej.ImageJFunctions; 51 | import net.imglib2.type.numeric.integer.UnsignedShortType; 52 | 53 | public class LegacyTileImgLoader implements LegacyBasicImgLoader< UnsignedShortType > 54 | { 55 | 56 | private static final Namespace NAMESPACE = Namespace.getNamespace( "bts", "http://www.yokogawa.co.jp/BTS/BTSSchema/1.0" ); 57 | 58 | private final File imageIndexFile; 59 | 60 | private Document document; 61 | 62 | private final List< ChannelInfo > channelInfos; 63 | 64 | public LegacyTileImgLoader( final File imageIndexFile, final List< ChannelInfo > channelInfos ) 65 | { 66 | 67 | if ( !imageIndexFile.exists() ) { throw new IllegalArgumentException( "The target file " + imageIndexFile + " does not exist." ); } 68 | if ( !imageIndexFile.isFile() ) { throw new IllegalArgumentException( "The target file " + imageIndexFile + " is not a file." ); } 69 | 70 | this.imageIndexFile = imageIndexFile; 71 | this.channelInfos = channelInfos; 72 | final SAXBuilder builder = new SAXBuilder(); 73 | try 74 | { 75 | document = builder.build( imageIndexFile ); 76 | } 77 | catch ( final JDOMException e ) 78 | { 79 | throw new IllegalArgumentException( "The target file " + imageIndexFile + " is malformed:\n" + e.getMessage() ); 80 | } 81 | catch ( final IOException e ) 82 | { 83 | throw new IllegalArgumentException( "Trouble reading " + imageIndexFile + ":\n" + e.getMessage() ); 84 | } 85 | 86 | if ( !document.getRootElement().getName().equals( "ImageIndex" ) ) { throw new IllegalArgumentException( "The target file " + imageIndexFile + " is not a CellVoyager Image Index file." ); } 87 | 88 | } 89 | 90 | @Override 91 | public RandomAccessibleInterval< UnsignedShortType > getImage( final ViewId view ) 92 | { 93 | 94 | final int setupIndex = view.getViewSetupId(); 95 | final int viewTimePoint = view.getTimePointId() + 1; // FIXME 96 | final ChannelInfo channelInfo = channelInfos.get( setupIndex ); 97 | 98 | final int viewChannel = channelInfo.channelNumber; 99 | 100 | /* 101 | * Collect file names 102 | */ 103 | 104 | // Map of z -> all the tiles. The tiles are a map of field index -> 105 | // filename 106 | final TreeMap< Double, Map< Integer, String > > filenames = new TreeMap<>(); 107 | final Element root = document.getRootElement(); 108 | 109 | for ( final Element element : root.getChildren( "MeasurementRecord", NAMESPACE ) ) 110 | { 111 | int field; 112 | int timepoint; 113 | int channel; 114 | double z; 115 | boolean isMIP; 116 | try 117 | { 118 | field = element.getAttribute( "FieldIndex", NAMESPACE ).getIntValue(); 119 | timepoint = element.getAttribute( "TimePoint", NAMESPACE ).getIntValue(); 120 | z = element.getAttribute( "Z", NAMESPACE ).getDoubleValue(); 121 | channel = element.getAttribute( "Ch", NAMESPACE ).getIntValue(); 122 | isMIP = element.getAttribute( "Mip", NAMESPACE ).getBooleanValue(); 123 | } 124 | catch ( final DataConversionException e ) 125 | { 126 | System.err.println( "Incorrect attribute formatting for " + element ); 127 | continue; 128 | } 129 | 130 | if ( isMIP || timepoint != viewTimePoint || channel != viewChannel ) 131 | { 132 | continue; 133 | } 134 | 135 | final String filename = element.getText(); 136 | final Double dz = Double.valueOf( z ); 137 | Map< Integer, String > tilesAtZ = filenames.get( dz ); 138 | if ( null == tilesAtZ ) 139 | { 140 | tilesAtZ = new HashMap<>(); 141 | filenames.put( dz, tilesAtZ ); 142 | } 143 | tilesAtZ.put( field, filename ); 144 | 145 | } 146 | 147 | /* 148 | * Build stack 149 | */ 150 | 151 | final long[] dimensions = new long[] { channelInfo.width, channelInfo.height, channelInfo.nZSlices }; 152 | final ArrayImg< UnsignedShortType, ShortArray > stack = ArrayImgs.unsignedShorts( dimensions ); 153 | final ArrayRandomAccess< UnsignedShortType > randomAccess = stack.randomAccess(); 154 | 155 | final Iterator< Map< Integer, String >> iterator = filenames.values().iterator(); 156 | 157 | for ( int zindex = 0; zindex < filenames.size(); zindex++ ) 158 | { 159 | final Map< Integer, String > tilesFilenames = iterator.next(); 160 | 161 | for ( final Integer fieldNumber : tilesFilenames.keySet() ) 162 | { 163 | final int fieldIndex = fieldNumber - 1; 164 | 165 | // Filename for this Z, this field 166 | String filename = tilesFilenames.get( fieldNumber ); 167 | 168 | // Comply to local path separator 169 | filename = filename.replace( '\\', File.separatorChar ); 170 | 171 | // Offset for this field index 172 | final long[] offset = channelInfo.offsets.get( fieldIndex ); 173 | 174 | // Open and copy this slice on the stack image 175 | randomAccess.setPosition( zindex, 2 ); 176 | final File filePath = new File( imageIndexFile.getParentFile(), filename ); 177 | final ImagePlus imp = new ImagePlus( filePath.getAbsolutePath() ); 178 | final Img< UnsignedShortType > sliceImg = ImageJFunctions.wrapShort( imp ); 179 | 180 | final Cursor< UnsignedShortType > cursor = sliceImg.cursor(); 181 | while ( cursor.hasNext() ) 182 | { 183 | cursor.fwd(); 184 | randomAccess.setPosition( offset[ 0 ] + cursor.getLongPosition( 0 ), 0 ); 185 | randomAccess.setPosition( offset[ 1 ] + cursor.getLongPosition( 1 ), 1 ); 186 | randomAccess.get().set( cursor.get() ); 187 | 188 | } 189 | 190 | } 191 | 192 | } 193 | 194 | return stack; 195 | } 196 | 197 | @Override 198 | public UnsignedShortType getImageType() 199 | { 200 | return new UnsignedShortType(); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/main/java/bdv/ij/BigDataBrowserPlugIn.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.ij; 23 | 24 | import com.google.gson.stream.JsonReader; 25 | 26 | import java.awt.Component; 27 | import java.awt.Dimension; 28 | import java.awt.Font; 29 | import java.awt.event.MouseAdapter; 30 | import java.awt.event.MouseEvent; 31 | import java.awt.image.BufferedImage; 32 | import java.io.File; 33 | import java.io.IOException; 34 | import java.io.InputStream; 35 | import java.io.InputStreamReader; 36 | import java.net.URL; 37 | import java.util.ArrayList; 38 | import java.util.HashMap; 39 | import java.util.Map; 40 | 41 | import javax.imageio.ImageIO; 42 | import javax.swing.DefaultListCellRenderer; 43 | import javax.swing.ImageIcon; 44 | import javax.swing.JFrame; 45 | import javax.swing.JLabel; 46 | import javax.swing.JList; 47 | import javax.swing.JOptionPane; 48 | import javax.swing.JScrollPane; 49 | 50 | import org.apache.commons.lang.StringUtils; 51 | import org.scijava.command.Command; 52 | import org.scijava.plugin.Plugin; 53 | 54 | import bdv.BigDataViewer; 55 | import bdv.ij.util.ProgressWriterIJ; 56 | import bdv.viewer.ViewerOptions; 57 | import ij.IJ; 58 | import ij.ImageJ; 59 | import mpicbg.spim.data.SpimDataException; 60 | 61 | /** 62 | * @author HongKee Moon <moon@mpi-cbg.de> 63 | */ 64 | @Plugin(type = Command.class, 65 | menuPath = "Plugins>BigDataViewer>Browse BigDataServer") 66 | public class BigDataBrowserPlugIn implements Command 67 | { 68 | private final Map< String, ImageIcon > imageMap = new HashMap<>(); 69 | 70 | private final Map< String, String > datasetUrlMap = new HashMap<>(); 71 | 72 | public static String serverUrl = "http://"; 73 | 74 | @Override 75 | public void run() 76 | { 77 | if ( ij.Prefs.setIJMenuBar ) 78 | System.setProperty( "apple.laf.useScreenMenuBar", "true" ); 79 | 80 | BufferedImage image = null; 81 | try 82 | { 83 | image = ImageIO.read( getClass().getResourceAsStream( "/fiji.png" ) ); 84 | } 85 | catch ( final IOException e ) 86 | { 87 | e.printStackTrace(); 88 | } 89 | 90 | final Object remoteUrl = JOptionPane.showInputDialog( null, "Enter BigDataServer Remote URL:", "BigDataServer", 91 | JOptionPane.QUESTION_MESSAGE, new ImageIcon( image ), null, serverUrl ); 92 | 93 | if ( remoteUrl == null ) 94 | return; 95 | 96 | serverUrl = remoteUrl.toString(); 97 | 98 | final ArrayList< String > nameList = new ArrayList<>(); 99 | try 100 | { 101 | getDatasetList( serverUrl, nameList ); 102 | } 103 | catch ( final IOException e ) 104 | { 105 | IJ.showMessage( "Error connecting to server at " + serverUrl ); 106 | e.printStackTrace(); 107 | } 108 | createDatasetListUI( serverUrl, nameList.toArray() ); 109 | } 110 | 111 | private boolean getDatasetList( final String remoteUrl, final ArrayList< String > nameList ) throws IOException 112 | { 113 | // Get JSON string from the server 114 | final URL url = new URL( remoteUrl + "/json/" ); 115 | 116 | final InputStream is = url.openStream(); 117 | final JsonReader reader = new JsonReader( new InputStreamReader( is, "UTF-8" ) ); 118 | 119 | reader.beginObject(); 120 | 121 | while ( reader.hasNext() ) 122 | { 123 | // skipping id 124 | reader.nextName(); 125 | 126 | reader.beginObject(); 127 | 128 | String id = null, description = null, thumbnailUrl = null, datasetUrl = null; 129 | while ( reader.hasNext() ) 130 | { 131 | final String name = reader.nextName(); 132 | if ( name.equals( "id" ) ) 133 | id = reader.nextString(); 134 | else if ( name.equals( "description" ) ) 135 | description = reader.nextString(); 136 | else if ( name.equals( "thumbnailUrl" ) ) 137 | thumbnailUrl = reader.nextString(); 138 | else if ( name.equals( "datasetUrl" ) ) 139 | datasetUrl = reader.nextString(); 140 | else 141 | reader.skipValue(); 142 | } 143 | 144 | if ( id != null ) 145 | { 146 | nameList.add( id ); 147 | if ( thumbnailUrl != null && StringUtils.isNotEmpty( thumbnailUrl ) ) 148 | imageMap.put( id, new ImageIcon( new URL( thumbnailUrl ) ) ); 149 | if ( datasetUrl != null ) 150 | datasetUrlMap.put( id, datasetUrl ); 151 | } 152 | 153 | reader.endObject(); 154 | } 155 | 156 | reader.endObject(); 157 | 158 | reader.close(); 159 | 160 | return true; 161 | } 162 | 163 | private void createDatasetListUI( final String remoteUrl, final Object[] values ) 164 | { 165 | final JList< ? > list = new JList<>( values ); 166 | list.setCellRenderer( new ThumbnailListRenderer() ); 167 | list.addMouseListener( new MouseAdapter() 168 | { 169 | @Override 170 | public void mouseClicked( final MouseEvent evt ) 171 | { 172 | final JList< ? > list = ( JList< ? > ) evt.getSource(); 173 | if ( evt.getClickCount() == 2 ) 174 | { 175 | final int index = list.locationToIndex( evt.getPoint() ); 176 | final String key = String.valueOf( list.getModel().getElementAt( index ) ); 177 | System.out.println( key ); 178 | try 179 | { 180 | final String filename = datasetUrlMap.get( key ); 181 | final String title = new File( filename ).getName(); 182 | BigDataViewer.open( filename, title, new ProgressWriterIJ(), ViewerOptions.options() ); 183 | } 184 | catch ( final SpimDataException e ) 185 | { 186 | e.printStackTrace(); 187 | } 188 | } 189 | } 190 | } ); 191 | 192 | final JScrollPane scroll = new JScrollPane( list ); 193 | scroll.setPreferredSize( new Dimension( 600, 800 ) ); 194 | 195 | final JFrame frame = new JFrame(); 196 | frame.setTitle( "BigDataServer Browser - " + remoteUrl ); 197 | frame.add( scroll ); 198 | frame.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE ); 199 | frame.pack(); 200 | frame.setLocationRelativeTo( null ); 201 | frame.setVisible( true ); 202 | } 203 | 204 | public class ThumbnailListRenderer extends DefaultListCellRenderer 205 | { 206 | private static final long serialVersionUID = 1L; 207 | 208 | Font font = new Font( "helvetica", Font.BOLD, 12 ); 209 | 210 | @Override 211 | public Component getListCellRendererComponent( 212 | final JList< ? > list, final Object value, final int index, 213 | final boolean isSelected, final boolean cellHasFocus ) 214 | { 215 | 216 | final JLabel label = ( JLabel ) super.getListCellRendererComponent( 217 | list, value, index, isSelected, cellHasFocus ); 218 | label.setIcon( imageMap.get( value ) ); 219 | label.setHorizontalTextPosition( JLabel.RIGHT ); 220 | label.setFont( font ); 221 | return label; 222 | } 223 | } 224 | 225 | public static void main( final String[] args ) 226 | { 227 | ImageJ.main( args ); 228 | new BigDataBrowserPlugIn().run(); 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /src/main/java/bdv/ij/export/imgloader/FusionImageLoader.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.ij.export.imgloader; 23 | 24 | import java.util.HashMap; 25 | import java.util.Map.Entry; 26 | 27 | import ij.ImagePlus; 28 | import io.scif.img.ImgIOException; 29 | import io.scif.img.ImgOpener; 30 | import mpicbg.spim.data.generic.sequence.BasicSetupImgLoader; 31 | import mpicbg.spim.data.generic.sequence.ImgLoaderHint; 32 | import mpicbg.spim.data.sequence.FinalVoxelDimensions; 33 | import mpicbg.spim.data.sequence.ImgLoader; 34 | import mpicbg.spim.data.sequence.SetupImgLoader; 35 | import mpicbg.spim.data.sequence.ViewDescription; 36 | import mpicbg.spim.data.sequence.VoxelDimensions; 37 | import net.imglib2.Cursor; 38 | import net.imglib2.Dimensions; 39 | import net.imglib2.FinalDimensions; 40 | import net.imglib2.RandomAccessibleInterval; 41 | import net.imglib2.converter.Converters; 42 | import net.imglib2.converter.RealUnsignedShortConverter; 43 | import net.imglib2.img.Img; 44 | import net.imglib2.img.ImgFactory; 45 | import net.imglib2.img.array.ArrayImgFactory; 46 | import net.imglib2.img.display.imagej.ImageJFunctions; 47 | import net.imglib2.img.planar.PlanarImgFactory; 48 | import net.imglib2.type.NativeType; 49 | import net.imglib2.type.numeric.RealType; 50 | import net.imglib2.type.numeric.integer.UnsignedByteType; 51 | import net.imglib2.type.numeric.integer.UnsignedShortType; 52 | import net.imglib2.type.numeric.real.FloatType; 53 | import net.imglib2.view.Views; 54 | 55 | /** 56 | * This {@link ImgLoader} loads images that represent a 3D stack as a sequence 57 | * of slice with one image file per slice, such as created by Stephan 58 | * Preibisch's Multi-view fusion plugin. It is constructed with the pattern of 59 | * the image filenames. Then, to laod the image for a given 60 | * {@link ViewDescription}, its TODO timepoint? index?, channel, and slice 61 | * indices are filled into the template to get the slice filenames. 62 | * 63 | * This {@link ImgLoader} is used for exporting spim sequences to hdf5. Only the 64 | * {@link BasicSetupImgLoader#getImage(int, ImgLoaderHint...)} method is 65 | * implemented because this is the only method required for exporting to hdf5. 66 | * 67 | * @author Tobias Pietzsch <tobias.pietzsch@gmail.com> 68 | */ 69 | @Deprecated 70 | public class FusionImageLoader< T extends RealType< T > > implements ImgLoader 71 | { 72 | private final String pattern; 73 | 74 | private final int numSlices; 75 | 76 | private final SliceLoader< T > sliceLoader; 77 | 78 | private final RealUnsignedShortConverter< T > converter; 79 | 80 | private final ImgFactory< UnsignedShortType > factory; 81 | 82 | private final UnsignedShortType type; 83 | 84 | private final HashMap< Integer, SetupLoader > setupIdToSetupImgLoader; 85 | 86 | public FusionImageLoader( final String pattern, final HashMap< Integer, Integer > setupIdToChannelId, final int numSlices, final SliceLoader< T > sliceLoader, final double sliceValueMin, final double sliceValueMax ) 87 | { 88 | this( pattern, setupIdToChannelId, numSlices, sliceLoader, sliceValueMin, sliceValueMax, new PlanarImgFactory< UnsignedShortType >() ); 89 | } 90 | 91 | public FusionImageLoader( final String pattern, final HashMap< Integer, Integer > setupIdToChannelId, final int numSlices, final SliceLoader< T > sliceLoader, final double sliceValueMin, final double sliceValueMax, final ImgFactory< UnsignedShortType > factory ) 92 | { 93 | this.pattern = pattern; 94 | this.numSlices = numSlices; 95 | this.sliceLoader = sliceLoader; 96 | converter = new RealUnsignedShortConverter<>( sliceValueMin, sliceValueMax ); 97 | this.factory = factory; 98 | type = new UnsignedShortType(); 99 | setupIdToSetupImgLoader = new HashMap<>(); 100 | for ( final Entry< Integer, Integer > entry : setupIdToChannelId.entrySet() ) 101 | setupIdToSetupImgLoader.put( entry.getKey(), new SetupLoader( entry.getValue() ) ); 102 | } 103 | 104 | public static interface SliceLoader< T > 105 | { 106 | public RandomAccessibleInterval< T > load( String fn ); 107 | } 108 | 109 | public static class ArrayImgLoader< T extends RealType< T > & NativeType< T > > implements SliceLoader< T > 110 | { 111 | final ImgOpener opener; 112 | 113 | final ArrayImgFactory< T > factory; 114 | 115 | final T type; 116 | 117 | public ArrayImgLoader( final T type ) 118 | { 119 | opener = new ImgOpener(); 120 | factory = new ArrayImgFactory<>(); 121 | this.type = type; 122 | } 123 | 124 | @Override 125 | public RandomAccessibleInterval< T > load( final String fn ) 126 | { 127 | try 128 | { 129 | System.out.println( fn ); 130 | return opener.openImgs( fn, factory, type ).get( 0 ); 131 | } 132 | catch ( final ImgIOException e ) 133 | { 134 | e.printStackTrace(); 135 | } 136 | return null; 137 | } 138 | } 139 | 140 | public static class Gray32ImagePlusLoader implements SliceLoader< FloatType > 141 | { 142 | @Override 143 | public RandomAccessibleInterval< FloatType > load( final String fn ) 144 | { 145 | return ImageJFunctions.wrapFloat( new ImagePlus( fn ) ); 146 | } 147 | } 148 | 149 | public static class Gray16ImagePlusLoader implements SliceLoader< UnsignedShortType > 150 | { 151 | @Override 152 | public RandomAccessibleInterval< UnsignedShortType > load( final String fn ) 153 | { 154 | return ImageJFunctions.wrapShort( new ImagePlus( fn ) ); 155 | } 156 | } 157 | 158 | public static class Gray8ImagePlusLoader implements SliceLoader< UnsignedByteType > 159 | { 160 | @Override 161 | public RandomAccessibleInterval< UnsignedByteType > load( final String fn ) 162 | { 163 | return ImageJFunctions.wrapByte( new ImagePlus( fn ) ); 164 | } 165 | } 166 | 167 | @Override 168 | public SetupLoader getSetupImgLoader( final int setupId ) 169 | { 170 | return setupIdToSetupImgLoader.get( setupId ); 171 | } 172 | 173 | public class SetupLoader implements SetupImgLoader< UnsignedShortType > 174 | { 175 | private final int channelId; 176 | 177 | protected SetupLoader( final int channelId ) 178 | { 179 | this.channelId = channelId; 180 | } 181 | 182 | @Override 183 | public RandomAccessibleInterval< UnsignedShortType > getImage( final int timepointId, final ImgLoaderHint... hints ) 184 | { 185 | final Dimensions dimensions = getImageSize( timepointId ); 186 | final Img< UnsignedShortType > img = factory.create( dimensions, type ); 187 | 188 | for ( int z = 0; z < numSlices; ++z ) 189 | { 190 | final RandomAccessibleInterval< T > slice = sliceLoader.load( String.format( pattern, timepointId, channelId, z ) ); 191 | 192 | final Cursor< UnsignedShortType > d = Views.flatIterable( Views.hyperSlice( img, 2, z ) ).cursor(); 193 | for ( final UnsignedShortType t : Converters.convert( Views.flatIterable( slice ), converter, type ) ) 194 | d.next().set( t ); 195 | } 196 | return img; 197 | } 198 | 199 | @Override 200 | public UnsignedShortType getImageType() 201 | { 202 | return type; 203 | } 204 | 205 | @Override 206 | public RandomAccessibleInterval< FloatType > getFloatImage( final int timepointId, final boolean normalize, final ImgLoaderHint... hints ) 207 | { 208 | throw new UnsupportedOperationException(); 209 | } 210 | 211 | @Override 212 | public Dimensions getImageSize( final int timepointId ) 213 | { 214 | final RandomAccessibleInterval< T > slice = sliceLoader.load( String.format( pattern, timepointId, channelId, 0 ) ); 215 | return new FinalDimensions( 216 | slice.dimension( 0 ), 217 | slice.dimension( 1 ), 218 | numSlices ); 219 | } 220 | 221 | @Override 222 | public VoxelDimensions getVoxelSize( final int timepointId ) 223 | { 224 | return new FinalVoxelDimensions( "px", 1, 1, 1 ); 225 | } 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /src/main/java/bdv/ij/ExportCellVoyagerPlugIn.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.ij; 23 | 24 | import java.awt.Button; 25 | import java.awt.FlowLayout; 26 | import java.awt.GridBagConstraints; 27 | import java.awt.GridBagLayout; 28 | import java.awt.Panel; 29 | import java.awt.TextField; 30 | import java.awt.event.ActionEvent; 31 | import java.awt.event.ActionListener; 32 | import java.io.File; 33 | import java.io.FilenameFilter; 34 | 35 | import javax.swing.JFileChooser; 36 | import javax.swing.filechooser.FileFilter; 37 | 38 | import org.scijava.command.Command; 39 | import org.scijava.plugin.Plugin; 40 | 41 | import bdv.export.ProgressWriter; 42 | import bdv.ij.export.tiles.CellVoyagerDataExporter; 43 | import bdv.ij.util.PluginHelper; 44 | import bdv.ij.util.ProgressWriterIJ; 45 | import fiji.util.gui.GenericDialogPlus; 46 | import ij.IJ; 47 | import ij.ImageJ; 48 | 49 | @Plugin(type = Command.class, 50 | menuPath = "Plugins>BigDataViewer>Deprecated>Export CellVoyager dataset as XML/HDF5") 51 | public class ExportCellVoyagerPlugIn implements Command 52 | { 53 | 54 | protected static class Parameters 55 | { 56 | final int[][] resolutions; 57 | 58 | final int[][] subdivisions; 59 | 60 | final File seqFile; 61 | 62 | final File hdf5File; 63 | 64 | final File sourceFolder; 65 | 66 | public Parameters( final int[][] resolutions, final int[][] subdivisions, final File sourceFile, final File seqFile, final File hdf5File ) 67 | { 68 | this.resolutions = resolutions; 69 | this.subdivisions = subdivisions; 70 | this.sourceFolder = sourceFile; 71 | this.seqFile = seqFile; 72 | this.hdf5File = hdf5File; 73 | } 74 | } 75 | 76 | static String lastSubsampling = "{1,1,1}, {2,2,1}, {4,4,1}, {8,8,2}"; 77 | 78 | static String lastChunkSizes = "{32,32,4}, {16,16,8}, {8,8,4}, {16, 16, 16}"; 79 | 80 | static String lastExportPath; 81 | 82 | static String sourceFolderStr; 83 | 84 | private String sourcePath; 85 | 86 | public void setSourcePath( final String sourcePath ) 87 | { 88 | this.sourcePath = sourcePath; 89 | } 90 | 91 | @Override 92 | public void run() 93 | { 94 | if ( ij.Prefs.setIJMenuBar ) 95 | System.setProperty( "apple.laf.useScreenMenuBar", "true" ); 96 | 97 | final Parameters params = getParameters( sourcePath ); 98 | if ( params == null ) 99 | return; 100 | 101 | final File measurementFolder = params.sourceFolder; 102 | final File imageIndexFile = new File( measurementFolder, "ImageIndex.xml" ); 103 | final File measurementSettingFile = new File( measurementFolder, "MeasurementSetting.xml" ); 104 | 105 | final CellVoyagerDataExporter exporter = new CellVoyagerDataExporter( measurementSettingFile, imageIndexFile ); 106 | final ProgressWriter progressWriter = new ProgressWriterIJ(); 107 | exporter.export( params.seqFile, params.hdf5File, params.resolutions, params.subdivisions, progressWriter ); 108 | 109 | } 110 | 111 | protected Parameters getParameters( final String sourcePathStr ) 112 | { 113 | while ( true ) 114 | { 115 | final GenericDialogPlus gd = new GenericDialogPlus( "CellVoyager Import" ); 116 | 117 | gd.addStringField( "Subsampling factors", lastSubsampling, 25 ); 118 | gd.addStringField( "Hdf5 chunk sizes", lastChunkSizes, 25 ); 119 | gd.addMessage( "" ); 120 | 121 | if ( null == sourcePathStr ) 122 | { 123 | if ( null == sourceFolderStr ) 124 | { 125 | final File folder = new File( System.getProperty( "user.dir" ) ).getParentFile().getParentFile(); 126 | sourceFolderStr = folder.getAbsolutePath(); 127 | } 128 | } 129 | else 130 | { 131 | sourceFolderStr = sourcePathStr; 132 | } 133 | addBrowseToCellVoyagerFolder( gd, "Measurement folder", sourceFolderStr, 25 ); 134 | 135 | if ( null == lastExportPath ) 136 | { 137 | final File sourceFile = new File( sourceFolderStr ); 138 | final String parentFolder = sourceFile.getParent(); 139 | lastExportPath = new File( parentFolder, "export.xml" ).getAbsolutePath(); 140 | } 141 | PluginHelper.addSaveAsFileField( gd, "Export to", lastExportPath, 25 ); 142 | 143 | gd.showDialog(); 144 | if ( gd.wasCanceled() ) 145 | return null; 146 | 147 | lastSubsampling = gd.getNextString(); 148 | lastChunkSizes = gd.getNextString(); 149 | sourceFolderStr = gd.getNextString(); 150 | lastExportPath = gd.getNextString(); 151 | 152 | // parse mipmap resolutions and cell sizes 153 | final int[][] resolutions = PluginHelper.parseResolutionsString( lastSubsampling ); 154 | final int[][] subdivisions = PluginHelper.parseResolutionsString( lastChunkSizes ); 155 | if ( resolutions.length == 0 ) 156 | { 157 | IJ.showMessage( "Cannot parse subsampling factors: " + lastSubsampling ); 158 | continue; 159 | } 160 | if ( subdivisions.length == 0 ) 161 | { 162 | IJ.showMessage( "Cannot parse hdf5 chunk sizes: " + lastChunkSizes ); 163 | continue; 164 | } 165 | else if ( resolutions.length != subdivisions.length ) 166 | { 167 | IJ.showMessage( "Subsampling factors and hdf5 chunk sizes must have the same number of elements." ); 168 | continue; 169 | } 170 | 171 | final File sourceFolder = new File( sourceFolderStr ); 172 | 173 | String seqFilename = lastExportPath; 174 | if ( !seqFilename.endsWith( ".xml" ) ) 175 | seqFilename += ".xml"; 176 | final File seqFile = new File( seqFilename ); 177 | final File parent = seqFile.getParentFile(); 178 | if ( parent == null || !parent.exists() || !parent.isDirectory() ) 179 | { 180 | IJ.showMessage( "Invalid export filename " + seqFilename ); 181 | continue; 182 | } 183 | final String hdf5Filename = seqFilename.substring( 0, seqFilename.length() - 4 ) + ".h5"; 184 | final File hdf5File = new File( hdf5Filename ); 185 | 186 | return new Parameters( resolutions, subdivisions, sourceFolder, seqFile, hdf5File ); 187 | } 188 | } 189 | 190 | /* 191 | * STATIC METHODS 192 | */ 193 | 194 | public static void addBrowseToCellVoyagerFolder( final GenericDialogPlus dialog, final String label, final String defaultPath, final int columns ) 195 | { 196 | dialog.addStringField( label, defaultPath, columns ); 197 | 198 | final TextField text = ( TextField ) dialog.getStringFields().lastElement(); 199 | final GridBagLayout layout = ( GridBagLayout ) dialog.getLayout(); 200 | final GridBagConstraints constraints = layout.getConstraints( text ); 201 | 202 | final Button button = new Button( "Browse..." ); 203 | final ChooseCellVoyagerDirListener listener = new ChooseCellVoyagerDirListener( text ); 204 | button.addActionListener( listener ); 205 | button.addKeyListener( dialog ); 206 | 207 | final Panel panel = new Panel(); 208 | panel.setLayout( new FlowLayout( FlowLayout.LEFT, 0, 0 ) ); 209 | panel.add( text ); 210 | panel.add( button ); 211 | 212 | layout.setConstraints( panel, constraints ); 213 | dialog.add( panel ); 214 | } 215 | 216 | /* 217 | * STATIC CLASSES 218 | */ 219 | 220 | private static class ChooseCellVoyagerDirListener implements ActionListener 221 | { 222 | TextField text; 223 | 224 | public ChooseCellVoyagerDirListener( final TextField text ) 225 | { 226 | this.text = text; 227 | } 228 | 229 | @Override 230 | public void actionPerformed( final ActionEvent e ) 231 | { 232 | File directory = new File( text.getText() ); 233 | while ( directory != null && !directory.exists() ) 234 | directory = directory.getParentFile(); 235 | 236 | final JFileChooser fc = new JFileChooser( directory ); 237 | fc.setFileFilter( new FileFilter() 238 | { 239 | @Override 240 | public String getDescription() 241 | { 242 | return "CellVoyager measurement folder"; 243 | } 244 | 245 | @Override 246 | public boolean accept( final File f ) 247 | { 248 | if ( f.isDirectory() ) 249 | { 250 | final File[] files = f.listFiles( new FilenameFilter() 251 | { 252 | 253 | @Override 254 | public boolean accept( final File dir, final String name ) 255 | { 256 | return name.equals( "MeasurementSetting.xml" ); 257 | } 258 | } ); 259 | return files.length > 0; 260 | } 261 | else 262 | { 263 | return false; 264 | } 265 | } 266 | } ); 267 | 268 | fc.setFileSelectionMode( JFileChooser.DIRECTORIES_ONLY ); 269 | 270 | final int returnVal = fc.showOpenDialog( null ); 271 | if ( returnVal == JFileChooser.APPROVE_OPTION ) 272 | { 273 | final String f = fc.getSelectedFile().getAbsolutePath(); 274 | text.setText( f ); 275 | } 276 | } 277 | } 278 | 279 | public static void main( final String[] args ) 280 | { 281 | ImageJ.main( args ); 282 | 283 | final ExportCellVoyagerPlugIn plugin = new ExportCellVoyagerPlugIn(); 284 | final File file = new File( "/Users/tinevez/Desktop/Data/1_7_6_1_2/20130703T145244/" ); 285 | plugin.setSourcePath( file.getAbsolutePath() ); 286 | plugin.run(); 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /src/main/java/bdv/ij/ImportPlugIn.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.ij; 23 | 24 | import static mpicbg.spim.data.generic.sequence.ImgLoaderHints.LOAD_COMPLETELY; 25 | 26 | import java.awt.AWTEvent; 27 | import java.awt.Checkbox; 28 | import java.awt.Scrollbar; 29 | import java.awt.TextField; 30 | import java.awt.event.TextEvent; 31 | import java.io.File; 32 | import java.util.List; 33 | 34 | import net.imglib2.RandomAccessibleInterval; 35 | import net.imglib2.type.numeric.NumericType; 36 | 37 | import org.scijava.command.Command; 38 | import org.scijava.plugin.Plugin; 39 | 40 | import bdv.ViewerImgLoader; 41 | import bdv.spimdata.SequenceDescriptionMinimal; 42 | import bdv.spimdata.SpimDataMinimal; 43 | import bdv.spimdata.XmlIoSpimDataMinimal; 44 | import fiji.util.gui.GenericDialogPlus; 45 | import ij.IJ; 46 | import ij.ImageJ; 47 | import ij.ImagePlus; 48 | import ij.gui.DialogListener; 49 | import ij.gui.GenericDialog; 50 | import ij.measure.Calibration; 51 | import mpicbg.spim.data.SpimDataException; 52 | import mpicbg.spim.data.generic.sequence.BasicImgLoader; 53 | import mpicbg.spim.data.generic.sequence.BasicMultiResolutionImgLoader; 54 | import mpicbg.spim.data.generic.sequence.BasicViewSetup; 55 | import mpicbg.spim.data.generic.sequence.ImgLoaderHint; 56 | import mpicbg.spim.data.sequence.TimePoint; 57 | import mpicbg.spim.data.sequence.VoxelDimensions; 58 | 59 | /** 60 | * ImageJ plugin to import a raw image from xml/hdf5. 61 | * 62 | * @author Tobias Pietzsch <tobias.pietzsch@gmail.com> 63 | */ 64 | @Plugin(type = Command.class, menuPath = "File>Import>BigDataViewer...") 65 | public class ImportPlugIn implements Command 66 | { 67 | public static String xmlFile = ""; 68 | public static int timepoint = 0; 69 | public static int setup = 0; 70 | public static int mipmap = 0; 71 | public static boolean openAsVirtualStack = false; 72 | 73 | private static SequenceDescriptionMinimal openSequence( final String xmlFilename ) throws SpimDataException 74 | { 75 | final File f = new File( xmlFilename ); 76 | if ( f.exists() && f.isFile() && f.getName().endsWith( ".xml" ) ) 77 | { 78 | final SpimDataMinimal spimData = new XmlIoSpimDataMinimal().load( xmlFilename ); 79 | return spimData.getSequenceDescription(); 80 | } 81 | else 82 | return null; 83 | } 84 | 85 | @Override 86 | public void run() 87 | { 88 | if ( ij.Prefs.setIJMenuBar ) 89 | System.setProperty( "apple.laf.useScreenMenuBar", "true" ); 90 | 91 | final GenericDialogPlus gd = new GenericDialogPlus( "Import from BigDataViewer file" ); 92 | gd.addFileField( "xml file", xmlFile ); 93 | final TextField tfXmlFile = (TextField) gd.getStringFields().lastElement(); 94 | gd.addSlider( "timepoint index", 0, 0, timepoint ); 95 | final Scrollbar slTimepoint = (Scrollbar) gd.getSliders().lastElement(); 96 | final TextField tfTimepoint = (TextField) gd.getNumericFields().lastElement(); 97 | gd.addSlider( "setup index", 0, 0, setup ); 98 | final Scrollbar slSetup = (Scrollbar) gd.getSliders().lastElement(); 99 | final TextField tfSetup = (TextField) gd.getNumericFields().lastElement(); 100 | gd.addSlider( "resolution level", 0, 0, setup ); 101 | final Scrollbar slMipmap = (Scrollbar) gd.getSliders().lastElement(); 102 | final TextField tfMipmap = (TextField) gd.getNumericFields().lastElement(); 103 | gd.addCheckbox( "open as virtual stack", openAsVirtualStack ); 104 | final Checkbox cVirtual = (Checkbox) gd.getCheckboxes().lastElement(); 105 | 106 | class TryOpen 107 | { 108 | void check( final String xmlFilename ) 109 | { 110 | boolean enable = false; 111 | boolean enableMipmap = false; 112 | try 113 | { 114 | final SequenceDescriptionMinimal seq = openSequence( xmlFilename ); 115 | if ( seq != null ) 116 | { 117 | final int numTimepoints = seq.getTimePoints().size(); 118 | final int numSetups = seq.getViewSetupsOrdered().size(); 119 | 120 | slTimepoint.setMaximum( numTimepoints ); 121 | slSetup.setMaximum( numSetups ); 122 | enable = true; 123 | 124 | if ( seq.getImgLoader() instanceof ViewerImgLoader ) 125 | { 126 | final ViewerImgLoader vil = ( ViewerImgLoader ) seq.getImgLoader(); 127 | final int numMipmapLevels = vil.getSetupImgLoader( seq.getViewSetupsOrdered().get( 0 ).getId() ).numMipmapLevels(); 128 | 129 | slMipmap.setMaximum( numMipmapLevels ); 130 | enableMipmap = true; 131 | } 132 | else 133 | { 134 | enableMipmap = false; 135 | } 136 | } 137 | } 138 | catch ( final Exception ex ) 139 | { 140 | IJ.error( ex.getMessage() ); 141 | ex.printStackTrace(); 142 | } 143 | slTimepoint.setEnabled( enable ); 144 | tfTimepoint.setEnabled( enable ); 145 | slSetup.setEnabled( enable ); 146 | tfSetup.setEnabled( enable ); 147 | slMipmap.setEnabled( enableMipmap ); 148 | tfMipmap.setEnabled( enableMipmap ); 149 | cVirtual.setEnabled( enable ); 150 | } 151 | } 152 | final TryOpen tryOpen = new TryOpen(); 153 | tryOpen.check( xmlFile ); 154 | 155 | gd.addDialogListener( new DialogListener() 156 | { 157 | @Override 158 | public boolean dialogItemChanged( final GenericDialog dialog, final AWTEvent e ) 159 | { 160 | gd.getNextString(); 161 | gd.getNextNumber(); 162 | gd.getNextNumber(); 163 | gd.getNextNumber(); 164 | gd.getNextBoolean(); 165 | if ( e instanceof TextEvent && e.getID() == TextEvent.TEXT_VALUE_CHANGED && e.getSource() == tfXmlFile ) 166 | { 167 | final TextField tf = ( TextField ) e.getSource(); 168 | final String xmlFilename = tf.getText(); 169 | tryOpen.check( xmlFilename ); 170 | } 171 | return true; 172 | } 173 | } ); 174 | 175 | gd.showDialog(); 176 | if ( gd.wasCanceled() ) 177 | return; 178 | 179 | xmlFile = gd.getNextString(); 180 | timepoint = ( int ) gd.getNextNumber(); 181 | setup = ( int ) gd.getNextNumber(); 182 | mipmap = ( int ) gd.getNextNumber(); 183 | openAsVirtualStack = gd.getNextBoolean(); 184 | 185 | System.out.println( xmlFile + " " + timepoint + " " + setup ); 186 | try 187 | { 188 | final SequenceDescriptionMinimal seq = openSequence( xmlFile ); 189 | if ( seq != null ) 190 | { 191 | final List< TimePoint > timepointsOrdered = seq.getTimePoints().getTimePointsOrdered(); 192 | final List< BasicViewSetup > setupsOrdered = seq.getViewSetupsOrdered(); 193 | final int numTimepoints = timepointsOrdered.size(); 194 | final int numSetups = setupsOrdered.size(); 195 | timepoint = Math.max( Math.min( timepoint, numTimepoints - 1 ), 0 ); 196 | setup = Math.max( Math.min( setup, numSetups - 1 ), 0 ); 197 | final int timepointId = timepointsOrdered.get( timepoint ).getId(); 198 | final int setupId = setupsOrdered.get( setup ).getId(); 199 | 200 | final BasicImgLoader il = seq.getImgLoader(); 201 | final boolean duplicateImp = !openAsVirtualStack; 202 | final ImgLoaderHint[] hints = openAsVirtualStack ? new ImgLoaderHint[ 0 ] : new ImgLoaderHint[] { LOAD_COMPLETELY }; 203 | 204 | final RandomAccessibleInterval< ? > img; 205 | final double[] mipmapResolution; 206 | if ( il instanceof BasicMultiResolutionImgLoader ) 207 | { 208 | final BasicMultiResolutionImgLoader mil = ( BasicMultiResolutionImgLoader ) il; 209 | final int numMipmapLevels = mil.getSetupImgLoader( setupId ).numMipmapLevels(); 210 | if ( mipmap >= numMipmapLevels ) 211 | mipmap = numMipmapLevels - 1; 212 | img = mil.getSetupImgLoader( setupId ).getImage( timepointId, mipmap, hints ); 213 | mipmapResolution = mil.getSetupImgLoader( setupId ).getMipmapResolutions()[ mipmap ]; 214 | } 215 | else 216 | { 217 | img = il.getSetupImgLoader( setupId ).getImage( timepointId, hints ); 218 | mipmapResolution = new double[] { 1, 1, 1 }; 219 | } 220 | 221 | @SuppressWarnings( { "unchecked", "rawtypes" } ) 222 | ImagePlus imp = net.imglib2.img.display.imagej.ImageJFunctions.wrap( ( RandomAccessibleInterval< NumericType > ) img, "" ); 223 | imp.setDimensions( 1, imp.getImageStackSize(), 1 ); 224 | if ( duplicateImp ) 225 | imp = imp.duplicate(); 226 | imp.setTitle( new File( xmlFile ).getName() + " " + timepoint + " " + setup ); 227 | final VoxelDimensions voxelSize = setupsOrdered.get( setup ).getVoxelSize(); 228 | if ( voxelSize != null ) 229 | { 230 | final Calibration calibration = imp.getCalibration(); 231 | calibration.setUnit( voxelSize.unit() ); 232 | calibration.pixelWidth = voxelSize.dimension( 0 ) * mipmapResolution[ 0 ]; 233 | calibration.pixelHeight = voxelSize.dimension( 1 ) * mipmapResolution[ 1 ]; 234 | calibration.pixelDepth = voxelSize.dimension( 2 ) * mipmapResolution[ 2 ]; 235 | } 236 | imp.show(); 237 | } 238 | } 239 | catch ( final Exception ex ) 240 | { 241 | IJ.error( ex.getMessage() ); 242 | ex.printStackTrace(); 243 | } 244 | } 245 | 246 | public static void main( final String[] args ) 247 | { 248 | new ImageJ(); 249 | new ImportPlugIn().run(); 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /src/main/java/bdv/ij/export/imgloader/ImagePlusImgLoader.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.ij.export.imgloader; 23 | 24 | import java.util.ArrayList; 25 | 26 | import bdv.img.cache.VolatileGlobalCellCache; 27 | import bdv.img.imagestack.ImageStackImageLoader; 28 | import bdv.img.virtualstack.VirtualStackImageLoader; 29 | import ij.ImagePlus; 30 | import mpicbg.spim.data.generic.sequence.BasicImgLoader; 31 | import mpicbg.spim.data.generic.sequence.BasicSetupImgLoader; 32 | import mpicbg.spim.data.generic.sequence.ImgLoaderHint; 33 | import mpicbg.spim.data.generic.sequence.TypedBasicImgLoader; 34 | import net.imglib2.RandomAccessibleInterval; 35 | import net.imglib2.algorithm.stats.ComputeMinMax; 36 | import net.imglib2.converter.Converter; 37 | import net.imglib2.converter.Converters; 38 | import net.imglib2.converter.RealFloatConverter; 39 | import net.imglib2.converter.RealUnsignedShortConverter; 40 | import net.imglib2.type.NativeType; 41 | import net.imglib2.type.Type; 42 | import net.imglib2.type.numeric.RealType; 43 | import net.imglib2.type.numeric.integer.UnsignedShortType; 44 | import net.imglib2.type.numeric.real.FloatType; 45 | 46 | /** 47 | * This {@link BasicImgLoader} implementation returns a wrapped, converted 48 | * {@link ImagePlus}. It is used for exporting {@link ImagePlus} to hdf5. 49 | * 50 | * Internally it relies on {@link VirtualStackImageLoader} to be able to handle 51 | * large virtual stacks. 52 | * 53 | * When loading images ({@link #getSetupImgLoader(int)}, 54 | * {@link BasicSetupImgLoader#getImage(int, ImgLoaderHint...)}) the provided 55 | * setup id is used as the channel index of the {@link ImagePlus}, the provided 56 | * timepoint id is used as the frame index of the {@link ImagePlus}. 57 | * 58 | * @author Tobias Pietzsch <tobias.pietzsch@gmail.com> 59 | */ 60 | @Deprecated 61 | public class ImagePlusImgLoader< T extends Type< T > > implements TypedBasicImgLoader< T > 62 | { 63 | public static enum MinMaxOption 64 | { 65 | SET, 66 | COMPUTE, 67 | TAKE_FROM_IMAGEPROCESSOR 68 | } 69 | 70 | public static ImagePlusImgLoader< UnsignedShortType > createGray8( final ImagePlus imp, final MinMaxOption minMaxOption, final double min, final double max ) 71 | { 72 | if( imp.getType() != ImagePlus.GRAY8 ) 73 | throw new RuntimeException( "expected ImagePlus type GRAY8" ); 74 | if ( imp.getStack() != null && imp.getStack().isVirtual() ) 75 | return new ImagePlusImgLoader<>( imp, VirtualStackImageLoader.createUnsignedByteInstance( imp ), minMaxOption, min, max, new UnsignedShortType(), new RealUnsignedShortConverterFactory() ); 76 | else 77 | return new ImagePlusImgLoader<>( imp, ImageStackImageLoader.createUnsignedByteInstance( imp ), minMaxOption, min, max, new UnsignedShortType(), new RealUnsignedShortConverterFactory() ); 78 | } 79 | 80 | public static ImagePlusImgLoader< FloatType > createFloatFromGray8( final ImagePlus imp, final MinMaxOption minMaxOption, final double min, final double max ) 81 | { 82 | if( imp.getType() != ImagePlus.GRAY8 ) 83 | throw new RuntimeException( "expected ImagePlus type GRAY8" ); 84 | if ( imp.getStack() != null && imp.getStack().isVirtual() ) 85 | return new ImagePlusImgLoader<>( imp, VirtualStackImageLoader.createUnsignedByteInstance( imp ), minMaxOption, min, max, new FloatType(), new RealFloatConverterFactory() ); 86 | else 87 | return new ImagePlusImgLoader<>( imp, ImageStackImageLoader.createUnsignedByteInstance( imp ), minMaxOption, min, max, new FloatType(), new RealFloatConverterFactory() ); 88 | } 89 | 90 | public static ImagePlusImgLoader< UnsignedShortType > createGray16( final ImagePlus imp, final MinMaxOption minMaxOption, final double min, final double max ) 91 | { 92 | if( imp.getType() != ImagePlus.GRAY16 ) 93 | throw new RuntimeException( "expected ImagePlus type GRAY16" ); 94 | if ( imp.getStack() != null && imp.getStack().isVirtual() ) 95 | return new ImagePlusImgLoader<>( imp, VirtualStackImageLoader.createUnsignedShortInstance( imp ), minMaxOption, min, max, new UnsignedShortType(), new RealUnsignedShortConverterFactory() ); 96 | else 97 | return new ImagePlusImgLoader<>( imp, ImageStackImageLoader.createUnsignedShortInstance( imp ), minMaxOption, min, max, new UnsignedShortType(), new RealUnsignedShortConverterFactory() ); 98 | } 99 | 100 | public static ImagePlusImgLoader< UnsignedShortType > createGray32( final ImagePlus imp, final MinMaxOption minMaxOption, final double min, final double max ) 101 | { 102 | if( imp.getType() != ImagePlus.GRAY32 ) 103 | throw new RuntimeException( "expected ImagePlus type GRAY32" ); 104 | if ( imp.getStack() != null && imp.getStack().isVirtual() ) 105 | return new ImagePlusImgLoader<>( imp, VirtualStackImageLoader.createFloatInstance( imp ), minMaxOption, min, max, new UnsignedShortType(), new RealUnsignedShortConverterFactory() ); 106 | else 107 | return new ImagePlusImgLoader<>( imp, ImageStackImageLoader.createFloatInstance( imp ), minMaxOption, min, max, new UnsignedShortType(), new RealUnsignedShortConverterFactory() ); 108 | } 109 | 110 | protected final ImagePlus imp; 111 | 112 | protected final BasicImgLoader loader; 113 | 114 | protected VolatileGlobalCellCache loadercache; 115 | 116 | protected final ArrayList< SetupImgLoader< ? > > setupImgLoaders; 117 | 118 | protected double impMin; 119 | 120 | protected double impMax; 121 | 122 | protected final T type; 123 | 124 | protected final ConverterFactory< T > converterFactory; 125 | 126 | public interface ConverterFactory< T > 127 | { 128 | public < S extends RealType< S > & NativeType< S > > Converter< S, T > create( double min, double max ); 129 | } 130 | 131 | static class RealUnsignedShortConverterFactory implements ConverterFactory< UnsignedShortType > 132 | { 133 | @Override 134 | public < S extends RealType< S > & NativeType< S > > Converter< S, UnsignedShortType > create( final double min, final double max ) 135 | { 136 | return new RealUnsignedShortConverter<>( min, max ); 137 | } 138 | } 139 | 140 | static class RealFloatConverterFactory implements ConverterFactory< FloatType > 141 | { 142 | @Override 143 | public < S extends RealType< S > & NativeType< S > > Converter< S, FloatType > create( final double min, final double max ) 144 | { 145 | return new RealFloatConverter<>(); 146 | } 147 | } 148 | 149 | protected < S extends RealType< S > & NativeType< S > > ImagePlusImgLoader( final ImagePlus imp, 150 | final TypedBasicImgLoader< S > loader, 151 | final MinMaxOption minMaxOption, 152 | final double min, 153 | final double max, 154 | final T type, 155 | final ConverterFactory< T > converterFactory ) 156 | { 157 | this.imp = imp; 158 | this.loader = loader; 159 | this.type = type; 160 | this.converterFactory = converterFactory; 161 | 162 | final int numSetups = imp.getNChannels(); 163 | setupImgLoaders = new ArrayList<>(); 164 | for ( int setupId = 0; setupId < numSetups; ++setupId ) 165 | setupImgLoaders.add( new SetupImgLoader<>( loader.getSetupImgLoader( setupId ) ) ); 166 | 167 | if ( loader instanceof VirtualStackImageLoader ) 168 | this.loadercache = ( ( VirtualStackImageLoader< ?, ?, ? > ) loader ).getCacheControl(); 169 | else 170 | this.loadercache = null; 171 | 172 | if ( minMaxOption == MinMaxOption.COMPUTE ) 173 | { 174 | impMin = Double.POSITIVE_INFINITY; 175 | impMax = Double.NEGATIVE_INFINITY; 176 | final S minT = loader.getSetupImgLoader( 0 ).getImageType().createVariable(); 177 | final S maxT = minT.createVariable(); 178 | final int numTimepoints = imp.getNFrames(); 179 | for ( int t = 0; t < numTimepoints; t++ ) 180 | for ( int s = 0; s < numSetups; ++s ) 181 | { 182 | ComputeMinMax.computeMinMax( loader.getSetupImgLoader( s ).getImage( t ), minT, maxT ); 183 | impMin = Math.min( minT.getRealDouble(), impMin ); 184 | impMax = Math.max( maxT.getRealDouble(), impMax ); 185 | if ( loadercache != null ) 186 | loadercache.clearCache(); 187 | } 188 | System.out.println( "COMPUTE" ); 189 | System.out.println( impMin + " " + impMax ); 190 | } 191 | else if ( minMaxOption == MinMaxOption.TAKE_FROM_IMAGEPROCESSOR ) 192 | { 193 | impMin = imp.getDisplayRangeMin(); 194 | impMax = imp.getDisplayRangeMax(); 195 | System.out.println( "TAKE_FROM_IMAGEPROCESSOR" ); 196 | System.out.println( impMin + " " + impMax ); 197 | } 198 | else 199 | { 200 | impMin = min; 201 | impMax = max; 202 | System.out.println( "SET" ); 203 | System.out.println( impMin + " " + impMax ); 204 | } 205 | } 206 | 207 | public void clearCache() 208 | { 209 | if ( loadercache != null ) 210 | { 211 | loadercache.clearCache(); 212 | System.runFinalization(); 213 | System.gc(); 214 | } 215 | } 216 | 217 | public class SetupImgLoader< S extends RealType< S > & NativeType< S > > implements BasicSetupImgLoader< T > 218 | { 219 | final BasicSetupImgLoader< S > loader; 220 | 221 | protected SetupImgLoader( final BasicSetupImgLoader< S > loader ) 222 | { 223 | this.loader = loader; 224 | } 225 | 226 | @Override 227 | public RandomAccessibleInterval< T > getImage( final int timepointId, final ImgLoaderHint... hints ) 228 | { 229 | if ( loadercache != null ) 230 | loadercache.clearCache(); 231 | final RandomAccessibleInterval< S > img = loader.getImage( timepointId ); 232 | return Converters.convert( img, converterFactory.< S >create( impMin, impMax ), type.createVariable() ); 233 | } 234 | 235 | @Override 236 | public T getImageType() 237 | { 238 | return type; 239 | } 240 | } 241 | 242 | @Override 243 | public SetupImgLoader< ? > getSetupImgLoader( final int setupId ) 244 | { 245 | return setupImgLoaders.get( setupId ); 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /src/main/java/bdv/img/virtualstack/VirtualStackImageLoader.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.img.virtualstack; 23 | 24 | import java.util.HashMap; 25 | import java.util.function.Function; 26 | 27 | import net.imglib2.RandomAccessibleInterval; 28 | import net.imglib2.Volatile; 29 | import net.imglib2.cache.img.CachedCellImg; 30 | import net.imglib2.cache.volatiles.CacheHints; 31 | import net.imglib2.cache.volatiles.LoadingStrategy; 32 | import net.imglib2.img.basictypeaccess.DataAccess; 33 | import net.imglib2.img.basictypeaccess.volatiles.VolatileAccess; 34 | import net.imglib2.img.basictypeaccess.volatiles.array.VolatileByteArray; 35 | import net.imglib2.img.basictypeaccess.volatiles.array.VolatileFloatArray; 36 | import net.imglib2.img.basictypeaccess.volatiles.array.VolatileIntArray; 37 | import net.imglib2.img.basictypeaccess.volatiles.array.VolatileShortArray; 38 | import net.imglib2.img.cell.AbstractCellImg; 39 | import net.imglib2.img.cell.CellGrid; 40 | import net.imglib2.realtransform.AffineTransform3D; 41 | import net.imglib2.type.NativeType; 42 | import net.imglib2.type.PrimitiveType; 43 | import net.imglib2.type.numeric.ARGBType; 44 | import net.imglib2.type.numeric.integer.UnsignedByteType; 45 | import net.imglib2.type.numeric.integer.UnsignedShortType; 46 | import net.imglib2.type.numeric.real.FloatType; 47 | import net.imglib2.type.volatiles.VolatileARGBType; 48 | import net.imglib2.type.volatiles.VolatileFloatType; 49 | import net.imglib2.type.volatiles.VolatileUnsignedByteType; 50 | import net.imglib2.type.volatiles.VolatileUnsignedShortType; 51 | 52 | import bdv.AbstractViewerSetupImgLoader; 53 | import bdv.ViewerImgLoader; 54 | import bdv.img.cache.CacheArrayLoader; 55 | import bdv.img.cache.VolatileGlobalCellCache; 56 | import ij.ImagePlus; 57 | import mpicbg.spim.data.generic.sequence.BasicSetupImgLoader; 58 | import mpicbg.spim.data.generic.sequence.ImgLoaderHint; 59 | import mpicbg.spim.data.generic.sequence.TypedBasicImgLoader; 60 | 61 | /** 62 | * ImageLoader backed by a ImagePlus. The ImagePlus may be virtual and in 63 | * contrast to the imglib2 wrappers, we do not try to load all slices into 64 | * memory. Instead slices are stored in {@link VolatileGlobalCellCache}. 65 | * 66 | * Use {@link #createFloatInstance(ImagePlus)}, 67 | * {@link #createUnsignedByteInstance(ImagePlus)} or 68 | * {@link #createUnsignedShortInstance(ImagePlus)} depending on the ImagePlus 69 | * pixel type. 70 | * 71 | * When loading images ({@link #getSetupImgLoader(int)}, 72 | * {@link BasicSetupImgLoader#getImage(int, ImgLoaderHint...)}) the provided 73 | * setup id is used as the channel index of the {@link ImagePlus}, the provided 74 | * timepoint id is used as the frame index of the {@link ImagePlus}. 75 | * 76 | * @param 77 | * (non-volatile) pixel type 78 | * @param 79 | * volatile pixel type 80 | * @param 81 | * volatile array access type 82 | * 83 | * @author Tobias Pietzsch <tobias.pietzsch@gmail.com> 84 | */ 85 | public class VirtualStackImageLoader< T extends NativeType< T >, V extends Volatile< T > & NativeType< V >, A extends DataAccess & VolatileAccess > 86 | implements ViewerImgLoader, TypedBasicImgLoader< T > 87 | { 88 | public static VirtualStackImageLoader< FloatType, VolatileFloatType, VolatileFloatArray > createFloatInstance( final ImagePlus imp ) 89 | { 90 | return createFloatInstance( imp, 0 ); 91 | } 92 | 93 | public static VirtualStackImageLoader< FloatType, VolatileFloatType, VolatileFloatArray > createFloatInstance( final ImagePlus imp, final int offset ) 94 | { 95 | return new VirtualStackImageLoader<>( imp,array -> new VolatileFloatArray( ( float[] ) array, true ), new FloatType(), new VolatileFloatType(), offset ); 96 | } 97 | 98 | public static VirtualStackImageLoader< UnsignedShortType, VolatileUnsignedShortType, VolatileShortArray > createUnsignedShortInstance( final ImagePlus imp ) 99 | { 100 | return createUnsignedShortInstance( imp, 0 ); 101 | } 102 | 103 | public static VirtualStackImageLoader< UnsignedShortType, VolatileUnsignedShortType, VolatileShortArray > createUnsignedShortInstance( final ImagePlus imp, final int offset ) 104 | { 105 | return new VirtualStackImageLoader<>( imp, array -> new VolatileShortArray( ( short[] ) array, true ), new UnsignedShortType(), new VolatileUnsignedShortType(), offset ); 106 | } 107 | 108 | public static VirtualStackImageLoader< UnsignedByteType, VolatileUnsignedByteType, VolatileByteArray > createUnsignedByteInstance( final ImagePlus imp ) 109 | { 110 | return createUnsignedByteInstance( imp, 0 ); 111 | } 112 | 113 | public static VirtualStackImageLoader< UnsignedByteType, VolatileUnsignedByteType, VolatileByteArray > createUnsignedByteInstance( final ImagePlus imp, final int offset ) 114 | { 115 | return new VirtualStackImageLoader<>( imp, array -> new VolatileByteArray( ( byte[] ) array, true ), new UnsignedByteType(), new VolatileUnsignedByteType(), offset ); 116 | } 117 | 118 | public static VirtualStackImageLoader< ARGBType, VolatileARGBType, VolatileIntArray > createARGBInstance( final ImagePlus imp ) 119 | { 120 | return createARGBInstance( imp, 0 ); 121 | } 122 | 123 | public static VirtualStackImageLoader< ARGBType, VolatileARGBType, VolatileIntArray > createARGBInstance( final ImagePlus imp, final int offset ) 124 | { 125 | return new VirtualStackImageLoader<>( imp, array -> new VolatileIntArray( ( int[] ) array, true ), new ARGBType(), new VolatileARGBType(), offset ); 126 | } 127 | 128 | private static double[][] mipmapResolutions = new double[][] { { 1, 1, 1 } }; 129 | 130 | private static AffineTransform3D[] mipmapTransforms = new AffineTransform3D[] { new AffineTransform3D() }; 131 | 132 | private final CacheArrayLoader< A > loader; 133 | 134 | private final VolatileGlobalCellCache cache; 135 | 136 | private final long[] dimensions; 137 | 138 | private final int[] cellDimensions; 139 | 140 | private final HashMap< Integer, SetupImgLoader > setupImgLoaders; 141 | 142 | private static int getByteCount( final PrimitiveType primitiveType ) 143 | { 144 | // TODO: PrimitiveType.getByteCount() should be public, then we wouldn't have to do this... 145 | switch ( primitiveType ) 146 | { 147 | case BYTE: 148 | return 1; 149 | case SHORT: 150 | return 2; 151 | case INT: 152 | case FLOAT: 153 | default: 154 | return 4; 155 | } 156 | } 157 | 158 | protected VirtualStackImageLoader( final ImagePlus imp, final Function< Object, A > wrapPixels, final T type, final V volatileType, final int setupOffset ) 159 | { 160 | this.loader = new VirtualStackArrayLoader<>( imp, wrapPixels, getByteCount( type.getNativeTypeFactory().getPrimitiveType() ) ); 161 | dimensions = new long[] { imp.getWidth(), imp.getHeight(), imp.getNSlices() }; 162 | cellDimensions = new int[] { imp.getWidth(), imp.getHeight(), 1 }; 163 | final int numSetups = imp.getNChannels(); 164 | cache = new VolatileGlobalCellCache( 1, 1 ); 165 | setupImgLoaders = new HashMap<>(); 166 | for ( int setupId = 0; setupId < numSetups; ++setupId ) 167 | setupImgLoaders.put( setupOffset + setupId, new SetupImgLoader( setupId, type, volatileType ) ); 168 | } 169 | 170 | protected VirtualStackImageLoader( final ImagePlus imp, final Function< Object, A > wrapPixels, final T type, final V volatileType ) 171 | { 172 | this( imp, wrapPixels, type, volatileType, 0 ); 173 | } 174 | 175 | @Override 176 | public VolatileGlobalCellCache getCacheControl() 177 | { 178 | return cache; 179 | } 180 | 181 | @Override 182 | public SetupImgLoader getSetupImgLoader( final int setupId ) 183 | { 184 | return setupImgLoaders.get( setupId ); 185 | } 186 | 187 | static class VirtualStackArrayLoader< A extends DataAccess > implements CacheArrayLoader< A > 188 | { 189 | private final ImagePlus imp; 190 | 191 | private final Function< Object, A > wrapPixels; 192 | 193 | private final int bytesPerElement; 194 | 195 | public VirtualStackArrayLoader( final ImagePlus imp, final Function< Object, A > wrapPixels, final int bytesPerElement ) 196 | { 197 | this.imp = imp; 198 | this.wrapPixels = wrapPixels; 199 | this.bytesPerElement = bytesPerElement; 200 | } 201 | 202 | @Override 203 | public A loadArray( final int timepoint, final int setup, final int level, final int[] dimensions, final long[] min ) throws InterruptedException 204 | { 205 | final int channel = setup + 1; 206 | final int slice = ( int ) min[ 2 ] + 1; 207 | final int frame = timepoint + 1; 208 | return wrapPixels.apply( imp.getStack().getProcessor( imp.getStackIndex( channel, slice, frame ) ).getPixels() ); 209 | } 210 | 211 | @Override 212 | public int getBytesPerElement() 213 | { 214 | return bytesPerElement; 215 | } 216 | } 217 | 218 | public class SetupImgLoader extends AbstractViewerSetupImgLoader< T, V > 219 | { 220 | private final int setupId; 221 | 222 | protected SetupImgLoader( final int setupId, final T type, final V volatileType ) 223 | { 224 | super( type, volatileType ); 225 | this.setupId = setupId; 226 | } 227 | 228 | @Override 229 | public RandomAccessibleInterval< V > getVolatileImage( final int timepointId, final int level, final ImgLoaderHint... hints ) 230 | { 231 | return prepareCachedImage( timepointId, level, LoadingStrategy.BUDGETED, volatileType ); 232 | } 233 | 234 | @Override 235 | public RandomAccessibleInterval< T > getImage( final int timepointId, final int level, final ImgLoaderHint... hints ) 236 | { 237 | return prepareCachedImage( timepointId, level, LoadingStrategy.BLOCKING, type ); 238 | } 239 | 240 | /** 241 | * Create a {@link CachedCellImg} backed by the cache. 242 | */ 243 | protected < T extends NativeType< T > > AbstractCellImg< T, A, ?, ? > prepareCachedImage( final int timepointId, final int level, final LoadingStrategy loadingStrategy, final T type ) 244 | { 245 | final int priority = 0; 246 | final CacheHints cacheHints = new CacheHints( loadingStrategy, priority, false ); 247 | final CellGrid grid = new CellGrid( dimensions, cellDimensions ); 248 | return cache.createImg( grid, timepointId, setupId, level, cacheHints, loader, type ); 249 | } 250 | 251 | @Override 252 | public double[][] getMipmapResolutions() 253 | { 254 | return mipmapResolutions; 255 | } 256 | 257 | @Override 258 | public AffineTransform3D[] getMipmapTransforms() 259 | { 260 | return mipmapTransforms; 261 | } 262 | 263 | @Override 264 | public int numMipmapLevels() 265 | { 266 | return 1; 267 | } 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /src/main/java/bdv/ij/export/Scripting.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.ij.export; 23 | 24 | import java.io.File; 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | import java.util.Map; 28 | 29 | import bdv.export.ExportMipmapInfo; 30 | import bdv.export.WriteSequenceToHdf5; 31 | import bdv.ij.util.PluginHelper; 32 | import bdv.img.hdf5.Hdf5ImageLoader; 33 | import bdv.img.hdf5.Partition; 34 | import bdv.spimdata.SequenceDescriptionMinimal; 35 | import bdv.spimdata.SpimDataMinimal; 36 | import bdv.spimdata.XmlIoSpimDataMinimal; 37 | import mpicbg.spim.data.SpimDataException; 38 | import mpicbg.spim.io.ConfigurationParserException; 39 | import net.imglib2.realtransform.AffineTransform3D; 40 | 41 | @Deprecated 42 | public class Scripting 43 | { 44 | /** 45 | * Create a {@link SpimRegistrationSequence} based on a Huisken experiment 46 | * xml file. 47 | * 48 | * @param huiskenExperimentXmlFile 49 | * path of the experiment xml file. 50 | * @param channels 51 | * String specifying the channels to include in the sequence. In 52 | * the format used by the SPIMRegistration plugin. 53 | * For single-channel sequence, set to null. 54 | * @param angles 55 | * String specifying the angles to include in the sequence. In 56 | * the format used by the SPIMRegistration plugin. 57 | * @param timepoints 58 | * String specifying the timepoints to include in the sequence. 59 | * In the format used by the SPIMRegistration plugin. 60 | * @param referenceTimePoint 61 | * the reference timepoint (registrations relative to this 62 | * timepoint are laoded), or -1, indicating to use 63 | * individual (non-timelapse) view registrations. 64 | * @return an initialized {@link SpimRegistrationSequence} sequence. 65 | * @throws ConfigurationParserException 66 | */ 67 | @Deprecated 68 | public static SpimRegistrationSequence createSpimRegistrationSequence( 69 | final String huiskenExperimentXmlFile, 70 | final String channels, 71 | final String angles, 72 | final String timepoints, 73 | final int referenceTimePoint ) throws ConfigurationParserException 74 | { 75 | return new SpimRegistrationSequence( huiskenExperimentXmlFile, channels, angles, timepoints, referenceTimePoint ); 76 | } 77 | 78 | /** 79 | * Create a {@link SpimRegistrationSequence} based on a directory of image 80 | * (.tif, .czi, etc...). 81 | * 82 | * @param inputDirectory 83 | * path of image directory. 84 | * @param inputFilePattern 85 | * pattern of the image file names In the format used by the 86 | * SPIMRegistration plugin. 87 | * @param channels 88 | * String specifying the channels to include in the sequence. In 89 | * the format used by the SPIMRegistration plugin. 90 | * For single-channel sequence, set to null. 91 | * @param angles 92 | * String specifying the angles to include in the sequence. In 93 | * the format used by the SPIMRegistration plugin. 94 | * @param timepoints 95 | * String specifying the timepoints to include in the sequence. 96 | * In the format used by the SPIMRegistration plugin. 97 | * @param referenceTimePoint 98 | * the reference timepoint (registrations relative to this 99 | * timepoint are laoded), or -1, indicating to use 100 | * individual (non-timelapse) view registrations. 101 | * @param overrideImageZStretching 102 | * whether the z-stretching of the images should be overridden. 103 | * If false, the z-stretching is read from the image 104 | * metadata. 105 | * @param zStretching 106 | * z-stretching to use (if 107 | * overrideImageZStretching == true). 108 | * @return an initialized {@link SpimRegistrationSequence} sequence. 109 | * @throws ConfigurationParserException 110 | */ 111 | @Deprecated 112 | public static SpimRegistrationSequence createSpimRegistrationSequence( 113 | final String inputDirectory, 114 | final String inputFilePattern, 115 | final String channels, 116 | final String angles, 117 | final String timepoints, 118 | final int referenceTimePoint, 119 | final boolean overrideImageZStretching, 120 | final double zStretching ) throws ConfigurationParserException 121 | { 122 | return new SpimRegistrationSequence( inputDirectory, inputFilePattern, channels, angles, timepoints, referenceTimePoint, overrideImageZStretching, zStretching ); 123 | } 124 | 125 | /** 126 | * TODO 127 | * 128 | * @param spimseq 129 | * @param scale 130 | * @param cropOffsetX 131 | * @param cropOffsetY 132 | * @param cropOffsetZ 133 | * @return 134 | */ 135 | @Deprecated 136 | public static Map< Integer, AffineTransform3D > getFusionTransforms( 137 | final SpimRegistrationSequence spimseq, 138 | final int scale, 139 | final int cropOffsetX, 140 | final int cropOffsetY, 141 | final int cropOffsetZ ) 142 | { 143 | return spimseq.getFusionTransforms( cropOffsetX, cropOffsetY, cropOffsetZ, scale ); 144 | } 145 | 146 | /** 147 | * TODO 148 | * 149 | * @param spimseq 150 | * @param filepath 151 | * @param filepattern 152 | * @param numSlices 153 | * @param sliceValueMin 154 | * @param sliceValueMax 155 | * @param fusionTransforms 156 | * @return 157 | */ 158 | @Deprecated 159 | public static FusionResult createFusionResult( 160 | final SpimRegistrationSequence spimseq, 161 | final String filepath, 162 | final String filepattern, 163 | final int numSlices, 164 | final double sliceValueMin, 165 | final double sliceValueMax, 166 | final Map< Integer, AffineTransform3D > fusionTransforms ) 167 | { 168 | return FusionResult.create( spimseq, filepath, filepattern, numSlices, sliceValueMin, sliceValueMax, fusionTransforms ); 169 | } 170 | 171 | /** 172 | * Split the sequence represented in aggregator into 173 | * partitions. 174 | * 175 | * @param aggregator 176 | * represents the full dataset. 177 | * @param timepointsPerPartition 178 | * how many timepoints should each partition contain (if this is 179 | * ≤0, put do not split timepoints across partitions). 180 | * @param setupsPerPartition 181 | * how many setups should each partition contain (if this is 182 | * ≤0, put do not split setups across partitions). 183 | * @param xmlFilename 184 | * path to the xml file to which the sequence will be saved. This 185 | * is used to generate paths for the partitions. 186 | * @return list of partitions. 187 | */ 188 | @Deprecated 189 | public static ArrayList< Partition > split( 190 | final SetupAggregator aggregator, 191 | final int timepointsPerPartition, 192 | final int setupsPerPartition, 193 | final String xmlFilename ) 194 | { 195 | final String basename = xmlFilename.endsWith( ".xml" ) ? xmlFilename.substring( 0, xmlFilename.length() - 4 ) : xmlFilename; 196 | return Partition.split( aggregator.timepoints.getTimePointsOrdered(), aggregator.setups, timepointsPerPartition, setupsPerPartition, basename ); 197 | } 198 | 199 | public static class PartitionedSequenceWriter 200 | { 201 | protected final SpimDataMinimal spimData; 202 | 203 | protected final Map< Integer, ExportMipmapInfo > perSetupMipmapInfo; 204 | 205 | protected final boolean deflate; 206 | 207 | protected final ArrayList< Partition > partitions; 208 | 209 | protected final File seqFile; 210 | 211 | protected final File hdf5File; 212 | 213 | public PartitionedSequenceWriter( final SetupAggregator aggregator, final String xmlFilename, final boolean deflate, final List< Partition > partitions ) 214 | { 215 | seqFile = new File( xmlFilename ); 216 | 217 | final String hdf5Filename = ( xmlFilename.endsWith( ".xml" ) ? xmlFilename.substring( 0, xmlFilename.length() - 4 ) : xmlFilename ) + ".h5"; 218 | hdf5File = new File( hdf5Filename ); 219 | 220 | spimData = aggregator.createSpimData( seqFile ); 221 | perSetupMipmapInfo = aggregator.getPerSetupMipmapInfo(); 222 | this.deflate = deflate; 223 | this.partitions = new ArrayList<>( partitions ); 224 | } 225 | 226 | public int numPartitions() 227 | { 228 | return partitions.size(); 229 | } 230 | 231 | public void writePartition( final int index ) 232 | { 233 | if ( index >= 0 && index < partitions.size() ) 234 | { 235 | final int numCellCreatorThreads = Math.max( 1, PluginHelper.numThreads() - 1 ); 236 | WriteSequenceToHdf5.writeHdf5PartitionFile( spimData.getSequenceDescription(), perSetupMipmapInfo, deflate, partitions.get( index ), null, null, numCellCreatorThreads, null ); 237 | } 238 | } 239 | 240 | public void writeXmlAndLinks() throws SpimDataException 241 | { 242 | final SequenceDescriptionMinimal seq = spimData.getSequenceDescription(); 243 | WriteSequenceToHdf5.writeHdf5PartitionLinkFile( seq, perSetupMipmapInfo, partitions, hdf5File ); 244 | final Hdf5ImageLoader loader = new Hdf5ImageLoader( hdf5File, partitions, null, false ); 245 | 246 | new XmlIoSpimDataMinimal().save( new SpimDataMinimal( spimData, loader ), seqFile.getAbsolutePath() ); 247 | } 248 | } 249 | 250 | public static void example_main( final String[] args ) throws Exception 251 | { 252 | final SetupAggregator aggregator = new SetupAggregator(); 253 | 254 | final String huiskenExperimentXmlFile = "/Users/pietzsch/workspace/data/fast fly/111010_weber/e012.xml"; 255 | final String angles = "0,120,240"; 256 | final String timepoints = "1-5"; 257 | final int referenceTimePoint = 50; 258 | 259 | final SpimRegistrationSequence spimseq = createSpimRegistrationSequence( huiskenExperimentXmlFile, null, angles, timepoints, referenceTimePoint ); 260 | 261 | final String filepath = "/Users/pietzsch/workspace/data/fast fly/111010_weber/e012/output/"; 262 | final String filepattern = "img_tl%1$d_ch%2$d_z%3$03d.tif"; 263 | final int numSlices = 387; 264 | final double sliceValueMin = 0; 265 | final double sliceValueMax = 8000; 266 | 267 | final int cropOffsetX = 58; 268 | final int cropOffsetY = 74; 269 | final int cropOffsetZ = 6; 270 | final int scale = 1; 271 | 272 | final Map< Integer, AffineTransform3D > fusionTransforms = getFusionTransforms( spimseq, scale, cropOffsetX, cropOffsetY, cropOffsetZ ); 273 | final FusionResult fusion = createFusionResult( spimseq, filepath, filepattern, numSlices, sliceValueMin, sliceValueMax, fusionTransforms ); 274 | 275 | final int[][] spimresolutions = { { 1, 1, 1 }, { 2, 2, 1 }, { 4, 4, 2 } }; 276 | final int[][] spimsubdivisions = { { 32, 32, 4 }, { 16, 16, 8 }, { 8, 8, 8 } }; 277 | 278 | final String fusionresolutions = "{ 1, 1, 1 }, { 2, 2, 2 }, { 4, 4, 4 }"; 279 | final String fusionsubdivisions = "{ 16, 16, 16 }, { 16, 16, 16 }, { 8, 8, 8 }"; 280 | 281 | aggregator.addSetups( spimseq, spimresolutions, spimsubdivisions ); 282 | aggregator.addSetups( fusion, fusionresolutions, fusionsubdivisions ); 283 | 284 | final String xmlFilename = "/Users/pietzsch/Desktop/everything.xml"; 285 | 286 | // splitting ... 287 | final int timepointsPerPartition = 3; 288 | final int setupsPerPartition = 2; 289 | final ArrayList< Partition > partitions = split( aggregator, timepointsPerPartition, setupsPerPartition, xmlFilename ); 290 | 291 | final PartitionedSequenceWriter writer = new PartitionedSequenceWriter( aggregator,xmlFilename, true, partitions ); 292 | System.out.println( writer.numPartitions() ); 293 | for ( int i = 0; i < writer.numPartitions(); ++i ) 294 | writer.writePartition( i ); 295 | writer.writeXmlAndLinks(); 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /src/main/java/bdv/ij/OpenImagePlusPlugIn.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.ij; 23 | 24 | import bdv.viewer.ConverterSetups; 25 | import bdv.viewer.SynchronizedViewerState; 26 | import bdv.viewer.ViewerState; 27 | import java.io.File; 28 | import java.util.ArrayList; 29 | import java.util.HashMap; 30 | 31 | import java.util.List; 32 | import net.imglib2.FinalDimensions; 33 | import net.imglib2.realtransform.AffineTransform3D; 34 | import net.imglib2.type.numeric.ARGBType; 35 | 36 | import org.scijava.command.Command; 37 | import org.scijava.plugin.Plugin; 38 | 39 | import bdv.BigDataViewer; 40 | import bdv.ViewerImgLoader; 41 | import bdv.cache.CacheControl; 42 | import bdv.ij.util.ProgressWriterIJ; 43 | import bdv.img.imagestack.ImageStackImageLoader; 44 | import bdv.img.virtualstack.VirtualStackImageLoader; 45 | import bdv.spimdata.SequenceDescriptionMinimal; 46 | import bdv.spimdata.SpimDataMinimal; 47 | import bdv.spimdata.WrapBasicImgLoader; 48 | import bdv.tools.brightness.ConverterSetup; 49 | import bdv.viewer.DisplayMode; 50 | import bdv.viewer.SourceAndConverter; 51 | import bdv.viewer.ViewerOptions; 52 | import ij.CompositeImage; 53 | import ij.IJ; 54 | import ij.ImageJ; 55 | import ij.ImagePlus; 56 | import ij.WindowManager; 57 | import ij.gui.GenericDialog; 58 | import ij.process.LUT; 59 | import mpicbg.spim.data.generic.AbstractSpimData; 60 | import mpicbg.spim.data.generic.sequence.BasicImgLoader; 61 | import mpicbg.spim.data.generic.sequence.BasicViewSetup; 62 | import mpicbg.spim.data.registration.ViewRegistration; 63 | import mpicbg.spim.data.registration.ViewRegistrations; 64 | import mpicbg.spim.data.sequence.Channel; 65 | import mpicbg.spim.data.sequence.FinalVoxelDimensions; 66 | import mpicbg.spim.data.sequence.TimePoint; 67 | import mpicbg.spim.data.sequence.TimePoints; 68 | 69 | /** 70 | * ImageJ plugin to show the current image in BigDataViewer. 71 | * 72 | * @author Tobias Pietzsch <tobias.pietzsch@gmail.com> 73 | */ 74 | @Plugin(type = Command.class, 75 | menuPath = "Plugins>BigDataViewer>Open Current Image") 76 | public class OpenImagePlusPlugIn implements Command 77 | { 78 | public static void main( final String[] args ) 79 | { 80 | System.setProperty( "apple.laf.useScreenMenuBar", "true" ); 81 | new ImageJ(); 82 | IJ.run( "Confocal Series (2.2MB)" ); 83 | // IJ.run( "Fly Brain (1MB)" ); 84 | new OpenImagePlusPlugIn().run(); 85 | } 86 | 87 | @Override 88 | public void run() 89 | { 90 | if ( ij.Prefs.setIJMenuBar ) 91 | System.setProperty( "apple.laf.useScreenMenuBar", "true" ); 92 | 93 | int nImages = WindowManager.getImageCount(); 94 | // get the current image 95 | final ImagePlus curr = WindowManager.getCurrentImage(); 96 | // make sure there is one 97 | if ( curr == null ) 98 | { 99 | IJ.showMessage( "Please open an image first." ); 100 | return; 101 | } 102 | 103 | ArrayList< ImagePlus > inputImgList = new ArrayList<>(); 104 | if ( nImages > 1 ) 105 | { 106 | final int[] idList = WindowManager.getIDList(); 107 | final String[] nameList = new String[ nImages ]; 108 | GenericDialog gd = new GenericDialog( "Images to open" ); 109 | for ( int i = 0; i < nImages; i++ ) 110 | { 111 | ImagePlus imp = WindowManager.getImage( idList[ i ] ); 112 | nameList[ i ] = imp.getTitle(); 113 | gd.addCheckbox( nameList[ i ], imp == curr ); 114 | } 115 | 116 | gd.showDialog(); 117 | if ( gd.wasCanceled() ) 118 | return; 119 | 120 | for ( int i = 0; i < nImages; i++ ) 121 | { 122 | if ( !gd.getNextBoolean() ) 123 | continue; 124 | ImagePlus imp = WindowManager.getImage( idList[ i ] ); 125 | inputImgList.add( imp ); 126 | } 127 | } 128 | else 129 | { 130 | inputImgList.add( curr ); 131 | } 132 | 133 | final ArrayList< ConverterSetup > converterSetups = new ArrayList<>(); 134 | final ArrayList< SourceAndConverter< ? > > sources = new ArrayList<>(); 135 | 136 | final CacheControl.CacheControls cache = new CacheControl.CacheControls(); 137 | int nTimepoints = 1; 138 | int setup_id_offset = 0; 139 | final ArrayList< ImagePlus > imgList = new ArrayList<>(); 140 | boolean is2D = true; 141 | for ( ImagePlus imp : inputImgList ) 142 | { 143 | if ( imp.getNSlices() > 1 ) 144 | is2D = false; 145 | final AbstractSpimData< ? > spimData = load( imp, converterSetups, sources, setup_id_offset ); 146 | if ( spimData != null ) 147 | { 148 | imgList.add( imp ); 149 | cache.addCacheControl( ( ( ViewerImgLoader ) spimData.getSequenceDescription().getImgLoader() ).getCacheControl() ); 150 | setup_id_offset += imp.getNChannels(); 151 | nTimepoints = Math.max( nTimepoints, imp.getNFrames() ); 152 | } 153 | } 154 | 155 | if ( !imgList.isEmpty() ) 156 | { 157 | final BigDataViewer bdv = BigDataViewer.open( converterSetups, sources, 158 | nTimepoints, cache, 159 | "BigDataViewer", new ProgressWriterIJ(), 160 | ViewerOptions.options().is2D( is2D ) ); 161 | 162 | final SynchronizedViewerState state = bdv.getViewer().state(); 163 | synchronized ( state ) 164 | { 165 | int channelOffset = 0; 166 | int numActiveChannels = 0; 167 | for ( ImagePlus imp : imgList ) 168 | { 169 | numActiveChannels += transferChannelVisibility( channelOffset, imp, state ); 170 | transferChannelSettings( channelOffset, imp, state, bdv.getConverterSetups() ); 171 | channelOffset += imp.getNChannels(); 172 | } 173 | state.setDisplayMode( numActiveChannels > 1 ? DisplayMode.FUSED : DisplayMode.SINGLE ); 174 | } 175 | } 176 | } 177 | 178 | protected AbstractSpimData< ? > load( ImagePlus imp, ArrayList< ConverterSetup > converterSetups, ArrayList< SourceAndConverter< ? > > sources, 179 | int setup_id_offset ) 180 | { 181 | // check the image type 182 | switch ( imp.getType() ) 183 | { 184 | case ImagePlus.GRAY8: 185 | case ImagePlus.GRAY16: 186 | case ImagePlus.GRAY32: 187 | case ImagePlus.COLOR_RGB: 188 | break; 189 | default: 190 | IJ.showMessage( imp.getShortTitle() + ": Only 8, 16, 32-bit images and RGB images are supported currently!" ); 191 | return null; 192 | } 193 | 194 | // get calibration and image size 195 | final double pw = imp.getCalibration().pixelWidth; 196 | final double ph = imp.getCalibration().pixelHeight; 197 | final double pd = imp.getCalibration().pixelDepth; 198 | String punit = imp.getCalibration().getUnit(); 199 | if ( punit == null || punit.isEmpty() ) 200 | punit = "px"; 201 | final FinalVoxelDimensions voxelSize = new FinalVoxelDimensions( punit, pw, ph, pd ); 202 | final int w = imp.getWidth(); 203 | final int h = imp.getHeight(); 204 | final int d = imp.getNSlices(); 205 | final FinalDimensions size = new FinalDimensions( w, h, d ); 206 | 207 | // propose reasonable mipmap settings 208 | // final ExportMipmapInfo autoMipmapSettings = ProposeMipmaps.proposeMipmaps( new BasicViewSetup( 0, "", size, voxelSize ) ); 209 | 210 | // create ImgLoader wrapping the image 211 | final BasicImgLoader imgLoader; 212 | if ( imp.getStack().isVirtual() ) 213 | { 214 | switch ( imp.getType() ) 215 | { 216 | case ImagePlus.GRAY8: 217 | imgLoader = VirtualStackImageLoader.createUnsignedByteInstance( imp, setup_id_offset ); 218 | break; 219 | case ImagePlus.GRAY16: 220 | imgLoader = VirtualStackImageLoader.createUnsignedShortInstance( imp, setup_id_offset ); 221 | break; 222 | case ImagePlus.GRAY32: 223 | imgLoader = VirtualStackImageLoader.createFloatInstance( imp, setup_id_offset ); 224 | break; 225 | case ImagePlus.COLOR_RGB: 226 | default: 227 | imgLoader = VirtualStackImageLoader.createARGBInstance( imp, setup_id_offset ); 228 | break; 229 | } 230 | } 231 | else 232 | { 233 | switch ( imp.getType() ) 234 | { 235 | case ImagePlus.GRAY8: 236 | imgLoader = ImageStackImageLoader.createUnsignedByteInstance( imp, setup_id_offset ); 237 | break; 238 | case ImagePlus.GRAY16: 239 | imgLoader = ImageStackImageLoader.createUnsignedShortInstance( imp, setup_id_offset ); 240 | break; 241 | case ImagePlus.GRAY32: 242 | imgLoader = ImageStackImageLoader.createFloatInstance( imp, setup_id_offset ); 243 | break; 244 | case ImagePlus.COLOR_RGB: 245 | default: 246 | imgLoader = ImageStackImageLoader.createARGBInstance( imp, setup_id_offset ); 247 | break; 248 | } 249 | } 250 | 251 | final int numTimepoints = imp.getNFrames(); 252 | final int numSetups = imp.getNChannels(); 253 | 254 | // create setups from channels 255 | final HashMap< Integer, BasicViewSetup > setups = new HashMap<>( numSetups ); 256 | for ( int s = 0; s < numSetups; ++s ) 257 | { 258 | final BasicViewSetup setup = new BasicViewSetup( setup_id_offset + s, String.format( imp.getTitle().replace("%", "%%") + " channel %d", s + 1 ), size, voxelSize ); 259 | setup.setAttribute( new Channel( s + 1 ) ); 260 | setups.put( setup_id_offset + s, setup ); 261 | } 262 | 263 | // create timepoints 264 | final ArrayList< TimePoint > timepoints = new ArrayList<>( numTimepoints ); 265 | for ( int t = 0; t < numTimepoints; ++t ) 266 | timepoints.add( new TimePoint( t ) ); 267 | final SequenceDescriptionMinimal seq = new SequenceDescriptionMinimal( new TimePoints( timepoints ), setups, imgLoader, null ); 268 | 269 | // create ViewRegistrations from the images calibration 270 | final AffineTransform3D sourceTransform = new AffineTransform3D(); 271 | sourceTransform.set( pw, 0, 0, 0, 0, ph, 0, 0, 0, 0, pd, 0 ); 272 | final ArrayList< ViewRegistration > registrations = new ArrayList<>(); 273 | for ( int t = 0; t < numTimepoints; ++t ) 274 | for ( int s = 0; s < numSetups; ++s ) 275 | registrations.add( new ViewRegistration( t, setup_id_offset + s, sourceTransform ) ); 276 | 277 | final File basePath = new File( "." ); 278 | final AbstractSpimData< ? > spimData = new SpimDataMinimal( basePath, seq, new ViewRegistrations( registrations ) ); 279 | WrapBasicImgLoader.wrapImgLoaderIfNecessary( spimData ); 280 | BigDataViewer.initSetups( spimData, converterSetups, sources ); 281 | 282 | return spimData; 283 | } 284 | 285 | /** 286 | * @return number of setups that were set active. 287 | */ 288 | protected int transferChannelVisibility( int channelOffset, final ImagePlus imp, final ViewerState state ) 289 | { 290 | final int nChannels = imp.getNChannels(); 291 | final CompositeImage ci = imp.isComposite() ? ( CompositeImage ) imp : null; 292 | final List< SourceAndConverter< ? > > sources = state.getSources(); 293 | if ( ci != null && ci.getCompositeMode() == IJ.COMPOSITE ) 294 | { 295 | final boolean[] activeChannels = ci.getActiveChannels(); 296 | int numActiveChannels = 0; 297 | for ( int i = 0; i < Math.min( activeChannels.length, nChannels ); ++i ) 298 | { 299 | final SourceAndConverter< ? > source = sources.get( channelOffset + i ); 300 | state.setSourceActive( source, activeChannels[ i ] ); 301 | state.setCurrentSource( source ); 302 | numActiveChannels += activeChannels[ i ] ? 1 : 0; 303 | } 304 | return numActiveChannels; 305 | } 306 | else 307 | { 308 | final int activeChannel = imp.getChannel() - 1; 309 | for ( int i = 0; i < nChannels; ++i ) 310 | state.setSourceActive( sources.get( channelOffset + i ), i == activeChannel ); 311 | state.setCurrentSource( sources.get( channelOffset + activeChannel ) ); 312 | return 1; 313 | } 314 | } 315 | 316 | protected void transferChannelSettings( int channelOffset, final ImagePlus imp, final ViewerState state, final ConverterSetups converterSetups ) 317 | { 318 | final int nChannels = imp.getNChannels(); 319 | final CompositeImage ci = imp.isComposite() ? ( CompositeImage ) imp : null; 320 | final List< SourceAndConverter< ? > > sources = state.getSources(); 321 | if ( ci != null ) 322 | { 323 | final int mode = ci.getCompositeMode(); 324 | final boolean transferColor = mode == IJ.COMPOSITE || mode == IJ.COLOR; 325 | for ( int c = 0; c < nChannels; ++c ) 326 | { 327 | final LUT lut = ci.getChannelLut( c + 1 ); 328 | final ConverterSetup setup = converterSetups.getConverterSetup( sources.get( channelOffset + c ) ); 329 | if ( transferColor ) 330 | setup.setColor( new ARGBType( lut.getRGB( 255 ) ) ); 331 | setup.setDisplayRange( lut.min, lut.max ); 332 | } 333 | } 334 | else 335 | { 336 | final double displayRangeMin = imp.getDisplayRangeMin(); 337 | final double displayRangeMax = imp.getDisplayRangeMax(); 338 | for ( int i = 0; i < nChannels; ++i ) 339 | { 340 | final ConverterSetup setup = converterSetups.getConverterSetup( sources.get( channelOffset + i ) ); 341 | final LUT[] luts = imp.getLuts(); 342 | if ( luts.length != 0 ) 343 | setup.setColor( new ARGBType( luts[ 0 ].getRGB( 255 ) ) ); 344 | setup.setDisplayRange( displayRangeMin, displayRangeMax ); 345 | } 346 | } 347 | } 348 | } 349 | -------------------------------------------------------------------------------- /src/main/java/bdv/ij/export/tiles/CellVoyagerDataExporter.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.ij.export.tiles; 23 | 24 | import java.awt.Color; 25 | import java.io.File; 26 | import java.io.IOException; 27 | import java.util.ArrayList; 28 | import java.util.List; 29 | 30 | import org.jdom2.Document; 31 | import org.jdom2.Element; 32 | import org.jdom2.JDOMException; 33 | import org.jdom2.input.SAXBuilder; 34 | 35 | import bdv.export.ProgressWriter; 36 | import bdv.export.WriteSequenceToHdf5; 37 | import bdv.ij.util.PluginHelper; 38 | import bdv.img.hdf5.Hdf5ImageLoader; 39 | import bdv.spimdata.SequenceDescriptionMinimal; 40 | import bdv.spimdata.SpimDataMinimal; 41 | import bdv.spimdata.XmlIoSpimDataMinimal; 42 | import ij.IJ; 43 | import mpicbg.spim.data.generic.base.Entity; 44 | import mpicbg.spim.data.generic.sequence.BasicViewSetup; 45 | import mpicbg.spim.data.registration.ViewRegistration; 46 | import mpicbg.spim.data.registration.ViewRegistrations; 47 | import mpicbg.spim.data.sequence.Channel; 48 | import mpicbg.spim.data.sequence.FinalVoxelDimensions; 49 | import mpicbg.spim.data.sequence.TimePoint; 50 | import mpicbg.spim.data.sequence.TimePoints; 51 | import mpicbg.spim.data.sequence.VoxelDimensions; 52 | import net.imglib2.Dimensions; 53 | import net.imglib2.FinalDimensions; 54 | import net.imglib2.realtransform.AffineTransform3D; 55 | 56 | public class CellVoyagerDataExporter 57 | { 58 | 59 | // private static final String SETTINGS_FILENAME = "MeasurementSetting.xml"; 60 | 61 | private static final String CHANNELS_ELEMENT = "Channels"; 62 | 63 | private Document document; 64 | 65 | private final File measurementSettingFile; 66 | 67 | private final File imageIndexFile; 68 | 69 | /** 70 | * Creates a new exporter that will browse the specified measurement folder 71 | * to build the data to export. 72 | * 73 | * @param measurementSettingFile 74 | * the CellVoyager measurement file to parse. It must be a XML 75 | * file containing the information on the acquisition to export. 76 | * This file is typically named 77 | * MeasurementSetting.xml 78 | * @param imageIndexFile 79 | * the CellVoyager image index file. This XML file contains the 80 | * path to and information on each individual image needed to 81 | * rebuild the whole dataset. The file is generally in the same 82 | * folder that og the measurement setting file and named 83 | * ImageIndex.xml. 84 | * 85 | */ 86 | public CellVoyagerDataExporter( final File measurementSettingFile, final File imageIndexFile ) 87 | { 88 | this.measurementSettingFile = measurementSettingFile; 89 | this.imageIndexFile = imageIndexFile; 90 | 91 | if ( !measurementSettingFile.exists() ) { throw new IllegalArgumentException( "The target file " + measurementSettingFile + " does not exist." ); } 92 | if ( !measurementSettingFile.isFile() ) { throw new IllegalArgumentException( "The target file " + measurementSettingFile + " is not a file." ); } 93 | 94 | final SAXBuilder builder = new SAXBuilder(); 95 | try 96 | { 97 | document = builder.build( measurementSettingFile ); 98 | } 99 | catch ( final JDOMException e ) 100 | { 101 | throw new IllegalArgumentException( "The target file " + measurementSettingFile + " is malformed:\n" + e.getMessage() ); 102 | } 103 | catch ( final IOException e ) 104 | { 105 | throw new IllegalArgumentException( "Trouble reading " + measurementSettingFile + ":\n" + e.getMessage() ); 106 | } 107 | 108 | if ( !document.getRootElement().getName().equals( "MeasurementSetting" ) ) { throw new IllegalArgumentException( "The target file " + measurementSettingFile + " is not a CellVoyager Measurement Setting file." ); } 109 | } 110 | 111 | public List< ChannelInfo > readInfo() 112 | { 113 | final List< ChannelInfo > channels = new ArrayList<>(); 114 | 115 | final Element root = document.getRootElement(); 116 | 117 | /* 118 | * Magnification 119 | */ 120 | 121 | final double objectiveMagnification = Double.parseDouble( root.getChild( "SelectedObjectiveLens" ).getChildText( "Magnification" ) ); 122 | final double zoomLensMagnification = Double.parseDouble( root.getChild( "ZoomLens" ).getChild( "Magnification" ).getChildText( "Value" ) ); 123 | final double magnification = objectiveMagnification * zoomLensMagnification; 124 | 125 | /* 126 | * Channels 127 | */ 128 | 129 | final Element channelsEl = root.getChild( CHANNELS_ELEMENT ); 130 | final List< Element > channelElements = channelsEl.getChildren(); 131 | 132 | for ( final Element channelElement : channelElements ) 133 | { 134 | final boolean isEnabled = Boolean.parseBoolean( channelElement.getChild( "IsEnabled" ).getText() ); 135 | if ( !isEnabled ) 136 | { 137 | continue; 138 | } 139 | 140 | final ChannelInfo ci = new ChannelInfo(); 141 | channels.add( ci ); 142 | 143 | ci.isEnabled = true; 144 | 145 | ci.channelNumber = Integer.parseInt( channelElement.getChild( "Number" ).getText() ); 146 | 147 | final Element acquisitionSettings = channelElement.getChild( "AcquisitionSetting" ); 148 | 149 | final Element cameraEl = acquisitionSettings.getChild( "Camera" ); 150 | ci.tileWidth = Integer.parseInt( cameraEl.getChildText( "EffectiveHorizontalPixels_pixel" ) ); 151 | ci.tileHeight = Integer.parseInt( cameraEl.getChildText( "EffectiveVerticalPixels_pixel" ) ); 152 | 153 | ci.unmagnifiedPixelWidth = Double.parseDouble( cameraEl.getChildText( "HorizonalCellSize_um" ) ); 154 | ci.unmagnifiedPixelHeight = Double.parseDouble( cameraEl.getChildText( "VerticalCellSize_um" ) ); 155 | 156 | final Element colorElement = channelElement.getChild( "ContrastEnhanceParam" ).getChild( "Color" ); 157 | final int r = Integer.parseInt( colorElement.getChildText( "R" ) ); 158 | final int g = Integer.parseInt( colorElement.getChildText( "G" ) ); 159 | final int b = Integer.parseInt( colorElement.getChildText( "B" ) ); 160 | final int a = Integer.parseInt( colorElement.getChildText( "A" ) ); 161 | ci.channelColor = new Color( r, g, b, a ); 162 | 163 | ci.bitDepth = channelElement.getChild( "ContrastEnhanceParam" ).getChildText( "BitDepth" ); 164 | ci.pixelWidth = ci.unmagnifiedPixelWidth / magnification; 165 | ci.pixelHeight = ci.unmagnifiedPixelWidth / magnification; 166 | 167 | } 168 | 169 | /* 170 | * Fields, for each channel 171 | */ 172 | 173 | for ( final ChannelInfo channelInfo : channels ) 174 | { 175 | 176 | final List< Element > fieldElements = root.getChild( "Wells" ).getChild( "Well" ).getChild( "Areas" ).getChild( "Area" ).getChild( "Fields" ).getChildren( "Field" ); 177 | 178 | // Read field position in um 179 | double xmin = Double.POSITIVE_INFINITY; 180 | double ymin = Double.POSITIVE_INFINITY; 181 | double xmax = Double.NEGATIVE_INFINITY; 182 | double ymax = Double.NEGATIVE_INFINITY; 183 | final ArrayList< double[] > offsetsUm = new ArrayList<>(); 184 | for ( final Element fieldElement : fieldElements ) 185 | { 186 | 187 | final double xum = Double.parseDouble( fieldElement.getChildText( "StageX_um" ) ); 188 | if ( xum < xmin ) 189 | { 190 | xmin = xum; 191 | } 192 | if ( xum > xmax ) 193 | { 194 | xmax = xum; 195 | } 196 | 197 | /* 198 | * Careful! For the fields to be padded correctly, we need to 199 | * invert their Y position, so that it matches the pixel 200 | * orientation. 201 | */ 202 | final double yum = -Double.parseDouble( fieldElement.getChildText( "StageY_um" ) ); 203 | if ( yum < ymin ) 204 | { 205 | ymin = yum; 206 | } 207 | if ( yum > ymax ) 208 | { 209 | ymax = yum; 210 | } 211 | 212 | offsetsUm.add( new double[] { xum, yum } ); 213 | } 214 | 215 | // Convert in pixel position 216 | final List< long[] > offsets = new ArrayList<>(); 217 | for ( final double[] offsetUm : offsetsUm ) 218 | { 219 | final long x = ( long ) ( ( offsetUm[ 0 ] - xmin ) / ( channelInfo.unmagnifiedPixelWidth / magnification ) ); 220 | final long y = ( long ) ( ( offsetUm[ 1 ] - ymin ) / ( channelInfo.unmagnifiedPixelHeight / magnification ) ); 221 | 222 | offsets.add( new long[] { x, y } ); 223 | } 224 | 225 | channelInfo.offsets = offsets; 226 | 227 | final int width = 1 + ( int ) ( ( xmax - xmin ) / ( channelInfo.unmagnifiedPixelWidth / magnification ) ); 228 | final int height = 1 + ( int ) ( ( ymax - ymin ) / ( channelInfo.unmagnifiedPixelWidth / magnification ) ); 229 | channelInfo.width = width + channelInfo.tileWidth; 230 | channelInfo.height = height + channelInfo.tileHeight; 231 | 232 | } 233 | 234 | /* 235 | * Z range 236 | */ 237 | 238 | final int nZSlices = Integer.parseInt( root.getChild( "ZRange" ).getChildText( "NumberOfSlices" ) ); 239 | final double zStroke = Double.parseDouble( root.getChild( "ZRange" ).getChildText( "Stroke_um" ) ); 240 | final double pixelDepth = zStroke / ( nZSlices - 1 ); 241 | 242 | for ( final ChannelInfo channelInfo : channels ) 243 | { 244 | channelInfo.nZSlices = nZSlices; 245 | channelInfo.pixelDepth = pixelDepth; 246 | channelInfo.spaceUnits = "µm"; 247 | } 248 | 249 | return channels; 250 | } 251 | 252 | public TimePoints readTimePoints() 253 | { 254 | final Element root = document.getRootElement(); 255 | final int nTimePoints = Integer.parseInt( root.getChild( "TimelapsCondition" ).getChildText( "Iteration" ) ); 256 | 257 | final List< TimePoint > timepoints = new ArrayList<>( nTimePoints ); 258 | for ( int i = 0; i < nTimePoints; i++ ) 259 | { 260 | timepoints.add( new TimePoint( Integer.valueOf( i ) ) ); 261 | } 262 | return new TimePoints( timepoints ); 263 | } 264 | 265 | public double readFrameInterval() 266 | { 267 | final Element root = document.getRootElement(); 268 | final double dt = Double.parseDouble( root.getChild( "TimelapsCondition" ).getChildText( "Interval" ) ); 269 | return dt; 270 | } 271 | 272 | /** 273 | * Export the target dataset to a xml/hd5 file couple. 274 | * 275 | * @param seqFile 276 | * the path to the target XML file to write. 277 | * @param hdf5File 278 | * the path to the target HDF5 file to write. 279 | * @param resolutions 280 | * the resolution definition for each level. 281 | * @param chunks 282 | * the chunck size definition for each level. 283 | * @param progressWriter 284 | * a {@link ProgressWriter} that will advance from 0 to 1 while 285 | * this method executes. 286 | */ 287 | public void export( final File seqFile, final File hdf5File, final int[][] resolutions, final int[][] chunks, final ProgressWriter progressWriter ) 288 | { 289 | 290 | progressWriter.setProgress( 0d ); 291 | 292 | final List< ChannelInfo > channelInfos = readInfo(); 293 | /* 294 | * Create view setups 295 | */ 296 | 297 | final List< BasicViewSetup > setups = new ArrayList<>( channelInfos.size() ); 298 | int viewSetupIndex = 0; 299 | for ( final ChannelInfo channelInfo : channelInfos ) 300 | { 301 | final Channel channel = new Channel( channelInfo.channelNumber ); 302 | final Dimensions size = new FinalDimensions( new int[] { 303 | channelInfo.width, 304 | channelInfo.height, 305 | channelInfo.nZSlices } ); 306 | final VoxelDimensions voxelSize = new FinalVoxelDimensions( 307 | channelInfo.spaceUnits, 308 | channelInfo.pixelWidth, 309 | channelInfo.pixelHeight, 310 | channelInfo.pixelDepth ); 311 | final BasicViewSetup viewSetup = new BasicViewSetup( viewSetupIndex++, null, size, voxelSize ); 312 | viewSetup.setAttribute( channel ); 313 | setups.add( viewSetup ); 314 | } 315 | 316 | /* 317 | * Instantiate the tile loader 318 | */ 319 | 320 | final TileImgLoader imgLoader = new TileImgLoader( imageIndexFile, channelInfos ); 321 | 322 | /* 323 | * Time points 324 | */ 325 | 326 | final TimePoints timePoints = readTimePoints(); 327 | 328 | /* 329 | * Sequence description 330 | */ 331 | 332 | final SequenceDescriptionMinimal sequenceDescriptionHDF5 = new SequenceDescriptionMinimal( timePoints, Entity.idMap( setups ), imgLoader, null ); 333 | 334 | /* 335 | * Write to HDF5 336 | */ 337 | 338 | final int numCellCreatorThreads = Math.max( 1, PluginHelper.numThreads() - 1 ); 339 | WriteSequenceToHdf5.writeHdf5File( sequenceDescriptionHDF5, resolutions, chunks, true, hdf5File, null, null, numCellCreatorThreads, progressWriter ); 340 | 341 | /* 342 | * write XML sequence description 343 | */ 344 | 345 | final SequenceDescriptionMinimal sequenceDescriptionXML = sequenceDescriptionHDF5; 346 | sequenceDescriptionXML.setImgLoader( new Hdf5ImageLoader( hdf5File, null, sequenceDescriptionXML, false ) ); 347 | 348 | /* 349 | * Build views 350 | */ 351 | 352 | final ArrayList< ViewRegistration > registrations = new ArrayList<>(); 353 | 354 | for ( int setupIndex = 0; setupIndex < setups.size(); setupIndex++ ) 355 | { 356 | final BasicViewSetup viewSetup = setups.get( setupIndex ); 357 | final int setupId = viewSetup.getId(); 358 | 359 | // A single transform for all the time points of a view 360 | final VoxelDimensions voxelSize = viewSetup.getVoxelSize(); 361 | final double pw = voxelSize.dimension( 0 ); 362 | final double ph = voxelSize.dimension( 1 ); 363 | final double pd = voxelSize.dimension( 2 ); 364 | final AffineTransform3D sourceTransform = new AffineTransform3D(); 365 | sourceTransform.set( pw, 0, 0, 0, 0, ph, 0, 0, 0, 0, pd, 0 ); 366 | 367 | for ( final TimePoint timepoint : timePoints.getTimePointsOrdered() ) 368 | { 369 | final int timepointId = timepoint.getId(); 370 | final ViewRegistration view = new ViewRegistration( timepointId, setupId, sourceTransform ); 371 | registrations.add( view ); 372 | } 373 | } 374 | 375 | final ViewRegistrations viewRegistrations = new ViewRegistrations( registrations ); 376 | final SpimDataMinimal spimData = new SpimDataMinimal( seqFile.getParentFile(), sequenceDescriptionXML, viewRegistrations ); 377 | try 378 | { 379 | new XmlIoSpimDataMinimal().save( spimData, seqFile.getAbsolutePath() ); 380 | IJ.showProgress( 1 ); 381 | } 382 | catch ( final Exception e ) 383 | { 384 | throw new RuntimeException( e ); 385 | } 386 | 387 | progressWriter.setProgress( 1d ); 388 | 389 | } 390 | 391 | public static final class ChannelInfo 392 | { 393 | 394 | public int height; 395 | 396 | public int width; 397 | 398 | public int nZSlices; 399 | 400 | public String spaceUnits; 401 | 402 | public double pixelHeight; 403 | 404 | public double pixelWidth; 405 | 406 | public double pixelDepth; 407 | 408 | public List< long[] > offsets; 409 | 410 | public boolean isEnabled; 411 | 412 | public String bitDepth; 413 | 414 | public Color channelColor; 415 | 416 | public double unmagnifiedPixelHeight; 417 | 418 | public double unmagnifiedPixelWidth; 419 | 420 | public int tileHeight; 421 | 422 | public int tileWidth; 423 | 424 | public int channelNumber; 425 | 426 | @Override 427 | public String toString() 428 | { 429 | final StringBuffer str = new StringBuffer(); 430 | str.append( "Channel " + channelNumber + ": \n" ); 431 | str.append( " - isEnabled: " + isEnabled + "\n" ); 432 | str.append( " - width: " + width + "\n" ); 433 | str.append( " - height: " + height + "\n" ); 434 | str.append( " - tile width: " + tileWidth + "\n" ); 435 | str.append( " - tile height: " + tileHeight + "\n" ); 436 | str.append( " - NZSlices: " + nZSlices + "\n" ); 437 | str.append( " - unmagnifiedPixelWidth: " + unmagnifiedPixelWidth + "\n" ); 438 | str.append( " - unmagnifiedPixelHeight: " + unmagnifiedPixelHeight + "\n" ); 439 | str.append( " - color: " + channelColor + "\n" ); 440 | str.append( " - bitDepth: " + bitDepth + "\n" ); 441 | str.append( " - has " + offsets.size() + " fields:\n" ); 442 | int index = 1; 443 | for ( final long[] offset : offsets ) 444 | { 445 | str.append( " " + index++ + ": x = " + offset[ 0 ] + ", y = " + offset[ 1 ] + "\n" ); 446 | } 447 | str.append( " - spatial calibration:\n" ); 448 | str.append( " dx = " + pixelWidth + " " + spaceUnits + "\n" ); 449 | str.append( " dy = " + pixelHeight + " " + spaceUnits + "\n" ); 450 | str.append( " dz = " + pixelDepth + " " + spaceUnits + "\n" ); 451 | return str.toString(); 452 | } 453 | 454 | } 455 | } 456 | -------------------------------------------------------------------------------- /src/main/java/bdv/ij/export/SpimRegistrationSequence.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.ij.export; 23 | 24 | import java.io.File; 25 | import java.util.ArrayList; 26 | import java.util.HashMap; 27 | import java.util.Map; 28 | 29 | import bdv.ij.export.imgloader.HuiskenImageLoader; 30 | import bdv.ij.export.imgloader.StackImageLoader; 31 | import bdv.spimdata.SequenceDescriptionMinimal; 32 | import mpicbg.spim.data.generic.base.Entity; 33 | import mpicbg.spim.data.generic.sequence.BasicImgLoader; 34 | import mpicbg.spim.data.registration.ViewRegistration; 35 | import mpicbg.spim.data.registration.ViewRegistrations; 36 | import mpicbg.spim.data.sequence.Angle; 37 | import mpicbg.spim.data.sequence.Channel; 38 | import mpicbg.spim.data.sequence.FinalVoxelDimensions; 39 | import mpicbg.spim.data.sequence.Illumination; 40 | import mpicbg.spim.data.sequence.TimePoint; 41 | import mpicbg.spim.data.sequence.TimePoints; 42 | import mpicbg.spim.data.sequence.ViewId; 43 | import mpicbg.spim.data.sequence.ViewSetup; 44 | import mpicbg.spim.data.sequence.VoxelDimensions; 45 | import mpicbg.spim.fusion.SPIMImageFusion; 46 | import mpicbg.spim.io.ConfigurationParserException; 47 | import mpicbg.spim.io.SPIMConfiguration; 48 | import mpicbg.spim.registration.ViewDataBeads; 49 | import mpicbg.spim.registration.ViewStructure; 50 | import mpicbg.spim.registration.bead.BeadRegistration; 51 | import net.imglib2.Dimensions; 52 | import net.imglib2.FinalDimensions; 53 | import net.imglib2.FinalRealInterval; 54 | import net.imglib2.RealInterval; 55 | import net.imglib2.realtransform.AffineTransform3D; 56 | import spim.vecmath.Point3f; 57 | import spimopener.SPIMExperiment; 58 | 59 | @Deprecated 60 | public class SpimRegistrationSequence 61 | { 62 | private final SequenceDescriptionMinimal sequenceDescription; 63 | 64 | private final ViewRegistrations viewRegistrations; 65 | 66 | private final SPIMConfiguration conf; 67 | 68 | public SpimRegistrationSequence( final SPIMConfiguration conf ) 69 | { 70 | this.conf = conf; 71 | final ArrayList< ViewSetup > setups = createViewSetups( conf ); 72 | // final HashMap< Integer, ViewSetup > setups = Entity.idMap( createViewSetups( conf ) ); 73 | final TimePoints timepoints = createTimePoints( conf ); 74 | 75 | 76 | final BasicImgLoader imgLoader = createImageLoader( conf, setups ); 77 | 78 | viewRegistrations = createViewRegistrations( conf, setups ); 79 | sequenceDescription = new SequenceDescriptionMinimal( timepoints, Entity.idMap( createViewSetups( conf ) ), imgLoader, null ); 80 | } 81 | 82 | public SpimRegistrationSequence( final String huiskenExperimentXmlFile, final String channels, final String angles, final String timepoints, final int referenceTimePoint ) throws ConfigurationParserException 83 | { 84 | this( initExperimentConfiguration( huiskenExperimentXmlFile, "", channels, angles, timepoints, referenceTimePoint, false, 0 ) ); 85 | } 86 | 87 | public SpimRegistrationSequence( final String inputDirectory, final String inputFilePattern, final String channels, final String angles, final String timepoints, final int referenceTimePoint, final boolean overrideImageZStretching, final double zStretching ) throws ConfigurationParserException 88 | { 89 | this( initExperimentConfiguration( inputDirectory, inputFilePattern, channels, angles, timepoints, referenceTimePoint, overrideImageZStretching, zStretching ) ); 90 | } 91 | 92 | public SequenceDescriptionMinimal getSequenceDescription() 93 | { 94 | return sequenceDescription; 95 | } 96 | 97 | public ViewRegistrations getViewRegistrations() 98 | { 99 | return viewRegistrations; 100 | } 101 | 102 | protected static BasicImgLoader createImageLoader( final SPIMConfiguration conf, final ArrayList< ViewSetup > setups ) 103 | { 104 | final int numTimepoints = conf.timepoints.length; 105 | final HashMap< ViewId, String > filenames = new HashMap<>(); 106 | for ( int timepoint = 0; timepoint < numTimepoints; ++timepoint ) 107 | { 108 | final int timepointId = conf.timepoints[ timepoint ]; 109 | final ViewStructure viewStructure = ViewStructure.initViewStructure( conf, timepoint, new mpicbg.models.AffineModel3D(), "ViewStructure Timepoint " + timepointId, conf.debugLevelInt ); 110 | 111 | for ( final ViewDataBeads viewDataBeads : viewStructure.getViews() ) 112 | { 113 | // get ViewId 114 | final int angle = viewDataBeads.getAcqusitionAngle(); 115 | final int illumination = viewDataBeads.getIllumination(); 116 | final int channel = viewDataBeads.getChannel(); 117 | final int setupId = getViewSetupId( setups, angle, illumination, channel ); 118 | filenames.put( new ViewId( timepointId, setupId ), viewDataBeads.getFileName() ); 119 | } 120 | } 121 | if ( conf.isHuiskenFormat() ) 122 | { 123 | final String exp = conf.inputdirectory.endsWith( "/" ) ? conf.inputdirectory.substring( 0, conf.inputdirectory.length() - 1 ) : conf.inputdirectory; 124 | return new HuiskenImageLoader( new File( exp + ".xml" ), Entity.idMap( setups ) ); 125 | } 126 | else 127 | { 128 | final boolean useImageJOpener = conf.inputFilePattern.endsWith( ".tif" ); 129 | return new StackImageLoader( filenames, useImageJOpener ); 130 | } 131 | } 132 | 133 | /** 134 | * Instantiate the SPIM configuration only with the necessary parameters 135 | * @return 136 | */ 137 | protected static SPIMConfiguration initExperimentConfiguration( final String inputDirectory, final String inputFilePattern, final String angles, final String timepoints, final int referenceTimePoint, final boolean overrideImageZStretching, final double zStretching ) throws ConfigurationParserException 138 | { 139 | return initExperimentConfiguration( inputDirectory, inputFilePattern, "", angles, timepoints, referenceTimePoint, overrideImageZStretching, zStretching ); 140 | } 141 | 142 | /** 143 | * Instantiate the SPIM configuration only with the necessary parameters 144 | * @return 145 | */ 146 | protected static SPIMConfiguration initExperimentConfiguration( final String inputDirectory, final String inputFilePattern, final String channels, final String angles, final String timepoints, final int referenceTimePoint, final boolean overrideImageZStretching, final double zStretching ) throws ConfigurationParserException 147 | { 148 | final SPIMConfiguration conf = new SPIMConfiguration(); 149 | conf.timepointPattern = timepoints; 150 | conf.channelPattern = conf.channelsToRegister = conf.channelsToFuse = (channels == null) ? "" : channels; 151 | conf.anglePattern = angles; 152 | 153 | final File f = new File( inputDirectory ); 154 | if ( f.exists() && f.isFile() && f.getName().endsWith( ".xml" ) ) 155 | { 156 | conf.spimExperiment = new SPIMExperiment( f.getAbsolutePath() ); 157 | conf.inputdirectory = f.getAbsolutePath().substring( 0, f.getAbsolutePath().length() - 4 ) + "/"; 158 | } 159 | else 160 | { 161 | conf.inputdirectory = inputDirectory; 162 | } 163 | 164 | conf.inputFilePattern = inputFilePattern; 165 | 166 | if ( referenceTimePoint >= 0 ) 167 | conf.timeLapseRegistration = true; 168 | conf.referenceTimePoint = referenceTimePoint; 169 | 170 | // check the directory string 171 | conf.inputdirectory = conf.inputdirectory.replace( '\\', '/' ); 172 | conf.inputdirectory = conf.inputdirectory.replaceAll( "//", "/" ); 173 | conf.inputdirectory = conf.inputdirectory.trim(); 174 | if (conf.inputdirectory.length() > 0 && !conf.inputdirectory.endsWith("/")) 175 | conf.inputdirectory = conf.inputdirectory + "/"; 176 | 177 | conf.outputdirectory = conf.inputdirectory + "output/"; 178 | conf.registrationFiledirectory = conf.inputdirectory + "registration/"; 179 | 180 | conf.overrideImageZStretching = overrideImageZStretching; 181 | conf.zStretching = zStretching; 182 | 183 | conf.fuseOnly= true; // this is to avoid an exception in the multi-channel case 184 | 185 | if ( conf.isHuiskenFormat() ) 186 | conf.getFilenamesHuisken(); 187 | else 188 | conf.getFileNames(); 189 | 190 | return conf; 191 | } 192 | 193 | protected static ArrayList< ViewSetup > createViewSetups( final SPIMConfiguration conf ) 194 | { 195 | final ArrayList< ViewSetup > setups = new ArrayList<>(); 196 | int setup_id = 0; 197 | for ( int channelIndex = 0; channelIndex < conf.file[ 0 ].length; channelIndex++ ) 198 | for ( int angleIndex = 0; angleIndex < conf.file[ 0 ][ channelIndex ].length; angleIndex++ ) 199 | for ( int illuminationIndex = 0; illuminationIndex < conf.file[ 0 ][ channelIndex ][ angleIndex ].length; ++illuminationIndex ) 200 | { 201 | String name = ""; 202 | if ( conf.angles.length > 1 ) 203 | { 204 | name += "a " + conf.angles[ angleIndex ]; 205 | } 206 | if ( conf.channels.length > 1 ) 207 | { 208 | name += ( name.isEmpty() ? "" : " " ) + "c " + conf.channels[ channelIndex ]; 209 | } 210 | if ( conf.illuminations.length > 1 ) 211 | { 212 | name += ( name.isEmpty() ? "" : " " ) + "i " + conf.illuminations[ illuminationIndex ]; 213 | } 214 | final Channel channel = new Channel( conf.channels[ channelIndex ] ); 215 | final Angle angle = new Angle( conf.angles[ angleIndex ] ); 216 | final Illumination illumination = new Illumination( conf.illuminations[ illuminationIndex ] ); 217 | 218 | Dimensions size = null; 219 | VoxelDimensions voxelSize = null; 220 | final ViewStructure viewStructure = ViewStructure.initViewStructure( conf, 0, new mpicbg.models.AffineModel3D(), "ViewStructure Timepoint " + 0, conf.debugLevelInt ); 221 | for ( final ViewDataBeads viewDataBeads : viewStructure.getViews() ) 222 | { 223 | if ( angle.getId() == viewDataBeads.getAcqusitionAngle() && 224 | illumination.getId() == viewDataBeads.getIllumination() && 225 | channel.getId() == viewDataBeads.getChannel() ) 226 | { 227 | voxelSize = new FinalVoxelDimensions( "px", 1.0, 1.0, viewDataBeads.getZStretching() ); 228 | size = new FinalDimensions( viewDataBeads.getImageSize() ); 229 | break; 230 | } 231 | } 232 | 233 | setups.add( new ViewSetup( setup_id++, name, size, voxelSize, channel, angle, illumination ) ); 234 | } 235 | return setups; 236 | } 237 | 238 | protected static TimePoints createTimePoints( final SPIMConfiguration conf ) 239 | { 240 | final ArrayList< TimePoint > timepoints = new ArrayList<>(); 241 | for ( final int tp : conf.timepoints ) 242 | timepoints.add( new TimePoint( tp ) ); 243 | return new TimePoints( timepoints ); 244 | } 245 | 246 | public Map< Integer, AffineTransform3D > getFusionTransforms( final int cropOffsetX, final int cropOffsetY, final int cropOffsetZ, final int scale ) 247 | { 248 | conf.cropOffsetX = cropOffsetX; 249 | conf.cropOffsetY = cropOffsetY; 250 | conf.cropOffsetZ = cropOffsetZ; 251 | conf.scale = scale; 252 | 253 | final HashMap< Integer, AffineTransform3D > transforms = new HashMap<>(); 254 | if ( conf.timeLapseRegistration ) 255 | { 256 | SPIMConfiguration refconf = conf; 257 | if ( conf.getTimePointIndex( conf.referenceTimePoint ) < 0 ) 258 | { 259 | try 260 | { 261 | final String inputdirectory; 262 | if ( conf.isHuiskenFormat() ) 263 | inputdirectory = conf.inputdirectory.substring( 0, conf.inputdirectory.length() - 1 ) + ".xml"; 264 | else 265 | inputdirectory = conf.inputdirectory; 266 | refconf = initExperimentConfiguration( inputdirectory, conf.inputFilePattern, conf.anglePattern, "" + conf.referenceTimePoint, conf.referenceTimePoint, conf.overrideImageZStretching, conf.zStretching ); 267 | refconf.cropOffsetX = cropOffsetX; 268 | refconf.cropOffsetY = cropOffsetY; 269 | refconf.cropOffsetZ = cropOffsetZ; 270 | refconf.scale = scale; 271 | } 272 | catch ( final ConfigurationParserException e ) 273 | { 274 | e.printStackTrace(); 275 | } 276 | } 277 | final RealInterval interval = getFusionBoundingBox( refconf, 0 ); 278 | final double tx = interval.realMin( 0 ); 279 | final double ty = interval.realMin( 1 ); 280 | final double tz = interval.realMin( 2 ); 281 | final double s = scale; 282 | System.out.println( "tx = " + tx + " ty = " + ty + " tz = " + tz + " scale = " + scale ); 283 | final AffineTransform3D transform = new AffineTransform3D(); 284 | transform.set( s, 0, 0, tx, 0, s, 0, ty, 0, 0, s, tz ); 285 | for ( final int tp : conf.timepoints ) 286 | transforms.put( tp, transform ); 287 | } 288 | else 289 | { 290 | for ( final int tp : conf.timepoints ) 291 | { 292 | final RealInterval interval = getFusionBoundingBox( conf, tp ); 293 | final double tx = interval.realMin( 0 ); 294 | final double ty = interval.realMin( 1 ); 295 | final double tz = interval.realMin( 2 ); 296 | final double s = scale; 297 | System.out.println( "tx = " + tx + " ty = " + ty + " tz = " + tz + " scale = " + scale ); 298 | final AffineTransform3D transform = new AffineTransform3D(); 299 | transform.set( s, 0, 0, tx, 0, s, 0, ty, 0, 0, s, tz ); 300 | transforms.put( tp, transform ); 301 | } 302 | } 303 | return transforms; 304 | } 305 | 306 | protected static RealInterval getFusionBoundingBox( final SPIMConfiguration conf, final int timepointId ) 307 | { 308 | final Point3f min = new Point3f(); 309 | final Point3f max = new Point3f(); 310 | final Point3f size = new Point3f(); 311 | 312 | final int tp = conf.getTimePointIndex( conf.timeLapseRegistration ? conf.referenceTimePoint : timepointId ); 313 | 314 | @SuppressWarnings( "unchecked" ) 315 | final ViewStructure reference = ViewStructure.initViewStructure( conf, tp, conf.getModel(), "Reference ViewStructure Timepoint " + conf.referenceTimePoint, conf.debugLevelInt ); 316 | for ( final ViewDataBeads viewDataBeads : reference.getViews() ) 317 | { 318 | // coordinate system) 319 | if ( conf.timeLapseRegistration ) 320 | viewDataBeads.loadRegistrationTimePoint( conf.referenceTimePoint ); 321 | else 322 | viewDataBeads.loadRegistration(); 323 | 324 | // apply the z-scaling to the transformation 325 | BeadRegistration.concatenateAxialScaling( viewDataBeads, reference.getDebugLevel() ); 326 | } 327 | SPIMImageFusion.computeImageSize( reference.getViews(), min, max, size, conf.scale, conf.cropSizeX, conf.cropSizeY, conf.cropSizeZ, reference.getDebugLevel() ); 328 | 329 | final int scale = conf.scale; 330 | final int cropOffsetX = conf.cropOffsetX; 331 | final int cropOffsetY = conf.cropOffsetY; 332 | final int cropOffsetZ = conf.cropOffsetZ; 333 | final int imgW; 334 | final int imgH; 335 | final int imgD; 336 | 337 | if (conf.cropSizeX == 0) 338 | imgW = (Math.round((float)Math.ceil(size.x)) + 1)/scale; 339 | else 340 | imgW = conf.cropSizeX/scale; 341 | 342 | if (conf.cropSizeY == 0) 343 | imgH = (Math.round((float)Math.ceil(size.y)) + 1)/scale; 344 | else 345 | imgH = conf.cropSizeY/scale; 346 | 347 | if (conf.cropSizeZ == 0) 348 | imgD = (Math.round((float)Math.ceil(size.z)) + 1)/scale; 349 | else 350 | imgD = conf.cropSizeZ/scale; 351 | 352 | return FinalRealInterval.createMinMax( 353 | ( int ) min.x + cropOffsetX, 354 | ( int ) min.y + cropOffsetY, 355 | ( int ) min.z + cropOffsetZ, 356 | ( int ) min.x + cropOffsetX + imgW - 1, 357 | ( int ) min.y + cropOffsetY + imgH - 1, 358 | ( int ) min.z + cropOffsetZ + imgD - 1 ); 359 | } 360 | 361 | protected static ViewRegistrations createViewRegistrations( final SPIMConfiguration conf, final ArrayList< ViewSetup > setups ) 362 | { 363 | final ArrayList< ViewRegistration > regs = new ArrayList<>(); 364 | 365 | // for each time-point initialize the view structure, load&apply 366 | // registrations, instantiate the View objects for Tracking 367 | for ( int i = 0; i < conf.timepoints.length; ++i ) 368 | { 369 | final int timepointId = conf.timepoints[ i ]; 370 | final ViewStructure viewStructure = ViewStructure.initViewStructure( conf, i, new mpicbg.models.AffineModel3D(), "ViewStructure Timepoint " + timepointId, conf.debugLevelInt ); 371 | 372 | for ( final ViewDataBeads viewDataBeads : viewStructure.getViews() ) 373 | { 374 | // load time-point registration (to map into the global 375 | // coordinate system) 376 | if ( conf.timeLapseRegistration ) 377 | viewDataBeads.loadRegistrationTimePoint( conf.referenceTimePoint ); 378 | else 379 | viewDataBeads.loadRegistration(); 380 | 381 | // apply the z-scaling to the transformation 382 | BeadRegistration.concatenateAxialScaling( viewDataBeads, viewStructure.getDebugLevel() ); 383 | 384 | final int angle = viewDataBeads.getAcqusitionAngle(); 385 | final int illumination = viewDataBeads.getIllumination(); 386 | final int channel = viewDataBeads.getChannel(); 387 | final AffineTransform3D model = new AffineTransform3D(); 388 | final double[][] tmp = new double[3][4]; 389 | ( ( mpicbg.models.AffineModel3D ) viewDataBeads.getTile().getModel() ).toMatrix( tmp ); 390 | model.set( tmp ); 391 | 392 | // get corresponding setup id 393 | final int setupId = getViewSetupId( setups, angle, illumination, channel ); 394 | 395 | // create ViewRegistration 396 | regs.add( new ViewRegistration( timepointId, setupId, model ) ); 397 | } 398 | } 399 | 400 | return new ViewRegistrations( regs ); 401 | } 402 | 403 | protected static ArrayList< Integer > makeList( final int[] ints ) 404 | { 405 | final ArrayList< Integer > list = new ArrayList<>( ints.length ); 406 | for ( final int i : ints ) 407 | list.add( i ); 408 | return list; 409 | } 410 | 411 | /** 412 | * find ViewSetup index corresponding to given (angle, illumination, 413 | * channel) triple. 414 | * 415 | * @return setup index or -1 if no corresponding setup was found. 416 | */ 417 | protected static int getViewSetupId( final ArrayList< ViewSetup > setups, final int angle, final int illumination, final int channel ) 418 | { 419 | for ( final ViewSetup s : setups ) 420 | if ( s.getAngle().getId() == angle && s.getIllumination().getId() == illumination && s.getChannel().getId() == channel ) 421 | return s.getId(); 422 | return -1; 423 | } 424 | 425 | public SPIMConfiguration getConf() 426 | { 427 | return conf; 428 | } 429 | } 430 | 431 | -------------------------------------------------------------------------------- /src/main/java/bdv/ij/ExportImagePlusPlugIn.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.ij; 23 | 24 | import java.awt.AWTEvent; 25 | import java.awt.Checkbox; 26 | import java.awt.Choice; 27 | import java.awt.TextField; 28 | import java.awt.event.ItemEvent; 29 | import java.io.File; 30 | import java.util.ArrayList; 31 | import java.util.HashMap; 32 | import java.util.Map; 33 | 34 | import net.imglib2.FinalDimensions; 35 | import net.imglib2.RandomAccessibleInterval; 36 | import net.imglib2.realtransform.AffineTransform3D; 37 | 38 | import net.imglib2.util.Intervals; 39 | import org.scijava.command.Command; 40 | import org.scijava.plugin.Plugin; 41 | 42 | import bdv.export.ExportMipmapInfo; 43 | import bdv.export.ExportScalePyramid.AfterEachPlane; 44 | import bdv.export.ExportScalePyramid.LoopbackHeuristic; 45 | import bdv.export.ProgressWriter; 46 | import bdv.export.ProposeMipmaps; 47 | import bdv.export.SubTaskProgressWriter; 48 | import bdv.export.WriteSequenceToHdf5; 49 | import bdv.ij.export.imgloader.ImagePlusImgLoader; 50 | import bdv.ij.export.imgloader.ImagePlusImgLoader.MinMaxOption; 51 | import bdv.ij.util.PluginHelper; 52 | import bdv.ij.util.ProgressWriterIJ; 53 | import bdv.img.hdf5.Hdf5ImageLoader; 54 | import bdv.img.hdf5.Partition; 55 | import bdv.spimdata.SequenceDescriptionMinimal; 56 | import bdv.spimdata.SpimDataMinimal; 57 | import bdv.spimdata.XmlIoSpimDataMinimal; 58 | import bdv.img.imagestack.ImageStackImageLoader; 59 | import bdv.img.n5.N5ImageLoader; 60 | import bdv.img.virtualstack.VirtualStackImageLoader; 61 | import fiji.util.gui.GenericDialogPlus; 62 | import ij.IJ; 63 | import ij.ImageJ; 64 | import ij.ImagePlus; 65 | import ij.WindowManager; 66 | import ij.gui.DialogListener; 67 | import ij.gui.GenericDialog; 68 | import mpicbg.spim.data.generic.sequence.BasicViewSetup; 69 | import mpicbg.spim.data.generic.sequence.TypedBasicImgLoader; 70 | import mpicbg.spim.data.registration.ViewRegistration; 71 | import mpicbg.spim.data.registration.ViewRegistrations; 72 | import mpicbg.spim.data.sequence.Channel; 73 | import mpicbg.spim.data.sequence.FinalVoxelDimensions; 74 | import mpicbg.spim.data.sequence.TimePoint; 75 | import mpicbg.spim.data.sequence.TimePoints; 76 | 77 | /** 78 | * ImageJ plugin to export the current image to xml/hdf5. 79 | * 80 | * @author Tobias Pietzsch <tobias.pietzsch@gmail.com> 81 | */ 82 | @Plugin(type = Command.class, 83 | menuPath = "Plugins>BigDataViewer>Export Current Image as XML/HDF5") 84 | public class ExportImagePlusPlugIn implements Command 85 | { 86 | public static void main( final String[] args ) 87 | { 88 | new ImageJ(); 89 | IJ.run("Confocal Series (2.2MB)"); 90 | new ExportImagePlusPlugIn().run(); 91 | } 92 | 93 | @Override 94 | public void run() 95 | { 96 | if ( ij.Prefs.setIJMenuBar ) 97 | System.setProperty( "apple.laf.useScreenMenuBar", "true" ); 98 | 99 | // get the current image 100 | final ImagePlus imp = WindowManager.getCurrentImage(); 101 | 102 | // make sure there is one 103 | if ( imp == null ) 104 | { 105 | IJ.showMessage( "Please open an image first." ); 106 | return; 107 | } 108 | 109 | // check the image type 110 | switch ( imp.getType() ) 111 | { 112 | case ImagePlus.GRAY8: 113 | case ImagePlus.GRAY16: 114 | case ImagePlus.GRAY32: 115 | break; 116 | default: 117 | IJ.showMessage( "Only 8, 16, 32-bit images are supported currently!" ); 118 | return; 119 | } 120 | 121 | // get calibration and image size 122 | final double pw = imp.getCalibration().pixelWidth; 123 | final double ph = imp.getCalibration().pixelHeight; 124 | final double pd = imp.getCalibration().pixelDepth; 125 | String punit = imp.getCalibration().getUnit(); 126 | if ( punit == null || punit.isEmpty() ) 127 | punit = "px"; 128 | final FinalVoxelDimensions voxelSize = new FinalVoxelDimensions( punit, pw, ph, pd ); 129 | final int w = imp.getWidth(); 130 | final int h = imp.getHeight(); 131 | final int d = imp.getNSlices(); 132 | final FinalDimensions size = new FinalDimensions( w, h, d ); 133 | 134 | // propose reasonable mipmap settings 135 | final ExportMipmapInfo autoMipmapSettings = ProposeMipmaps.proposeMipmaps( new BasicViewSetup( 0, "", size, voxelSize ) ); 136 | 137 | // show dialog to get output paths, resolutions, subdivisions, min-max option 138 | final Parameters params = getParameters( imp.getDisplayRangeMin(), imp.getDisplayRangeMax(), autoMipmapSettings ); 139 | if ( params == null ) 140 | return; 141 | 142 | final ProgressWriter progressWriter = new ProgressWriterIJ(); 143 | progressWriter.out().println( "starting export..." ); 144 | 145 | // create ImgLoader wrapping the image 146 | final TypedBasicImgLoader< ? > imgLoader; 147 | final Runnable clearCache; 148 | final boolean isVirtual = imp.getStack() != null && imp.getStack().isVirtual(); 149 | if ( isVirtual ) 150 | { 151 | final VirtualStackImageLoader< ?, ?, ? > il; 152 | switch ( imp.getType() ) 153 | { 154 | case ImagePlus.GRAY8: 155 | il = VirtualStackImageLoader.createUnsignedByteInstance( imp ); 156 | break; 157 | case ImagePlus.GRAY16: 158 | il = VirtualStackImageLoader.createUnsignedShortInstance( imp ); 159 | break; 160 | case ImagePlus.GRAY32: 161 | default: 162 | il = VirtualStackImageLoader.createFloatInstance( imp ); 163 | break; 164 | } 165 | imgLoader = il; 166 | clearCache = il.getCacheControl()::clearCache; 167 | } 168 | else 169 | { 170 | switch ( imp.getType() ) 171 | { 172 | case ImagePlus.GRAY8: 173 | imgLoader = ImageStackImageLoader.createUnsignedByteInstance( imp ); 174 | break; 175 | case ImagePlus.GRAY16: 176 | imgLoader = ImageStackImageLoader.createUnsignedShortInstance( imp ); 177 | break; 178 | case ImagePlus.GRAY32: 179 | default: 180 | imgLoader = ImageStackImageLoader.createFloatInstance( imp ); 181 | break; 182 | } 183 | clearCache = () -> {}; 184 | } 185 | 186 | final int numTimepoints = imp.getNFrames(); 187 | final int numSetups = imp.getNChannels(); 188 | 189 | // create SourceTransform from the images calibration 190 | final AffineTransform3D sourceTransform = new AffineTransform3D(); 191 | sourceTransform.set( pw, 0, 0, 0, 0, ph, 0, 0, 0, 0, pd, 0 ); 192 | 193 | // write hdf5 194 | final HashMap< Integer, BasicViewSetup > setups = new HashMap<>( numSetups ); 195 | for ( int s = 0; s < numSetups; ++s ) 196 | { 197 | final BasicViewSetup setup = new BasicViewSetup( s, String.format( "channel %d", s + 1 ), size, voxelSize ); 198 | setup.setAttribute( new Channel( s + 1 ) ); 199 | setups.put( s, setup ); 200 | } 201 | final ArrayList< TimePoint > timepoints = new ArrayList<>( numTimepoints ); 202 | for ( int t = 0; t < numTimepoints; ++t ) 203 | timepoints.add( new TimePoint( t ) ); 204 | final SequenceDescriptionMinimal seq = new SequenceDescriptionMinimal( new TimePoints( timepoints ), setups, imgLoader, null ); 205 | 206 | final Map< Integer, ExportMipmapInfo > perSetupExportMipmapInfo = new HashMap<>(); 207 | final ExportMipmapInfo mipmapInfo = params.setMipmapManual 208 | ? new ExportMipmapInfo( params.resolutions, params.subdivisions ) 209 | : autoMipmapSettings; 210 | for ( final BasicViewSetup setup : seq.getViewSetupsOrdered() ) 211 | perSetupExportMipmapInfo.put( setup.getId(), mipmapInfo ); 212 | 213 | // LoopBackHeuristic: 214 | // - If saving more than 8x on pixel reads use the loopback image over 215 | // original image 216 | // - For virtual stacks also consider the cache size that would be 217 | // required for all original planes contributing to a "plane of 218 | // blocks" at the current level. If this is more than 1/4 of 219 | // available memory, use the loopback image. 220 | final long planeSizeInBytes = imp.getWidth() * imp.getHeight() * imp.getBytesPerPixel(); 221 | final long ijMaxMemory = IJ.maxMemory(); 222 | final int numCellCreatorThreads = Math.max( 1, PluginHelper.numThreads() - 1 ); 223 | final LoopbackHeuristic loopbackHeuristic = new LoopbackHeuristic() 224 | { 225 | @Override 226 | public boolean decide( final RandomAccessibleInterval< ? > originalImg, final int[] factorsToOriginalImg, final int previousLevel, final int[] factorsToPreviousLevel, final int[] chunkSize ) 227 | { 228 | if ( previousLevel < 0 ) 229 | return false; 230 | 231 | if ( Intervals.numElements( factorsToOriginalImg ) / Intervals.numElements( factorsToPreviousLevel ) >= 8 ) 232 | return true; 233 | 234 | if ( isVirtual ) 235 | { 236 | final long requiredCacheSize = planeSizeInBytes * factorsToOriginalImg[ 2 ] * chunkSize[ 2 ]; 237 | if ( requiredCacheSize > ijMaxMemory / 4 ) 238 | return true; 239 | } 240 | 241 | return false; 242 | } 243 | }; 244 | 245 | final AfterEachPlane afterEachPlane = new AfterEachPlane() 246 | { 247 | @Override 248 | public void afterEachPlane( final boolean usedLoopBack ) 249 | { 250 | if ( !usedLoopBack && isVirtual ) 251 | { 252 | final long free = Runtime.getRuntime().freeMemory(); 253 | final long total = Runtime.getRuntime().totalMemory(); 254 | final long max = Runtime.getRuntime().maxMemory(); 255 | final long actuallyFree = max - total + free; 256 | 257 | if ( actuallyFree < max / 2 ) 258 | clearCache.run(); 259 | } 260 | } 261 | 262 | }; 263 | 264 | final ArrayList< Partition > partitions; 265 | if ( params.split ) 266 | { 267 | final String xmlFilename = params.seqFile.getAbsolutePath(); 268 | final String basename = xmlFilename.endsWith( ".xml" ) ? xmlFilename.substring( 0, xmlFilename.length() - 4 ) : xmlFilename; 269 | partitions = Partition.split( timepoints, seq.getViewSetupsOrdered(), params.timepointsPerPartition, params.setupsPerPartition, basename ); 270 | 271 | for ( int i = 0; i < partitions.size(); ++i ) 272 | { 273 | final Partition partition = partitions.get( i ); 274 | final ProgressWriter p = new SubTaskProgressWriter( progressWriter, 0, 0.95 * i / partitions.size() ); 275 | WriteSequenceToHdf5.writeHdf5PartitionFile( seq, perSetupExportMipmapInfo, params.deflate, partition, loopbackHeuristic, afterEachPlane, numCellCreatorThreads, p ); 276 | } 277 | WriteSequenceToHdf5.writeHdf5PartitionLinkFile( seq, perSetupExportMipmapInfo, partitions, params.hdf5File ); 278 | } 279 | else 280 | { 281 | partitions = null; 282 | WriteSequenceToHdf5.writeHdf5File( seq, perSetupExportMipmapInfo, params.deflate, params.hdf5File, loopbackHeuristic, afterEachPlane, numCellCreatorThreads, new SubTaskProgressWriter( progressWriter, 0, 0.95 ) ); 283 | } 284 | 285 | // write xml sequence description 286 | final Hdf5ImageLoader hdf5Loader = new Hdf5ImageLoader( params.hdf5File, partitions, null, false ); 287 | final SequenceDescriptionMinimal seqh5 = new SequenceDescriptionMinimal( seq, hdf5Loader ); 288 | 289 | final ArrayList< ViewRegistration > registrations = new ArrayList<>(); 290 | for ( int t = 0; t < numTimepoints; ++t ) 291 | for ( int s = 0; s < numSetups; ++s ) 292 | registrations.add( new ViewRegistration( t, s, sourceTransform ) ); 293 | 294 | final File basePath = params.seqFile.getParentFile(); 295 | final SpimDataMinimal spimData = new SpimDataMinimal( basePath, seqh5, new ViewRegistrations( registrations ) ); 296 | 297 | try 298 | { 299 | new XmlIoSpimDataMinimal().save( spimData, params.seqFile.getAbsolutePath() ); 300 | progressWriter.setProgress( 1.0 ); 301 | } 302 | catch ( final Exception e ) 303 | { 304 | throw new RuntimeException( e ); 305 | } 306 | progressWriter.out().println( "done" ); 307 | } 308 | 309 | protected static class Parameters 310 | { 311 | final boolean setMipmapManual; 312 | 313 | final int[][] resolutions; 314 | 315 | final int[][] subdivisions; 316 | 317 | final File seqFile; 318 | 319 | final File hdf5File; 320 | 321 | final boolean deflate; 322 | 323 | final boolean split; 324 | 325 | final int timepointsPerPartition; 326 | 327 | final int setupsPerPartition; 328 | 329 | public Parameters( 330 | final boolean setMipmapManual, final int[][] resolutions, final int[][] subdivisions, 331 | final File seqFile, final File hdf5File, 332 | final boolean deflate, 333 | final boolean split, final int timepointsPerPartition, final int setupsPerPartition ) 334 | { 335 | this.setMipmapManual = setMipmapManual; 336 | this.resolutions = resolutions; 337 | this.subdivisions = subdivisions; 338 | this.seqFile = seqFile; 339 | this.hdf5File = hdf5File; 340 | this.deflate = deflate; 341 | this.split = split; 342 | this.timepointsPerPartition = timepointsPerPartition; 343 | this.setupsPerPartition = setupsPerPartition; 344 | } 345 | } 346 | 347 | static boolean lastSetMipmapManual = false; 348 | 349 | static String lastSubsampling = "{1,1,1}, {2,2,1}, {4,4,2}"; 350 | 351 | static String lastChunkSizes = "{32,32,4}, {16,16,8}, {8,8,8}"; 352 | 353 | static boolean lastSplit = false; 354 | 355 | static int lastTimepointsPerPartition = 0; 356 | 357 | static int lastSetupsPerPartition = 0; 358 | 359 | static boolean lastDeflate = true; 360 | 361 | static String lastExportPath = "./export.xml"; 362 | 363 | protected Parameters getParameters( final double impMin, final double impMax, final ExportMipmapInfo autoMipmapSettings ) 364 | { 365 | while ( true ) 366 | { 367 | final GenericDialogPlus gd = new GenericDialogPlus( "Export for BigDataViewer" ); 368 | 369 | gd.addCheckbox( "manual_mipmap_setup", lastSetMipmapManual ); 370 | final Checkbox cManualMipmap = ( Checkbox ) gd.getCheckboxes().lastElement(); 371 | gd.addStringField( "Subsampling_factors", lastSubsampling, 25 ); 372 | final TextField tfSubsampling = ( TextField ) gd.getStringFields().lastElement(); 373 | gd.addStringField( "Hdf5_chunk_sizes", lastChunkSizes, 25 ); 374 | final TextField tfChunkSizes = ( TextField ) gd.getStringFields().lastElement(); 375 | 376 | gd.addMessage( "" ); 377 | gd.addCheckbox( "split_hdf5", lastSplit ); 378 | final Checkbox cSplit = ( Checkbox ) gd.getCheckboxes().lastElement(); 379 | gd.addNumericField( "timepoints_per_partition", lastTimepointsPerPartition, 0, 25, "" ); 380 | final TextField tfSplitTimepoints = ( TextField ) gd.getNumericFields().lastElement(); 381 | gd.addNumericField( "setups_per_partition", lastSetupsPerPartition, 0, 25, "" ); 382 | final TextField tfSplitSetups = ( TextField ) gd.getNumericFields().lastElement(); 383 | 384 | gd.addMessage( "" ); 385 | gd.addCheckbox( "use_deflate_compression", lastDeflate ); 386 | 387 | gd.addMessage( "" ); 388 | PluginHelper.addSaveAsFileField( gd, "Export_path", lastExportPath, 25 ); 389 | 390 | final String autoSubsampling = ProposeMipmaps.getArrayString( autoMipmapSettings.getExportResolutions() ); 391 | final String autoChunkSizes = ProposeMipmaps.getArrayString( autoMipmapSettings.getSubdivisions() ); 392 | gd.addDialogListener( ( dialog, e ) -> { 393 | gd.getNextBoolean(); 394 | gd.getNextString(); 395 | gd.getNextString(); 396 | gd.getNextBoolean(); 397 | gd.getNextNumber(); 398 | gd.getNextNumber(); 399 | gd.getNextBoolean(); 400 | gd.getNextString(); 401 | if ( e instanceof ItemEvent && e.getID() == ItemEvent.ITEM_STATE_CHANGED && e.getSource() == cManualMipmap ) 402 | { 403 | final boolean useManual = cManualMipmap.getState(); 404 | tfSubsampling.setEnabled( useManual ); 405 | tfChunkSizes.setEnabled( useManual ); 406 | if ( !useManual ) 407 | { 408 | tfSubsampling.setText( autoSubsampling ); 409 | tfChunkSizes.setText( autoChunkSizes ); 410 | } 411 | } 412 | else if ( e instanceof ItemEvent && e.getID() == ItemEvent.ITEM_STATE_CHANGED && e.getSource() == cSplit ) 413 | { 414 | final boolean split = cSplit.getState(); 415 | tfSplitTimepoints.setEnabled( split ); 416 | tfSplitSetups.setEnabled( split ); 417 | } 418 | return true; 419 | } ); 420 | 421 | tfSubsampling.setEnabled( lastSetMipmapManual ); 422 | tfChunkSizes.setEnabled( lastSetMipmapManual ); 423 | if ( !lastSetMipmapManual ) 424 | { 425 | tfSubsampling.setText( autoSubsampling ); 426 | tfChunkSizes.setText( autoChunkSizes ); 427 | } 428 | 429 | tfSplitTimepoints.setEnabled( lastSplit ); 430 | tfSplitSetups.setEnabled( lastSplit ); 431 | 432 | gd.showDialog(); 433 | if ( gd.wasCanceled() ) 434 | return null; 435 | 436 | lastSetMipmapManual = gd.getNextBoolean(); 437 | lastSubsampling = gd.getNextString(); 438 | lastChunkSizes = gd.getNextString(); 439 | lastSplit = gd.getNextBoolean(); 440 | lastTimepointsPerPartition = ( int ) gd.getNextNumber(); 441 | lastSetupsPerPartition = ( int ) gd.getNextNumber(); 442 | lastDeflate = gd.getNextBoolean(); 443 | lastExportPath = gd.getNextString(); 444 | 445 | // parse mipmap resolutions and cell sizes 446 | final int[][] resolutions = PluginHelper.parseResolutionsString( lastSubsampling ); 447 | final int[][] subdivisions = PluginHelper.parseResolutionsString( lastChunkSizes ); 448 | if ( resolutions.length == 0 ) 449 | { 450 | IJ.showMessage( "Cannot parse subsampling factors " + lastSubsampling ); 451 | continue; 452 | } 453 | if ( subdivisions.length == 0 ) 454 | { 455 | IJ.showMessage( "Cannot parse hdf5 chunk sizes " + lastChunkSizes ); 456 | continue; 457 | } 458 | else if ( resolutions.length != subdivisions.length ) 459 | { 460 | IJ.showMessage( "subsampling factors and hdf5 chunk sizes must have the same number of elements" ); 461 | continue; 462 | } 463 | 464 | String seqFilename = lastExportPath; 465 | if ( !seqFilename.endsWith( ".xml" ) ) 466 | seqFilename += ".xml"; 467 | final File seqFile = new File( seqFilename ); 468 | final File parent = seqFile.getParentFile(); 469 | if ( parent == null || !parent.exists() || !parent.isDirectory() ) 470 | { 471 | IJ.showMessage( "Invalid export filename " + seqFilename ); 472 | continue; 473 | } 474 | final String hdf5Filename = seqFilename.substring( 0, seqFilename.length() - 4 ) + ".h5"; 475 | final File hdf5File = new File( hdf5Filename ); 476 | 477 | return new Parameters( lastSetMipmapManual, resolutions, subdivisions, seqFile, hdf5File, lastDeflate, lastSplit, lastTimepointsPerPartition, lastSetupsPerPartition ); 478 | } 479 | } 480 | } 481 | -------------------------------------------------------------------------------- /src/main/java/bdv/ij/export/SetupAggregator.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji plugins for starting BigDataViewer and exporting data. 4 | * %% 5 | * Copyright (C) 2014 - 2024 BigDataViewer developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package bdv.ij.export; 23 | 24 | import java.io.File; 25 | import java.util.ArrayList; 26 | import java.util.HashMap; 27 | import java.util.Map; 28 | 29 | import bdv.export.ExportMipmapInfo; 30 | import bdv.export.WriteSequenceToHdf5; 31 | import bdv.ij.util.PluginHelper; 32 | import bdv.spimdata.SequenceDescriptionMinimal; 33 | import bdv.spimdata.SpimDataMinimal; 34 | import mpicbg.spim.data.generic.XmlIoAbstractSpimData; 35 | import mpicbg.spim.data.generic.base.Entity; 36 | import mpicbg.spim.data.generic.sequence.AbstractSequenceDescription; 37 | import mpicbg.spim.data.generic.sequence.BasicImgLoader; 38 | import mpicbg.spim.data.generic.sequence.BasicSetupImgLoader; 39 | import mpicbg.spim.data.generic.sequence.BasicViewSetup; 40 | import mpicbg.spim.data.registration.ViewRegistration; 41 | import mpicbg.spim.data.registration.ViewRegistrations; 42 | import mpicbg.spim.data.sequence.ImgLoader; 43 | import mpicbg.spim.data.sequence.SequenceDescription; 44 | import mpicbg.spim.data.sequence.TimePoint; 45 | import mpicbg.spim.data.sequence.TimePoints; 46 | import mpicbg.spim.data.sequence.ViewId; 47 | import net.imglib2.img.cell.CellImg; 48 | 49 | /** 50 | * Aggregate {@link BasicViewSetup setups}, i.e., SPIM source angles and fused 51 | * datasets from multiple {@link SequenceDescription}s. Also keeps for each 52 | * setup the mipmap resolutions and subdivisions to be created. 53 | * 54 | * Note, that added setups are assigned new, consecutive ids starting from 0. 55 | * 56 | * @author Tobias Pietzsch <tobias.pietzsch@gmail.com> 57 | */ 58 | @Deprecated 59 | public class SetupAggregator 60 | { 61 | /** 62 | * timepoint id for every timepoint index. 63 | */ 64 | protected TimePoints timepoints; 65 | 66 | protected final ArrayList< ViewRegistration > registrations; 67 | 68 | /** 69 | * Contains {@link ViewSetupWrapper wrappers} around setups in other sequences. 70 | */ 71 | protected final ArrayList< ViewSetupWrapper > setups; 72 | 73 | protected final ArrayList< BasicSetupImgLoader< ? > > setupImgLoaders; 74 | 75 | protected final Map< Integer, ExportMipmapInfo > perSetupMipmapInfo; 76 | 77 | /** 78 | * An {@link ImgLoader} that forwards to wrapped source sequences. 79 | */ 80 | protected final BasicImgLoader imgLoader; 81 | 82 | /** 83 | * Create an empty aggregator. 84 | */ 85 | public SetupAggregator() 86 | { 87 | timepoints = null; 88 | registrations = new ArrayList<>(); 89 | setups = new ArrayList<>(); 90 | setupImgLoaders = new ArrayList<>(); 91 | perSetupMipmapInfo = new HashMap<>(); 92 | imgLoader = new BasicImgLoader() 93 | { 94 | @Override 95 | public BasicSetupImgLoader< ? > getSetupImgLoader( final int setupId ) 96 | { 97 | return setupImgLoaders.get( setupId ); 98 | } 99 | }; 100 | } 101 | 102 | /** 103 | * Add a new {@link BasicViewSetup} to the aggregator. 104 | * 105 | * Adds a setup of the given source {@link AbstractSequenceDescription} to the 106 | * aggregator. A reference to the source sequence is kept and the source 107 | * {@link ViewRegistrations} are copied. In the viewer format, every image 108 | * is stored in multiple resolutions. The resolutions are described as int[] 109 | * arrays defining multiple of original pixel size in every dimension. For 110 | * example {1,1,1} is the original resolution, {4,4,2} is downsampled by 111 | * factor 4 in X and Y and factor 2 in Z. Each resolution of the image is 112 | * stored as a chunked three-dimensional array (each chunk corresponds to 113 | * one cell of a {@link CellImg} when the data is loaded). The chunk sizes 114 | * are defined by the subdivisions parameter which is an array of int[], one 115 | * per resolution. Each int[] array describes the X,Y,Z chunk size for one 116 | * resolution. 117 | * 118 | * @param sourceSetup 119 | * the setup to add 120 | * @param sourceSequence 121 | * the source sequence to which the given setup refers. 122 | * @param sourceRegs 123 | * the view registrations associated with the source sequence. 124 | * registrations for the given setup are copied. 125 | * @param resolutions 126 | * the set of resolutions to store. each nested int[] array 127 | * defines one resolution. 128 | * @param subdivisions 129 | * the set of subdivisions to store. each nested int[] array 130 | * defines one subdivision. 131 | * @return the setup id of the new {@link BasicViewSetup} in the aggregator. 132 | */ 133 | public int add( final BasicViewSetup sourceSetup, final AbstractSequenceDescription< ?, ?, ? > sourceSequence, final ViewRegistrations sourceRegs, final int[][] resolutions, final int[][] subdivisions ) 134 | { 135 | final int setupId = setups.size(); 136 | setups.add( new ViewSetupWrapper( setupId, sourceSequence, sourceSetup ) ); 137 | setupImgLoaders.add( sourceSequence.getImgLoader().getSetupImgLoader( setupId ) ); 138 | if ( setupId == 0 ) 139 | { 140 | // if this is the first setup added, initialize the timepoints 141 | timepoints = sourceSequence.getTimePoints(); 142 | } 143 | final int sourceSetupId = sourceSetup.getId(); 144 | for ( final TimePoint timepoint : timepoints.getTimePointsOrdered() ) 145 | { 146 | final int timepointId = timepoint.getId(); 147 | final ViewRegistration r = sourceRegs.getViewRegistrations().get( new ViewId( timepointId, sourceSetupId ) ); 148 | if ( r == null ) 149 | throw new RuntimeException( "could not find ViewRegistration for timepoint " + timepointId + " in the source sequence." ); 150 | registrations.add( new ViewRegistration( timepointId, setupId, r.getModel() ) ); 151 | } 152 | perSetupMipmapInfo.put( setupId, new ExportMipmapInfo( resolutions, subdivisions ) ); 153 | return setupId; 154 | } 155 | 156 | /** 157 | * Create a {@link SpimDataMinimal} for the setups currently aggregated. 158 | * This can be used to write the sequence (see {@link WriteSequenceToHdf5} 159 | * and 160 | * {@link XmlIoAbstractSpimData#toXml(mpicbg.spim.data.generic.AbstractSpimData, File)} 161 | * 162 | * @param basePath 163 | * @return a {@link SpimDataMinimal} with the currently aggregated setups. 164 | */ 165 | public SpimDataMinimal createSpimData( final File basePath ) 166 | { 167 | final SequenceDescriptionMinimal seq = new SequenceDescriptionMinimal( timepoints, Entity.idMap( setups ), imgLoader, null ); 168 | return new SpimDataMinimal( basePath, seq, new ViewRegistrations( registrations ) ); 169 | } 170 | 171 | /** 172 | * Get the aggregated per-setup {@link ExportMipmapInfo}s. 173 | */ 174 | public Map< Integer, ExportMipmapInfo > getPerSetupMipmapInfo() 175 | { 176 | return perSetupMipmapInfo; 177 | } 178 | 179 | /** 180 | * Add the setup (angle) of the given {@link SpimRegistrationSequence} 181 | * to this collection. In the viewer format, every image is stored in 182 | * multiple resolutions. The resolutions are described as int[] arrays 183 | * defining multiple of original pixel size in every dimension. For 184 | * example {1,1,1} is the original resolution, {4,4,2} is downsampled by 185 | * factor 4 in X and Y and factor 2 in Z. Each resolution of the image 186 | * is stored as a chunked three-dimensional array (each chunk 187 | * corresponds to one cell of a {@link CellImg} when the data is 188 | * loaded). The chunk sizes are defined by the subdivisions parameter 189 | * which is an array of int[], one per resolution. Each int[] array 190 | * describes the X,Y,Z chunk size for one resolution. 191 | * 192 | * @param sequence 193 | * a registered spim sequence. see 194 | * {@link Scripting#createSpimRegistrationSequence(String, String, String, String, String, int, boolean, double)} 195 | * @param setupIndex 196 | * which of the setups of the spim sequence to add. 197 | * @param resolutionsString 198 | * the set of resolutions to store, formatted like 199 | * "{1,1,1}, {2,2,1}, {4,4,4}" where each "{...}" defines one 200 | * resolution. 201 | * @param subdivisionsString 202 | * the set of subdivisions to store, formatted like 203 | * "{32,32,32}, {16,16,8}, {8,8,8}" where each "{...}" 204 | * defines one subdivision. 205 | */ 206 | @Deprecated 207 | public void addSetup( final SpimRegistrationSequence sequence, final int setupIndex, final String resolutionsString, final String subdivisionsString ) 208 | { 209 | final AbstractSequenceDescription< ?, ?, ? > desc = sequence.getSequenceDescription(); 210 | final ViewRegistrations regs = sequence.getViewRegistrations(); 211 | final int[][] resolutions = PluginHelper.parseResolutionsString( resolutionsString ); 212 | final int[][] subdivisions = PluginHelper.parseResolutionsString( subdivisionsString ); 213 | if ( resolutions.length == 0 ) 214 | throw new RuntimeException( "Cannot parse mipmap resolutions" + resolutionsString ); 215 | if ( subdivisions.length == 0 ) 216 | throw new RuntimeException( "Cannot parse subdivisions " + subdivisionsString ); 217 | else if ( resolutions.length != subdivisions.length ) 218 | throw new RuntimeException( "mipmap resolutions and subdivisions must have the same number of elements" ); 219 | final BasicViewSetup setup = desc.getViewSetups().get( setupIndex ); 220 | add( setup, desc, regs, resolutions, subdivisions ); 221 | } 222 | 223 | /** 224 | * Add the setup (angle) of the given {@link SpimRegistrationSequence} 225 | * to this collection. In the viewer format, every image is stored in 226 | * multiple resolutions. The resolutions are described as int[] arrays 227 | * defining multiple of original pixel size in every dimension. For 228 | * example {1,1,1} is the original resolution, {4,4,2} is downsampled by 229 | * factor 4 in X and Y and factor 2 in Z. Each resolution of the image 230 | * is stored as a chunked three-dimensional array (each chunk 231 | * corresponds to one cell of a {@link CellImg} when the data is 232 | * loaded). The chunk sizes are defined by the subdivisions parameter 233 | * which is an array of int[], one per resolution. Each int[] array 234 | * describes the X,Y,Z chunk size for one resolution. 235 | * 236 | * @param sequence 237 | * a registered spim sequence. see 238 | * {@link Scripting#createSpimRegistrationSequence(String, String, String, String, String, int, boolean, double)} 239 | * @param setupIndex 240 | * which of the setups of the spim sequence to add. 241 | * @param resolutions 242 | * the set of resolutions to store. each nested int[] array 243 | * defines one resolution. 244 | * @param subdivisions 245 | * the set of subdivisions to store. each nested int[] array 246 | * defines one subdivision. 247 | */ 248 | @Deprecated 249 | public void addSetup( final SpimRegistrationSequence sequence, final int setupIndex, final int[][] resolutions, final int[][] subdivisions ) 250 | { 251 | final AbstractSequenceDescription< ?, ?, ? > desc = sequence.getSequenceDescription(); 252 | final ViewRegistrations regs = sequence.getViewRegistrations(); 253 | if ( resolutions.length != subdivisions.length ) 254 | throw new RuntimeException( "mipmap resolutions and subdivisions must have the same number of elements" ); 255 | final BasicViewSetup setup = desc.getViewSetups().get( setupIndex ); 256 | add( setup, desc, regs, resolutions, subdivisions ); 257 | } 258 | 259 | /** 260 | * Add all setups (angles) of the given {@link SpimRegistrationSequence} 261 | * to this collection. In the viewer format, every image is stored in 262 | * multiple resolutions. The resolutions are described as int[] arrays 263 | * defining multiple of original pixel size in every dimension. For 264 | * example {1,1,1} is the original resolution, {4,4,2} is downsampled by 265 | * factor 4 in X and Y and factor 2 in Z. Each resolution of the image 266 | * is stored as a chunked three-dimensional array (each chunk 267 | * corresponds to one cell of a {@link CellImg} when the data is 268 | * loaded). The chunk sizes are defined by the subdivisions parameter 269 | * which is an array of int[], one per resolution. Each int[] array 270 | * describes the X,Y,Z chunk size for one resolution. 271 | * 272 | * @param sequence 273 | * a registered spim sequence. see 274 | * {@link Scripting#createSpimRegistrationSequence(String, String, String, String, String, int, boolean, double)} 275 | * @param resolutionsString 276 | * the set of resolutions to store, formatted like 277 | * "{1,1,1}, {2,2,1}, {4,4,4}" where each "{...}" defines one 278 | * resolution. 279 | * @param subdivisionsString 280 | * the set of subdivisions to store, formatted like 281 | * "{32,32,32}, {16,16,8}, {8,8,8}" where each "{...}" 282 | * defines one subdivision. 283 | */ 284 | @Deprecated 285 | public void addSetups( final SpimRegistrationSequence sequence, final String resolutionsString, final String subdivisionsString ) 286 | { 287 | for ( int s = 0; s < sequence.getSequenceDescription().getViewSetups().size(); ++s ) 288 | addSetup( sequence, s, resolutionsString, subdivisionsString ); 289 | } 290 | 291 | /** 292 | * Add all setups (angles) of the given {@link SpimRegistrationSequence} 293 | * to this collection. In the viewer format, every image is stored in 294 | * multiple resolutions. The resolutions are described as int[] arrays 295 | * defining multiple of original pixel size in every dimension. For 296 | * example {1,1,1} is the original resolution, {4,4,2} is downsampled by 297 | * factor 4 in X and Y and factor 2 in Z. Each resolution of the image 298 | * is stored as a chunked three-dimensional array (each chunk 299 | * corresponds to one cell of a {@link CellImg} when the data is 300 | * loaded). The chunk sizes are defined by the subdivisions parameter 301 | * which is an array of int[], one per resolution. Each int[] array 302 | * describes the X,Y,Z chunk size for one resolution. 303 | * 304 | * @param sequence 305 | * a registered spim sequence. see 306 | * {@link Scripting#createSpimRegistrationSequence(String, String, String, String, String, int, boolean, double)} 307 | * @param resolutions 308 | * the set of resolutions to store. each nested int[] array 309 | * defines one resolution. 310 | * @param subdivisions 311 | * the set of subdivisions to store. each nested int[] array 312 | * defines one subdivision. 313 | */ 314 | @Deprecated 315 | public void addSetups( final SpimRegistrationSequence sequence, final int[][] resolutions, final int[][] subdivisions ) 316 | { 317 | for ( int s = 0; s < sequence.getSequenceDescription().getViewSetups().size(); ++s ) 318 | addSetup( sequence, s, resolutions, subdivisions ); 319 | } 320 | 321 | /** 322 | * Add result of SPIM fusion or deconvolution as a setup to this 323 | * collection. In the viewer format, every image is stored in multiple 324 | * resolutions. The resolutions are described as int[] arrays defining 325 | * multiple of original pixel size in every dimension. For example 326 | * {1,1,1} is the original resolution, {4,4,2} is downsampled by factor 327 | * 4 in X and Y and factor 2 in Z. Each resolution of the image is 328 | * stored as a chunked three-dimensional array (each chunk corresponds 329 | * to one cell of a {@link CellImg} when the data is loaded). The chunk 330 | * sizes are defined by the subdivisions parameter which is an array of 331 | * int[], one per resolution. Each int[] array describes the X,Y,Z chunk 332 | * size for one resolution. 333 | * 334 | * @param fusionResult 335 | * a fused spim sequence. 336 | * {@link Scripting#createFusionResult(SpimRegistrationSequence, String, String, int, double, double, Map)} 337 | * @param resolutionsString 338 | * the set of resolutions to store, formatted like 339 | * "{1,1,1}, {2,2,1}, {4,4,4}" where each "{...}" defines one 340 | * resolution. 341 | * @param subdivisionsString 342 | * the set of subdivisions to store, formatted like 343 | * "{32,32,32}, {16,16,8}, {8,8,8}" where each "{...}" 344 | * defines one subdivision. 345 | */ 346 | @Deprecated 347 | public void addSetups( final FusionResult fusionResult, final String resolutionsString, final String subdivisionsString ) 348 | { 349 | final int[][] resolutions = PluginHelper.parseResolutionsString( resolutionsString ); 350 | final int[][] subdivisions = PluginHelper.parseResolutionsString( subdivisionsString ); 351 | if ( resolutions.length == 0 ) 352 | throw new RuntimeException( "Cannot parse mipmap resolutions" + resolutionsString ); 353 | if ( subdivisions.length == 0 ) 354 | throw new RuntimeException( "Cannot parse subdivisions " + subdivisionsString ); 355 | else if ( resolutions.length != subdivisions.length ) 356 | throw new RuntimeException( "mipmap resolutions and subdivisions must have the same number of elements" ); 357 | for( final BasicViewSetup setup : fusionResult.getSequenceDescription().getViewSetupsOrdered() ) 358 | add( setup, fusionResult.getSequenceDescription(), fusionResult.getViewRegistrations(), resolutions, subdivisions ); 359 | } 360 | 361 | /** 362 | * Add result of SPIM fusion or deconvolution as a setup to this 363 | * collection. In the viewer format, every image is stored in multiple 364 | * resolutions. The resolutions are described as int[] arrays defining 365 | * multiple of original pixel size in every dimension. For example 366 | * {1,1,1} is the original resolution, {4,4,2} is downsampled by factor 367 | * 4 in X and Y and factor 2 in Z. Each resolution of the image is 368 | * stored as a chunked three-dimensional array (final each chunk 369 | * corresponds to one cell of a {@link CellImg} when the data is 370 | * loaded). The chunk sizes are defined by the subdivisions parameter 371 | * which is an array of int[], one per resolution. Each int[] array 372 | * describes the X,Y,Z chunk size for one resolution. 373 | * 374 | * @param fusionResult 375 | * a fused spim sequence. 376 | * {@link Scripting#createSpimRegistrationSequence(String, String, String, String, String, int, boolean, double)} 377 | * @param resolutions 378 | * the set of resolutions to store. each nested int[] array 379 | * defines one resolution. 380 | * @param subdivisions 381 | * the set of subdivisions to store. each nested int[] array 382 | * defines one subdivision. 383 | */ 384 | @Deprecated 385 | public void addSetups( final FusionResult fusionResult, final int[][] resolutions, final int[][] subdivisions ) 386 | { 387 | if ( resolutions.length != subdivisions.length ) 388 | throw new RuntimeException( "mipmap resolutions and subdivisions must have the same number of elements" ); 389 | for( final BasicViewSetup setup : fusionResult.getSequenceDescription().getViewSetupsOrdered() ) 390 | add( setup, fusionResult.getSequenceDescription(), fusionResult.getViewRegistrations(), resolutions, subdivisions ); 391 | } 392 | } 393 | --------------------------------------------------------------------------------