| /* |
| * 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(¶ms); |
| params.enable = false; |
| |
| set_gamma_params(¶ms); |
| |
| 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(¶ms); |
| |
| set_gamma_params(¶ms); |
| |
| 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(¶ms); |
| set_gamma_params(¶ms); |
| |
| 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); |
| } |
| } |
| } |
| } |