From patchwork Tue Mar 19 12:05:03 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Klug X-Patchwork-Id: 19742 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 7BA72C3274 for ; Tue, 19 Mar 2024 12:05:36 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 50F1862D35; Tue, 19 Mar 2024 13:05:31 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="OzjbpHBf"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E399362CA7 for ; Tue, 19 Mar 2024 13:05:27 +0100 (CET) Received: from jasper.fritz.box (unknown [IPv6:2a00:6020:448c:6c00:1478:344b:8fcb:baf5]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 3201716F9; Tue, 19 Mar 2024 13:05:01 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1710849901; bh=sum0LNiyBWcI02Cm1czo0s5fVuD5BqSZAwIRpFzcYm8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=OzjbpHBfB1TDaV0FOBeuw1qT8thNb25Mrs8D8Cr++mnEX743epAs/TWMY5Cn7Zy9d Ul+RR4oidHlN7Bd03dSOlyNnEHLgcms9hP/voOwjXVqwWaL6yhJ3pDiFMrfu6IIBnl FSh2LCdDEJDQsQO4x4Arp7nOZAltGFV1H1AHnDWc= From: Stefan Klug To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug Subject: [PATCH v3 02/16] libcamera: lc-compliance: Add TimeSheet class Date: Tue, 19 Mar 2024 13:05:03 +0100 Message-Id: <20240319120517.362082-3-stefan.klug@ideasonboard.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20240319120517.362082-1-stefan.klug@ideasonboard.com> References: <20240319120517.362082-1-stefan.klug@ideasonboard.com> MIME-Version: 1.0 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: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" This class allows us to prepare a time sheet with controls for the frames to capture (a bit like the capture script in cam). During capture the metadata is recorded. Additionally a mean brightness value of a 20x20 pixel spot in the middle of the frame is calculated. This allows easy analysis after running the capture without complicated state handling due to the asynchronous nature of the capturing process. Signed-off-by: Stefan Klug --- src/apps/lc-compliance/meson.build | 1 + src/apps/lc-compliance/time_sheet.cpp | 148 ++++++++++++++++++++++++++ src/apps/lc-compliance/time_sheet.h | 62 +++++++++++ 3 files changed, 211 insertions(+) create mode 100644 src/apps/lc-compliance/time_sheet.cpp create mode 100644 src/apps/lc-compliance/time_sheet.h diff --git a/src/apps/lc-compliance/meson.build b/src/apps/lc-compliance/meson.build index c792f072..eb7b2d71 100644 --- a/src/apps/lc-compliance/meson.build +++ b/src/apps/lc-compliance/meson.build @@ -16,6 +16,7 @@ lc_compliance_sources = files([ 'environment.cpp', 'main.cpp', 'simple_capture.cpp', + 'time_sheet.cpp', ]) lc_compliance = executable('lc-compliance', lc_compliance_sources, diff --git a/src/apps/lc-compliance/time_sheet.cpp b/src/apps/lc-compliance/time_sheet.cpp new file mode 100644 index 00000000..8048cf30 --- /dev/null +++ b/src/apps/lc-compliance/time_sheet.cpp @@ -0,0 +1,148 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024, Ideas on Board Oy + * + * time_sheet.cpp + */ + +#include "time_sheet.h" + +#include +#include + +#include "libcamera/internal/formats.h" +#include "libcamera/internal/mapped_framebuffer.h" + +using namespace libcamera; + +namespace { + +double calculateMeanBrightnessFromCenterSpot(libcamera::Request *request) +{ + const Request::BufferMap &buffers = request->buffers(); + for (const auto &[stream, buffer] : buffers) { + MappedFrameBuffer in(buffer, MappedFrameBuffer::MapFlag::Read); + if (!in.isValid()) + continue; + + const uint8_t *data = in.planes()[0].data(); + const auto &streamConfig = stream->configuration(); + const auto &formatInfo = PixelFormatInfo::info(streamConfig.pixelFormat); + double (*calcPixelMean)(const uint8_t *); + int pixelStride; + + switch (streamConfig.pixelFormat) { + case formats::NV12: + calcPixelMean = [](const uint8_t *d) -> double { + return static_cast(*d); + }; + pixelStride = 1; + break; + case formats::SRGGB10: + calcPixelMean = [](const uint8_t *d) -> double { + return static_cast(*reinterpret_cast(d)); + }; + pixelStride = 2; + break; + default: + std::stringstream s; + s << "Unsupported Pixelformat " << formatInfo.name; + throw std::invalid_argument(s.str()); + } + + double sum = 0; + int w = 20; + int xs = streamConfig.size.width / 2 - w / 2; + int ys = streamConfig.size.height / 2 - w / 2; + + for (auto y = ys; y < ys + w; y++) { + auto line = data + y * streamConfig.stride; + for (auto x = xs; x < xs + w; x++) + sum += calcPixelMean(line + x * pixelStride); + } + sum = sum / (w * w); + return sum; + } + + return 0; +} + +} /* namespace */ + +TimeSheetEntry::TimeSheetEntry(const ControlIdMap &idmap) + : controls_(idmap) +{ +} + +void TimeSheetEntry::handleCompleteRequest(libcamera::Request *request, + const TimeSheetEntry *previous) +{ + metadata_ = request->metadata(); + + spotBrightness_ = calculateMeanBrightnessFromCenterSpot(request); + if (previous) + brightnessChange_ = spotBrightness_ / previous->spotBrightness(); + + sequence_ = request->sequence(); +} + +std::ostream &operator<<(std::ostream &os, const TimeSheetEntry &te) +{ + os << "=== Frame " << te.sequence_ << std::endl; + if (!te.controls_.empty()) { + os << "Controls:" << std::endl; + const auto &idMap = te.controls_.idMap(); + assert(idMap); + for (const auto &[id, value] : te.controls_) + os << " " << idMap->at(id)->name() << " : " + << value.toString() << std::endl; + } + + if (!te.metadata_.empty()) { + os << "Metadata:" << std::endl; + const auto &idMap = te.metadata_.idMap(); + assert(idMap); + for (const auto &[id, value] : te.metadata_) + os << " " << idMap->at(id)->name() << " : " + << value.toString() << std::endl; + } + + os << "Calculated Brightness: " << te.spotBrightness() << std::endl; + return os; +} + +TimeSheetEntry &TimeSheet::get(size_t pos) +{ + entries_.reserve(pos + 1); + auto &entry = entries_[pos]; + if (!entry) + entry = std::make_unique(idmap_); + + return *entry; +} + +void TimeSheet::prepareForQueue(libcamera::Request *request, uint32_t sequence) +{ + request->controls() = get(sequence).controls(); +} + +void TimeSheet::handleCompleteRequest(libcamera::Request *request) +{ + uint32_t sequence = request->sequence(); + auto &entry = get(sequence); + TimeSheetEntry *previous = nullptr; + if (sequence >= 1) + previous = entries_[sequence - 1].get(); + + entry.handleCompleteRequest(request, previous); +} + +std::ostream &operator<<(std::ostream &os, const TimeSheet &ts) +{ + for (const auto &entry : ts.entries_) { + if (entry) + os << *entry; + } + + return os; +} diff --git a/src/apps/lc-compliance/time_sheet.h b/src/apps/lc-compliance/time_sheet.h new file mode 100644 index 00000000..417277aa --- /dev/null +++ b/src/apps/lc-compliance/time_sheet.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024, Ideas on Board Oy + * + * time_sheet.h + */ + +#pragma once + +#include +#include +#include + +#include + +class TimeSheetEntry +{ +public: + TimeSheetEntry() = delete; + TimeSheetEntry(const libcamera::ControlIdMap &idmap); + TimeSheetEntry(TimeSheetEntry &&other) = default; + TimeSheetEntry(const TimeSheetEntry &) = delete; + ~TimeSheetEntry() = default; + + libcamera::ControlList &controls() { return controls_; } + const libcamera::ControlList &metadata() const { return metadata_; } + void handleCompleteRequest(libcamera::Request *request, + const TimeSheetEntry *previous); + double spotBrightness() const { return spotBrightness_; } + double brightnessChange() const { return brightnessChange_; } + + friend std::ostream &operator<<(std::ostream &os, const TimeSheetEntry &te); + +private: + double spotBrightness_ = 0.0; + double brightnessChange_ = 0.0; + libcamera::ControlList controls_; + libcamera::ControlList metadata_; + uint32_t sequence_ = 0; +}; + +class TimeSheet +{ +public: + TimeSheet(int count, const libcamera::ControlIdMap &idmap) + : idmap_(idmap), entries_(count) + { + } + + void prepareForQueue(libcamera::Request *request, uint32_t sequence); + void handleCompleteRequest(libcamera::Request *request); + + TimeSheetEntry &operator[](size_t pos) { return get(pos); } + TimeSheetEntry &get(size_t pos); + size_t size() const { return entries_.size(); } + + friend std::ostream &operator<<(std::ostream &os, const TimeSheet &ts); + +private: + const libcamera::ControlIdMap &idmap_; + std::vector> entries_; +};