diff --git a/src/main/java/fr/pasteur/ida/zellige/steps/selection/util/Unbinning.java b/src/main/java/fr/pasteur/ida/zellige/steps/selection/util/Unbinning.java
new file mode 100644
index 0000000000000000000000000000000000000000..a459cd24caccc9d677e90345d92c0d80cbda98ba
--- /dev/null
+++ b/src/main/java/fr/pasteur/ida/zellige/steps/selection/util/Unbinning.java
@@ -0,0 +1,139 @@
+package fr.pasteur.ida.zellige.steps.selection.util;
+
+import io.scif.img.ImgOpener;
+import net.imagej.ImageJ;
+import net.imglib2.RandomAccess;
+import net.imglib2.RandomAccessibleInterval;
+import net.imglib2.img.Img;
+import net.imglib2.img.ImgFactory;
+import net.imglib2.img.display.imagej.ImageJFunctions;
+import net.imglib2.type.NativeType;
+import net.imglib2.type.numeric.RealType;
+import net.imglib2.type.numeric.integer.UnsignedByteType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static fr.pasteur.ida.zellige.steps.Utils.setPosition;
+import static fr.pasteur.ida.zellige.steps.Utils.setPositionAndGet;
+
+/**
+ * Generates an unbinned image, according to the input image and bin value. Only for 2D images.
+ * @param <T> the image type
+ */
+public class Unbinning< T extends RealType< T > & NativeType< T > >
+{
+    private final static Logger LOGGER = LoggerFactory.getLogger( Unbinning.class );
+    private final Img< T > output;
+    private final RandomAccessibleInterval< T > binned;
+    private final int bin;
+
+
+    /**
+     *
+     * @param input a binned image
+     * @param factory the input image factory
+     * @param bin the bin size
+     * @return the input image unbinned
+     * @param <T> input and output image type
+     */
+    public static < T extends RealType< T > & NativeType< T> > Img<T> run( RandomAccessibleInterval<T> input, ImgFactory<T> factory,  int bin)
+    {
+        Img <T> output = factory.create( input.dimension( 0 ) * bin,  input.dimension( 1 ) * bin);
+        Unbinning<T> unbinning = new Unbinning<>( input, bin, output );
+        unbinning.run();
+        return output;
+    }
+
+    /**
+     * Constructor
+     * @param input  a binned image
+     * @param bin the bin size
+     * @param output he input image unbinned
+     */
+    public Unbinning( RandomAccessibleInterval< T > input, int bin, Img< T > output )
+    {
+        this.binned = input;
+        this.bin = bin;
+        this.output = output;
+    }
+
+    /**
+     * Parses the binned image and assign the corresponding values to the unbinned image.
+     */
+    public void run()
+    {
+        LOGGER.debug( "Starting  process..." );
+
+        double startTime = System.currentTimeMillis();
+
+        RandomAccess<T> binnedAccess = binned.randomAccess();
+        RandomAccess<T> outputAccess = output.randomAccess();
+
+        for ( int y = 0; y < binned.dimension( 1 ) ; y++ )
+        {
+            for ( int x = 0; x < binned.dimension( 0 ); x ++)
+            {
+                T value =  setPositionAndGet( binnedAccess, x, y );
+                applyValueToGrid( x, y, bin, outputAccess,  value);
+            }
+        }
+        double endTime = System.currentTimeMillis();
+
+        LOGGER.debug( "Process completed in {}s" , (endTime-startTime)/1000);
+    }
+
+
+    /**
+     *
+     * @param X the starting x value
+     * @param Y the starting y value
+     * @param bin the bin size
+     * @param access the unbinned image {@link RandomAccess<T>}
+     * @param value the pixel value to assign
+     */
+    private void applyValueToGrid( int X, int Y, int bin, RandomAccess< T > access, T value )
+    {
+        for ( int y = Y * bin; y < Y * bin + bin; y++ )
+        {
+            for ( int x = X * bin; x < X * bin + bin; x++ )
+            {
+                setPosition( access, x, y );
+                access.get().set( value );
+            }
+        }
+
+    }
+
+    public static void main( String[] args )
+    {
+        ImageJ ij = new ImageJ();
+        ij.launch( args );
+        final ImgOpener io = new ImgOpener();
+        @SuppressWarnings( "unchecked" ) final Img< UnsignedByteType > kernel = ( Img< UnsignedByteType > ) io.openImgs( "doc/Scan1_volume_crop.tif" ).get( 0 );
+        ImageJFunctions.show( kernel, "original" );
+        double time1 = System.currentTimeMillis();
+        int bin = 4;
+        Img< UnsignedByteType > output = Binning.binning( kernel, bin);
+        double time2 = System.currentTimeMillis();
+        LOGGER.debug( "Multithreading time  = {}s", ( time2 - time1 ) / 1000 );
+        assert output != null;
+        ImageJFunctions.show( output, "output " );
+
+        Img<UnsignedByteType> i =kernel.factory().create( output.dimension( 0 ), output.dimension(1) );
+        RandomAccess<UnsignedByteType> access = i.randomAccess();
+        RandomAccess<UnsignedByteType> cursor = output.randomAccess();
+        for ( int y = 0; y < i.dimension( 1 ); y++ )
+        {
+            for ( int x = 0; x < i.dimension( 0 ); x++ )
+            {
+                setPosition( access, x, y );
+                access.get().setReal(setPositionAndGet(cursor, x, y).getRealDouble() );
+            }
+        }
+
+        Img<UnsignedByteType> output_unbinned = Unbinning.run(i, i.factory(), bin);
+        ImageJFunctions.show( output_unbinned, "unbinned 1" );
+    }
+
+}
+