Browse Source

优化文字显示

leon 1 month ago
parent
commit
2d92ce518c
3 changed files with 170 additions and 2 deletions
  1. 2 1
      .vscode/settings.json
  2. 15 1
      src/nodes/draw/drawNode.cpp
  3. 153 0
      src/nodes/draw/position.hpp

+ 2 - 1
.vscode/settings.json

@@ -52,6 +52,7 @@
         "unordered_map": "cpp",
         "variant": "cpp",
         "vector": "cpp",
-        "algorithm": "cpp"
+        "algorithm": "cpp",
+        "tuple": "cpp"
     }
 }

+ 15 - 1
src/nodes/draw/drawNode.cpp

@@ -1,10 +1,19 @@
 #include "nodes/base/base.hpp"
 #include "nodes/draw/drawNode.hpp"
+#include "nodes/draw/position.hpp"
 #include <opencv2/opencv.hpp>
 
 namespace Node
 {
 
+static std::tuple<int, int, int> getFontSize(const std::string& text)
+{
+    int baseline = 0;
+    cv::Size textSize = cv::getTextSize(text, cv::FONT_HERSHEY_SIMPLEX, 1.0, 2, &baseline);
+    std::cout << textSize << std::endl;
+    return std::make_tuple(textSize.width, textSize.height, baseline);
+}
+
 void DrawNode::work() 
 {
     printf("DrawNode %s\n", name_.c_str());
@@ -20,10 +29,15 @@ void DrawNode::work()
             printf("Node %s get data from %s\n", name_.c_str(), input_buffer.first.c_str());
             // do something
             cv::Mat image = metaData->image.clone();
+            PositionManager<float> pm(getFontSize);
             for (auto& box : metaData->boxes)
             {
                 cv::rectangle(image, cv::Point(box.left, box.top), cv::Point(box.right, box.bottom), cv::Scalar(0, 255, 0), 2);
-                cv::putText(image, box.label, cv::Point(box.left, box.top), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 255, 0), 2);
+
+                std::tuple<int, int, int, int> box = std::make_tuple(box.left, box.top, box.right, box.bottom);
+                int x, y;
+                std::tie(x, y) = pm.selectOptimalPosition(box, 1920, 1080, box.label);
+                cv::putText(image, box.label, cv::Point(x, y), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 255, 0), 2);
             }
             metaData->draw_image = image;
             cv::imwrite("dtest.jpg", image);

+ 153 - 0
src/nodes/draw/position.hpp

