[libcamera-devel] [PATCH v5 08/12] test: Add unit test for Transform and Orientation

Laurent Pinchart laurent.pinchart at ideasonboard.com
Wed Oct 18 22:54:16 CEST 2023


Hi Jacopo,

Thank you for the patch.

On Fri, Sep 01, 2023 at 05:02:11PM +0200, Jacopo Mondi via libcamera-devel wrote:
> Add a unit test for Transform and Orientation to validate the
> implementation of the operations between the two types.
> 
> In particular, test that:
> 
> 	o1 / o2 = t
> 	o2 * t = o1
> 
> Signed-off-by: Jacopo Mondi <jacopo.mondi at ideasonboard.com>
> Reviewed-by: David Plowman <david.plowman at raspberrypi.com>
> ---
>  test/meson.build   |   1 +
>  test/transform.cpp | 331 +++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 332 insertions(+)
>  create mode 100644 test/transform.cpp
> 
> diff --git a/test/meson.build b/test/meson.build
> index b227be818419..189e1428485a 100644
> --- a/test/meson.build
> +++ b/test/meson.build
> @@ -46,6 +46,7 @@ public_tests = [
>      {'name': 'public-api', 'sources': ['public-api.cpp']},
>      {'name': 'signal', 'sources': ['signal.cpp']},
>      {'name': 'span', 'sources': ['span.cpp']},
> +    {'name': 'transform', 'sources': ['transform.cpp']},
>  ]
>  
>  internal_tests = [
> diff --git a/test/transform.cpp b/test/transform.cpp
> new file mode 100644
> index 000000000000..4a6cf7387f02
> --- /dev/null
> +++ b/test/transform.cpp
> @@ -0,0 +1,331 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2023, Ideas On Board Oy
> + *
> + * transform.cpp - Transform and Orientation tests
> + */
> +
> +#include <iostream>
> +
> +#include <libcamera/orientation.h>
> +#include <libcamera/transform.h>
> +
> +#include "test.h"
> +
> +using namespace std;
> +using namespace libcamera;
> +
> +class TransformTest : public Test
> +{
> +protected:
> +	int run();
> +};
> +
> +int TransformTest::run()
> +{
> +	/*
> +	 * RotationTestEntry collects two Orientation and one Transform that
> +	 * gets combined to validate that (o1 / o2 = T) and (o1 = o2 * T)
> +	 *
> +	 * o1 / o2 = t computes the Transform to apply to o2 to obtain o1
> +	 * o2 * t = o1 combines o2 with t by applying o2 first then t
> +	 *
> +	 * The comments on the (most complex) transform show how applying to
> +	 * an image with orientation o2 the Transform t allows to obtain o1.
> +	 *
> +	 * The image with basic rotation0 is assumed to be:
> +	 *
> +	 * 	AB
> +	 * 	CD
> +	 *
> +	 * And the Transform operators are:
> +	 *
> +	 * 	V = vertical flip
> +	 * 	H = horizontal flip
> +	 * 	T = transpose
> +	 *
> +	 * the operator '* (T|V)' applies V first then T.
> +	 */
> +	struct RotationTestEntry {

static const

> +		Orientation o1;
> +		Orientation o2;
> +		Transform t;
> +	} testEntries[] = {
> +		/* Test identities transforms first. */
> +		{
> +			Orientation::rotate0, Orientation::rotate0,
> +			Transform::Identity,
> +		},
> +		{
> +			Orientation::rotate0Flip, Orientation::rotate0Flip,
> +			Transform::Identity,
> +		},
> +		{
> +			Orientation::rotate180, Orientation::rotate180,
> +			Transform::Identity,
> +		},
> +		{
> +			Orientation::rotate180Flip, Orientation::rotate180Flip,
> +			Transform::Identity,
> +		},
> +		{
> +			Orientation::rotate90, Orientation::rotate90,
> +			Transform::Identity,
> +		},
> +		{
> +			Orientation::rotate90Flip, Orientation::rotate90Flip,
> +			Transform::Identity,
> +		},
> +		{
> +			Orientation::rotate270, Orientation::rotate270,
> +			Transform::Identity,
> +		},
> +		{
> +			Orientation::rotate270Flip, Orientation::rotate270Flip,
> +			Transform::Identity,
> +		},
> +		/*
> +		 * Combine 0 and 180 degrees rotation as they're the most common
> +		 * ones.
> +		 */
> +		{
> +			/*
> +			 *      o2      t               o1
> +			 *      --------------------------
> +			 *	CD  * (H|V) =  	BA	AB
> +			 *	BA		CD	CD
> +			 */
> +			Orientation::rotate0, Orientation::rotate180,
> +			Transform::Rot180,
> +		},
> +		{
> +			/*
> +			 *      o2      t               o1
> +			 *      --------------------------
> +			 *	AB  * (H|V) =  	CD	DC
> +			 *	CD		AB	BA
> +			 */
> +			Orientation::rotate180, Orientation::rotate0,
> +			Transform::Rot180
> +		},
> +		/* Test that transpositions are handled correctly. */
> +		{
> +			/*
> +			 *      o2      t               o1
> +			 *      --------------------------
> +			 *	AB  * (T|V) =  	CD	CA
> +			 *	CD		AB	DB
> +			 */
> +			Orientation::rotate90, Orientation::rotate0,
> +			Transform::Rot90,
> +		},
> +		{
> +			/*
> +			 *      o2      t               o1
> +			 *      --------------------------
> +			 *	CA  * (T|H) =  	AC	AB
> +			 *	DB		BD	CD
> +			 */
> +			Orientation::rotate0, Orientation::rotate90,
> +			Transform::Rot270,
> +		},
> +		{
> +			/*
> +			 *      o2      t               o1
> +			 *      --------------------------
> +			 *	AB  * (T|H) =  	BA	BD
> +			 *	CD		DC	AC
> +			 */
> +			Orientation::rotate270, Orientation::rotate0,
> +			Transform::Rot270,
> +		},
> +		{
> +			/*
> +			 *      o2      t               o1
> +			 *      --------------------------
> +			 *	BD  * (T|V) =  	AC	AB
> +			 *	AC		BD	CD
> +			 */
> +			Orientation::rotate0, Orientation::rotate270,
> +			Transform::Rot90,
> +		},
> +		{
> +			/*
> +			 *      o2      t               o1
> +			 *      --------------------------
> +			 *	CD  * (T|H) =  	DC	DA
> +			 *	BA		AB	CB
> +			 */
> +			Orientation::rotate90, Orientation::rotate180,
> +			Transform::Rot270,
> +		},
> +		{
> +			/*
> +			 *      o2      t               o1
> +			 *      --------------------------
> +			 *	DA  * (T|V) =  	CB	CD
> +			 *	CB		DA	BA
> +			 */
> +			Orientation::rotate180, Orientation::rotate90,
> +			Transform::Rot90,
> +		},
> +		{
> +			/*
> +			 *      o2      t               o1
> +			 *      --------------------------
> +			 *	CD  * (T|V) =  	BA	BC
> +			 *	BA		CD	AD
> +			 */
> +			Orientation::rotate270, Orientation::rotate180,
> +			Transform::Rot90,
> +		},
> +		{
> +			/*
> +			 *      o2      t               o1
> +			 *      --------------------------
> +			 *	BC  * (T|H) =  	CB	CD
> +			 *	AD		DA	BA
> +			 */
> +			Orientation::rotate180, Orientation::rotate270,
> +			Transform::Rot270,
> +		},
> +		{
> +			/*
> +			 *      o2      t               o1
> +			 *      --------------------------
> +			 *	DA  * (V|H) =  	AD	BC
> +			 *	CB		BC	AD
> +			 */
> +			Orientation::rotate270, Orientation::rotate90,
> +			Transform::Rot180,
> +		},
> +		/* Test that mirroring is handled correctly. */
> +		{
> +			Orientation::rotate0, Orientation::rotate0Flip,
> +			Transform::HFlip
> +		},
> +		{
> +			Orientation::rotate0Flip, Orientation::rotate0,
> +			Transform::HFlip
> +		},
> +		{
> +			Orientation::rotate180, Orientation::rotate180Flip,
> +			Transform::HFlip
> +		},
> +		{
> +			Orientation::rotate180Flip, Orientation::rotate180,
> +			Transform::HFlip
> +		},
> +		{
> +			Orientation::rotate90, Orientation::rotate90Flip,
> +			Transform::HFlip
> +		},
> +		{
> +			Orientation::rotate90Flip, Orientation::rotate90,
> +			Transform::HFlip
> +		},
> +		{
> +			Orientation::rotate270, Orientation::rotate270Flip,
> +			Transform::HFlip
> +		},
> +		{
> +			Orientation::rotate270Flip, Orientation::rotate270,
> +			Transform::HFlip
> +		},
> +		{
> +			Orientation::rotate0, Orientation::rotate0Flip,
> +			Transform::HFlip
> +		},
> +		/*
> +		 * More exotic transforms which include Transpositions and
> +		 * mirroring.
> +		 */
> +		{
> +			/*
> +			 *      o2      t       o1
> +			 *      ------------------
> +			 *	BC  * (V) =  	AD
> +			 *	AD		BC
> +			 */
> +			Orientation::rotate90Flip, Orientation::rotate270,
> +			Transform::VFlip,
> +		},
> +		{
> +			/*
> +			 *      o2      t       o1
> +			 *      ------------------
> +			 *	CB  * (T) =  	CD
> +			 *	DA		BA
> +			 */
> +			Orientation::rotate180, Orientation::rotate270Flip,
> +			Transform::Transpose,
> +		},
> +		{
> +			/*
> +			 *      o2      t       o1
> +			 *      ------------------
> +			 *	AD  * (T) =  	AB
> +			 *	BC		DC
> +			 */
> +			Orientation::rotate0, Orientation::rotate90Flip,
> +			Transform::Transpose,
> +		},
> +		{
> +			/*
> +			 *      o2      t       o1
> +			 *      ------------------
> +			 *	AD  * (V) =  	BC
> +			 *	BC		AD
> +			 */
> +			Orientation::rotate270, Orientation::rotate90Flip,
> +			Transform::VFlip,
> +		},
> +		{
> +			/*
> +			 *      o2      t       o1
> +			 *      ------------------
> +			 *	DA  * (V) =  	CB
> +			 *	CB		DA
> +			 */
> +			Orientation::rotate270Flip, Orientation::rotate90,
> +			Transform::VFlip,
> +		},
> +		{
> +			/*
> +			 *      o2      t               o1
> +			 *      --------------------------
> +			 *	CB  * (V|H) =	BC  	AD
> +			 *	DA		AD	BC
> +			 */
> +			Orientation::rotate90Flip, Orientation::rotate270Flip,
> +			Transform::Rot180,
> +		},
> +	};
> +
> +	for (const auto &entry : testEntries) {
> +		Transform transform = entry.o1 / entry.o2;
> +		cout << "Testing: " << entry.o1 << " / " << entry.o2
> +		     << " = " << transformToString(transform) << endl;

I would drop this message, it's unnecessarily verbose when everything
runs fine. The error messages below are enough.

Reviewed-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>

> +		if (transform != entry.t) {
> +			cerr << "Failed to validate: " << entry.o1
> +			     << " / " << entry.o2
> +			     << " = " << transformToString(entry.t) << endl;
> +			cerr << "Got back: "
> +			     << transformToString(transform) << endl;
> +			return TestFail;
> +		}
> +
> +		Orientation adjusted = entry.o2 * entry.t;
> +		if (adjusted != entry.o1) {
> +			cerr << "Failed to validate: " << entry.o2
> +			     << " * " << transformToString(entry.t)
> +			     << " = " << entry.o1 << endl;
> +			cerr << "Got back: " << adjusted << endl;
> +			return TestFail;
> +		}
> +	}
> +
> +	return TestPass;
> +}
> +
> +TEST_REGISTER(TransformTest)

-- 
Regards,

Laurent Pinchart


More information about the libcamera-devel mailing list