From d1df365e4e07c67b6b11d4f98fb0d794cafc78e7 Mon Sep 17 00:00:00 2001
From: David Hammer <dhammer@mailbox.org>
Date: Fri, 28 Jan 2022 13:50:11 +0100
Subject: [PATCH] Allow downsampling like in FemDataAssembler

---
 src/calng/SimpleAssembler.py | 56 +++++++++++++++++++++++++++++++++++-
 1 file changed, 55 insertions(+), 1 deletion(-)

diff --git a/src/calng/SimpleAssembler.py b/src/calng/SimpleAssembler.py
index 5081e3ad..3a7708f7 100644
--- a/src/calng/SimpleAssembler.py
+++ b/src/calng/SimpleAssembler.py
@@ -3,7 +3,6 @@ import pickle
 import re
 
 import numpy as np
-from calng import utils
 from karabo.bound import (
     FLOAT_ELEMENT,
     IMAGEDATA_ELEMENT,
@@ -12,6 +11,7 @@ from karabo.bound import (
     OUTPUT_CHANNEL,
     OVERWRITE_ELEMENT,
     STRING_ELEMENT,
+    UINT32_ELEMENT,
     UINT64_ELEMENT,
     ChannelMetaData,
     Dims,
@@ -42,6 +42,7 @@ preview_schema = Schema()
 xtdf_source_re = re.compile(r".*\/DET\/(\d+)CH0:xtdf")
 daq_source_re = re.compile(r".*\/DET\/.*?(\d+):daqOutput")
 
+
 # TODO: merge scene with TrainMatcher's nice overview
 @KARABO_CLASSINFO("SimpleAssembler", deviceVersion)
 class SimpleAssembler(TrainMatcher.TrainMatcher):
@@ -81,6 +82,28 @@ class SimpleAssembler(TrainMatcher.TrainMatcher):
             .defaultValue("image.data")
             .commit(),
 
+            UINT32_ELEMENT(expected)
+            .key("downsamplingFactor")
+            .description(
+                "If greater than 1, the assembled image will be downsampled by this "
+                "factor in x and y dimensions before sending. This is only to save "
+                "bandwidth in case GUI updates start lagging."
+            )
+            .assignmentOptional()
+            .defaultValue(1)
+            .options("1,2,4,8")
+            .reconfigurable()
+            .commit(),
+
+            STRING_ELEMENT(expected)
+            .key("downsamplingFunction")
+            .description("Reduction function used during downsampling.")
+            .assignmentOptional()
+            .defaultValue("nanmax")
+            .options("nanmax,nanmean,nanmin,nanmedian")
+            .reconfigurable()
+            .commit(),
+
             INPUT_CHANNEL(expected)
             .key("geometryInput")
             .displayedName("Geometry input")
@@ -157,6 +180,14 @@ class SimpleAssembler(TrainMatcher.TrainMatcher):
         # TODO: reusable output buffer to save on allocation
         assembled, _ = self.geometry.position_modules_fast(self.input_buffer)
 
+        downsampling_factor = self.get("downsamplingFactor")
+        if downsampling_factor > 1:
+            assembled = downsample_2d(
+                assembled,
+                downsampling_factor,
+                reduction_fun=getattr(np, self.get("downsamplingFunction"))
+            )
+
         # TODO: optionally include control data
         out_hash = Hash(
             "image",
@@ -197,3 +228,26 @@ class SimpleAssembler(TrainMatcher.TrainMatcher):
 
         self.log.WARN(f"Couldn't figure out index for source {source}")
         return 0
+
+
+def downsample_2d(arr, factor, reduction_fun=np.nanmax):
+    """Generalization of downsampling from FemDataAssembler
+
+    Expects first two dimensions of arr to be multiple of 2 ** factor
+    Useful if you're sitting at home and ssh connection is slow to get full-resolution
+    previews."""
+
+    for i in range(factor // 2):
+        arr = reduction_fun(
+            (
+                arr[:-1:2],
+                arr[1::2],
+            ), axis=0
+        )
+        arr = reduction_fun(
+            (
+                arr[:, :-1:2],
+                arr[:, 1::2],
+            ), axis=0
+        )
+    return arr
-- 
GitLab