From 2c24aef51c133e5c712d708897d6be9ff7362d24 Mon Sep 17 00:00:00 2001
From: Rainer Kartmann <rainer.kartmann@kit.edu>
Date: Wed, 1 Sep 2021 14:46:33 +0200
Subject: [PATCH] Handle image in visitors and InstanceView

---
 .../armem_gui/instance/ImageView.cpp          |   1 -
 .../libraries/armem_gui/instance/ImageView.h  |   4 +-
 .../armem_gui/instance/InstanceView.cpp       | 119 ++++++++++++++++--
 .../display_visitors/DataDisplayVisitor.cpp   |   3 +-
 .../TypedDataDisplayVisitor.cpp               |   8 ++
 .../TypedDataDisplayVisitor.h                 |   1 +
 .../navigator/visitors/TypedDataVisitor.cpp   |   4 +
 .../navigator/visitors/TypedDataVisitor.h     |  11 ++
 8 files changed, 137 insertions(+), 14 deletions(-)

diff --git a/source/RobotAPI/libraries/armem_gui/instance/ImageView.cpp b/source/RobotAPI/libraries/armem_gui/instance/ImageView.cpp
index 05979fe8f..8c75fbd87 100644
--- a/source/RobotAPI/libraries/armem_gui/instance/ImageView.cpp
+++ b/source/RobotAPI/libraries/armem_gui/instance/ImageView.cpp
@@ -13,7 +13,6 @@
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
-* @package    ArmarX::
 * @author     Rainer Kartmann ( rainer dot kartmann at kit dot edu)
 * @date       2021
 * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
diff --git a/source/RobotAPI/libraries/armem_gui/instance/ImageView.h b/source/RobotAPI/libraries/armem_gui/instance/ImageView.h
index d684bbccc..4b85e093a 100644
--- a/source/RobotAPI/libraries/armem_gui/instance/ImageView.h
+++ b/source/RobotAPI/libraries/armem_gui/instance/ImageView.h
@@ -13,7 +13,6 @@
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
-* @package    ArmarX::
 * @author     Rainer Kartmann ( rainer dot kartmann at kit dot edu)
 * @date       2021
 * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
