Qt:提取由感兴趣区域多边形裁剪的强度值 [英] Qt: Extract intensity values clipped by a region of interest polygon

查看:110
本文介绍了Qt:提取由感兴趣区域多边形裁剪的强度值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

基于灰度图像和有序的闭合多边形(可能是凹面),我想获取位于感兴趣区域多边形内的所有灰度值(如

Based on a grayscale image and an ordered closed polygon (may be concave), I want to get all grayscale values that lie inside a region of interest polygon (as likewise described in SciPy Create 2D Polygon Mask). What is the most performant realisation of that in Qt 4.8? Endpoint should be some kind of QList<double>. Thanks for your advices.

此外,是否可以计算浮点掩码(例如,对于多边形外部为0,对于30%的像素区域在多边形内部为0.3,对于完全在多边形内部为1)?但是,这只是一个额外的结果,端点将是QPair<double, double>(百分比,值).谢谢.

In addition, is it possible to compute a floating point mask (e.g. 0 for outside the polygon, 0.3 for 30% of the pixel area is within the polygon, 1 for completely inside the polygon)? However, that's just an extra, endpoint would be QPair<double, double> (percentage, value) then. Thanks.

推荐答案

首先,您需要扫描线将多边形转换为水平线-这是由getScanLines()完成的.扫描线用于使用scanlineScanner()对图像进行采样,以获取每行端点内的所有值.

First you need to scanline convert the polygon into horizontal lines -- this is done by getScanLines(). The scanlines are used to sample the image to get all the values within the endpoints of each line, using scanlineScanner().

下面是一个完整,独立且可编译的示例,我展示了该示例以证明scanline算法的行为良好.也可以调整它以计算定点蒙版.到目前为止,如果扫描线在水平范围内覆盖了一半以上(由于scanlineScanner中的round()s),则包括一个点.

Below is a complete, standalone and compileable example I had laying around to show that the scanline algorithm is well behaved. It could be tweaked to calculate the fixed point mask as well. So far a point is included if the scanline covers more than half of it in the horizontal extent (due to round()s in scanlineScanner).

启动时,调整窗口大小并单击以在多边形中定义连续点.您看到的多边形仅使用扫描线渲染.为了进行比较,您可以启用多边形的轮廓.

Upon startup, resize the window and click around to define consecutive points in the polygon. The polygon you see is rendered solely using the scanlines. For comparison, you can enable the outline of the polygon.

我确信可以进一步优化扫描线转换器.我没有进行任何图像采样,但是scanlineScanner可以显示这是一件微不足道的事情.

I'm sure the scanline converter could be further optimized. I'm not doing any image sampling, but the scanlineScanner is there to show that it's a trivial thing to do.

// https://github.com/KubaO/stackoverflown/tree/master/questions/scanline-converter-11037252
#define QT_DISABLE_DEPRECATED_BEFORE 5
#include <QtGui>
#if QT_VERSION > QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif
#include <algorithm>

typedef QVector<QPointF> PointVector;
typedef QVector<QLineF> LineVector;

// A list of vertex indices in the polygon, sorted ascending in their y coordinate
static QVector<int> sortedVertices(const QPolygonF & poly)
{
   Q_ASSERT(! poly.isEmpty());
   QVector<int> vertices;
   vertices.reserve(poly.size());
   for (int i = 0; i < poly.size(); ++i) { vertices << i; }
   std::sort(vertices.begin(), vertices.end(), [&](int i, int j){
      return poly[i].y() < poly[j].y();
   });
   return vertices;
}

// Returns point of intersection of infinite lines ref and target
static inline QPointF intersect(const QLineF & ref, const QLineF & target)
{
   QPointF p;
   target.intersect(ref, &p);
   return p;
}

// Allows accessing polygon vertices using an indirect index into a vector of indices.
class VertexAccessor {
   const QPolygonF & p;
   const QVector<int> & i;
public:
   VertexAccessor(const QPolygonF & poly, const QVector<int> & indices) :
      p(poly), i(indices) {}
   inline QPointF operator[](int ii) const {
      return p[i[ii]];
   }
   inline QPointF prev(int ii) const {
      int index = i[ii] - 1;
      if (index < 0) index += p.size();
      return p[index];
   }
   inline QPointF next(int ii) const {
      int index = i[ii] + 1;
      if (index >= p.size()) index -= p.size();
      return p[index];
   }
};

// Returns a horizontal line scanline rendering of an unconstrained polygon.
// The lines are generated on an integer grid, but this could be modified for any other grid.
static LineVector getScanlines(const QPolygonF & poly)
{
   LineVector lines;
   if (poly.isEmpty()) return lines;
   const QVector<int> indices = sortedVertices(poly);
   VertexAccessor vertex{poly, indices};
   const QRectF bound = poly.boundingRect();
   const auto l = bound.left();
   const auto r = bound.right();
   int ii = 0;
   int yi = qFloor(vertex[0].y());
   QList<int> active;
   PointVector points;
   forever {
      const qreal y = yi;
      const QLineF sweeper{l, y, r, y};
      // Remove vertex from the active list if both lines extending from it are above sweeper
      for (int i = 0; i < active.size(); ) {
         const int ii = active.at(i);
         // Remove vertex
         if (vertex.prev(ii).y() < y && vertex.next(ii).y() < y) {
            active.removeAt(i);
         } else {
            ++ i;
         }
      }
      // Add new vertices to the active list
      while (ii < poly.count() && vertex[ii].y() < y) {
         active << ii++;
      }
      if (ii >= poly.count() && active.isEmpty()) break;
      // Generate sorted intersection points
      points.clear();
      for (auto ii : active) {
         const auto a = vertex[ii];
         auto b = vertex.prev(ii);
         if (b.y() >= y)
            points << intersect(sweeper, QLineF{a, b});
         b = vertex.next(ii);
         if (b.y() >= y)
            points << intersect(sweeper, QLineF{a, b});
      }
      std::sort(points.begin(), points.end(), [](const QPointF & p1, const QPointF & p2){
         return p1.x() < p2.x();
      });
      // Generate horizontal fill segments
      for (int i = 0; i < points.size() - 1; i += 2) {
         lines << QLineF{points.at(i).x(), y, points.at(i+1).x(), y};
      }
      yi++;
   };
   return lines;
}

QVector<int> scanlineScanner(const QImage & image, const LineVector & tess)
{
   QVector<int> values;
   for (auto & line : tess) {
      for (int x = round(line.x1()); x <= round(line.x2()); ++ x) {
         values << qGray(image.pixel(x, line.y1()));
      }
   }
   return values;
}

class Ui : public QWidget
{
   Q_OBJECT
   QPointF lastPoint;
   QPolygonF polygon;
   LineVector scanlines;
   QGridLayout layout{this};
   QPushButton reset{"Reset"};
   QCheckBox outline{"Outline"};
public:
   Ui() {
      setMinimumSize(200, 200);
      layout.addItem(new QSpacerItem{0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding}, 0, 0, 1, 3);
      layout.addWidget(&reset, 1, 0);
      layout.addWidget(&outline, 1, 1);
      layout.addItem(new QSpacerItem{0, 0, QSizePolicy::Expanding}, 1, 2);
      reset.setObjectName("reset");
      outline.setObjectName("outline");
      QMetaObject::connectSlotsByName(this);
   }
protected:
   Q_SLOT void on_reset_clicked() {
      polygon.clear();
      scanlines.clear();
      lastPoint = QPointF{};
      update();
   }
   Q_SLOT void on_outline_stateChanged() {
      update();
   }
   void paintEvent(QPaintEvent *) override {
      QPainter p{this};
      if (false) p.setRenderHint(QPainter::Antialiasing);
      p.setPen("cadetblue");
      if (!polygon.isEmpty() && scanlines.isEmpty()) {
         scanlines = getScanlines(polygon);
         qDebug() << "new scanlines";
      }
      p.drawLines(scanlines);
      if (outline.isChecked()) {
         p.setPen("orangered");
         p.setBrush(Qt::NoBrush);
         p.drawPolygon(polygon);
      }
      if (!lastPoint.isNull()) {
         p.setPen("navy");
         p.drawEllipse(lastPoint, 3, 3);
      }
   }
   void mousePressEvent(QMouseEvent * ev) override {
      lastPoint = ev->posF();
      polygon << ev->posF();
      scanlines.clear();
      update();
   }
};

int main(int argc, char** argv)
{
   QApplication app{argc, argv};
   Ui ui;
   ui.show();
   return app.exec();
}

#include "main.moc"

这篇关于Qt:提取由感兴趣区域多边形裁剪的强度值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