From c2e6608cb45ffc4c52d7c88948057c46bbc0844e Mon Sep 17 00:00:00 2001 From: Milosz Wasilewski Date: Wed, 16 Oct 2024 09:28:03 +0100 Subject: [PATCH] automated: linux: add video output comparison test The test takes a screenshot from video output and compares to the reference image. It is intended to test HDMI output from DUT. Signed-off-by: Milosz Wasilewski --- automated/linux/video/Dockerfile | 4 ++ automated/linux/video/compare.py | 95 ++++++++++++++++++++++++++ automated/linux/video/requirements.txt | 3 + automated/linux/video/video.sh | 72 +++++++++++++++++++ automated/linux/video/video.yaml | 43 ++++++++++++ 5 files changed, 217 insertions(+) create mode 100644 automated/linux/video/Dockerfile create mode 100644 automated/linux/video/compare.py create mode 100644 automated/linux/video/requirements.txt create mode 100755 automated/linux/video/video.sh create mode 100644 automated/linux/video/video.yaml diff --git a/automated/linux/video/Dockerfile b/automated/linux/video/Dockerfile new file mode 100644 index 000000000..c7dd02da4 --- /dev/null +++ b/automated/linux/video/Dockerfile @@ -0,0 +1,4 @@ +FROM python:3 + +COPY requirements.txt /home/ +RUN python3 -m pip install opencv-python scikit-image requests diff --git a/automated/linux/video/compare.py b/automated/linux/video/compare.py new file mode 100644 index 000000000..ec2d77eb0 --- /dev/null +++ b/automated/linux/video/compare.py @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (C) 2024 Qualcomm Inc. + +import cv2 +import numpy as np +import requests +import sys +from argparse import ArgumentParser +from skimage.metrics import structural_similarity + +parser = ArgumentParser() +parser.add_argument("--reference", required=True, help="Reference image path") +auth_group = parser.add_mutually_exclusive_group() +auth_group.add_argument( + "--reference-auth-user", help="Username required to download reference image" +) +parser.add_argument( + "--reference-auth-password", help="Password required to download reference image" +) +auth_group.add_argument( + "--reference-auth-token", help="Token required to download reference image" +) +parser.add_argument( + "--threshold", + required=True, + type=float, + help="Minimal threshold to pass the test. Value must be between 0 and 1", +) +parser.add_argument( + "--lava", + default=True, + action="store_true", + help="Print results in LAVA friendly format", +) +parser.add_argument("--no-lava", dest="lava", action="store_false") +group = parser.add_mutually_exclusive_group(required=True) +group.add_argument("--device", help="Video device to capture image from") +group.add_argument("--image", help="Compared image path") + +args = parser.parse_args() + +if args.threshold < 0 or args.threshold > 1: + print(f"Invalid threshold: {args.threshold}") + sys.exit(1) + +first = None + +if args.reference.startswith("http"): + # download reference image + s = requests.Session() + if args.reference_auth_user and args.reference_auth_password: + s.auth = (args.reference_auth_user, args.reference_auth_password) + if args.reference_auth_token: + s.headers.update({"Authorization": f"Token {args.reference_auth_token}"}) + s.stream = True + print(f"Retrieving reference from: {args.reference}") + first_resp = s.get(args.reference) + data = first_resp.raw.read() + first = cv2.imdecode(np.frombuffer(data, dtype=np.uint8), cv2.IMREAD_COLOR) + +else: + first = cv2.imread(args.reference) + +second = None + +if args.device is not None: + cam = cv2.VideoCapture(args.device) + + if not (cam.isOpened()): + print(f"Could not open video device {args.device}") + sys.exit(1) + + cam.set(cv2.CAP_PROP_FRAME_WIDTH, first.shape[1]) + cam.set(cv2.CAP_PROP_FRAME_HEIGHT, first.shape[0]) + ret, second = cam.read() + cam.release() + cv2.destroyAllWindows() + +if args.image: + second = cv2.imread(args.image) + +first_gray = cv2.cvtColor(first, cv2.COLOR_BGR2GRAY) +second_gray = cv2.cvtColor(second, cv2.COLOR_BGR2GRAY) + +score, diff = structural_similarity(first_gray, second_gray, full=True) +print("Similarity Score: {:.3f}%".format(score * 100)) +print("Required threshold: {:.3f}%".format(args.threshold * 100)) +if score < args.threshold: + print("Test fail") + if args.lava: + print("") +else: + print("Test pass") + if args.lava: + print("") diff --git a/automated/linux/video/requirements.txt b/automated/linux/video/requirements.txt new file mode 100644 index 000000000..108d324c4 --- /dev/null +++ b/automated/linux/video/requirements.txt @@ -0,0 +1,3 @@ +opencv-python +requests +scikit-image diff --git a/automated/linux/video/video.sh b/automated/linux/video/video.sh new file mode 100755 index 000000000..6c62e12f2 --- /dev/null +++ b/automated/linux/video/video.sh @@ -0,0 +1,72 @@ +#!/bin/sh + +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (C) 2024 Qualcomm Inc. + +# shellcheck disable=SC1091 +. ../../lib/sh-test-lib +OUTPUT="$(pwd)/output" +RESULT_FILE="${OUTPUT}/result.txt" +export RESULT_FILE +REFERENCE_IMAGE="" +REFERENCE_IMAGE_USER="" +REFERENCE_IMAGE_PASSWORD="" +REFERENCE_IMAGE_TOKEN="" +THRESHOLD=0.99 +DEVICE="/dev/video0" +IMAGE="" + +usage() { + echo "\ + Usage: $0 [-p ] [-t ] [-s ] [-l ] + + -r + -t + -d + -u + -p + -T + -i + " +} + +while getopts "p:t:d:u:r:T:i:h" opts; do + case "$opts" in + p) REFERENCE_IMAGE_PASSWORD="${OPTARG}";; + t) THRESHOLD="${OPTARG}";; + d) DEVICE="${OPTARG}";; + u) REFERENCE_IMAGE_USER="${OPTARG}";; + r) REFERENCE_IMAGE="${OPTARG}";; + T) REFERENCE_IMAGE_TOKEN="${OPTARG}";; + i) IMAGE="${OPTARG}";; + h|*) usage ; exit 1 ;; + esac +done + +! check_root && error_msg "You need to be root to run this script." + +# Test run. +create_out_dir "${OUTPUT}" + +if [ -z "${REFERENCE_IMAGE}" ]; then + error_msg "Reference image missing" +fi + +ARGS="--lava --threshold ${THRESHOLD} --reference ${REFERENCE_IMAGE}" + +if [ -n "${IMAGE}" ]; then + ARGS="${ARGS} --image ${IMAGE}" +fi + +if [ -n "${DEVICE}" ]; then + ARGS="${ARGS} --device ${DEVICE}" +fi + +if [ -n "${REFERENCE_IMAGE_USER}" ] && [ -n "${REFERENCE_IMAGE_PASSWORD}" ]; then + ARGS="${ARGS} --reference-auth-user ${REFERENCE_IMAGE_USER} --reference-auth-password ${REFERENCE_IMAGE_PASSWORD}" +elif [ -n "${REFERENCE_IMAGE_TOKEN}" ]; then + ARGS="${ARGS} --reference-auth-token ${REFERENCE_IMAGE_TOKEN}" +fi + +# shellcheck disable=SC2086 +python3 compare.py ${ARGS} diff --git a/automated/linux/video/video.yaml b/automated/linux/video/video.yaml new file mode 100644 index 000000000..ff59c22e5 --- /dev/null +++ b/automated/linux/video/video.yaml @@ -0,0 +1,43 @@ +metadata: + format: Lava-Test Test Definition 1.0 + name: video-compare + description: | + The test compares two images. The second image + can be either captured from v4l2 device by the script + or delivered to the script from outside. + + Main purpose of the test is to verify video output + using a capture device connected to DUT + + The test script should run on host, where the capture device is connected. + + maintainer: + - mwasilew@quicinc.com + os: + - debian + - ubuntu + - centos + - fedora + - openembedded + scope: + - functional + devices: + - imx8mm + - imx6ull + +params: + # https://example.com/image.jpg + REFERENCE_IMAGE: "" + REFERENCE_IMAGE_USER: "" + REFERENCE_IMAGE_PASSWORD: "" + REFERENCE_IMAGE_TOKEN: "" + THRESHOLD: 0.99 + DEVICE: "/dev/video0" + # /home/user/image.jpg + IMAGE: "" + +run: + steps: + - cd ./automated/linux/video/ + - ./video.sh -r "${REFERENCE_IMAGE}" -t "${THRESHOLD}" -d "${DEVICE}" -u "${REFERENCE_IMAGE_USER}" -p "${REFERENCE_IMAGE_PASSWORD}" -T "${REFERENCE_IMAGE_TOKEN}" -i "${IMAGE}" + - ../../utils/send-to-lava.sh ./output/result.txt