@@ -29,6 +28,9 @@
 namespace armarx::armem::gui::instance
 {
 
+    /**
+     * @brief A widget drawing an image in itself.
+     */
     class ImageView : public QWidget
     {
         Q_OBJECT
diff --git a/source/RobotAPI/libraries/armem_gui/instance/InstanceView.cpp b/source/RobotAPI/libraries/armem_gui/instance/InstanceView.cpp
index 8fda3cc4b..8108d64a9 100644
--- a/source/RobotAPI/libraries/armem_gui/instance/InstanceView.cpp
+++ b/source/RobotAPI/libraries/armem_gui/instance/InstanceView.cpp
@@ -15,6 +15,7 @@
 #include <QVBoxLayout>
 
 #include <SimoxUtility/algorithm/string.h>
+#include <SimoxUtility/color/cmaps.h>
 
 #include <ArmarXCore/core/exceptions/local/ExpressionException.h>
 
@@ -266,6 +267,7 @@ namespace armarx::armem::gui::instance
         aron::type::Descriptor type = static_cast<aron::type::Descriptor>(item->data(int(Columns::TYPE), Qt::UserRole).toInt());
         switch (type)
         {
+            case aron::type::Descriptor::eImage:
             case aron::type::Descriptor::eIVTCByteImage:
             {
                 if (const std::optional<aron::Path> path = getElementPath(item))
@@ -395,11 +397,11 @@ namespace armarx::armem::gui::instance
 
     void InstanceView::showImageView(const aron::Path& elementPath)
     {
-        if (!currentInstance)
+        if (not currentInstance)
         {
             return;
         }
-        if (!imageView)
+        if (not imageView)
         {
             WidgetsWithToolbar* toolbar = new WidgetsWithToolbar();
 
@@ -425,15 +427,75 @@ namespace armarx::armem::gui::instance
     }
 
 
+    static
+    QImage convertDepth32ToRGB32(const aron::datanavigator::NDArrayNavigator& aron)
+    {
+        const std::vector<int> shape = aron.getDimensions();
+        ARMARX_CHECK_EQUAL(shape.size(), 3);
+        ARMARX_CHECK_EQUAL(shape.at(2), 4) << "Expected Depth32 image to have 4 bytes per pixel.";
+
+        const int rows = shape.at(0);
+        const int cols = shape.at(1);
+
+        // Rendering seems to be optimized for RGB32
+        // rows go along 0 = height, cols go along 1 = width
+        QImage image(cols, rows, QImage::Format::Format_RGB32);
+        const float* data = reinterpret_cast<float*>(aron.getData());
+
+        // Find data range and adapt cmap.
+        simox::ColorMap cmap = simox::color::cmaps::plasma();
+        float min = data[0];
+        float max = data[0];
+
+        {
+            const float* sourceRow = data;
+            for (int row = 0; row < rows; ++row)
+            {
+                for (int col = 0; col < cols; ++col)
+                {
+                    float value = sourceRow[col];
+                    min = std::min(min, value);
+                    max = std::max(max, value);
+                }
+                sourceRow += cols;
+            }
+            cmap.set_vlimits(min, max);
+        }
+        {
+            const float* sourceRow = data;
+
+            const int bytesPerLine = image.bytesPerLine();
+            uchar* targetRow = image.bits();
+
+            for (int row = 0; row < rows; ++row)
+            {
+                for (int col = 0; col < cols; ++col)
+                {
+                    float value = sourceRow[col];
+                    simox::Color color = cmap(value);
+                    targetRow[col*4 + 0] = color.b;
+                    targetRow[col*4 + 1] = color.g;
+                    targetRow[col*4 + 2] = color.r;
+                    targetRow[col*4 + 3] = color.a;
+                }
+                sourceRow += cols;
+                targetRow += bytesPerLine;
+            }
+        }
+
+        return image;
+    }
+
+
     void InstanceView::updateImageView(const aron::datanavigator::DictNavigatorPtr& data)
     {
         using aron::datanavigator::NDArrayNavigator;
 
-        if (!imageView)
+        if (not imageView)
         {
             return;
         }
-        if (!data)
+        if (not data)
         {
             removeImageView();
             return;
@@ -460,39 +522,74 @@ namespace armarx::armem::gui::instance
         }
 
         NDArrayNavigator::PointerType imageData = NDArrayNavigator::DynamicCast(element);
-        if (!imageData)
+        if (not imageData)
         {
             showErrorMessage("Expected NDArrayNavigator, but got: " + simox::meta::get_type_name(element));
             return;
         }
 
-        std::vector<int> shape = imageData->getDimensions();
+        const std::vector<int> shape = imageData->getDimensions();
         if (shape.size() != 3)
         {
             showErrorMessage("Expected array shape with 3 dimensions, but got: "
                              + NDArrayNavigator::DimensionsToString(shape));
             return;
         }
+        const int rows = shape.at(0);
+        const int cols = shape.at(1);
+
+        using aron::typenavigator::ImagePixelType;
+        std::optional<ImagePixelType> pixelType;
+        try
+        {
+            pixelType = aron::typenavigator::ImageNavigator::pixelTypeFromName(imageData->getType());
+        }
+        catch (const aron::error::AronException&)
+        {
+        }
 
-        QImage::Format format = QImage::Format::Format_Invalid;
-        switch (shape.at(2))
+        std::optional<QImage> image;
+        if (pixelType)
         {
+            switch (pixelType.value())
+            {
+                case ImagePixelType::RGB24:
+                    ARMARX_CHECK_EQUAL(shape.at(2), 3) << "Expected Rgb24 image to have 3 bytes per pixel.";
+                    image = QImage(imageData->getData(), cols, rows, QImage::Format::Format_RGB888);
+                    break;
+
+                case ImagePixelType::DEPTH32:
+                    image = convertDepth32ToRGB32(*imageData);
+                    break;
+            }
+        }
+        else
+        {
+            QImage::Format format = QImage::Format_Invalid;
+            switch (shape.at(2))
+            {
             case 1:
                 format = QImage::Format::Format_Grayscale8;
                 break;
+
             case 3:
                 format = QImage::Format::Format_RGB888;
                 break;
+
             default:
                 showErrorMessage("Expected 1 or 3 elements in last dimension, but got shape: "
                                  + NDArrayNavigator::DimensionsToString(shape));
                 return;
+            }
+            image = QImage(imageData->getData(), cols, rows, format);
         }
 
+        ARMARX_CHECK(image.has_value());
+
         std::stringstream title;
-        title << "Image element '" << imageView->elementPath.toString() << "'"; // of entity instance " << currentInstance->id();
+        title << "Image element '" << imageView->elementPath.toString() << "'";  // of entity instance " << currentInstance->id();
         imageView->setTitle(QString::fromStdString(title.str()));
-        imageView->view->setImage(QImage(imageData->getData(), shape.at(0), shape.at(1), format));
+        imageView->view->setImage(image.value());
     }
 
 
@@ -501,7 +598,7 @@ namespace armarx::armem::gui::instance
         setLayout(new QHBoxLayout());
         int margin = 2;
         layout()->setContentsMargins(margin, margin, margin, margin);
-        if (false)
+        if (/* DISABLES CODE */ (false))
         {
             QFont font = this->font();
             font.setPointSizeF(font.pointSize() * 0.75);
diff --git a/source/RobotAPI/libraries/armem_gui/instance/display_visitors/DataDisplayVisitor.cpp b/source/RobotAPI/libraries/armem_gui/instance/display_visitors/DataDisplayVisitor.cpp
index f41f5d4cb..af2192c2a 100644
--- a/source/RobotAPI/libraries/armem_gui/instance/display_visitors/DataDisplayVisitor.cpp
+++ b/source/RobotAPI/libraries/armem_gui/instance/display_visitors/DataDisplayVisitor.cpp
@@ -68,7 +68,8 @@ namespace armarx::aron
 
     bool DataDisplayVisitor::visit(NDArrayDataNavigator& n)
     {
-        value << "shape " << aron::datanavigator::NDArrayNavigator::DimensionsToString(n.getDimensions());
+        value << "shape " << aron::datanavigator::NDArrayNavigator::DimensionsToString(n.getDimensions())
+              << ", type '" << n.getType() << "'";
         return false;
     }
 
diff --git a/source/RobotAPI/libraries/armem_gui/instance/display_visitors/TypedDataDisplayVisitor.cpp b/source/RobotAPI/libraries/armem_gui/instance/display_visitors/TypedDataDisplayVisitor.cpp
index eb1cda5fd..16c54007a 100644
--- a/source/RobotAPI/libraries/armem_gui/instance/display_visitors/TypedDataDisplayVisitor.cpp
+++ b/source/RobotAPI/libraries/armem_gui/instance/display_visitors/TypedDataDisplayVisitor.cpp
@@ -140,6 +140,14 @@ namespace armarx::aron
         return false;
     }
 
+    bool TypedDataDisplayVisitor::visit(ImageNavigator&, NDArrayDataNavigator& data)
+    {
+        // aron::typenavigator::ImagePixelType pixelType = ImageNavigator::pixelTypeFromName(data.getType());
+        //value << DataDisplayVisitor::getValue(data) << " pixel type: " << data.getType()";
+        value << DataDisplayVisitor::getValue(data);
+        return false;
+    }
+
     bool TypedDataDisplayVisitor::visit(IVTCByteImageTypeNavigator&, NDArrayDataNavigator& data)
     {
         value << DataDisplayVisitor::getValue(data);
diff --git a/source/RobotAPI/libraries/armem_gui/instance/display_visitors/TypedDataDisplayVisitor.h b/source/RobotAPI/libraries/armem_gui/instance/display_visitors/TypedDataDisplayVisitor.h
index 87220babb..4bf2ec5d8 100644
--- a/source/RobotAPI/libraries/armem_gui/instance/display_visitors/TypedDataDisplayVisitor.h
+++ b/source/RobotAPI/libraries/armem_gui/instance/display_visitors/TypedDataDisplayVisitor.h
@@ -44,6 +44,7 @@ namespace armarx::aron
 
         bool visit(EigenMatrixTypeNavigator&, NDArrayDataNavigator& data) override;
         bool visit(EigenQuaternionTypeNavigator&, NDArrayDataNavigator& data) override;
+        bool visit(ImageNavigator&, NDArrayDataNavigator& data) override;
         bool visit(IVTCByteImageTypeNavigator&, NDArrayDataNavigator& data) override;
         bool visit(OpenCVMatTypeNavigator&, NDArrayDataNavigator& data) override;
         bool visit(PCLPointCloudTypeNavigator&, NDArrayDataNavigator& data) override;
diff --git a/source/RobotAPI/libraries/aron/core/navigator/visitors/TypedDataVisitor.cpp b/source/RobotAPI/libraries/aron/core/navigator/visitors/TypedDataVisitor.cpp
index e99a7f916..fe98e3abe 100644
--- a/source/RobotAPI/libraries/aron/core/navigator/visitors/TypedDataVisitor.cpp
+++ b/source/RobotAPI/libraries/aron/core/navigator/visitors/TypedDataVisitor.cpp
@@ -155,6 +155,10 @@ namespace armarx::aron::visitor
             {
                 return visit(*t, key, dynamic_cast<NDArrayDataNavigator&>(data));
             }
+            else if (auto t = dynamic_cast<ImageNavigator*>(&type))
+            {
+                return visit(*t, key, dynamic_cast<NDArrayDataNavigator&>(data));
+            }
             else if (auto t = dynamic_cast<IVTCByteImageTypeNavigator*>(&type))
             {
                 return visit(*t, key, dynamic_cast<NDArrayDataNavigator&>(data));
diff --git a/source/RobotAPI/libraries/aron/core/navigator/visitors/TypedDataVisitor.h b/source/RobotAPI/libraries/aron/core/navigator/visitors/TypedDataVisitor.h
index f577903a4..b2efdaa79 100644
--- a/source/RobotAPI/libraries/aron/core/navigator/visitors/TypedDataVisitor.h
+++ b/source/RobotAPI/libraries/aron/core/navigator/visitors/TypedDataVisitor.h
@@ -79,6 +79,7 @@ namespace armarx::aron::visitor
         // Array-valued
         using EigenMatrixTypeNavigator = typenavigator::EigenMatrixNavigator;
         using EigenQuaternionTypeNavigator = typenavigator::EigenQuaternionNavigator;
+        using ImageNavigator = typenavigator::ImageNavigator;
         using IVTCByteImageTypeNavigator = typenavigator::IVTCByteImageNavigator;
         using OpenCVMatTypeNavigator = typenavigator::OpenCVMatNavigator;
         using PCLPointCloudTypeNavigator = typenavigator::PCLPointCloudNavigator;
@@ -193,6 +194,11 @@ namespace armarx::aron::visitor
             (void) type, (void) data;
             return true;
         }
+        virtual bool visit(ImageNavigator& type, NDArrayDataNavigator& data)
+        {
+            (void) type, (void) data;
+            return true;
+        }
         virtual bool visit(IVTCByteImageTypeNavigator& type, NDArrayDataNavigator& data)
         {
             (void) type, (void) data;
@@ -323,6 +329,11 @@ namespace armarx::aron::visitor
             (void) type, (void) key;
             return visit(type, data);
         }
+        virtual bool visit(ImageNavigator& type, const std::string& key, NDArrayDataNavigator& data)
+        {
+            (void) type, (void) key;
+            return visit(type, data);
+        }
         virtual bool visit(IVTCByteImageTypeNavigator& type, const std::string& key, NDArrayDataNavigator& data)
         {
             (void) type, (void) key;
-- 
GitLab