From patchwork Tue Aug 17 15:42:00 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Siyuan Fan X-Patchwork-Id: 13378 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 15E73BD87C for ; Tue, 17 Aug 2021 15:42:08 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D49C3688A2; Tue, 17 Aug 2021 17:42:07 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=foxmail.com header.i=@foxmail.com header.b="yyFK77i+"; dkim-atps=neutral Received: from out162-62-57-252.mail.qq.com (out162-62-57-252.mail.qq.com [162.62.57.252]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4F2EF6025C for ; Tue, 17 Aug 2021 17:42:06 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=foxmail.com; s=s201512; t=1629214923; bh=DWtivy05nTGHyPkjDPkoKhqut1dksWt2RYr8riNC19A=; h=From:To:Cc:Subject:Date; b=yyFK77i+SNcK4zG9otTxNA0+6hzN+2t7xCCydxVKHnHRbf/a3m78przcju8dEbI2b 7wqPtfe9rqdMnVotro9cIFTo4t+L8LaKz/QD53IjB/hbCOT7BCgi7qCmNP3tJT2yfn gKj63l/0czIXtBtC+CInU8AlUen4XyiCEfcK7B3k= Received: from localhost.localdomain ([123.126.82.10]) by newxmesmtplogicsvrszc7.qq.com (NewEsmtp) with SMTP id A81A1CD5; Tue, 17 Aug 2021 23:42:01 +0800 X-QQ-mid: xmsmtpt1629214921t6h6x25w3 Message-ID: X-QQ-XMAILINFO: MDlC9u6qHHGcke0xx4Xm5HXv3CpktQWDq0h7nNaW6gC1Is3s/WiCDZmC5VFdOx d9LNvTgBo/BnXHuH0Dg3bJdWJRmhOXAdNpGAhoCypNvcgW0GBz4Om6kIxydXva3W8wVhmXhOqKsb LCDMBJipssz1xWarUfbvOiMiPkvjfA1LifenESD6/JztePOG47yrc9s/nWG88Byl9wOJ/HP4WncB rM13bSWDPaYvhjK2Z06K8hO83r99UCD4plZbbZKpe/XVEs8tV1QxlygL8pBPooUSHpwXCL6nPyLn ysDiJp5ZohcEn9gmOHp0mRKmhCaReFJNu1yYSx4VyxwZpxh1CIYWwCG6HJRrRZJSmwGLLJVPblCk bZJYlO7VtjGFhT3otbZ4NjzxpXuZnRIvXQ65fh/cVcqDXke6tZx3q4rtnfEffaR2mG2MNT8WGcis REOajfGFSTLZ6gzQ+8U+HTPUg7afsvvf2EY/LV2I+oPXPiskp7vXo/yfqIUwmL2ZqAKfJ9Qc9eRw 9/nCXmN+j2zmX3U/S70QV+ioEbd0fIE6g+XjxviJgOcIrCOMr7NLvnUotGg6ST1UriCpySQGkMiU BtWh1H2FDWMOMedP98VlFtFvfsHraiIkt8kZjTEI4U8AXUNnZUqkf29JLZ829HA4qJbf7JNIrurw APorP2piBpCkDeEd2SJEnvtGiAjdJ1GwTqnTz2qclXN77PcegvrfNL3n4XTpwAPZ5/Sor0NPAxgm qIr7pyiXDs/amTtzvPTUc8fDv88Mqms4DtcXeGQt76UdYgRyHSvMwV80fVX1/qFo+T0VIto0ruTL RRpD+lwwJudCeEWGiGPUsSN9igO8QEXpPcaW+G2NOD6WoUvEpzs3mym7PihW0d/hPJi/wSiQe/jU I2f4KOpk4WU+lIzl+C9gI0rK+bzMmir0IN2uISCF7IVwCBdi1gy7OUTha1CgadbqRaQ9y8OQkF9G 3XU/UwAO+GOuQPHa6A3Z7jxU9bGS+peRYknu5//G/g1qR6AdjC9HqrKlkLDItHdWqa0xLS7ywI1I vGRJN+Ug== From: Siyuan Fan To: libcamera-devel@lists.libcamera.org Date: Tue, 17 Aug 2021 16:42:00 +0100 X-OQ-MSGID: <20210817154200.6763-1-siyuan.fan@foxmail.com> X-Mailer: git-send-email 2.20.1 MIME-Version: 1.0 Subject: [libcamera-devel] [RFC PATCH v3 1/4] pipeline: isp: The software ISP-based pipeline handler 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" From: Fan Siyuan Using the output format supported by ISP initializes StreamFormats. In validate(), if the request format is not supported, adjust format to RGB888. Signed-off-by: Fan Siyuan --- src/libcamera/pipeline/isp/isp.cpp | 315 +++++++++++++++++++++++++++++ 1 file changed, 315 insertions(+) create mode 100644 src/libcamera/pipeline/isp/isp.cpp diff --git a/src/libcamera/pipeline/isp/isp.cpp b/src/libcamera/pipeline/isp/isp.cpp new file mode 100644 index 00000000..49f55ddb --- /dev/null +++ b/src/libcamera/pipeline/isp/isp.cpp @@ -0,0 +1,315 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021, Siyuan Fan + * + * isp.cpp - The software ISP-based pipeline handler + */ + +#include "../../swisp/isp.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "libcamera/internal/device_enumerator.h" +#include "libcamera/internal/media_device.h" +#include "libcamera/internal/pipeline_handler.h" +#include "libcamera/internal/v4l2_videodevice.h" + +namespace libcamera { + +LOG_DEFINE_CATEGORY(ISP) + +class ISPCameraData : public CameraData +{ +public: + ISPCameraData(PipelineHandler *pipe, MediaDevice *media) + : CameraData(pipe), media_(media), video_(nullptr) + { + } + + ~ISPCameraData() + { + delete video_; + } + + int init(); + void bufferReady(FrameBuffer *buffer); + void ISPCompleted(FrameBuffer *rawbuffer, FrameBuffer *rgbBuffer); + + Stream stream_; + ISPCPU isp_; + int width_; + int height_; + + std::vector> rawBuffers_; + std::queue rawBufferQueue_; + std::queue rgbBufferQueue_; + + MediaDevice *media_; + V4L2VideoDevice *video_; +}; + + +class ISPCameraConfiguration : public CameraConfiguration +{ +public: + ISPCameraConfiguration(); + + Status validate() override; +}; + +class PipelineHandlerISP : public PipelineHandler +{ +public: + PipelineHandlerISP(CameraManager *manager); + + CameraConfiguration *generateConfiguration(Camera *camera, + const StreamRoles &roles) override; + int configure(Camera *camera, CameraConfiguration *config) override; + + int exportFrameBuffers(Camera *camera, Stream *stream, + std::vector> *buffers) override; + + int start(Camera *camera, const ControlList *controls) override; + void stop(Camera *camera) override; + + int queueRequestDevice(Camera *camera, Request *request) override; + + bool match(DeviceEnumerator *enumerator) override; + +private: + ISPCameraData *cameraData(const Camera *camera) + { + return static_cast( + PipelineHandler::cameraData(camera)); + } +}; + +ISPCameraConfiguration::ISPCameraConfiguration() + : CameraConfiguration() +{ +} + +CameraConfiguration::Status ISPCameraConfiguration::validate() +{ + Status status = Valid; + + if (config_.empty()) + return Invalid; + + if (config_.size() > 1) { + config_.resize(1); + status = Adjusted; + } + + StreamConfiguration &cfg = config_[0]; + const std::vector formats = cfg.formats().pixelformats(); + if (std::find(formats.begin(), formats.end(), cfg.pixelFormat) == formats.end()) { + cfg.pixelFormat = cfg.formats().pixelformats()[0]; + LOG(ISP, Debug) << "Adjusting format to" << cfg.pixelFormat.toString(); + status = Adjusted; + } + + cfg.bufferCount = 4; + + return status; +} + +PipelineHandlerISP::PipelineHandlerISP(CameraManager *manager) + : PipelineHandler(manager) +{ +} + +CameraConfiguration *PipelineHandlerISP::generateConfiguration(Camera *camera, + const StreamRoles &roles) +{ + ISPCameraData *data = cameraData(camera); + CameraConfiguration *config = new ISPCameraConfiguration(); + + if (roles.empty()) + return config; + + std::map> ispFormat; + ispFormat = data->isp_.pixelFormatConfiguration(); + StreamFormats formats(ispFormat); + StreamConfiguration cfg(formats); + + cfg.pixelFormat = formats::RGB888; + cfg.size = { 640, 480 }; + cfg.bufferCount = 4; + + config->addConfiguration(cfg); + + config->validate(); + + data->isp_.outputpixelformat = data->isp_.getOutputPixelFormat(config->at(0).pixelFormat); ; + + return config; +} + +int PipelineHandlerISP::configure(Camera *camera, CameraConfiguration *config) +{ + ISPCameraData *data = cameraData(camera); + StreamConfiguration &cfg = config->at(0); + + PixelFormat bayerFormat = formats::SBGGR10; + V4L2DeviceFormat format = {}; + format.fourcc = data->video_->toV4L2PixelFormat(bayerFormat);; + format.size = {640, 480}; + + data->width_ = format.size.width; + data->height_ = format.size.height; + + int ret = data->video_->setFormat(&format); + if (ret) + return ret; + + cfg.setStream(&data->stream_); + cfg.stride = format.planes[0].bpl; + + return 0; +} + +int PipelineHandlerISP::exportFrameBuffers(Camera *camera, Stream *stream, + std::vector> *buffers) +{ + unsigned int count = stream->configuration().bufferCount; + ISPCameraData *data = cameraData(camera); + + count = data->isp_.exportBuffers(buffers, count, data->width_, data->height_); + + return count; + +} + +int PipelineHandlerISP::start(Camera *camera, [[maybe_unused]] const ControlList *controls) +{ + ISPCameraData *data = cameraData(camera); + unsigned int count = data->stream_.configuration().bufferCount; + + int ret = data->video_->allocateBuffers(count, &data->rawBuffers_); + if (ret < 0) { + LOG(ISP, Error) << strerror(-ret); + return ret; + } + + for (unsigned int i = 0; i < count; i++) { + data->rawBufferQueue_.push(data->rawBuffers_[i].get()); + } + + + ret = data->video_->streamOn(); + if (ret < 0) { + data->video_->releaseBuffers(); + return ret; + } + + data->isp_.startThreadISP(); + + return 0; +} + +void PipelineHandlerISP::stop(Camera *camera) +{ + ISPCameraData *data = cameraData(camera); + + if (!(data->rawBuffers_.empty())) { + data->rawBuffers_.clear(); + } + + data->isp_.stopThreadISP(); + + data->video_->streamOff(); + data->video_->releaseBuffers(); +} + +int PipelineHandlerISP::queueRequestDevice(Camera *camera, Request *request) +{ + ISPCameraData *data = cameraData(camera); + FrameBuffer *rgbBuffer = request->findBuffer(&data->stream_); + if (!rgbBuffer) { + LOG(ISP, Error) << "Attempt to queue request with invalid stream"; + return -ENOENT; + } + data->rgbBufferQueue_.push(rgbBuffer); + + FrameBuffer *buffer = data->rawBufferQueue_.front(); + int ret = data->video_->queueBuffer(buffer); + if (ret < 0) { + LOG(ISP, Error) << "Queue raw buffer error"; + return ret; + } + data->rawBufferQueue_.pop(); + + return 0; +} + +bool PipelineHandlerISP::match(DeviceEnumerator *enumerator) +{ + DeviceMatch unicam("unicam"); + + unicam.add("unicam-embedded"); + unicam.add("unicam-image"); + + MediaDevice *unicam_ = acquireMediaDevice(enumerator, unicam); + if (!unicam_) { + LOG(ISP, Debug) << "unicam Device not found"; + return false; + } + + LOG(ISP, Debug) << "unicam Device Identified"; + + std::unique_ptr data = std::make_unique(this, unicam_); + + if(data->init()) return false; + + std::set streams{&data->stream_}; + std::shared_ptr camera = Camera::create(this, data->video_->deviceName(), streams); + registerCamera(std::move(camera), std::move(data)); + + return true; +} + + +void ISPCameraData::ISPCompleted(FrameBuffer *rawBuffer, FrameBuffer *rgbBuffer) +{ + Request *request = rgbBuffer->request(); + + rawBufferQueue_.push(rawBuffer); + + pipe_->completeBuffer(request, rgbBuffer); + pipe_->completeRequest(request); + +} + +void ISPCameraData::bufferReady(FrameBuffer *buffer) +{ + FrameBuffer *rgbBuffer = rgbBufferQueue_.front(); + isp_.invokeMethod(&ISPCPU::processing, ConnectionTypeQueued, buffer, rgbBuffer, width_, height_); + rgbBufferQueue_.pop(); + +} + +int ISPCameraData::init() +{ + video_ = new V4L2VideoDevice(media_->getEntityByName("unicam-image")); + if (video_->open()) + return -ENODEV; + + video_->bufferReady.connect(this, &ISPCameraData::bufferReady); + isp_.ispCompleted.connect(this, &ISPCameraData::ISPCompleted); + + return 0; +} + +REGISTER_PIPELINE_HANDLER(PipelineHandlerISP) + +} /* namespace libcamera */ From patchwork Tue Aug 17 15:42:19 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Siyuan Fan X-Patchwork-Id: 13379 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 6EFDABD87C for ; Tue, 17 Aug 2021 15:42:29 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 38F1D688A6; Tue, 17 Aug 2021 17:42:29 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=foxmail.com header.i=@foxmail.com header.b="kXe1m1/n"; dkim-atps=neutral Received: from out203-205-251-59.mail.qq.com (out203-205-251-59.mail.qq.com [203.205.251.59]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 77DE26025C for ; Tue, 17 Aug 2021 17:42:26 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=foxmail.com; s=s201512; t=1629214943; bh=aIEXWV0RsMtac+GysMTfwctYOposR+CvYa98/CI3dww=; h=From:To:Cc:Subject:Date; b=kXe1m1/nvQUuUzs0aLJYFWk1bgONM+DljzmcTWFJl1ro+aqNy4/qzVkcs/9HWte3k 7hKldlzbSb7A/l8x4lcR7pmLVMUaOfvRGhszktWcPIZJyOWXUKiolVW5N54MA/e+v/ BznS+LevmZBu+6ilnEbIQVN2E3upCnBJl+4sOfL4= Received: from localhost.localdomain ([123.126.82.10]) by newxmesmtplogicsvrszc8.qq.com (NewEsmtp) with SMTP id A95010E2; Tue, 17 Aug 2021 23:42:21 +0800 X-QQ-mid: xmsmtpt1629214941tjjvfb056 Message-ID: X-QQ-XMAILINFO: ONalwM1aM/hii5DS326SgkMMsj+9Y1w8pgnemX/eYMPMap6o1JWObDFkqQ8IxZ 0sjn2EJpLKNfjf00ijE4CWa3Bb9xnP73CyiKXI9zfPwdyxDHNzZktwphiKc9OnePgunAFaKyMibm ntJvnJQM9yCA3mbcjLl00aRKvytlrwWR5O0w32YwuwEdPhx72qf51PPVlwfNln/zOqGEq/zpKGDH Y21jXrfwBBwEewVm/FX8tRtG1MgDl9ZkQHD7eeMI2IQt7+ammJjKtpCeMovJwOR7RxG52QzixXyk v+2hRVFmpnZzEToDMokfayl2FZd9vcK5Da5O0DaSOBAT8jFDXMB9oLpw6W+Tcb9Fzx/1o840FQDF SAGB8RO23wWjkd5EoC6pXCSsoP6Nz6R0HfqsW6JfttG+5jyVrAZojoJ0y7jPScNiqNKC55lVqsPy QwpixmoLFLZr3WpxyN+MPL5y1eP/PTaGGh9dM55wrDQDfhmqPCTwFRuBsaa5xovetgyvUFeWMLjl 3VUtamlCFbPGIXlUxXRhZq1SNTNlCtDUh5g9JYNZnanxuNWSh/CWbOOgr8Lo5IwdGIITpiwGHdWF tWkVk4sowMhKRxW9IxKZoNnPz6awrZscLODiMpjCADoPQp7/gzkDnKnCjCZoaYc1jAQVheadWldT WUKPm3dSm1G/EtCn4hA/HArJJ4ull4SWiMqE+wGAL8hR2rofXkXNJdzRbaAp1fwiSOO6+vs4xgoD LqfXHfIMFFfz9GrNXbSv0wPoeUOh2zjodXocE+SUPmZhSMEi/BIDpjfVA5Fh4PEulOAWxvl+t1SW AvDf7Yjamjuwj+7p71pmsGma/70RdX0oMG2D5DQCsPONk7zcmTy8d4UhAoMQpNBwQ4o9jtztm298 spA9+CuU128zQd7jKILMnzKIudTgWVK5VYL9G9FIHiyiCOuu8U6HkBk6OUbAn/UkfYJBjlEbz/Uo sLNx6ghg24b9dwSUoY5JhPV8Kk9WkjVRXGXMp6tjSR6tXPuqcEvPL+m1tly8gVwQbqFhxvA3U= From: Siyuan Fan To: libcamera-devel@lists.libcamera.org Date: Tue, 17 Aug 2021 16:42:19 +0100 X-OQ-MSGID: <20210817154219.6924-1-siyuan.fan@foxmail.com> X-Mailer: git-send-email 2.20.1 MIME-Version: 1.0 Subject: [libcamera-devel] [RFC PATCH v3 2/4] libcamera: swisp: The software ISP class 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" From: Fan Siyuan Currently class ISPCPU only supports to output RGB888 and BGR888(640x480). Based on format set by application, using getOutputPixelFormat() to match output format and compressAndTransformFormat() to transform corresponding format. Signed-off-by: Fan Siyuan --- src/libcamera/swisp/isp.cpp | 726 ++++++++++++++++++++++++++++++++++++ src/libcamera/swisp/isp.h | 125 +++++++ 2 files changed, 851 insertions(+) create mode 100644 src/libcamera/swisp/isp.cpp create mode 100644 src/libcamera/swisp/isp.h diff --git a/src/libcamera/swisp/isp.cpp b/src/libcamera/swisp/isp.cpp new file mode 100644 index 00000000..b0f801e9 --- /dev/null +++ b/src/libcamera/swisp/isp.cpp @@ -0,0 +1,726 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021, Siyuan Fan + * + * isp.cpp - The software ISP class + */ + +#include "isp.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include "libcamera/base/log.h" + +namespace libcamera{ + +LOG_DECLARE_CATEGORY(ISP) + +void ISPCPU::autoContrast(uint16_t *data, float lowCut, float highCut, int width, int height) +{ + int blue, gr, gb, red; + int histBlue[1024] = {0}, histGb[1024] = {0}, histGr[1024] = {0}, histRed[1024] = {0}; + + int index; + for (int i = 0; i < height; i++) { + index = i * width; + for (int j = 0; j < width; j++) { + if (i % 2 == 0 && j % 2 == 0) { + blue = data[index]; + histBlue[blue]++; + } + else if ((i % 2 == 0 && j % 2 == 1)) { + gb = data[index]; + histGb[gb]++; + } + else if ((i % 2 == 1 && j % 2 == 0)) { + gr = data[index]; + histGr[gr]++; + } + else { + red = data[index]; + histRed[red]++; + } + index++; + } + } + + int pixelAmount = width * height; + int sum = 0; + int minBlue; + for (int i = 0; i < 1024; i++){ + sum = sum + histBlue[i]; + if (sum >= pixelAmount * lowCut * 0.01) { + minBlue = i; + break; + } + } + + sum = 0; + int maxBlue; + for (int i = 1023; i >= 0; i--){ + sum = sum + histBlue[i]; + if (sum >= pixelAmount * highCut * 0.01) { + maxBlue = i; + break; + } + } + + sum = 0; + int minGb; + for (int i = 0; i < 1024; i++){ + sum = sum + histGb[i]; + if (sum >= pixelAmount * lowCut * 0.01) { + minGb = i; + break; + } + } + + sum = 0; + int maxGb; + for (int i = 1023; i >= 0; i--){ + sum = sum + histGb[i]; + if (sum >= pixelAmount * highCut * 0.01) { + maxGb = i; + break; + } + } + + sum = 0; + int minGr; + for (int i = 0; i < 1024; i++){ + sum = sum + histGr[i]; + if (sum >= pixelAmount * lowCut * 0.01) { + minGr = i; + break; + } + } + + sum = 0; + int maxGr; + for (int i = 1023; i >= 0; i--){ + sum = sum + histGr[i]; + if (sum >= pixelAmount * highCut * 0.01) { + maxGr = i; + break; + } + } + + sum = 0; + int minRed; + for (int i = 0; i < 1024; i++){ + sum = sum + histRed[i]; + if (sum >= pixelAmount * lowCut * 0.01) { + minRed = i; + break; + } + } + + sum = 0; + int maxRed; + for (int i = 1023; i >= 0; i--){ + sum = sum + histRed[i]; + if (sum >= pixelAmount * highCut * 0.01) { + maxRed = i; + break; + } + } + + int blueMap[1024]; + float norb = 1.0 / (maxBlue - minBlue); + for (int i = 0; i < 1024; i++) { + if (i < minBlue) { + blueMap[i] = 0; + } + else if (i > maxBlue) { + blueMap[i] = 1023; + } + else { + blueMap[i] = (i - minBlue) * norb * 1023; + } + if (blueMap[i] > 1023) blueMap[i] = 1023; + } + + int gbMap[1024]; + float norgb = 1.0 / (maxGb - minGb); + for (int i = 0; i < 1024; i++) { + if (i < minGb) { + gbMap[i] = 0; + } + else if (i > maxGb) { + gbMap[i] = 1023; + } + else { + gbMap[i] = (i - minGb) * norgb * 1023; + } + if (gbMap[i] > 1023) gbMap[i] = 1023; + } + + int grMap[1024]; + float norgr = 1.0 / (maxGr - minGr); + for (int i = 0; i < 1024; i++) { + if (i < minGr) { + grMap[i] = 0; + } + else if (i > maxGr) { + grMap[i] = 1023; + } + else { + grMap[i] = (i - minGr) * norgr * 1023; + } + if (grMap[i] > 1023) grMap[i] = 1023; + } + + int redMap[1024]; + float norr = 1.0 / (maxRed - minRed); + for (int i = 0; i < 1024; i++) { + if (i < minRed) { + redMap[i] = 0; + } + else if (i > maxRed) { + redMap[i] = 1023; + } + else{ + redMap[i] = (i - minRed) * norr * 1023; + } + if (redMap[i] > 1023) redMap[i] = 1023; + } + + for (int i = 0;i < height; i++) { + for (int j = 0; j < width; j++){ + index = i * width; + if (i % 2 == 0 && j % 2 == 0) { + data[index] = blueMap[data[index]]; + } + else if (i % 2 == 0 && j % 2 == 1) { + data[index] = gbMap[data[index]]; + } + else if (i % 2 == 1 && j % 2 == 0) { + data[index] = grMap[data[index]]; + } + else { + data[index] = redMap[data[index]]; + } + index++; + } + } +} + +void ISPCPU::blackLevelCorrect(uint16_t *data, uint16_t offset, int width, int height) +{ + int len = width * height; + for(int i = 0; i < len; i++) { + if (data[i] < offset){ + data[i] = 0; + } + else { + data[i] -= offset; + } + } +} + +void ISPCPU::readChannels(uint16_t *data, uint16_t *R, uint16_t *G, uint16_t *B, + int width, int height) +{ + int index; + for (int i = 0; i < height; i++) { + index = i * width; + for (int j = 0; j < width; j++) { + if (i % 2 == 0 && j % 2 == 0) { + B[index] = data[index]; + } + else if ((i % 2 == 0 && j % 2 == 1) || (i % 2 == 1 && j % 2 == 0)){ + G[index] = data[index]; + } + else { + R[index] = data[index]; + } + index++; + } + } +} + +void ISPCPU::firstPixelInsert(uint16_t *src, uint16_t *dst, int width, int height) +{ + int index; + for (int i = 0; i < height; i++) { + index = i * width; + for (int j = 0; j < width; j++){ + if (i % 2 == 0 && j % 2 == 1) { + if (j == (width - 1)) { + dst[index] = src[index - 1]; + } + else { + dst[index] = (src[index - 1] + + src[index + 1]) >> 1; + } + } + + if (i % 2 == 1 && j % 2 == 0) { + if(i == height - 1) { + dst[index] = src[index - width]; + } + else { + dst[index] = (src[index - width]+ + src[index + width]) >> 1; + } + } + + if (i % 2 == 1 && j % 2 == 1) { + if (j < width - 1 && i < height - 1) { + dst[index] = (src[index - width - 1] + + src[index - width + 1] + + src[index + width - 1] + + src[index + width + 1]) >> 2; + } + else if (i == height - 1 && j < width - 1) { + dst[index] = (src[index - width - 1] + + src[index - width + 1]) >> 1; + } + else if (i < height - 1 && j == width - 1) { + dst[index] = (src[index - width - 1] + + src[index + width - 1]) >> 1; + } + else { + dst[index] = src[index - width - 1]; + } + } + index++; + } + } +} + +void ISPCPU::twoPixelInsert(uint16_t *src, uint16_t *dst, int width, int height) +{ + int index; + for (int i = 0; i < height; i++) { + index = i * width; + for (int j = 0; j < width; j++) { + if (i == 0 && j == 0) { + dst[index] = (src[index + width] + + src[index + 1]) >> 1; + } + else if (i == 0 && j > 0 && j % 2 == 0) { + dst[index] = (src[index - 1] + + src[index + width] + + src[index + 1]) / 3; + } + else if (i > 0 && j == 0 && i % 2 == 0) { + dst[index] = (src[index - width] + + src[index + 1] + + src[index + width]) / 3; + } + else if (i == (height - 1) && j < (width - 1) && j % 2 == 1) { + dst[index] = (src[index - 1] + + src[index - width] + + src[index + 1]) / 3; + } + else if (i < (height - 1) && j == (width - 1) && i % 2 == 1) { + dst[index] = (src[index - width] + + src[index - 1] + + src[index + width]) / 3; + } + else if (i == (height - 1) && j == (width - 1)) { + dst[index] = (src[index - width] + + src[index - 1]) >> 1; + } + else if ((i % 2 == 0 && j % 2 == 0) || (i % 2 == 1 && j % 2 == 1)) { + dst[index] = (src[index - 1] + + src[index + 1] + + src[index - width] + + src[index + width]) / 4; + } + index++; + } + } +} + +void ISPCPU::lastPixelInsert(uint16_t *src, uint16_t *dst, int width, int height) +{ + int index; + for (int i = 0; i < height; i++) { + index = i * width; + for (int j = 0; j < width; j++){ + if (i % 2 == 1 && j % 2 == 0) { + if (j == 0) { + dst[index] = src[index + 1]; + } + else { + dst[index] = (src[index - 1] + + src[index + 1]) >> 1; + } + } + + if (i % 2 == 0 && j % 2 == 1) { + if(i == 0) { + dst[index] = src[index + width]; + } + else { + dst[index] = (src[index - width]+ + src[index + width]) >> 1; + } + } + + if (i % 2 == 0 && j % 2 == 0) { + if (i > 0 && j > 0) { + dst[index] = (src[index - width - 1] + + src[index - width + 1] + + src[index + width - 1] + + src[index + width + 1]) >> 2; + } + else if (i == 0 && j > 0) { + dst[index] = (src[index + width - 1] + + src[index + width + 1]) >> 1; + } + else if (i > 0 && j == 0) { + dst[index] = (src[index - width + 1] + + src[index + width + 1]) >> 1; + } + else { + dst[index] = src[index + width + 1]; + } + } + index++; + } + } +} + +void ISPCPU::demosaic(uint16_t *data, uint16_t *R, uint16_t *G, uint16_t *B, + int width, int height) +{ + firstPixelInsert(data, B, width, height); + twoPixelInsert(data, G, width, height); + lastPixelInsert(data, R, width, height); +} + +void ISPCPU::autoWhiteBalance(uint16_t *R, uint16_t *G, uint16_t *B, int width, int height) +{ + float aveB = 0, aveG = 0, aveR = 0; + float Kb, Kg, Kr; + + for (int i = 0; i < width * height; i++) { + aveB += 1.0 * B[i]; + aveG += 1.0 * G[i]; + aveR += 1.0 * R[i]; + } + + aveB *= (1.0 / (width * height)); + aveG *= (1.0 / (width * height)); + aveR *= (1.0 / (width * height)); + + Kr = (aveB + aveG + aveR) / aveR * (1.0 / 3.0); + Kg = (aveB + aveG + aveR) / aveG * (1.0 / 3.0); + Kb = (aveB + aveG + aveR) / aveB * (1.0 / 3.0); + + for (int i = 0; i < width * height; i++) { + B[i] = B[i] * Kb; + G[i] = G[i] * Kg; + R[i] = R[i] * Kr; + + if (R[i] > 1023) R[i] = 1023; + if (G[i] > 1023) G[i] = 1023; + if (R[i] > 1023) B[i] = 1023; + } +} + +void ISPCPU::gammaCorrect(uint16_t *R, uint16_t *G, uint16_t *B, float val, int width, int height) +{ + float nor = 1.0 / 1023.0; + float gamma = 1.0 / val; + for (int i = 0; i < width * height; i++) { + R[i] = pow(R[i] * nor, gamma) * 1023; + G[i] = pow(G[i] * nor, gamma) * 1023; + B[i] = pow(B[i] * nor, gamma) * 1023; + + if (R[i] > 1023) R[i] = 1023; + if (G[i] > 1023) G[i] = 1023; + if (B[i] > 1023) B[i] = 1023; + } +} + +void ISPCPU::compressAndTransformFormat(uint16_t *src, uint8_t *dst, int width, int height) +{ + switch(outputpixelformat) + { + case RGB888: { + int j = 0; + for (int i = 0; i < width * height; i++, j += 3) { + dst[i] = src[j] >> 2 & 0xff; + } + + j = 1; + for (int i = 0; i < width * height; i++, j += 3) { + dst[i + width * height] = src[j] >> 2 & 0xff; + } + + j = 2; + for (int i = 0; i < width * height; i++, j += 3) { + dst[i + width * height *2] = src[j] >> 2 & 0xff; + } + break; + } + + case BGR888: { + int j = 2; + for (int i = 0; i < width * height; i++, j += 3) { + dst[i] = src[j] >> 2 & 0xff; + } + + j = 1; + for (int i = 0; i < width * height; i++, j += 3) { + dst[i + width * height] = src[j] >> 2 & 0xff; + } + + j = 0; + for (int i = 0; i < width * height; i++, j += 3) { + dst[i + width * height *2] = src[j] >> 2 & 0xff; + } + break; + } + } +} + +float ISPCPU::distance(int x, int y, int i, int j) +{ + return float(sqrt(pow(x - i, 2) + pow(y - j, 2))); +} + +double ISPCPU::gaussian(float x, double sigma) +{ + return exp(-(pow(x, 2)) / (2 * pow(sigma, 2))) / (2 * 3.1415926 * pow(sigma, 2)); +} + +void ISPCPU::bilateralFilter(uint16_t *R, uint16_t *G, uint16_t *B, + int diameter, double sigmaI, + double sigmaS, int width, int height) +{ + for (int i = 2; i < height - 2; i++) { + for (int j = 2; j < width - 2; j++) { + double iFiltered = 0; + double wp = 0; + int neighbor_x = 0; + int neighbor_y = 0; + int half = diameter / 2; + + for (int k = 0; k < diameter; k++) { + for (int l = 0; l < diameter; l++) { + neighbor_x = i - (half - k); + neighbor_y = j - (half - l); + double gi = gaussian(R[neighbor_x * width + neighbor_y] - R[i * width +j], sigmaI); + double gs = gaussian(distance(i, j, neighbor_x, neighbor_y), sigmaS); + double w = gi * gs; + iFiltered = iFiltered + R[neighbor_x * width + neighbor_y] * w; + wp = wp + w; + } + } + + iFiltered = iFiltered / wp; + R[i * width + j] = iFiltered; + } + } + + for (int i = 2; i < height - 2; i++) { + for (int j = 2; j < width - 2; j++) { + double iFiltered = 0; + double wp = 0; + int neighbor_x = 0; + int neighbor_y = 0; + int half = diameter / 2; + + for (int k = 0; k < diameter; k++) { + for (int l = 0; l < diameter; l++) { + neighbor_x = i - (half - k); + neighbor_y = j - (half - l); + double gi = gaussian(G[neighbor_x * width + neighbor_y] - G[i * width +j], sigmaI); + double gs = gaussian(distance(i, j, neighbor_x, neighbor_y), sigmaS); + double w = gi * gs; + iFiltered = iFiltered + G[neighbor_x * width + neighbor_y] * w; + wp = wp + w; + } + } + + iFiltered = iFiltered / wp; + G[i * width + j] = iFiltered; + } + } + + for (int i = 2; i < height - 2; i++) { + for (int j = 2; j < width - 2; j++) { + double iFiltered = 0; + double wp = 0; + int neighbor_x = 0; + int neighbor_y = 0; + int half = diameter / 2; + + for (int k = 0; k < diameter; k++) { + for (int l = 0; l < diameter; l++) { + neighbor_x = i - (half - k); + neighbor_y = j - (half - l); + double gi = gaussian(B[neighbor_x * width + neighbor_y] - B[i * width +j], sigmaI); + double gs = gaussian(distance(i, j, neighbor_x, neighbor_y), sigmaS); + double w = gi * gs; + iFiltered = iFiltered + B[neighbor_x * width + neighbor_y] * w; + wp = wp + w; + } + } + + iFiltered = iFiltered / wp; + B[i * width + j] = iFiltered; + } + } +} + +void ISPCPU::noiseReduction(uint16_t *R, uint16_t *G, uint16_t *B, int width, int height) +{ + bilateralFilter(R, G, B, 5, 24.0, 32.0, width, height); +} + +void ISPCPU::processing(FrameBuffer *srcBuffer, FrameBuffer *dstBuffer, int width, int height) +{ + uint8_t *rgb_buf; + uint16_t *rawData; + uint16_t *rgbData = new std::uint16_t[width * height * 3]; + + uint16_t *rData = rgbData; + uint16_t *gData = rData + width * height; + uint16_t *bData = gData + width * height; + memset(rgbData, 0x0, width * height * 3); + + const FrameBuffer::Plane &plane = srcBuffer->planes()[0]; + rawData = (uint16_t *)mmap(NULL, plane.length, PROT_READ|PROT_WRITE, MAP_SHARED, plane.fd.fd(), 0); + if (rawData == MAP_FAILED) { + LOG(ISP, Error) << "Read raw data failed"; + ispCompleted.emit(srcBuffer, dstBuffer); + } + + blackLevelCorrect(rawData, 16, width, height); + readChannels(rawData, rData, gData, bData, width, height); + demosaic(rawData, rData, gData, bData, width, height); + autoWhiteBalance(rData, gData, bData, width, height); + autoContrast(rData, 0.01, 0.01, width, height); + autoContrast(gData, 0.01, 0.01, width, height); + autoContrast(bData, 0.01, 0.01, width, height); + gammaCorrect(rData, gData, bData, 2.2, width, height); + //bilateralFilter(rData, gData, bData, 5, 24.0, 32.0, width, height); + + const FrameBuffer::Plane &rgbPlane = dstBuffer->planes()[0]; + rgb_buf = (uint8_t *)mmap(NULL, rgbPlane.length, PROT_READ|PROT_WRITE, MAP_SHARED, rgbPlane.fd.fd(), 0); + if (rgb_buf == MAP_FAILED) { + LOG(ISP, Error) << "Read rgb data failed"; + ispCompleted.emit(srcBuffer, dstBuffer); + } + + compressAndTransformFormat(rgbData, rgb_buf, width, height); + + dstBuffer->metadata_.status = srcBuffer->metadata().status; + dstBuffer->metadata_.sequence = srcBuffer->metadata().sequence; + dstBuffer->metadata_.timestamp = srcBuffer->metadata().timestamp; + + dstBuffer->metadata_.planes.clear(); + dstBuffer->metadata_.planes.push_back({rgbPlane.length}); + + delete[] rgbData; + + ispCompleted.emit(srcBuffer, dstBuffer); +} + +ISPCPU::outputPixelFormat ISPCPU::getOutputPixelFormat(PixelFormat format) +{ + static const std::map transform { + {formats::RGB888, RGB888}, + {formats::BGR888, BGR888}, + }; + + auto itr = transform.find(format); + return itr->second; +} + +std::map> ISPCPU::pixelFormatConfiguration() +{ + SizeRange sizeRange({640, 480}); + std::vector sizeRanges({std::move(sizeRange)}); + ispFormat.insert({formats::RGB888, sizeRanges}); + ispFormat.insert({formats::BGR888, sizeRanges}); + + return ispFormat; +} + +void ISPCPU::paramConfiguration() +{ + struct BLC_PARAM blc = {16}; + + struct LSC_PARAM lsc_grid = { + {{1.4305, 1.4355, 1.4390, 1.4440, 1.4530, 1.4640, 1.4740, 1.4800, 1.4810, 1.4800, 1.4710, 1.4615, 1.4525, 1.4480, 1.4410, 1.4405}, + {1.4315, 1.4370, 1.4425, 1.4520, 1.4635, 1.4760, 1.4855, 1.4955, 1.4955, 1.4920, 1.4830, 1.4695, 1.4590, 1.4510, 1.4445, 1.4405}, + {1.4335, 1.4410, 1.4500, 1.4625, 1.4755, 1.4920, 1.5055, 1.5155, 1.5170, 1.5165, 1.4975, 1.4830, 1.4680, 1.4540, 1.4475, 1.4425}, + {1.4325, 1.4430, 1.4550, 1.4705, 1.4920, 1.5070, 1.5250, 1.5370, 1.5380, 1.5325, 1.5165, 1.4975, 1.4750, 1.4575, 1.4490, 1.4455}, + {1.4325, 1.4425, 1.4575, 1.4805, 1.5050, 1.5250, 1.5380, 1.5490, 1.5495, 1.5410, 1.5320, 1.5070, 1.4825, 1.4600, 1.4485, 1.4450}, + {1.4315, 1.4425, 1.4575, 1.4805, 1.5055, 1.5270, 1.5470, 1.5550, 1.5550, 1.5465, 1.5325, 1.5080, 1.4825, 1.4600, 1.4455, 1.4430}, + {1.4300, 1.4400, 1.4555, 1.4785, 1.5050, 1.5260, 1.5435, 1.5485, 1.5495, 1.5380, 1.5270, 1.5075, 1.4795, 1.4580, 1.4430, 1.4390}, + {1.4275, 1.4345, 1.4480, 1.4690, 1.4965, 1.5135, 1.5275, 1.5370, 1.5365, 1.5270, 1.5105, 1.4965, 1.4725, 1.4525, 1.4390, 1.4335}, + {1.4215, 1.4285, 1.4395, 1.4580, 1.4795, 1.4980, 1.5135, 1.5205, 1.5205, 1.5090, 1.4965, 1.4780, 1.4600, 1.4435, 1.4330, 1.4290}, + {1.4165, 1.4230, 1.4300, 1.4410, 1.4590, 1.4795, 1.4955, 1.5005, 1.5005, 1.4885, 1.4780, 1.4600, 1.4500, 1.4360, 1.4310, 1.4250}, + {1.4125, 1.4160, 1.4230, 1.4290, 1.4410, 1.4575, 1.4705, 1.4760, 1.4760, 1.4690, 1.4545, 1.4495, 1.4355, 1.4300, 1.4250, 1.4250}, + {1.4100, 1.4135, 1.4175, 1.4230, 1.4290, 1.4410, 1.4545, 1.4560, 1.4560, 1.4525, 1.4485, 1.4365, 1.4305, 1.4235, 1.4230, 1.4250}}, + + {{1.2955, 1.2935, 1.2805, 1.2660, 1.2490, 1.234, 1.2320, 1.2320, 1.2325, 1.2365, 1.2425, 1.2550, 1.2690, 1.2810, 1.2875, 1.2905}, + {1.2935, 1.2840, 1.2690, 1.2515, 1.2320, 1.2160, 1.2060, 1.2060, 1.2090, 1.2130, 1.2255, 1.2390, 1.2565, 1.2700, 1.2805, 1.2835}, + {1.2860, 1.2710, 1.2525, 1.2320, 1.2160, 1.2030, 1.1890, 1.1860, 1.1865, 1.1955, 1.2055, 1.2240, 1.2370, 1.2550, 1.2715, 1.2780}, + {1.2815, 1.2590, 1.2390, 1.2200, 1.2030, 1.1890, 1.1785, 1.1740, 1.1740, 1.1830, 1.1950, 1.2055, 1.2235, 1.2425, 1.2625, 1.2770}, + {1.2805, 1.2560, 1.2330, 1.2125, 1.1960, 1.1795, 1.1735, 1.1660, 1.1660, 1.1730, 1.1830, 1.1960, 1.2145, 1.2360, 1.2575, 1.2730}, + {1.2795, 1.2510, 1.2280, 1.2080, 1.1910, 1.1770, 1.1670, 1.1640, 1.1635, 1.1655, 1.1750, 1.1895, 1.2080, 1.2315, 1.2550, 1.2720}, + {1.2795, 1.2510, 1.2265, 1.2070, 1.1910, 1.1770, 1.1680, 1.1640, 1.1630, 1.1645, 1.1740, 1.1870, 1.2060, 1.2315, 1.2550, 1.2715}, + {1.2805, 1.2520, 1.2265, 1.2105, 1.1950, 1.1865, 1.1765, 1.1680, 1.1665, 1.1725, 1.1795, 1.1905, 1.2075, 1.2320, 1.2565, 1.2720}, + {1.2815, 1.2585, 1.2350, 1.2195, 1.2090, 1.1975, 1.1880, 1.1820, 1.1805, 1.1810, 1.1905, 1.2025, 1.2185, 1.2385, 1.2625, 1.2750}, + {1.2825, 1.2675, 1.2495, 1.2325, 1.2220, 1.2135, 1.2060, 1.2020, 1.2000, 1.1995, 1.2050, 1.2170, 1.2315, 1.2495, 1.2725, 1.2785}, + {1.2825, 1.2740, 1.2640, 1.2460, 1.2360, 1.2290, 1.2235, 1.2215, 1.2200, 1.2185, 1.2195, 1.2285, 1.2415, 1.2565, 1.2750, 1.2850}, + {1.2825, 1.2765, 1.2700, 1.2605, 1.2450, 1.2380, 1.2350, 1.2350, 1.2350, 1.2310, 1.2315, 1.2390, 1.2500, 1.2575, 1.2740, 1.2875}}, + }; + +} + +int ISPCPU::exportBuffers(std::vector> *buffers, + unsigned int count, int width, int height) +{ + int bufferByte = width * height * 3; + + for (unsigned int i = 0; i < count; i++) { + std::string name = "frame-" + std::to_string(i); + + const int isp_fd = memfd_create(name.c_str(), 0); + int ret = ftruncate(isp_fd, bufferByte); + if (ret < 0) { + LOG(ISP, Error) << "Failed to resize memfd" << strerror(-ret); + return ret; + } + + FrameBuffer::Plane rgbPlane; + rgbPlane.fd = FileDescriptor(std::move(isp_fd)); + rgbPlane.length = bufferByte; + + std::vector planes{rgbPlane}; + buffers->emplace_back(std::make_unique(std::move(planes))); + } + + return count; +} + +void ISPCPU::startThreadISP() +{ + moveToThread(&thread_); + thread_.start(); +} + +void ISPCPU::stopThreadISP() +{ + thread_.exit(); + thread_.wait(); +} + +} /* namespace libcamera */ diff --git a/src/libcamera/swisp/isp.h b/src/libcamera/swisp/isp.h new file mode 100644 index 00000000..535f1b61 --- /dev/null +++ b/src/libcamera/swisp/isp.h @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021, Siyuan Fan + * + * isp.h - The software ISP class + */ +#ifndef __LIBCAMERA_SWISP_ISP_H__ +#define __LIBCAMERA_SWISP_ISP_H__ + +#include + +#include +#include +#include +#include + +#include "libcamera/base/object.h" +#include "libcamera/base/signal.h" +#include "libcamera/base/thread.h" + +namespace libcamera{ + +using std::uint16_t; +using std::uint8_t; + +class ISP : public Object +{ +public: + ISP() {} + + virtual ~ISP() {} + + enum outputPixelFormat { + RGB888, + BGR888, + }; + + virtual outputPixelFormat getOutputPixelFormat(PixelFormat format) = 0; + + virtual void processing(FrameBuffer *srcBuffer, FrameBuffer *dstBuffer, int width, int height) = 0; + + virtual std::map> pixelFormatConfiguration() = 0; + + virtual void paramConfiguration() = 0; + + virtual int exportBuffers(std::vector> *buffers, + unsigned int count, int width, int height) = 0; + + virtual void startThreadISP() = 0; + + virtual void stopThreadISP() = 0; + + Signal ispCompleted; + + std::map> ispFormat; +}; + +class ISPCPU : public ISP +{ +public: + struct BLC_PARAM { + uint16_t black_level; + }; + + struct LSC_PARAM { + float bGain[12][16]; + float rGain[12][16]; + }; + + outputPixelFormat getOutputPixelFormat(PixelFormat format) override; + + void processing(FrameBuffer *srcBuffer, FrameBuffer *dstBuffer, int width, int height) override; + + std::map> pixelFormatConfiguration() override; + + void paramConfiguration() override; + + int exportBuffers(std::vector> *buffers, + unsigned int count, int width, int height) override; + + void startThreadISP() override; + + void stopThreadISP() override; + + enum outputPixelFormat outputpixelformat; + +private: + void autoContrast(uint16_t *data, float lowCut, float highCut, int width, int height); + + void blackLevelCorrect(uint16_t *data, uint16_t offset, int width, int height); + + void readChannels(uint16_t *data, uint16_t *R, uint16_t *G, uint16_t *B, + int width, int height); + + void firstPixelInsert(uint16_t *src, uint16_t *dst, int width, int height); + + void twoPixelInsert(uint16_t *src, uint16_t *dst, int width, int height); + + void lastPixelInsert(uint16_t *src, uint16_t *dst, int width, int height); + + void demosaic(uint16_t *data, uint16_t *R, uint16_t *G, uint16_t *B, + int width, int height); + + void autoWhiteBalance(uint16_t *R, uint16_t *G, uint16_t *B, int width, int height); + + void gammaCorrect(uint16_t *R, uint16_t *G, uint16_t *B, float val, int width, int height); + + float distance(int x, int y, int i, int j); + + double gaussian(float x, double sigma); + + void bilateralFilter(uint16_t *R, uint16_t *G, uint16_t *B, + int diameter, double sigmaI, double sigmaS, + int width, int height); + + void noiseReduction(uint16_t *R, uint16_t *G, uint16_t *B, int width, int height); + + void compressAndTransformFormat(uint16_t *src, uint8_t *dst, int width, int height); + + Thread thread_; +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_SWISP_ISP_H__ */