diff --git a/include/libcamera/internal/vector.h b/include/libcamera/internal/vector.h
index 093efafa8982fe553a953316ac794bef46eec5d4..17ecbda50f2dc4363548a4a29639a751a187dac6 100644
--- a/include/libcamera/internal/vector.h
+++ b/include/libcamera/internal/vector.h
@@ -185,6 +185,14 @@ public:
 		return apply(*this, scalar, [](T a, T b) -> T { return std::max(a, b); });
 	}
 
+	constexpr Vector clamp(T low, T high) const
+	{
+		Vector result;
+		for (unsigned int i = 0; i < Rows; i++)
+			result[i] = std::clamp(data_[i], low, high);
+		return result;
+	}
+
 	constexpr T dot(const Vector<T, Rows> &other) const
 	{
 		T ret = 0;
diff --git a/src/libcamera/vector.cpp b/src/libcamera/vector.cpp
index 47b3d1af36d90f9719c527b8458fbfe07412239e..a135ab498e17149b9dc54de7204e8a34262a2da9 100644
--- a/src/libcamera/vector.cpp
+++ b/src/libcamera/vector.cpp
@@ -224,6 +224,14 @@ LOG_DEFINE_CATEGORY(Vector)
  * \return The element-wise maximum of this vector and \a scalar
  */
 
+/**
+ * \fn Vector::clamp(T low, T high) const
+ * \brief Clamp the vector element-wise between \a low and \a high
+ * \param[in] low The lower limit
+ * \param[in] high The upper limit
+ * \return A vector with each element clamped between \a low and \a high
+ */
+
 /**
  * \fn Vector::dot(const Vector<T, Rows> &other) const
  * \brief Compute the dot product
diff --git a/test/vector.cpp b/test/vector.cpp
index 4ff908e8f682f19d65c55d071fcd300071cf90ac..2f3d97d93dccbe153f2d1cf536423f7446a22aab 100644
--- a/test/vector.cpp
+++ b/test/vector.cpp
@@ -72,6 +72,7 @@ protected:
 		ASSERT_EQ(v2.min(4.0), (Vector<double, 3>{{ 1.0, 4.0, 4.0 }}));
 		ASSERT_EQ(v2.max(v3), (Vector<double, 3>{{ 4.0, 4.0, 8.0 }}));
 		ASSERT_EQ(v2.max(4.0), (Vector<double, 3>{{ 4.0, 4.0, 8.0 }}));
+		ASSERT_EQ(v2.clamp(2.0, 6.0), (Vector<double, 3>{{ 2.0, 4.0, 6.0 }}));
 
 		ASSERT_EQ(v2.dot(v3), 52.0);
 
