[v4,7/9] tests: Validate native sensor crop coordinates
diff mbox series

Message ID 20261029132631.370507-8-halfline@gmail.com
State Superseded
Headers show
Series
  • software-isp: Align metering and report the configured crop
Related show

Commit Message

Ray Strode Oct. 29, 2026, 1:26 p.m. UTC
From: Ray Strode <rstrode@redhat.com>

This commit checks the conversion from debayer crop rectangles to native
sensor coordinates in the existing geometry unit test.

The new cases cover GPU and CPU crop sizes, physical pixel array origins,
binning, flips, transposition and fractional coordinates. An odd image
boundary checks that the conversion preserves image geometry before Bayer
statistics alignment.

Signed-off-by: Ray Strode <rstrode@redhat.com>
---
 test/debayer-geometry.cpp | 45 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 44 insertions(+), 1 deletion(-)

Patch
diff mbox series

diff --git a/test/debayer-geometry.cpp b/test/debayer-geometry.cpp
index cfc757fa..f2f0bc09 100644
--- a/test/debayer-geometry.cpp
+++ b/test/debayer-geometry.cpp
@@ -1,63 +1,106 @@ 
 /* SPDX-License-Identifier: GPL-2.0-or-later */
 /*
  * Copyright (C) 2026, Red Hat Inc.
  *
  * Sensor crop tests
  */
 
 #include <iostream>
 
 #include <libcamera/geometry.h>
 
 #include "libcamera/internal/software_isp/debayer_geometry.h"
 
 #include "test.h"
 
 using namespace libcamera;
 
 class DebayerGeometryTest : public Test
 {
 protected:
 	int run() override
 	{
-		if (testCrop())
+		if (testCrop() || testSensorCrop())
 			return TestFail;
 
 		return TestPass;
 	}
 
 private:
 	int testCrop()
 	{
 		static const struct {
 			Size input;
 			Size native;
 			Size output;
 			Rectangle expected;
 		} cases[] = {
 			{ { 1920, 1080 }, { 1920, 1080 }, { 1920, 1080 }, { 0, 0, 1920, 1080 } },
 			{ { 1928, 1092 }, { 1920, 1092 }, { 1920, 1092 }, { 4, 0, 1920, 1092 } },
 			/* Output resolution changes must not shrink the sensor window. */
 			{ { 1928, 1092 }, { 1920, 1092 }, { 1920, 1080 }, { 4, 6, 1920, 1080 } },
 			{ { 1928, 1092 }, { 1920, 1092 }, { 1280, 720 }, { 4, 6, 1920, 1080 } },
 			{ { 1928, 1092 }, { 1920, 1092 }, { 640, 480 }, { 236, 0, 1456, 1092 } },
 			{ { 1928, 1092 }, { 1920, 1092 }, { 512, 768 }, { 600, 0, 728, 1092 } },
 			{ { 1928, 1092 }, { 1920, 1092 }, { 1280, 640 }, { 4, 66, 1920, 960 } },
 			/* A smaller Bayer border leaves an odd top coordinate. */
 			{ { 1928, 1092 }, { 1924, 1092 }, { 1280, 720 }, { 2, 5, 1924, 1082 } },
 		};
 
 		for (const auto &entry : cases) {
 			Rectangle crop = debayerCrop(entry.input, entry.native, entry.output);
 			if (crop != entry.expected) {
 				std::cerr << "Crop " << entry.input << " -> " << entry.output
 					  << " is " << crop << ", expected " << entry.expected << std::endl;
 				return TestFail;
 			}
 		}
 
 		return TestPass;
 	}
+
+	int testSensorCrop()
+	{
+		static const struct {
+			Rectangle crop;
+			Size input;
+			Rectangle analog;
+			Transform transform;
+			Rectangle expected;
+		} cases[] = {
+			/* GPU scaling keeps the full visible sensor region. */
+			{ { 4, 6, 1920, 1080 }, { 1928, 1092 }, { 0, 0, 1928, 1092 }, Transform::Identity, { 4, 6, 1920, 1080 } },
+			/* A full readout keeps its physical pixel-array origin. */
+			{ { 0, 0, 2000, 1500 }, { 2000, 1500 }, { 8, 8, 4000, 3000 }, Transform::Identity, { 8, 8, 4000, 3000 } },
+			/* Sensor crop offsets and 2x binning use native pixels. */
+			{ { 4, 6, 1920, 1080 }, { 1928, 1092 }, { 100, 200, 3856, 2184 }, Transform::Identity, { 108, 212, 3840, 2160 } },
+			/* CPU debayering crops to output size without scaling. */
+			{ { 324, 186, 1280, 720 }, { 1928, 1092 }, { 100, 200, 3856, 2184 }, Transform::Identity, { 748, 572, 2560, 1440 } },
+			/* Preserve an odd image boundary before statistics alignment. */
+			{ { 2, 5, 1924, 1082 }, { 1928, 1092 }, { 0, 0, 3856, 2184 }, Transform::Identity, { 4, 10, 3848, 2164 } },
+			/* Undo sensor flips before reporting the native rectangle. */
+			{ { 2, 4, 20, 10 }, { 100, 60 }, { 10, 20, 200, 120 }, Transform::HFlip, { 166, 28, 40, 20 } },
+			{ { 2, 4, 20, 10 }, { 100, 60 }, { 10, 20, 200, 120 }, Transform::VFlip, { 14, 112, 40, 20 } },
+			{ { 2, 4, 20, 10 }, { 100, 60 }, { 10, 20, 200, 120 }, Transform::HVFlip, { 166, 112, 40, 20 } },
+			/* Transposition is undone before the flip. */
+			{ { 4, 2, 10, 20 }, { 60, 100 }, { 10, 20, 200, 120 }, Transform::Rot270, { 166, 28, 40, 20 } },
+			/* Fractional native coordinates and sizes truncate independently. */
+			{ { 1, 1, 3, 3 }, { 8, 8 }, { 10, 20, 12, 12 }, Transform::Identity, { 11, 21, 4, 4 } },
+		};
+
+		for (const auto &entry : cases) {
+			Rectangle crop = debayerCropToSensor(entry.crop, entry.input,
+							     entry.analog,
+							     entry.transform);
+			if (crop != entry.expected) {
+				std::cerr << "Sensor mapping of " << entry.crop << " in " << entry.input
+					  << " is " << crop << ", expected " << entry.expected << std::endl;
+				return TestFail;
+			}
+		}
+
+		return TestPass;
+	}
 };
 
 TEST_REGISTER(DebayerGeometryTest)