| /* |
| * 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 "risp4ml/common/utils.h" |
| |
| static const uint16_t kRgbPipelineBpp = 16; |
| static const uint32_t kRgbPipelineMaxVal = (1 << kRgbPipelineBpp) - 1; |
| |
| // Fixed HW Parameters |
| static const uint8_t kGammaNumberSegments = 4; |
| static const uint8_t kGammaLogSegmentOffsets[] = {0, 3, 2, 1}; |
| static const uint8_t kGammaLogSegmentSpacing[] = {8, 9, 10, 11}; |
| static const uint16_t kGammaSegmentLutOffset[] = {0, 32, 48, 64}; |
| |
| static GammaParams gamma_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}}; |
| |
| void set_gamma_params(GammaParams* params) { gamma_params = *params; } |
| |
| void gamma_process(Image* img) { |
| if (!gamma_params.enable) return; |
| |
| pixel_type_t* line[RGB_COLOR_CHANNELS]; |
| |
| for (uint16_t y = 0; y < img->height; ++y) { |
| for (uint16_t c = 0; c < RGB_COLOR_CHANNELS; ++c) { |
| line[c] = image_row(img, c, y); |
| } |
| |
| for (uint16_t x = 0; x < img->width; ++x) { |
| for (uint16_t c = 0; c < RGB_COLOR_CHANNELS; ++c) { |
| pixel_type_t pixel_val = |
| (pixel_type_t)Clamp(line[c][x], 0, kRgbPipelineMaxVal); |
| |
| // Determine segment |
| int segment_index = |
| (kGammaNumberSegments - 1) - |
| ClzMsb(pixel_val, kRgbPipelineBpp, kGammaNumberSegments - 1); |
| uint16_t segment_left = |
| segment_index ? 1 << (kRgbPipelineBpp - |
| kGammaLogSegmentOffsets[segment_index]) |
| : 0; |
| |
| // Bin index |
| int bin_index = ((pixel_val - segment_left) >> |
| kGammaLogSegmentSpacing[segment_index]) + |
| kGammaSegmentLutOffset[segment_index]; |
| |
| int offset_within_bin = |
| (pixel_val - segment_left) & |
| ((1 << kGammaLogSegmentSpacing[segment_index]) - 1); |
| |
| uint16_t l_val = gamma_params.lut[bin_index]; |
| uint16_t r_val = gamma_params.lut[bin_index + 1]; |
| |
| uint16_t bin_size = 1 << kGammaLogSegmentSpacing[segment_index]; |
| |
| uint32_t lerp_val = (l_val * (bin_size - offset_within_bin) + |
| r_val * offset_within_bin + (bin_size >> 1)) >> |
| kGammaLogSegmentSpacing[segment_index]; |
| |
| // Clamping is not requied |
| // TODO(alexkaplan): The comment above is from gChips source. |
| // this calc needs to be checked carefully: |
| // |
| line[c][x] = (pixel_type_t)lerp_val; |
| } |
| } |
| } |
| } |