| /* |
| * 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. |
| */ |
| |
| #include "risp4ml/isp_stages/gamma.h" |
| |
| #include <cmath> |
| |
| #include "pw_unit_test/framework.h" |
| |
| static constexpr uint16_t kRgbPipelineBpp = 16; |
| static constexpr uint16_t kPostGammaPipelineBpp = 16; |
| GammaParams linear_params = { |
| .enable = true, |
| .lut = {0, 256, 512, 768, 1024, 1280, 1536, 1792, 2048, |
| 2304, 2560, 2816, 3072, 3328, 3584, 3840, 4096, 4352, |
| 4608, 4864, 5120, 5376, 5632, 5888, 6144, 6400, 6656, |
| 6912, 7168, 7424, 7680, 7936, 8192, 8704, 9216, 9728, |
| 10240, 10752, 11264, 11776, 12288, 12800, 13312, 13824, 14336, |
| 14848, 15360, 15872, 16384, 17408, 18432, 19456, 20480, 21504, |
| 22528, 23552, 24576, 25600, 26624, 27648, 28672, 29696, 30720, |
| 31744, 32768, 34816, 36864, 38912, 40960, 43008, 45056, 47104, |
| 49152, 51200, 53248, 55296, 57344, 59392, 61440, 63488, 65535}}; |
| |
| GammaParams rgb_params = { |
| .enable = true, |
| .lut = {0, 3255, 5552, 7237, 8618, 9809, 10868, 11828, 12710, |
| 13531, 14300, 15026, 15713, 16368, 16995, 17596, 18173, 18731, |
| 19269, 19790, 20295, 20786, 21264, 21728, 22182, 22624, 23056, |
| 23479, 23892, 24297, 24694, 25083, 25466, 26209, 26928, 27623, |
| 28298, 28953, 29590, 30211, 30816, 31406, 31983, 32547, 33099, |
| 33640, 34170, 34689, 35199, 36192, 37151, 38080, 38980, 39855, |
| 40705, 41534, 42341, 43129, 43899, 44652, 45389, 46111, 46818, |
| 47512, 48192, 49517, 50798, 52037, 53239, 54407, 55542, 56648, |
| 57726, 58778, 59806, 60811, 61794, 62757, 63702, 64627, 65535}}; |
| |
| class GammaTest : 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_); |
| } |
| float sRgb_gamma(float in_) { |
| return (in_ < 0.0031308f) ? 12.92f * in_ |
| : 1.055f * std::pow(in_, 1.0f / 2.4f) - 0.055f; |
| } |
| |
| Image* in_; |
| Image* out_; |
| }; |
| |
| TEST_F(GammaTest, Bypass) { |
| setup((1 << 15) - 1); |
| |
| GammaParams params = rgb_params; |
| 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(GammaTest, Linear) { |
| setup((1 << 15) - 2); |
| |
| set_gamma_params(&linear_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(GammaTest, sRgbLUT) { |
| setup((1 << 15) - 1); |
| |
| constexpr float kToleranceRatio = 0.03; |
| |
| set_gamma_params(&rgb_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); |
| } |
| } |
| } |
| } |