diff --git a/src/main/java/cz/it4i/qcmp/io/CallbackInputData.java b/src/main/java/cz/it4i/qcmp/io/CallbackInputData.java
new file mode 100644
index 0000000000000000000000000000000000000000..ef046c29022f0cd371344c1cd9f01ddd370e1e16
--- /dev/null
+++ b/src/main/java/cz/it4i/qcmp/io/CallbackInputData.java
@@ -0,0 +1,43 @@
+package cz.it4i.qcmp.io;
+
+import cz.it4i.qcmp.data.V3i;
+
+public class CallbackInputData extends InputData {
+
+    private final String cacheHint;
+    private final LoadCallback pixelLoadCallback;
+
+    /**
+     * Interface defining method for pixel data loading.
+     */
+    public interface LoadCallback {
+        /**
+         * Callback to load pixel value at specified position.
+         *
+         * @param x X position.
+         * @param y Y position.
+         * @param z Z position.
+         * @return Pixel value at specified position.
+         */
+        int getValueAt(final int x, final int y, final int z);
+    }
+
+    public CallbackInputData(final LoadCallback pixelLoadCallback,
+                             final V3i dimensions,
+                             final String cacheHint) {
+        this.pixelLoadCallback = pixelLoadCallback;
+        this.cacheHint = cacheHint;
+        setDataLoaderType(DataLoaderType.CallbackLoader);
+        setPixelType(PixelType.Gray16);
+        setDimension(dimensions);
+    }
+
+    @Override
+    public String getCacheFileName() {
+        return cacheHint;
+    }
+
+    public final LoadCallback getPixelLoadCallback() {
+        return pixelLoadCallback;
+    }
+}
diff --git a/src/main/java/cz/it4i/qcmp/io/InputData.java b/src/main/java/cz/it4i/qcmp/io/InputData.java
index d6a3fe37d338a1b8587a7ed3966c6b02bb5476ce..0cda8309055e301d3aa15236a757a811d9227c5e 100644
--- a/src/main/java/cz/it4i/qcmp/io/InputData.java
+++ b/src/main/java/cz/it4i/qcmp/io/InputData.java
@@ -11,7 +11,8 @@ public abstract class InputData {
         RawDataLoader,
         SCIFIOLoader,
         ImageJBufferLoader,
-        FlatBufferLoader
+        FlatBufferLoader,
+        CallbackLoader
     }
 
     public enum PixelType {
diff --git a/src/main/java/cz/it4i/qcmp/io/loader/CallbackLoader.java b/src/main/java/cz/it4i/qcmp/io/loader/CallbackLoader.java
new file mode 100644
index 0000000000000000000000000000000000000000..629ac238110453a8e7aacbf0ff7f34432fa3df1f
--- /dev/null
+++ b/src/main/java/cz/it4i/qcmp/io/loader/CallbackLoader.java
@@ -0,0 +1,105 @@
+package cz.it4i.qcmp.io.loader;
+
+import cz.it4i.qcmp.data.Range;
+import cz.it4i.qcmp.data.V2i;
+import cz.it4i.qcmp.data.V3i;
+import cz.it4i.qcmp.io.CallbackInputData;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+public class CallbackLoader extends BasicLoader implements IPlaneLoader {
+
+    private final CallbackInputData callbackInputData;
+    private final CallbackInputData.LoadCallback pixelLoad;
+
+    public CallbackLoader(final CallbackInputData callbackInputData) {
+        super(callbackInputData.getDimensions());
+        this.callbackInputData = callbackInputData;
+        this.pixelLoad = callbackInputData.getPixelLoadCallback();
+    }
+
+    @Override
+    protected int valueAt(final int plane, final int x, final int y, final int width) {
+        return pixelLoad.getValueAt(x, y, plane);
+    }
+
+
+    @Override
+    public int[] loadPlaneData(final int plane) {
+        final int planePixelCount = dims.getX() * dims.getY();
+        final int[] planeData = new int[planePixelCount];
+
+        int index = 0;
+        for (int y = 0; y < dims.getY(); y++) {
+            for (int x = 0; x < dims.getX(); x++) {
+                planeData[index++] = pixelLoad.getValueAt(x, y, plane);
+            }
+        }
+        return planeData;
+    }
+
+    @Override
+    public int[] loadPlanesU16Data(final int[] planes) throws IOException {
+        if (planes.length < 1) {
+            return new int[0];
+        } else if (planes.length == 1) {
+            return loadPlaneData(planes[0]);
+        } else if (planes.length == dims.getZ()) {
+            return loadAllPlanesU16Data();
+        }
+        final int planePixelCount = dims.getX() * dims.getY();
+        final long totalValueCount = (long) planePixelCount * (long) planes.length;
+        if (totalValueCount > (long) Integer.MAX_VALUE) {
+            throw new IOException("Unable to load image data for planes, file size is too big.");
+        }
+
+        Arrays.sort(planes);
+
+        final int[] destBuffer = new int[(int) totalValueCount];
+        int index = 0;
+        for (final int plane : planes) {
+            for (int y = 0; y < dims.getY(); y++) {
+                for (int x = 0; x < dims.getX(); x++) {
+                    destBuffer[index++] = pixelLoad.getValueAt(x, y, plane);
+                }
+            }
+        }
+        return destBuffer;
+    }
+
+    @Override
+    public int[] loadAllPlanesU16Data() throws IOException {
+        final long totalValueCount = dims.multiplyTogether();
+        if (totalValueCount > (long) Integer.MAX_VALUE) {
+            throw new IOException("Unable to load all image data, file size is too big.");
+        }
+
+        final int[] destBuffer = new int[(int) totalValueCount];
+        int index = 0;
+        for (int z = 0; z < dims.getZ(); z++) {
+            for (int y = 0; y < dims.getY(); y++) {
+                for (int x = 0; x < dims.getX(); x++) {
+                    destBuffer[index++] = pixelLoad.getValueAt(x, y, z);
+                }
+            }
+        }
+
+        return destBuffer;
+    }
+
+    @Override
+    public int[][] loadRowVectors(final int vectorSize, final Range<Integer> planeRange) {
+        return loadRowVectorsImplByValueAt(vectorSize, planeRange);
+    }
+
+    @Override
+    public int[][] loadBlocks(final V2i blockDim, final Range<Integer> planeRange) {
+        return loadBlocksImplByValueAt(blockDim, planeRange);
+    }
+
+    @Override
+    public int[][] loadVoxels(final V3i voxelDim, final Range<Integer> planeRange) {
+        return loadVoxelsImplByValueAt(voxelDim, planeRange);
+    }
+}
diff --git a/src/main/java/cz/it4i/qcmp/io/loader/PlaneLoaderFactory.java b/src/main/java/cz/it4i/qcmp/io/loader/PlaneLoaderFactory.java
index e35ff329969de533a8b22e82f2660cd29ffc3bf2..ce597f26883727e3c6f60b20241a284a82acd6c3 100644
--- a/src/main/java/cz/it4i/qcmp/io/loader/PlaneLoaderFactory.java
+++ b/src/main/java/cz/it4i/qcmp/io/loader/PlaneLoaderFactory.java
@@ -1,9 +1,6 @@
 package cz.it4i.qcmp.io.loader;
 
-import cz.it4i.qcmp.io.BufferInputData;
-import cz.it4i.qcmp.io.FileInputData;
-import cz.it4i.qcmp.io.FlatBufferInputData;
-import cz.it4i.qcmp.io.InputData;
+import cz.it4i.qcmp.io.*;
 
 public final class PlaneLoaderFactory {
 
@@ -24,6 +21,8 @@ public final class PlaneLoaderFactory {
                 return new ImageJBufferLoader((BufferInputData) inputDataInfo);
             case FlatBufferLoader:
                 return new FlatBufferLoader((FlatBufferInputData) inputDataInfo);
+            case CallbackLoader:
+                return new CallbackLoader((CallbackInputData) inputDataInfo);
             default:
                 throw new Exception("Unsupported data loader.");
         }