[PATCH v3 03/17] libcamera: geometry: Add Rectangle::transformedBetween()

Stefan Klug stefan.klug at ideasonboard.com
Fri Dec 6 11:13:25 CET 2024


Handling cropping and scaling within a complicated pipeline involves
transformations of rectangles between different coordinate systems. For
example the full input of the dewarper (0,0)/1920x1080 might correspond
to the rectangle (0, 243)/2592x1458 in sensor coordinates (of a
2592x1944 sensor). Add a function that allows the transformation of a
rectangle defined in one reference frame (dewarper) into the coordinates
of a second reference frame (sensor).

Signed-off-by: Stefan Klug <stefan.klug at ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder at ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi at ideasonboard.com>

---
Changes in v3:
- Improved documentation
- Added testcase

Changes in v2:
- Renamed mappedBetween() to transformedBetween()
- Improved documentation
- Collected tags
---
 include/libcamera/geometry.h |  3 +++
 src/libcamera/geometry.cpp   | 49 ++++++++++++++++++++++++++++++++++++
 test/geometry.cpp            | 11 ++++++++
 3 files changed, 63 insertions(+)

diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
index 9ca5865a3d0d..e5f0a843d314 100644
--- a/include/libcamera/geometry.h
+++ b/include/libcamera/geometry.h
@@ -299,6 +299,9 @@ public:
 	__nodiscard Rectangle scaledBy(const Size &numerator,
 				       const Size &denominator) const;
 	__nodiscard Rectangle translatedBy(const Point &point) const;
+
+	Rectangle transformedBetween(const Rectangle &source,
+				     const Rectangle &target) const;
 };
 
 bool operator==(const Rectangle &lhs, const Rectangle &rhs);
diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
index 90ccf8c19f97..81cc8cd538a0 100644
--- a/src/libcamera/geometry.cpp
+++ b/src/libcamera/geometry.cpp
@@ -837,6 +837,55 @@ Rectangle Rectangle::translatedBy(const Point &point) const
 	return { x + point.x, y + point.y, width, height };
 }
 
+/**
+ * \brief Transform a Rectangle from one reference rectangle to another
+ * \param[in] source The \a source reference rectangle
+ * \param[in] destination The \a destination reference rectangle
+ *
+ * The \a source and \a destination parameters describe two rectangles defined
+ * in different reference systems. The Rectangle is translated from the source
+ * reference system into the destination reference system.
+ *
+ * The typical use case for this function is to translate a selection rectangle
+ * specified in a reference system, in example the sensor's pixel array, into
+ * the same rectangle re-scaled and translated into a different reference
+ * system, in example the output frame on which the selection rectangle is
+ * applied to.
+ *
+ * For example, consider a sensor with a resolution of 4040x2360 pixels and a
+ * assume a rectangle of (100, 100)/3840x2160 (sensorFrame) in sensor
+ * coordinates is mapped to a rectangle (0,0)/(1920,1080) (displayFrame) in
+ * display coordinates. This function can be used to transform an arbitrary
+ * rectangle from display coordinates to sensor coordinates or vice versa:
+ *
+ * \code{.cpp}
+ * Rectangle sensorReference(100, 100, 3840, 2160);
+ * Rectangle displayReference(0, 0, 1920, 1080);
+ *
+ * // Bottom right quarter in sensor coordinates
+ * Rectangle sensorRect(2020, 100, 1920, 1080);
+ * displayRect = sensorRect.transformedBetween(sensorReference, displayReference);
+ * // displayRect is now (960, 540)/960x540
+ *
+ * // Transformation back to sensor coordinates
+ * sensorRect = displayRect.transformedBetween(displayReference, sensorReference);
+ * \endcode
+ */
+Rectangle Rectangle::transformedBetween(const Rectangle &source,
+					const Rectangle &destination) const
+{
+	Rectangle r;
+	double sx = static_cast<double>(destination.width) / source.width;
+	double sy = static_cast<double>(destination.height) / source.height;
+
+	r.x = static_cast<int>((x - source.x) * sx) + destination.x;
+	r.y = static_cast<int>((y - source.y) * sy) + destination.y;
+	r.width = static_cast<int>(width * sx);
+	r.height = static_cast<int>(height * sy);
+
+	return r;
+}
+
 /**
  * \brief Compare rectangles for equality
  * \return True if the two rectangles are equal, false otherwise
diff --git a/test/geometry.cpp b/test/geometry.cpp
index 5760fa3c885a..11df043b733b 100644
--- a/test/geometry.cpp
+++ b/test/geometry.cpp
@@ -495,6 +495,17 @@ protected:
 			return TestFail;
 		}
 
+		Rectangle f1 = Rectangle(100, 200, 3000, 2000);
+		Rectangle f2 = Rectangle(200, 300, 1500, 1000);
+		/* Bottom right quarter of the corresponding frames. */
+		Rectangle r1 = Rectangle(100 + 1500, 200 + 1000, 1500, 1000);
+		Rectangle r2 = Rectangle(200 + 750, 300 + 500, 750, 500);
+		if (r1.transformedBetween(f1, f2) != r2 ||
+		    r2.transformedBetween(f2, f1) != r1) {
+			cout << "Rectangle::transformedBetween() test failed" << endl;
+			return TestFail;
+		}
+
 		return TestPass;
 	}
 };
-- 
2.43.0



More information about the libcamera-devel mailing list