@@ -0,0 +1,153 @@
+#ifndef POSITION_HPP__
+#define POSITION_HPP__
+#include <vector>
+#include <string>
+#include <tuple>
+#include <functional>
+#include <utility> // for std::forward
+
+template <typename T>
+float computeIntersectionArea(const std::tuple<T, T, T, T>& box1, const std::tuple<T, T, T, T>& box2) {
+    T l1, t1, r1, b1;
+    T l2, t2, r2, b2;
+    std::tie(l1, t1, r1, b1) = box1;
+    std::tie(l2, t2, r2, b2) = box2;
+
+    T intersectionWidth = std::max(T(0), std::min(r1, r2) - std::max(l1, l2));
+    T intersectionHeight = std::max(T(0), std::min(b1, b2) - std::max(t1, t2));
+    return intersectionWidth * intersectionHeight;
+}
+
+template <typename T>
+float computeIoU(const std::tuple<T, T, T, T>& box1, const std::tuple<T, T, T, T>& box2) {
+    T l1, t1, r1, b1;
+    T l2, t2, r2, b2;
+    std::tie(l1, t1, r1, b1) = box1;
+    std::tie(l2, t2, r2, b2) = box2;
+
+    T areaA = (r1 - l1) * (b1 - t1);
+    T areaB = (r2 - l2) * (b2 - t2);
+    T intersectionArea = computeIntersectionArea(box1, box2);
+
+    if (areaA == 0 || areaB == 0 || intersectionArea == 0) {
+        return 0;
+    }
+    return static_cast<float>(intersectionArea) / (areaA + areaB - intersectionArea);
+}
+
+template <typename T>
+float computeOverlap(const std::tuple<T, T, T, T>& box1, const std::tuple<T, T, T, T>& box2) {
+    T areaA = (std::get<2>(box1) - std::get<0>(box1)) * (std::get<3>(box1) - std::get<1>(box1));
+    T areaB = (std::get<2>(box2) - std::get<0>(box2)) * (std::get<3>(box2) - std::get<1>(box2));
+    T intersectionArea = computeIntersectionArea(box1, box2);
+    if (areaA == 0 || areaB == 0 || intersectionArea == 0) {
+        return 0;
+    }
+    return static_cast<float>(intersectionArea) / std::min(areaA, areaB);
+}
+
+
+template <typename T>
+class PositionManager {
+private:
+    std::vector<std::tuple<T, T, T, T>> markedPositions;
+    std::function<std::tuple<int, int, int>(const std::string&)> getFontSizeFunc;
+
+public:
+    template <typename Func>
+    PositionManager(Func&& fontSizeFunc)
+        : getFontSizeFunc(std::forward<Func>(fontSizeFunc))
+    {
+    }
+
+    // 获取最佳位置
+    std::tuple<T, T> selectOptimalPosition(const std::tuple<T, T, T, T>& box,
+        int canvasWidth, int canvasHeight, const std::string& text)
+    {
+        // 获取字体宽度、高度和基线
+        int textWidth, textHeight, baseline;
+        std::tie(textWidth, textHeight, baseline) = getFontSizeFunc(text);
+
+        std::vector<std::tuple<T, T, T, T>> candidatePositions =
+            findCandidatePositions(box, canvasWidth, canvasHeight, textWidth, textHeight, baseline);
+
+        float minIoU = 1.f;
+        std::tuple<T, T, T, T> candidatePosition = candidatePositions[0];
+        for (const auto& cposition : candidatePositions)
+        {
+            float maxIoU = 0.f;
+            for (const auto& mposition : markedPositions)
+            {
+                float IoU = computeIoU(cposition, mposition);
+                maxIoU = std::max(IoU, maxIoU);
+            }
+            if (maxIoU == 0.f)
+            {
+                candidatePosition = cposition;
+                break;
+            }
+            else if (maxIoU < minIoU)
+            {
+                minIoU = maxIoU;
+                candidatePosition = cposition;
+            }
+        }
+        markedPositions.push_back(candidatePosition);
+
+        T left, top, right, bottom;
+        std::tie(left, top, right, bottom) = candidatePosition;
+        std::tuple<T, T> result = std::make_tuple(left, top + textHeight);
+        return result;
+    }
+
+    void clearMarkedPositions()
+    {
+        markedPositions.clear();
+    }
+
+    // 获取候选区域 并附带画图位置起始点
+    std::vector<std::tuple<T, T, T, T>> findCandidatePositions(
+        const std::tuple<T, T, T, T>& box,
+        int canvasWidth, int canvasHeight, int textWidth, int textHeight, int baseline)
+    {
+        std::vector<std::tuple<T, T, T, T>> candidatePositions;
+        T left, top, right, bottom;
+        std::tie(left, top, right, bottom) = box;
+        left = std::max(0, left);
+        top = std::max(0, top);
+        right = std::min(canvasWidth, right);
+        bottom = std::min(canvasHeight, bottom);
+        std::tuple<T, T, T, T> all = std::make_tuple(0, 0, canvasWidth, canvasHeight);
+
+        // 一个框有6个可以画框的区域,判断那些区域没超过画面
+        std::vector<std::tuple<T, T, T, T>> positions =
+        {
+            std::make_tuple(left, top - textHeight - baseline, left + textWidth, top),
+            std::make_tuple(right, top, right + textWidth, top + textHeight + baseline),
+            std::make_tuple(left - textWidth, top, left, top + textHeight + baseline),
+            std::make_tuple(left, bottom, left + textWidth, bottom + textHeight + baseline),
+            std::make_tuple(right - textWidth, top - textHeight - baseline, right, top),
+            std::make_tuple(right - textWidth, bottom, right, bottom + textHeight + baseline),
+            std::make_tuple(left, top, left + textWidth, top + textHeight + baseline),
+            std::make_tuple(right - textWidth, top, right, top + textHeight + baseline),
+            std::make_tuple(right - textWidth, bottom - textHeight - baseline, right, bottom),
+            std::make_tuple(left, bottom - textHeight - baseline, left + textWidth, bottom)
+        };
+
+        for (const auto& position : positions)
+        {
+            if (computeOverlap(all, position) == 1.0f)
+            {
+                candidatePositions.push_back(position);
+            }
+        }
+        if (candidatePositions.size() == 0)
+        {
+            candidatePositions.push_back(std::make_tuple(left, top, left + textWidth, top + textHeight + baseline));
+        }
+        return candidatePositions;
+    }
+};
+
+
+#endif // POSITION_HPP__