blob: 078b660eb64f4666e8bf9f7567dac305b7640986 [file] [log] [blame]
/*
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Use separate test for RVV as gamma LUT is generated differently
#include <cmath>
#include "pw_unit_test/framework.h"
#include "risp4ml/isp_stages/gamma.h"
static constexpr uint16_t kRgbPipelineBpp = 16;
static constexpr uint16_t kPostGammaPipelineBpp = 16;
static constexpr uint16_t kRgbPipelineMaxVal = (1 << kRgbPipelineBpp) - 1;
static constexpr uint16_t kGammaShiftBits = 5;
static constexpr uint16_t kGammaSpacing = (1 << kGammaShiftBits);
class GammaRvvTest : public ::testing::Test {
protected:
void setup(uint16_t width) {
in_ = image_new(3, 2, width);
for (uint16_t c = 0; c < in_->num_channels; ++c) {
for (uint16_t y = 0; y < in_->height; ++y) {
for (uint16_t x = 0; x < in_->width; ++x) {
*image_pixel(in_, c, y, x) = x;
}
}
}
out_ = image_new(3, 2, width);
const uint32_t num_bytes =
in_->num_channels * in_->height * in_->width * sizeof(pixel_type_t);
memcpy(out_->data, in_->data, num_bytes);
}
void TearDown() override {
image_delete(in_);
image_delete(out_);
}
void CreateLinearGamma(GammaParams* params) {
params->enable = true;
for (int n = 0; n <= kRgbPipelineMaxVal; n += kGammaSpacing) {
params->lut[n / kGammaSpacing] = n;
}
params->lut[GAMMA_NUMBER_POINTS - 1] = kRgbPipelineMaxVal;
}
float sRgb_gamma(float in_) {
return (in_ < 0.0031308f) ? 12.92f * in_
: 1.055f * std::pow(in_, 1.0f / 2.4f) - 0.055f;
}
void CreateRgbGamma(GammaParams* params) {
params->enable = true;
params->lut[0] = 0;
for (int n = 0; n <= kRgbPipelineMaxVal; n += kGammaSpacing) {
params->lut[n / kGammaSpacing] =
(1 << kRgbPipelineBpp) *
sRgb_gamma(static_cast<float>(n) / (1 << kRgbPipelineBpp));
}
params->lut[GAMMA_NUMBER_POINTS - 1] = kRgbPipelineMaxVal;
}
Image* in_;
Image* out_;
};
TEST_F(GammaRvvTest, Bypass) {
setup((1 << 15) - 1);
GammaParams params;
CreateRgbGamma(&params);
params.enable = false;
set_gamma_params(&params);
gamma_process(out_);
for (uint16_t c = 0; c < in_->num_channels; ++c) {
for (uint16_t y = 0; y < in_->height; ++y) {
for (uint16_t x = 0; x < in_->width; ++x) {
pixel_type_t expected_val =
x >> (kRgbPipelineBpp - kPostGammaPipelineBpp);
ASSERT_EQ(expected_val, image_pixel_val(out_, c, y, x));
}
}
}
}
TEST_F(GammaRvvTest, Linear) {
setup((1 << 15) - 2);
GammaParams params;
CreateLinearGamma(&params);
set_gamma_params(&params);
gamma_process(out_);
for (uint16_t c = 0; c < in_->num_channels; ++c) {
for (uint16_t y = 0; y < in_->height; ++y) {
for (uint16_t x = 0; x < in_->width; ++x) {
ASSERT_EQ(image_pixel_val(out_, 0, y, x),
image_pixel_val(in_, 0, y, x));
}
}
}
}
TEST_F(GammaRvvTest, sRgbLUT) {
setup((1 << 15) - 1);
constexpr float kToleranceRatio = 0.03;
GammaParams params;
CreateRgbGamma(&params);
set_gamma_params(&params);
gamma_process(out_);
for (uint16_t c = 0; c < in_->num_channels; ++c) {
for (uint16_t y = 0; y < in_->height; ++y) {
for (uint16_t x = 0; x < in_->width; ++x) {
pixel_type_t expected_val =
(pixel_type_t)((1 << kRgbPipelineBpp) *
sRgb_gamma(static_cast<float>(x) /
(1 << kRgbPipelineBpp)));
float tolerance = ceilf(kToleranceRatio * expected_val);
float diff =
std::abs(static_cast<float>(expected_val) -
static_cast<float>(image_pixel_val(out_, c, y, x)));
ASSERT_LE(diff, tolerance);
}
}
}
}