blob: 0dcb1b9dea95e43037563aef0d1fab4419a5ea57 [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.
*/
#include <riscv_vector.h>
#include "risp4ml/common/utils.h"
#include "risp4ml/isp_stages/wbg.h"
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
static const uint16_t kWbgFractional = kRawPipelineFraction;
static const uint16_t kWbgUnityGain = 1 << kWbgFractional;
static WbgParams wbg_params = {
.enable = true,
.fixed = false,
.gains = {kWbgUnityGain, kWbgUnityGain, kWbgUnityGain, kWbgUnityGain}};
void set_wbg_params(WbgParams* params) { wbg_params = *params; }
static void compute_wbg_gain(Image* img) {
// Calculate the white-balance gain values using the "gray world" algorithm
uint32_t size = img->num_channels * img->height * img->width;
uint64_t sum_of_reds = 0;
// will use only one of the greens for scaling, since the difference between
// the two green sensor pixels is negligible
uint64_t sum_of_greens = 0;
uint64_t sum_of_blues = 0;
size_t vl;
// auxiliary variables
vuint16m8_t vx;
vuint32m1_t vy;
for (uint16_t y = 0; y < img->height; ++y) {
pixel_type_t* line = image_row(img, 0, y);
for (uint8_t n = 0; n < 2; n++) {
for (uint16_t x = n; x < img->width; x += 2 * vl) {
size_t avl = (img->width + 1 - x) / 2;
vl = __riscv_vsetvl_e16m8(avl);
vx =
__riscv_vlse16_v_u16m8(line + x, 2 * sizeof(uint16_t), vl); // load
vy = __riscv_vmv_v_x_u32m1(0, vl); // init
vy = __riscv_vwredsumu(vx, vy, vl); // sum
uint32_t sum = __riscv_vmv_x(vy);
if ((y & 0x1) == 0 && n == 0) {
sum_of_reds += sum;
} else if ((y & 0x1) == 1 && n == 1) {
sum_of_blues += sum;
} else {
sum_of_greens += sum;
}
}
}
}
// scale values to green channel
float average_red = 4.0 * sum_of_reds / size;
float average_green = 2.0 * sum_of_greens / size;
float average_blue = 4.0 * sum_of_blues / size;
float max_average = MAX(MAX(average_red, average_green), average_blue);
// Convert the float value to fixed point representation, i.e. 0xFF.FF
uint32_t red_wb = FloatToFixedPoint(max_average / average_red,
kRawPipelineInteger, kRawPipelineFraction,
/*bool is_signed*/ false);
uint32_t green_wb = FloatToFixedPoint(
max_average / average_green, kRawPipelineInteger, kRawPipelineFraction,
/*bool is_signed*/ false);
uint32_t blue_wb = FloatToFixedPoint(
max_average / average_blue, kRawPipelineInteger, kRawPipelineFraction,
/*bool is_signed*/ false);
wbg_params.gains[0] = red_wb;
wbg_params.gains[1] = green_wb;
wbg_params.gains[2] = green_wb;
wbg_params.gains[3] = blue_wb;
}
void wbg_process(Image* img) {
if (!wbg_params.enable) return;
if (!wbg_params.fixed) {
compute_wbg_gain(img);
}
size_t vl;
uint32_t offset = 1 << (kWbgFractional - 1);
uint32_t max_val = kRawPipelineMaxVal << kWbgFractional;
uint16_t gain;
// auxiliary variables
vuint16m4_t vx;
vuint32m8_t vy;
for (uint16_t y = 0; y < img->height; ++y) {
pixel_type_t* line = image_row(img, 0, y);
for (uint8_t n = 0; n < 2; n++) {
gain = (y & 0x1) ? wbg_params.gains[2 + n] : wbg_params.gains[n];
for (uint16_t x = n; x < img->width; x += 2 * vl) {
size_t avl = (img->width + 1 - x) / 2;
vl = __riscv_vsetvl_e16m4(avl);
vx =
__riscv_vlse16_v_u16m4(line + x, 2 * sizeof(uint16_t), vl); // load
vy = __riscv_vwmulu(vx, gain, vl); // multiply
vy = __riscv_vadd(vy, offset, vl); // add
vy = __riscv_vminu(vy, max_val, vl); // clamp
vx = __riscv_vnsrl(vy, kWbgFractional, vl); // bit shift
__riscv_vsse16(line + x, 2 * sizeof(uint16_t), vx, vl); // save
}
}
}
}