/**
* License: GPL
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package mpicbg.trakem2.align;
import ij.process.ColorProcessor;
import ij.process.FloatProcessor;
import ini.trakem2.Project;
import ini.trakem2.display.Patch;
import ini.trakem2.persistence.FSLoader;
import ini.trakem2.persistence.Loader;
import ini.trakem2.utils.Utils;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import mpicbg.imagefeatures.Feature;
import mpicbg.models.AbstractModel;
import mpicbg.models.HomographyModel2D;
import mpicbg.models.PointMatch;
import mpicbg.models.RigidModel2D;
import mpicbg.models.SimilarityModel2D;
import mpicbg.models.TranslationModel2D;
import mpicbg.trakem2.transform.AffineModel2D;
import mpicbg.trakem2.transform.CoordinateTransform;
import mpicbg.trakem2.transform.CoordinateTransformList;
/**
*
*
* @author Stephan Saalfeld saalfeld@mpi-cbg.de
* @version 0.1a
*/
public class Util
{
final static protected class Features implements Serializable
{
private static final long serialVersionUID = -5707602842402593215L;
final Object key;
final ArrayList< Feature > features;
Features( final Object key, final ArrayList< Feature > features )
{
this.key = key;
this.features = features;
}
}
final static protected class PointMatches implements Serializable
{
private static final long serialVersionUID = 7905488521562090982L;
final Object key;
final ArrayList< PointMatch > pointMatches;
PointMatches( final Object key, final ArrayList< PointMatch > pointMatches )
{
this.key = key;
this.pointMatches = pointMatches;
}
}
/**
* Save a {@link Collection} of {@link Feature Features} to the TrakEM2
* project folder. The saved file contains a key {@link Object} which
* may specify the properties of the {@link Feature} {@link Collection}.
*
* @param project
* @param key
* @param prefix
* @param id
* @param f
* @return
*/
final static public boolean serializeFeatures(
final Project project,
final Object key,
final String prefix,
final long id,
final Collection< Feature > f )
{
final ArrayList< Feature > list = new ArrayList< Feature >();
list.addAll( f );
final String name = prefix == null ? "features" : prefix + ".features";
final Loader loader = project.getLoader();
final Features fe = new Features( key, list );
return loader.serialize(
fe,
new StringBuilder( loader.getUNUIdFolder() )
.append( "features.ser/" )
.append( FSLoader.createIdPath( Long.toString( id ), name, ".ser" ) ).toString() );
}
/**
* Retrieve a {@link Collection} of {@link Feature Features} from the
* TrakEM2 project folder. The {@link Collection} is only returned if
* <ol>
* <li>the file as identified by project, prefix, and id exists and</li>
* <li>its contained key {@link Object#equals(Object) equals} key.</li>
* </ol>
*
* @param project
* @param key
* @param prefix
* @param id
* @return
*/
final static protected ArrayList< Feature > deserializeFeatures(
final Project project,
final Object key,
final String prefix,
final long id )
{
final String name = prefix == null ? "features" : prefix + ".features";
final Loader loader = project.getLoader();
final Object ob = loader.deserialize(
new StringBuilder( loader.getUNUIdFolder() )
.append( "features.ser/" )
.append( FSLoader.createIdPath( Long.toString( id ), name, ".ser" ) ).toString() );
if ( ob != null )
{
try
{
final Features fe = ( Features )ob;
// Utils.log( fe.key == null ? "key is null" : key.equals( fe.key ) ? "key is equal" : "key is not equal" );
if ( fe.key != null && key.equals( fe.key ) )
return fe.features;
}
catch ( final Exception e )
{
Utils.log( "Exception during feature deserialization." );
e.printStackTrace();
}
}
else
Utils.log( "features file null" );
return null;
}
/**
* Save a {@link Collection} of {@link PointMatch PointMatches} two-sided.
* Creates two serialization files which is desperately required to clean
* up properly invalid serializations on change of a {@link Patch}.
*
* @param project
* @param key
* @param prefix
* @param id1
* @param id2
* @param m
* @return
*/
final static public boolean serializePointMatches(
final Project project,
final Object key,
final String prefix,
final long id1,
final long id2,
final Collection< PointMatch > m )
{
final ArrayList< PointMatch > list = new ArrayList< PointMatch >();
list.addAll( m );
final ArrayList< PointMatch > tsil = new ArrayList< PointMatch >();
PointMatch.flip( m, tsil );
final String name = prefix == null ? "pointmatches" : prefix + ".pointmatches";
final Loader loader = project.getLoader();
return
loader.serialize(
new PointMatches( key, list ),
new StringBuilder( loader.getUNUIdFolder() )
.append( "pointmatches.ser/" )
.append( FSLoader.createIdPath( Long.toString( id1 ) + "_" + Long.toString( id2 ), name, ".ser" ) ).toString() ) &&
loader.serialize(
new PointMatches( key, tsil ),
new StringBuilder( loader.getUNUIdFolder() )
.append( "pointmatches.ser/" )
.append( FSLoader.createIdPath( Long.toString( id2 ) + "_" + Long.toString( id1 ), name, ".ser" ) ).toString() );
}
final static protected ArrayList< PointMatch > deserializePointMatches(
final Project project,
final Object key,
final String prefix,
final long id1,
final long id2 )
{
final String name = prefix == null ? "pointmatches" : prefix + ".pointmatches";
final Loader loader = project.getLoader();
final Object ob = loader.deserialize(
new StringBuilder( loader.getUNUIdFolder() )
.append( "pointmatches.ser/" )
.append( FSLoader.createIdPath( Long.toString( id1 ) + "_" + Long.toString( id2 ), name, ".ser" ) ).toString() );
if ( null != ob )
{
try
{
final PointMatches pm = ( PointMatches )ob;
if ( pm.key != null && key.equals( pm.key ) )
return pm.pointMatches;
}
catch ( final Exception e )
{
Utils.log( "Exception during pointmatch deserialization." );
e.printStackTrace();
}
}
return null;
}
/**
* <p>Transfer and ARGB AWT image into a FloatProcessor with its grey values
* and a FloatProcessor with its alpha values as [0...1].</p>
*
* <p><em>Note</em>, this method currently relies on how ImageJ reuses the
* pixels of an AWT image as generated by {@link Loader#getFlatAWTImage(ini.trakem2.display.Layer, java.awt.Rectangle, double, int, int, Class, java.util.List, boolean, java.awt.Color, ini.trakem2.display.Displayable) Loader.getFlatAWTImage(...)}
* for creating a ColorProcessor. This may change in the future as have
* many things in the past. This method is then the place to fix it.
*
* @param input
* @param output
* @param alpha
*/
final static public void imageToFloatAndMask( final Image input, final FloatProcessor output, final FloatProcessor alpha )
{
final ColorProcessor cp = new ColorProcessor( input );
final int[] inputPixels = ( int[] )cp.getPixels();
for ( int i = 0; i < inputPixels.length; ++i )
{
final int argb = inputPixels[ i ];
final int a = ( argb >> 24 ) & 0xff;
final int r = ( argb >> 16 ) & 0xff;
final int g = ( argb >> 8 ) & 0xff;
final int b = argb & 0xff;
final float v = ( r + g + b ) / ( float )3;
final float w = a / ( float )255;
output.setf( i, v );
alpha.setf( i, w );
}
}
final static public void applyLayerTransformToPatch( final Patch patch, final CoordinateTransform ct ) throws Exception
{
final Rectangle pbox = patch.getCoordinateTransformBoundingBox();
final AffineTransform pat = new AffineTransform();
pat.translate( -pbox.x, -pbox.y );
pat.preConcatenate( patch.getAffineTransform() );
final AffineModel2D toWorld = new AffineModel2D();
toWorld.set( pat );
final CoordinateTransformList< CoordinateTransform > ctl = new CoordinateTransformList< CoordinateTransform >();
ctl.add( toWorld );
ctl.add( ct );
ctl.add( toWorld.createInverse() );
patch.appendCoordinateTransform( ctl );
}
final static public AbstractModel< ? > createModel( final int modelIndex )
{
switch ( modelIndex )
{
case 0:
return new TranslationModel2D();
case 1:
return new RigidModel2D();
case 2:
return new SimilarityModel2D();
case 3:
return new AffineModel2D();
case 4:
return new HomographyModel2D();
default:
return null;
}
}
}