From patchwork Tue Jul 18 10:52:08 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 18849 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 3D5FDC32AC for ; Tue, 18 Jul 2023 10:52:32 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B6A5E628E0; Tue, 18 Jul 2023 12:52:31 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1689677551; bh=rHECqQXSuBFFChP6fsCWWExJnx2HNzOorTkOkQ2f/kw=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=3nyufN5TXO6kY1e+cuJNKmkTafp6k5ooHfV3mMUO4/ZmWP7ET4hnhUFg4nSfmP/2+ JC3ZkWokw7ajAgbyjsMcwNrHRHb5Xj8coSy/evk5AKhTrP45GvHLoS+xJiCmujGE0M 4+cx8ybK+Un6XK80W6aF5N6U9UQc5tq0slPkO0FYLuPxvhpHDVPgNUBY3Y2DZZ3r+l MVPVXmoOHebJNE9LYd5GTZtszh2JB81diuGdCdZK5DQacNljLOIWPZCXZPaN3I4gdF okZYKeRJsOvFIlqvX8KUu4MG02L7c8xeVZUzqO+4VMhJvrYwGf7PfFmxZc3B3myuQz wa9ShEO81+Wzg== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5D429628C0 for ; Tue, 18 Jul 2023 12:52:28 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="HYMQWrQe"; dkim-atps=neutral Received: from uno.localdomain (mob-5-90-54-150.net.vodafone.it [5.90.54.150]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 672B1838; Tue, 18 Jul 2023 12:51:34 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1689677495; bh=rHECqQXSuBFFChP6fsCWWExJnx2HNzOorTkOkQ2f/kw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=HYMQWrQecUIIR58oSwWBYl+ecpr+O2d4fFVyL3TyC9SxVXRu7/37/RU810lNXCVEY sRFTGCjv1HGpBaLUsN3f983vOkK04ZAtaJrQk9X8Lf++deGiqo7Fs7q3gOhZA9UF6+ P6LxPO3lsdhzg++PTgkY3pfoY16mlOMz1YsSjF8Q= To: libcamera-devel@lists.libcamera.org Date: Tue, 18 Jul 2023 12:52:08 +0200 Message-Id: <20230718105210.83558-9-jacopo.mondi@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230718105210.83558-1-jacopo.mondi@ideasonboard.com> References: <20230718105210.83558-1-jacopo.mondi@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 08/10] test: Add unit test for Transform and Orientation X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Jacopo Mondi via libcamera-devel From: Jacopo Mondi Reply-To: Jacopo Mondi Cc: Jacopo Mondi Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" 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 Reviewed-by: David Plowman --- include/libcamera/transform.h | 7 + test/meson.build | 1 + test/transform.cpp | 335 ++++++++++++++++++++++++++++++++++ 3 files changed, 343 insertions(+) create mode 100644 test/transform.cpp diff --git a/include/libcamera/transform.h b/include/libcamera/transform.h index 847a1f94713c..833b50d46fd0 100644 --- a/include/libcamera/transform.h +++ b/include/libcamera/transform.h @@ -16,13 +16,20 @@ enum class Orientation; enum class Transform : int { Identity = 0, Rot0 = Identity, + HFlip = 1, + VFlip = 2, + HVFlip = HFlip | VFlip, Rot180 = HVFlip, + Transpose = 4, + Rot270 = HFlip | Transpose, + Rot90 = VFlip | Transpose, + Rot180Transpose = HFlip | VFlip | Transpose }; 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..f79a1a892d16 --- /dev/null +++ b/test/transform.cpp @@ -0,0 +1,335 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2023, Ideas On Board Oy + * + * transform.cpp - Transform and Orientation tests + */ + +#include + +#include +#include + +#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 { + 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, + }, + { + Orientation::rotate180, Orientation::rotate180, + Transform::Identity + }, + { + /* + * 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; + 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)