/* * aiq_handler.cpp - AIQ handler * * Copyright (c) 2012-2015 Intel Corporation * * 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. * * Author: Wind Yuan * Author: Yan Zhang */ #include "aiq_handler.h" #include "x3a_isp_config.h" #include #include #include "ia_isp_2_2.h" #define MAX_STATISTICS_WIDTH 150 #define MAX_STATISTICS_HEIGHT 150 //#define USE_RGBS_GRID_WEIGHTING #define USE_HIST_GRID_WEIGHTING namespace XCam { struct IspInputParameters { ia_aiq_frame_use frame_use; ia_aiq_frame_params *sensor_frame_params; ia_aiq_exposure_parameters *exposure_results; ia_aiq_awb_results *awb_results; ia_aiq_gbce_results *gbce_results; ia_aiq_pa_results *pa_results; #ifdef HAVE_AIQ_2_7 ia_aiq_sa_results *sa_results; #endif int8_t manual_brightness; int8_t manual_contrast; int8_t manual_hue; int8_t manual_saturation; int8_t manual_sharpness; int8_t manual_nr_level; ia_isp_effect effects; IspInputParameters () : frame_use (ia_aiq_frame_use_preview) , sensor_frame_params (NULL) , exposure_results (NULL) , awb_results (NULL) , gbce_results (NULL) , pa_results (NULL) #ifdef HAVE_AIQ_2_7 , sa_results (NULL) #endif , manual_brightness (0) , manual_contrast (0) , manual_hue (0) , manual_saturation (0) , manual_sharpness (0) , manual_nr_level (0) , effects (ia_isp_effect_none) {} }; class IaIspAdaptor22 : public IaIspAdaptor { public: IaIspAdaptor22 () { xcam_mem_clear (_input_params); } ~IaIspAdaptor22 () { if (_handle) ia_isp_2_2_deinit (_handle); } virtual bool init ( const ia_binary_data *cpf, unsigned int max_width, unsigned int max_height, ia_cmc_t *cmc, ia_mkn *mkn); virtual bool convert_statistics ( void *statistics, ia_aiq_rgbs_grid **out_rgbs_grid, ia_aiq_af_grid **out_af_grid); virtual bool run ( const IspInputParameters *isp_input_params, ia_binary_data *output_data); private: ia_isp_2_2_input_params _input_params; }; bool IaIspAdaptor22::init ( const ia_binary_data *cpf, unsigned int max_width, unsigned int max_height, ia_cmc_t *cmc, ia_mkn *mkn) { xcam_mem_clear (_input_params); _input_params.isp_vamem_type = 1; _handle = ia_isp_2_2_init (cpf, max_width, max_height, cmc, mkn); XCAM_FAIL_RETURN (ERROR, _handle, false, "ia_isp 2.2 init failed"); return true; } bool IaIspAdaptor22::convert_statistics ( void *statistics, ia_aiq_rgbs_grid **out_rgbs_grid, ia_aiq_af_grid **out_af_grid) { ia_err err; err = ia_isp_2_2_statistics_convert (_handle, statistics, out_rgbs_grid, out_af_grid); XCAM_FAIL_RETURN (ERROR, err == ia_err_none, false, "ia_isp 2.2 convert stats failed"); return true; } bool IaIspAdaptor22::run ( const IspInputParameters *isp_input_params, ia_binary_data *output_data) { ia_err err; _input_params.frame_use = isp_input_params->frame_use; _input_params.sensor_frame_params = isp_input_params->sensor_frame_params; _input_params.exposure_results = isp_input_params->exposure_results; _input_params.awb_results = isp_input_params->awb_results; _input_params.gbce_results = isp_input_params->gbce_results; _input_params.pa_results = isp_input_params->pa_results; #ifdef HAVE_AIQ_2_7 _input_params.sa_results = isp_input_params->sa_results; #endif _input_params.manual_brightness = isp_input_params->manual_brightness; _input_params.manual_contrast = isp_input_params->manual_contrast; _input_params.manual_hue = isp_input_params->manual_hue; _input_params.manual_saturation = isp_input_params->manual_saturation; _input_params.nr_setting.feature_level = ia_isp_feature_level_high; _input_params.nr_setting.strength = isp_input_params->manual_nr_level; _input_params.ee_setting.feature_level = ia_isp_feature_level_high; _input_params.ee_setting.strength = isp_input_params->manual_sharpness; _input_params.effects = isp_input_params->effects; err = ia_isp_2_2_run (_handle, &_input_params, output_data); XCAM_FAIL_RETURN (ERROR, err == ia_err_none, false, "ia_isp 2.2 run failed"); return true; } #if 0 class IaIspAdaptor15 : public IaIspAdaptor { public: IaIspAdaptor15 () { xcam_mem_clear (&_input_params); } ~IaIspAdaptor15 () { if (_handle) ia_isp_1_5_deinit (_handle); } virtual bool init ( const ia_binary_data *cpf, unsigned int max_width, unsigned int max_height, ia_cmc_t *cmc, ia_mkn *mkn); virtual bool convert_statistics ( void *statistics, ia_aiq_rgbs_grid **out_rgbs_grid, ia_aiq_af_grid **out_af_grid); virtual bool run ( const IspInputParameters *isp_input_params, ia_binary_data *output_data); private: ia_isp_1_5_input_params _input_params; }; bool IaIspAdaptor15::init ( const ia_binary_data *cpf, unsigned int max_width, unsigned int max_height, ia_cmc_t *cmc, ia_mkn *mkn) { xcam_mem_clear (&_input_params); _input_params.isp_vamem_type = 1; _handle = ia_isp_1_5_init (cpf, max_width, max_height, cmc, mkn); XCAM_FAIL_RETURN (ERROR, _handle, false, "ia_isp 1.5 init failed"); return true; } bool IaIspAdaptor15::convert_statistics ( void *statistics, ia_aiq_rgbs_grid **out_rgbs_grid, ia_aiq_af_grid **out_af_grid) { ia_err err; err = ia_isp_1_5_statistics_convert (_handle, statistics, out_rgbs_grid, out_af_grid); XCAM_FAIL_RETURN (ERROR, err == ia_err_none, false, "ia_isp 1.5 convert stats failed"); return true; } bool IaIspAdaptor15::run ( const IspInputParameters *isp_input_params, ia_binary_data *output_data) { ia_err err; _input_params.frame_use = isp_input_params->frame_use; _input_params.sensor_frame_params = isp_input_params->sensor_frame_params; _input_params.exposure_results = isp_input_params->exposure_results; _input_params.awb_results = isp_input_params->awb_results; _input_params.gbce_results = isp_input_params->gbce_results; _input_params.pa_results = isp_input_params->pa_results; _input_params.manual_brightness = isp_input_params->manual_brightness; _input_params.manual_contrast = isp_input_params->manual_contrast; _input_params.manual_hue = isp_input_params->manual_hue; _input_params.manual_saturation = isp_input_params->manual_saturation; _input_params.nr_setting.feature_level = ia_isp_feature_level_high; _input_params.nr_setting.strength = isp_input_params->manual_nr_level; _input_params.ee_setting.feature_level = ia_isp_feature_level_high; _input_params.ee_setting.strength = isp_input_params->manual_sharpness; _input_params.effects = isp_input_params->effects; err = ia_isp_1_5_run (_handle, &_input_params, output_data); XCAM_FAIL_RETURN (ERROR, err == ia_err_none, false, "ia_isp 1.5 run failed"); return true; } #endif static double _calculate_new_value_by_speed (double start, double end, double speed) { XCAM_ASSERT (speed >= 0.0 && speed <= 1.0); static const double value_equal_range = 0.000001; if (fabs (end - start) <= value_equal_range) return end; return (start * (1.0 - speed) + end * speed); } static double _imx185_sensor_gain_code_to_mutiplier (uint32_t code) { /* 185 sensor code : DB = 160 : 48 */ double db; db = code * 48.0 / 160.0; return pow (10.0, db / 20.0); } static uint32_t _mutiplier_to_imx185_sensor_gain_code (double mutiplier) { double db = log10 (mutiplier) * 20; if (db > 48) db = 48; return (uint32_t) (db * 160 / 48); } static uint32_t _time_to_coarse_line (const ia_aiq_exposure_sensor_descriptor *desc, uint32_t time_us) { float value = time_us * desc->pixel_clock_freq_mhz; value = (value + desc->pixel_periods_per_line / 2) / desc->pixel_periods_per_line; return (uint32_t)(value); } static uint32_t _coarse_line_to_time (const ia_aiq_exposure_sensor_descriptor *desc, uint32_t coarse_line) { return coarse_line * desc->pixel_periods_per_line / desc->pixel_clock_freq_mhz; } AiqAeHandler::AiqAeResult::AiqAeResult() { xcam_mem_clear (ae_result); xcam_mem_clear (ae_exp_ret); xcam_mem_clear (aiq_exp_param); xcam_mem_clear (sensor_exp_param); xcam_mem_clear (weight_grid); xcam_mem_clear (flash_param); } void AiqAeHandler::AiqAeResult::copy (ia_aiq_ae_results *result) { XCAM_ASSERT (result); this->ae_result = *result; this->aiq_exp_param = *result->exposures[0].exposure; this->sensor_exp_param = *result->exposures[0].sensor_exposure; this->weight_grid = *result->weight_grid; #ifdef HAVE_AIQ_2_7 this->flash_param = result->flashes[0]; #else this->flash_param = *result->flash; #endif this->ae_exp_ret.exposure = &this->aiq_exp_param; this->ae_exp_ret.sensor_exposure = &this->sensor_exp_param; this->ae_result.exposures = &this->ae_exp_ret; this->ae_result.weight_grid = &this->weight_grid; #ifdef HAVE_AIQ_2_7 this->ae_result.flashes[0] = this->flash_param; #else this->ae_result.flash = &this->flash_param; #endif this->ae_result.num_exposures = 1; } AiqAeHandler::AiqAeHandler (SmartPtr &aiq_compositor) : _aiq_compositor (aiq_compositor) , _started (false) { xcam_mem_clear (_ia_ae_window); xcam_mem_clear (_sensor_descriptor); xcam_mem_clear (_manual_limits); xcam_mem_clear (_input); _input.num_exposures = 1; _input.frame_use = _aiq_compositor->get_frame_use(); _input.flash_mode = ia_aiq_flash_mode_off; _input.operation_mode = ia_aiq_ae_operation_mode_automatic; _input.metering_mode = ia_aiq_ae_metering_mode_evaluative; _input.priority_mode = ia_aiq_ae_priority_mode_normal; _input.flicker_reduction_mode = ia_aiq_ae_flicker_reduction_auto; _input.sensor_descriptor = NULL; _input.exposure_window = NULL; _input.exposure_coordinate = NULL; _input.ev_shift = 0.0; _input.manual_exposure_time_us = -1; _input.manual_analog_gain = -1.0; _input.manual_iso = -1.0; _input.aec_features = NULL; _input.manual_limits = &_manual_limits; } bool AiqAeHandler::set_description (struct atomisp_sensor_mode_data *sensor_data) { XCAM_ASSERT (sensor_data); _sensor_descriptor.pixel_clock_freq_mhz = sensor_data->vt_pix_clk_freq_mhz / 1000000.0f; _sensor_descriptor.pixel_periods_per_line = sensor_data->line_length_pck; _sensor_descriptor.line_periods_per_field = sensor_data->frame_length_lines; _sensor_descriptor.line_periods_vertical_blanking = sensor_data->frame_length_lines - (sensor_data->crop_vertical_end - sensor_data->crop_vertical_start + 1) / sensor_data->binning_factor_y; _sensor_descriptor.fine_integration_time_min = sensor_data->fine_integration_time_def; _sensor_descriptor.fine_integration_time_max_margin = sensor_data->line_length_pck - sensor_data->fine_integration_time_def; _sensor_descriptor.coarse_integration_time_min = sensor_data->coarse_integration_time_min; _sensor_descriptor.coarse_integration_time_max_margin = sensor_data->coarse_integration_time_max_margin; return true; } bool AiqAeHandler::ensure_ia_parameters () { bool ret = true; ret = ret && ensure_ae_mode (); ret = ret && ensure_ae_metering_mode (); ret = ret && ensure_ae_priority_mode (); ret = ret && ensure_ae_flicker_mode (); ret = ret && ensure_ae_manual (); ret = ret && ensure_ae_ev_shift (); _input.sensor_descriptor = &_sensor_descriptor; return ret; } bool AiqAeHandler::ensure_ae_mode () { XCamAeMode mode = this->get_mode_unlock(); switch (mode) { case XCAM_AE_MODE_AUTO: case XCAM_AE_MODE_MANUAL: _input.operation_mode = ia_aiq_ae_operation_mode_automatic; break; case XCAM_AE_MODE_NOT_SET: default: XCAM_LOG_ERROR("unsupported ae mode:%d", mode); return false; } return true; } bool AiqAeHandler::ensure_ae_metering_mode () { XCamAeMeteringMode mode = this->get_metering_mode_unlock(); _input.exposure_window = NULL; switch (mode) { case XCAM_AE_METERING_MODE_AUTO: _input.metering_mode = ia_aiq_ae_metering_mode_evaluative; break; case XCAM_AE_METERING_MODE_SPOT: { _input.metering_mode = ia_aiq_ae_metering_mode_evaluative; const XCam3AWindow & window = this->get_window_unlock(); if (window.x_end > window.x_start && window.y_end > window.y_start) { _aiq_compositor->convert_window_to_ia(window, _ia_ae_window); _input.exposure_window = &_ia_ae_window; } } break; case XCAM_AE_METERING_MODE_CENTER: _input.metering_mode = ia_aiq_ae_metering_mode_center; break; case XCAM_AE_METERING_MODE_WEIGHTED_WINDOW: { _input.metering_mode = ia_aiq_ae_metering_mode_evaluative; const XCam3AWindow & weighted_window = this->get_window_unlock(); XCAM_LOG_DEBUG ("ensure_ae_metering_mode weighted_window x_start = %d, y_start = %d, x_end = %d, y_end = %d ", weighted_window.x_start, weighted_window.y_start, weighted_window.x_end, weighted_window.y_end); if (weighted_window.x_end > weighted_window.x_start && weighted_window.y_end > weighted_window.y_start) { _aiq_compositor->convert_window_to_ia(weighted_window, _ia_ae_window); _input.exposure_window = &_ia_ae_window; } } break; default: XCAM_LOG_ERROR("unsupported ae mode:%d", mode); return false; } return true; } bool AiqAeHandler::ensure_ae_priority_mode () { _input.priority_mode = ia_aiq_ae_priority_mode_normal; return true; } bool AiqAeHandler::ensure_ae_flicker_mode () { XCamFlickerMode mode = this->get_flicker_mode_unlock (); switch (mode) { case XCAM_AE_FLICKER_MODE_AUTO: _input.flicker_reduction_mode = ia_aiq_ae_flicker_reduction_auto; break; case XCAM_AE_FLICKER_MODE_50HZ: _input.flicker_reduction_mode = ia_aiq_ae_flicker_reduction_50hz; break; case XCAM_AE_FLICKER_MODE_60HZ: _input.flicker_reduction_mode = ia_aiq_ae_flicker_reduction_60hz; break; case XCAM_AE_FLICKER_MODE_OFF: _input.flicker_reduction_mode = ia_aiq_ae_flicker_reduction_off; break; default: XCAM_LOG_ERROR ("flicker mode(%d) unknown", mode); return false; } return true; } bool AiqAeHandler::ensure_ae_manual () { if (this->get_mode_unlock () == XCAM_AE_MODE_MANUAL) { _input.manual_exposure_time_us = get_manual_exposure_time_unlock (); _input.manual_analog_gain = get_manual_analog_gain_unlock (); } else { _input.manual_exposure_time_us = -1; _input.manual_analog_gain = -1; } _input.manual_limits->manual_exposure_time_min = _sensor_descriptor.coarse_integration_time_min * _sensor_descriptor.pixel_periods_per_line / _sensor_descriptor.pixel_clock_freq_mhz; _input.manual_limits->manual_exposure_time_max = (_sensor_descriptor.line_periods_per_field - _sensor_descriptor.coarse_integration_time_max_margin) * _sensor_descriptor.pixel_periods_per_line / _sensor_descriptor.pixel_clock_freq_mhz; uint64_t exp_min_us = 0, exp_max_us = 0; get_exposure_time_range_unlock (exp_min_us, exp_max_us); if (exp_min_us && (int64_t)exp_min_us > _input.manual_limits->manual_exposure_time_min) { _input.manual_limits->manual_exposure_time_min = exp_min_us; } if (exp_max_us && (int64_t)exp_max_us < _input.manual_limits->manual_exposure_time_max) { _input.manual_limits->manual_exposure_time_max = exp_max_us; } _input.manual_limits->manual_frame_time_us_min = -1; _input.manual_limits->manual_frame_time_us_max = 1000000 / _aiq_compositor->get_framerate (); _input.manual_limits->manual_iso_min = -1; _input.manual_limits->manual_iso_max = -1; return true; } bool AiqAeHandler::ensure_ae_ev_shift () { _input.ev_shift = this->get_ev_shift_unlock(); return true; } SmartPtr AiqAeHandler::pop_result () { //AnalyzerHandler::HandlerLock lock(this); X3aIspExposureResult *result = new X3aIspExposureResult(XCAM_IMAGE_PROCESS_ONCE); struct atomisp_exposure sensor; XCam3aResultExposure exposure; xcam_mem_clear (sensor); sensor.integration_time[0] = _result.sensor_exp_param.coarse_integration_time; sensor.integration_time[1] = _result.sensor_exp_param.fine_integration_time; sensor.gain[0] = _result.sensor_exp_param.analog_gain_code_global; sensor.gain[1] = _result.sensor_exp_param.digital_gain_global; result->set_isp_config (sensor); xcam_mem_clear (exposure); exposure.exposure_time = _result.aiq_exp_param.exposure_time_us; exposure.analog_gain = _result.aiq_exp_param.analog_gain; exposure.digital_gain = _result.aiq_exp_param.digital_gain; exposure.aperture = _result.aiq_exp_param.aperture_fn; result->set_standard_result (exposure); return result; } XCamReturn AiqAeHandler::analyze (X3aResultList &output) { ia_aiq *ia_handle = NULL; ia_aiq_ae_results *ae_result = NULL; ia_aiq_exposure_sensor_parameters *cur_sensor_result = NULL; ia_err ia_error = ia_err_none; bool need_apply = false; SmartPtr result; AnalyzerHandler::HandlerLock lock(this); if (!ensure_ia_parameters ()) { XCAM_LOG_ERROR ("AIQ AE ensure ia parameters failed"); return XCAM_RETURN_ERROR_PARAM; } ia_handle = _aiq_compositor->get_handle (); XCAM_ASSERT (ia_handle); ia_error = ia_aiq_ae_run (ia_handle, &_input, &ae_result); XCAM_FAIL_RETURN (ERROR, ia_error == ia_err_none, XCAM_RETURN_ERROR_AIQ, "AIQ run AE failed"); cur_sensor_result = ae_result->exposures[0].sensor_exposure; if (!_started) { _result.copy (ae_result); _started = true; need_apply = true; } else { //TODO ia_aiq_exposure_sensor_parameters *last_sensor_res = &_result.sensor_exp_param; if (last_sensor_res->coarse_integration_time != cur_sensor_result->coarse_integration_time || last_sensor_res->fine_integration_time != cur_sensor_result->fine_integration_time || last_sensor_res->analog_gain_code_global != cur_sensor_result->analog_gain_code_global || last_sensor_res->digital_gain_global != cur_sensor_result->digital_gain_global) { ia_aiq_exposure_sensor_parameters cur_cp_res = *cur_sensor_result; ia_aiq_exposure_parameters cur_aiq_exp = *ae_result->exposures[0].exposure; if (!manual_control_result (cur_cp_res, cur_aiq_exp, *last_sensor_res)) { XCAM_LOG_WARNING ("manual control AE result failed"); } _result.copy (ae_result); _result.sensor_exp_param = cur_cp_res; _result.aiq_exp_param = cur_aiq_exp; need_apply = true; } } if (need_apply) { result = pop_result (); if (result.ptr()) output.push_back (result); } return XCAM_RETURN_NO_ERROR; } bool AiqAeHandler::manual_control_result ( ia_aiq_exposure_sensor_parameters &cur_res, ia_aiq_exposure_parameters &cur_aiq_exp, const ia_aiq_exposure_sensor_parameters &last_res) { adjust_ae_speed (cur_res, cur_aiq_exp, last_res, this->get_speed_unlock()); adjust_ae_limitation (cur_res, cur_aiq_exp); return true; } void AiqAeHandler::adjust_ae_speed ( ia_aiq_exposure_sensor_parameters &cur_res, ia_aiq_exposure_parameters &cur_aiq_exp, const ia_aiq_exposure_sensor_parameters &last_res, double ae_speed) { double last_gain, input_gain, ret_gain; ia_aiq_exposure_sensor_parameters tmp_res; if (XCAM_DOUBLE_EQUAL_AROUND(ae_speed, 1.0 )) return; xcam_mem_clear (tmp_res); tmp_res.coarse_integration_time = _calculate_new_value_by_speed ( last_res.coarse_integration_time, cur_res.coarse_integration_time, ae_speed); last_gain = _imx185_sensor_gain_code_to_mutiplier (last_res.analog_gain_code_global); input_gain = _imx185_sensor_gain_code_to_mutiplier (cur_res.analog_gain_code_global); ret_gain = _calculate_new_value_by_speed (last_gain, input_gain, ae_speed); tmp_res.analog_gain_code_global = _mutiplier_to_imx185_sensor_gain_code (ret_gain); XCAM_LOG_DEBUG ("AE speed: from (shutter:%d, gain:%d[%.03f]) to (shutter:%d, gain:%d[%.03f])", cur_res.coarse_integration_time, cur_res.analog_gain_code_global, input_gain, tmp_res.coarse_integration_time, tmp_res.analog_gain_code_global, ret_gain); cur_res.coarse_integration_time = tmp_res.coarse_integration_time; cur_res.analog_gain_code_global = tmp_res.analog_gain_code_global; cur_aiq_exp.exposure_time_us = _coarse_line_to_time (&_sensor_descriptor, cur_res.coarse_integration_time); cur_aiq_exp.analog_gain = ret_gain; } void AiqAeHandler::adjust_ae_limitation (ia_aiq_exposure_sensor_parameters &cur_res, ia_aiq_exposure_parameters &cur_aiq_exp) { ia_aiq_exposure_sensor_descriptor * desc = &_sensor_descriptor; uint64_t exposure_min = 0, exposure_max = 0; double analog_max = get_max_analog_gain_unlock (); uint32_t min_coarse_value = desc->coarse_integration_time_min; uint32_t max_coarse_value = desc->line_periods_per_field - desc->coarse_integration_time_max_margin; uint32_t value; get_exposure_time_range_unlock (exposure_min, exposure_max); if (exposure_min) { value = _time_to_coarse_line (desc, (uint32_t)exposure_min); min_coarse_value = (value > min_coarse_value) ? value : min_coarse_value; } if (cur_res.coarse_integration_time < min_coarse_value) { cur_res.coarse_integration_time = min_coarse_value; cur_aiq_exp.exposure_time_us = _coarse_line_to_time (desc, min_coarse_value); } if (exposure_max) { value = _time_to_coarse_line (desc, (uint32_t)exposure_max); max_coarse_value = (value < max_coarse_value) ? value : max_coarse_value; } if (cur_res.coarse_integration_time > max_coarse_value) { cur_res.coarse_integration_time = max_coarse_value; cur_aiq_exp.exposure_time_us = _coarse_line_to_time (desc, max_coarse_value); } if (analog_max >= 1.0) { /* limit gains */ double gain = _imx185_sensor_gain_code_to_mutiplier (cur_res.analog_gain_code_global); if (gain > analog_max) { cur_res.analog_gain_code_global = _mutiplier_to_imx185_sensor_gain_code (analog_max); cur_aiq_exp.analog_gain = analog_max; } } } XCamFlickerMode AiqAeHandler::get_flicker_mode () { { AnalyzerHandler::HandlerLock lock(this); } return AeHandler::get_flicker_mode (); } int64_t AiqAeHandler::get_current_exposure_time () { AnalyzerHandler::HandlerLock lock(this); return (int64_t)_result.aiq_exp_param.exposure_time_us; } double AiqAeHandler::get_current_analog_gain () { AnalyzerHandler::HandlerLock lock(this); return (double)_result.aiq_exp_param.analog_gain; } double AiqAeHandler::get_max_analog_gain () { { AnalyzerHandler::HandlerLock lock(this); } return AeHandler::get_max_analog_gain (); } XCamReturn AiqAeHandler::set_RGBS_weight_grid (ia_aiq_rgbs_grid **out_rgbs_grid) { AnalyzerHandler::HandlerLock lock(this); rgbs_grid_block *rgbs_grid_ptr = (*out_rgbs_grid)->blocks_ptr; uint32_t rgbs_grid_index = 0; uint16_t rgbs_grid_width = (*out_rgbs_grid)->grid_width; uint16_t rgbs_grid_height = (*out_rgbs_grid)->grid_height; XCAM_LOG_DEBUG ("rgbs_grid_width = %d, rgbs_grid_height = %d", rgbs_grid_width, rgbs_grid_height); uint64_t weight_sum = 0; uint32_t image_width = 0; uint32_t image_height = 0; _aiq_compositor->get_size (image_width, image_height); XCAM_LOG_DEBUG ("image_width = %d, image_height = %d", image_width, image_height); uint32_t hor_pixels_per_grid = (image_width + (rgbs_grid_width >> 1)) / rgbs_grid_width; uint32_t vert_pixels_per_gird = (image_height + (rgbs_grid_height >> 1)) / rgbs_grid_height; XCAM_LOG_DEBUG ("rgbs grid: %d x %d pixels per grid cell", hor_pixels_per_grid, vert_pixels_per_gird); XCam3AWindow weighted_window = this->get_window_unlock (); uint32_t weighted_grid_width = ((weighted_window.x_end - weighted_window.x_start + 1) + (hor_pixels_per_grid >> 1)) / hor_pixels_per_grid; uint32_t weighted_grid_height = ((weighted_window.y_end - weighted_window.y_start + 1) + (vert_pixels_per_gird >> 1)) / vert_pixels_per_gird; XCAM_LOG_DEBUG ("weighted_grid_width = %d, weighted_grid_height = %d", weighted_grid_width, weighted_grid_height); uint32_t *weighted_avg_gr = (uint32_t*)xcam_malloc0 (5 * weighted_grid_width * weighted_grid_height * sizeof(uint32_t)); if (NULL == weighted_avg_gr) { return XCAM_RETURN_ERROR_MEM; } uint32_t *weighted_avg_r = weighted_avg_gr + (weighted_grid_width * weighted_grid_height); uint32_t *weighted_avg_b = weighted_avg_r + (weighted_grid_width * weighted_grid_height); uint32_t *weighted_avg_gb = weighted_avg_b + (weighted_grid_width * weighted_grid_height); uint32_t *weighted_sat = weighted_avg_gb + (weighted_grid_width * weighted_grid_height); for (uint32_t win_index = 0; win_index < XCAM_AE_MAX_METERING_WINDOW_COUNT; win_index++) { XCAM_LOG_DEBUG ("window start point(%d, %d), end point(%d, %d), weight = %d", _params.window_list[win_index].x_start, _params.window_list[win_index].y_start, _params.window_list[win_index].x_end, _params.window_list[win_index].y_end, _params.window_list[win_index].weight); if ((_params.window_list[win_index].weight <= 0) || (_params.window_list[win_index].x_start < 0) || ((uint32_t)_params.window_list[win_index].x_end > image_width) || (_params.window_list[win_index].y_start < 0) || ((uint32_t)_params.window_list[win_index].y_end > image_height) || (_params.window_list[win_index].x_start >= _params.window_list[win_index].x_end) || (_params.window_list[win_index].y_start >= _params.window_list[win_index].y_end) || ((uint32_t)_params.window_list[win_index].x_end - (uint32_t)_params.window_list[win_index].x_start > image_width) || ((uint32_t)_params.window_list[win_index].y_end - (uint32_t)_params.window_list[win_index].y_start > image_height)) { XCAM_LOG_DEBUG ("skip window index = %d ", win_index); continue; } rgbs_grid_index = (_params.window_list[win_index].x_start + (hor_pixels_per_grid >> 1)) / hor_pixels_per_grid + ((_params.window_list[win_index].y_start + (vert_pixels_per_gird >> 1)) / vert_pixels_per_gird) * rgbs_grid_width; weight_sum += _params.window_list[win_index].weight; XCAM_LOG_DEBUG ("cumulate rgbs grid statistic, window index = %d ", win_index); for (uint32_t i = 0; i < weighted_grid_height; i++) { for (uint32_t j = 0; j < weighted_grid_width; j++) { weighted_avg_gr[j + i * weighted_grid_width] += rgbs_grid_ptr[rgbs_grid_index + j + i * rgbs_grid_width].avg_gr * _params.window_list[win_index].weight; weighted_avg_r[j + i * weighted_grid_width] += rgbs_grid_ptr[rgbs_grid_index + j + i * rgbs_grid_width].avg_r * _params.window_list[win_index].weight; weighted_avg_b[j + i * weighted_grid_width] += rgbs_grid_ptr[rgbs_grid_index + j + i * rgbs_grid_width].avg_b * _params.window_list[win_index].weight; weighted_avg_gb[j + i * weighted_grid_width] += rgbs_grid_ptr[rgbs_grid_index + j + i * rgbs_grid_width].avg_gb * _params.window_list[win_index].weight; weighted_sat[j + i * weighted_grid_width] += rgbs_grid_ptr[rgbs_grid_index + j + i * rgbs_grid_width].sat * _params.window_list[win_index].weight; } } } XCAM_LOG_DEBUG ("sum of weighing factor = %" PRIu64, weight_sum); rgbs_grid_index = (weighted_window.x_start + (hor_pixels_per_grid >> 1)) / hor_pixels_per_grid + (weighted_window.y_start + (vert_pixels_per_gird >> 1)) / vert_pixels_per_gird * rgbs_grid_width; for (uint32_t i = 0; i < weighted_grid_height; i++) { for (uint32_t j = 0; j < weighted_grid_width; j++) { rgbs_grid_ptr[rgbs_grid_index + j + i * rgbs_grid_width].avg_gr = weighted_avg_gr[j + i * weighted_grid_width] / weight_sum; rgbs_grid_ptr[rgbs_grid_index + j + i * rgbs_grid_width].avg_r = weighted_avg_r[j + i * weighted_grid_width] / weight_sum; rgbs_grid_ptr[rgbs_grid_index + j + i * rgbs_grid_width].avg_b = weighted_avg_b[j + i * weighted_grid_width] / weight_sum; rgbs_grid_ptr[rgbs_grid_index + j + i * rgbs_grid_width].avg_gb = weighted_avg_gb[j + i * weighted_grid_width] / weight_sum; rgbs_grid_ptr[rgbs_grid_index + j + i * rgbs_grid_width].sat = weighted_sat[j + i * weighted_grid_width] / weight_sum; } } xcam_free (weighted_avg_gr); return XCAM_RETURN_NO_ERROR; } XCamReturn AiqAeHandler::set_hist_weight_grid (ia_aiq_hist_weight_grid **out_weight_grid) { AnalyzerHandler::HandlerLock lock(this); uint16_t hist_grid_width = (*out_weight_grid)->width; uint16_t hist_grid_height = (*out_weight_grid)->height; uint32_t hist_grid_index = 0; unsigned char* weights_map_ptr = (*out_weight_grid)->weights; uint32_t image_width = 0; uint32_t image_height = 0; _aiq_compositor->get_size (image_width, image_height); uint32_t hor_pixels_per_grid = (image_width + (hist_grid_width >> 1)) / hist_grid_width; uint32_t vert_pixels_per_gird = (image_height + (hist_grid_height >> 1)) / hist_grid_height; XCAM_LOG_DEBUG ("hist weight grid: %d x %d pixels per grid cell", hor_pixels_per_grid, vert_pixels_per_gird); memset (weights_map_ptr, 0, hist_grid_width * hist_grid_height); for (uint32_t win_index = 0; win_index < XCAM_AE_MAX_METERING_WINDOW_COUNT; win_index++) { XCAM_LOG_DEBUG ("window start point(%d, %d), end point(%d, %d), weight = %d", _params.window_list[win_index].x_start, _params.window_list[win_index].y_start, _params.window_list[win_index].x_end, _params.window_list[win_index].y_end, _params.window_list[win_index].weight); if ((_params.window_list[win_index].weight <= 0) || (_params.window_list[win_index].weight > 15) || (_params.window_list[win_index].x_start < 0) || ((uint32_t)_params.window_list[win_index].x_end > image_width) || (_params.window_list[win_index].y_start < 0) || ((uint32_t)_params.window_list[win_index].y_end > image_height) || (_params.window_list[win_index].x_start >= _params.window_list[win_index].x_end) || (_params.window_list[win_index].y_start >= _params.window_list[win_index].y_end) || ((uint32_t)_params.window_list[win_index].x_end - (uint32_t)_params.window_list[win_index].x_start > image_width) || ((uint32_t)_params.window_list[win_index].y_end - (uint32_t)_params.window_list[win_index].y_start > image_height)) { XCAM_LOG_DEBUG ("skip window index = %d ", win_index); continue; } uint32_t weighted_grid_width = ((_params.window_list[win_index].x_end - _params.window_list[win_index].x_start + 1) + (hor_pixels_per_grid >> 1)) / hor_pixels_per_grid; uint32_t weighted_grid_height = ((_params.window_list[win_index].y_end - _params.window_list[win_index].y_start + 1) + (vert_pixels_per_gird >> 1)) / vert_pixels_per_gird; hist_grid_index = (_params.window_list[win_index].x_start + (hor_pixels_per_grid >> 1)) / hor_pixels_per_grid + ((_params.window_list[win_index].y_start + (vert_pixels_per_gird >> 1)) / vert_pixels_per_gird) * hist_grid_width; for (uint32_t i = 0; i < weighted_grid_height; i++) { for (uint32_t j = 0; j < weighted_grid_width; j++) { weights_map_ptr[hist_grid_index + j + i * hist_grid_width] = _params.window_list[win_index].weight; } } } return XCAM_RETURN_NO_ERROR; } XCamReturn AiqAeHandler::dump_hist_weight_grid (const ia_aiq_hist_weight_grid *weight_grid) { XCAM_LOG_DEBUG ("E dump_hist_weight_grid"); if (NULL == weight_grid) { return XCAM_RETURN_ERROR_PARAM; } uint16_t grid_width = weight_grid->width; uint16_t grid_height = weight_grid->height; for (uint32_t i = 0; i < grid_height; i++) { for (uint32_t j = 0; j < grid_width; j++) { printf ("%d ", weight_grid->weights[j + i * grid_width]); } printf("\n"); } XCAM_LOG_DEBUG ("X dump_hist_weight_grid"); return XCAM_RETURN_NO_ERROR; } XCamReturn AiqAeHandler::dump_RGBS_grid (const ia_aiq_rgbs_grid *rgbs_grid) { XCAM_LOG_DEBUG ("E dump_RGBS_grid"); if (NULL == rgbs_grid) { return XCAM_RETURN_ERROR_PARAM; } uint16_t grid_width = rgbs_grid->grid_width; uint16_t grid_height = rgbs_grid->grid_height; printf("AVG B\n"); for (uint32_t i = 0; i < grid_height; i++) { for (uint32_t j = 0; j < grid_width; j++) { printf ("%d ", rgbs_grid->blocks_ptr[j + i * grid_width].avg_b); } printf("\n"); } printf("AVG Gb\n"); for (uint32_t i = 0; i < grid_height; i++) { for (uint32_t j = 0; j < grid_width; j++) { printf ("%d ", rgbs_grid->blocks_ptr[j + i * grid_width].avg_gb); } printf("\n"); } printf("AVG Gr\n"); for (uint32_t i = 0; i < grid_height; i++) { for (uint32_t j = 0; j < grid_width; j++) { printf ("%d ", rgbs_grid->blocks_ptr[j + i * grid_width].avg_gr); } printf("\n"); } printf("AVG R\n"); for (uint32_t i = 0; i < grid_height; i++) { for (uint32_t j = 0; j < grid_width; j++) { printf ("%d ", rgbs_grid->blocks_ptr[j + i * grid_width].avg_r); //printf ("%d ", rgbs_grid->blocks_ptr[j + i * grid_width].sat); } printf("\n"); } XCAM_LOG_DEBUG ("X dump_RGBS_grid"); return XCAM_RETURN_NO_ERROR; } AiqAwbHandler::AiqAwbHandler (SmartPtr &aiq_compositor) : _aiq_compositor (aiq_compositor) , _started (false) { xcam_mem_clear (_cct_range); xcam_mem_clear (_result); xcam_mem_clear (_history_result); xcam_mem_clear (_cct_range); xcam_mem_clear (_input); _input.frame_use = aiq_compositor->get_frame_use (); _input.scene_mode = ia_aiq_awb_operation_mode_auto; _input.manual_cct_range = NULL; _input.manual_white_coordinate = NULL; } XCamReturn AiqAwbHandler::analyze (X3aResultList &output) { ia_aiq *ia_handle = NULL; ia_aiq_awb_results *awb_ret = NULL; ia_err ia_error = ia_err_none; XCAM_UNUSED (output); AnalyzerHandler::HandlerLock lock(this); if (!ensure_ia_parameters ()) { XCAM_LOG_ERROR ("AIQ AE ensure ia parameters failed"); return XCAM_RETURN_ERROR_PARAM; } ia_handle = _aiq_compositor->get_handle (); XCAM_ASSERT (ia_handle); ia_error = ia_aiq_awb_run (ia_handle, &_input, &awb_ret); XCAM_FAIL_RETURN (ERROR, ia_error == ia_err_none, XCAM_RETURN_ERROR_AIQ, "AIQ run AWB failed"); _result = *awb_ret; if (!_started) { _history_result = _result; _started = true; } adjust_speed (_history_result); _history_result = _result; return XCAM_RETURN_NO_ERROR; } bool AiqAwbHandler::ensure_ia_parameters () { bool ret = true; _input.frame_use = _aiq_compositor->get_frame_use (); ret = ret && ensure_awb_mode (); return ret; } bool AiqAwbHandler::ensure_awb_mode () { XCamAwbMode mode = get_mode_unlock(); _input.manual_cct_range = NULL; _input.scene_mode = ia_aiq_awb_operation_mode_auto; switch (mode) { case XCAM_AWB_MODE_AUTO: _input.scene_mode = ia_aiq_awb_operation_mode_auto; break; case XCAM_AWB_MODE_MANUAL: { uint32_t cct_min = 0, cct_max = 0; get_cct_range_unlock (cct_min, cct_max); if (cct_min && cct_max) { _input.scene_mode = ia_aiq_awb_operation_mode_manual_cct_range; _cct_range.max_cct = cct_min; _cct_range.min_cct = cct_max; _input.manual_cct_range = &_cct_range; } else _input.scene_mode = ia_aiq_awb_operation_mode_auto; break; } case XCAM_AWB_MODE_DAYLIGHT: _input.scene_mode = ia_aiq_awb_operation_mode_daylight; break; case XCAM_AWB_MODE_SUNSET: _input.scene_mode = ia_aiq_awb_operation_mode_sunset; break; case XCAM_AWB_MODE_CLOUDY: _input.scene_mode = ia_aiq_awb_operation_mode_partly_overcast; break; case XCAM_AWB_MODE_TUNGSTEN: _input.scene_mode = ia_aiq_awb_operation_mode_incandescent; break; case XCAM_AWB_MODE_FLUORESCENT: _input.scene_mode = ia_aiq_awb_operation_mode_fluorescent; break; case XCAM_AWB_MODE_WARM_FLUORESCENT: _input.scene_mode = ia_aiq_awb_operation_mode_incandescent; break; case XCAM_AWB_MODE_SHADOW: _input.scene_mode = ia_aiq_awb_operation_mode_fully_overcast; break; case XCAM_AWB_MODE_WARM_INCANDESCENT: _input.scene_mode = ia_aiq_awb_operation_mode_incandescent; break; case XCAM_AWB_MODE_NOT_SET: break; default: XCAM_LOG_ERROR ("unknown or unsupported AWB mode(%d)", mode); return false; } return true; } void AiqAwbHandler::adjust_speed (const ia_aiq_awb_results &last_ret) { _result.final_r_per_g = _calculate_new_value_by_speed ( last_ret.final_r_per_g, _result.final_r_per_g, get_speed_unlock ()); _result.final_b_per_g = _calculate_new_value_by_speed ( last_ret.final_b_per_g, _result.final_b_per_g, get_speed_unlock ()); } uint32_t AiqAwbHandler::get_current_estimate_cct () { AnalyzerHandler::HandlerLock lock(this); return (uint32_t)_result.cct_estimate; } XCamReturn AiqAfHandler::analyze (X3aResultList &output) { // TODO XCAM_UNUSED (output); return XCAM_RETURN_NO_ERROR; } AiqCommonHandler::AiqCommonHandler (SmartPtr &aiq_compositor) : _aiq_compositor (aiq_compositor) , _gbce_result (NULL) { } XCamReturn AiqCommonHandler::analyze (X3aResultList &output) { ia_aiq *ia_handle = NULL; ia_aiq_gbce_results *gbce_result = NULL; ia_err ia_error = ia_err_none; XCAM_UNUSED (output); AnalyzerHandler::HandlerLock lock(this); ia_aiq_gbce_input_params gbce_input; xcam_mem_clear (gbce_input); if (has_gbce_unlock()) { gbce_input.gbce_level = ia_aiq_gbce_level_use_tuning; } else { gbce_input.gbce_level = ia_aiq_gbce_level_bypass; } gbce_input.frame_use = _aiq_compositor->get_frame_use (); gbce_input.ev_shift = _aiq_compositor->get_ae_ev_shift_unlock (); ia_handle = _aiq_compositor->get_handle (); XCAM_ASSERT (ia_handle); ia_error = ia_aiq_gbce_run (ia_handle, &gbce_input, &gbce_result); XCAM_FAIL_RETURN (ERROR, ia_error == ia_err_none, XCAM_RETURN_ERROR_AIQ, "AIQ run GBCE failed"); //TODO, need copy GBCE result out, not just assign _gbce_result = gbce_result; return XCAM_RETURN_NO_ERROR; } class CmcParser { public: explicit CmcParser (ia_binary_data &cpf) { _cmc = ia_cmc_parser_init (&cpf); } ~CmcParser () { if (_cmc) ia_cmc_parser_deinit (_cmc); } ia_cmc_t *get() { return _cmc; } private: ia_cmc_t *_cmc; }; void AiqCompositor::convert_window_to_ia (const XCam3AWindow &window, ia_rectangle &ia_window) { ia_rectangle source; ia_coordinate_system source_system; ia_coordinate_system target_system = {IA_COORDINATE_TOP, IA_COORDINATE_LEFT, IA_COORDINATE_BOTTOM, IA_COORDINATE_RIGHT}; source_system.left = 0; source_system.top = 0; source_system.right = this->_width; source_system.bottom = this->_height; XCAM_ASSERT (_width && _height); source.left = window.x_start; source.top = window.y_start; source.right = window.x_end; source.bottom = window.y_end; ia_coordinate_convert_rect (&source_system, &source, &target_system, &ia_window); } AiqCompositor::AiqCompositor () : _ia_handle (NULL) , _ia_mkn (NULL) , _pa_result (NULL) #ifdef HAVE_AIQ_2_7 , _sa_result (NULL) #endif , _frame_use (ia_aiq_frame_use_video) , _width (0) , _height (0) { xcam_mem_clear (_frame_params); } AiqCompositor::~AiqCompositor () { } bool AiqCompositor::open (ia_binary_data &cpf) { CmcParser cmc (cpf); _ia_mkn = ia_mkn_init (ia_mkn_cfg_compression, 32000, 100000); _ia_handle = ia_aiq_init ( &cpf, NULL, NULL, MAX_STATISTICS_WIDTH, MAX_STATISTICS_HEIGHT, 1, //max_num_stats_in cmc.get(), _ia_mkn); if (_ia_handle == NULL) { XCAM_LOG_WARNING ("AIQ init failed"); return false; } _adaptor = new IaIspAdaptor22; XCAM_ASSERT (_adaptor.ptr()); if (!_adaptor->init (&cpf, MAX_STATISTICS_WIDTH, MAX_STATISTICS_HEIGHT, cmc.get(), _ia_mkn)) { XCAM_LOG_WARNING ("AIQ isp adaptor init failed"); return false; } _pa_result = NULL; #ifdef HAVE_AIQ_2_7 _sa_result = NULL; #endif XCAM_LOG_DEBUG ("Aiq compositor opened"); return true; } void AiqCompositor::close () { _adaptor.release (); if (_ia_handle) { ia_aiq_deinit (_ia_handle); _ia_handle = NULL; } if (_ia_mkn) { ia_mkn_uninit (_ia_mkn); _ia_mkn = NULL; } _ae_handler.release (); _awb_handler.release (); _af_handler.release (); _common_handler.release (); _pa_result = NULL; #ifdef HAVE_AIQ_2_7 _sa_result = NULL; #endif XCAM_LOG_DEBUG ("Aiq compositor closed"); } bool AiqCompositor::set_sensor_mode_data (struct atomisp_sensor_mode_data *sensor_mode) { _frame_params.horizontal_crop_offset = sensor_mode->crop_horizontal_start; _frame_params.vertical_crop_offset = sensor_mode->crop_vertical_start; _frame_params.cropped_image_height = sensor_mode->crop_vertical_end - sensor_mode->crop_vertical_start + 1; _frame_params.cropped_image_width = sensor_mode->crop_horizontal_end - sensor_mode->crop_horizontal_start + 1; /* hard code to 254? */ _frame_params.horizontal_scaling_denominator = 254; _frame_params.vertical_scaling_denominator = 254; if ((_frame_params.cropped_image_width == 0) || (_frame_params.cropped_image_height == 0)) { _frame_params.horizontal_scaling_numerator = 0; _frame_params.vertical_scaling_numerator = 0; } else { _frame_params.horizontal_scaling_numerator = sensor_mode->output_width * 254 * sensor_mode->binning_factor_x / _frame_params.cropped_image_width; _frame_params.vertical_scaling_numerator = sensor_mode->output_height * 254 * sensor_mode->binning_factor_y / _frame_params.cropped_image_height; } if (!_ae_handler->set_description (sensor_mode)) { XCAM_LOG_WARNING ("AIQ set ae description failed"); return XCAM_RETURN_ERROR_AIQ; } return true; } bool AiqCompositor::set_3a_stats (SmartPtr &stats) { ia_aiq_statistics_input_params aiq_stats_input; ia_aiq_rgbs_grid *rgbs_grids = NULL; ia_aiq_af_grid *af_grids = NULL; xcam_mem_clear (aiq_stats_input); aiq_stats_input.frame_timestamp = stats->get_timestamp(); aiq_stats_input.frame_id = stats->get_timestamp() + 1; aiq_stats_input.rgbs_grids = (const ia_aiq_rgbs_grid **)&rgbs_grids; aiq_stats_input.num_rgbs_grids = 1; aiq_stats_input.af_grids = (const ia_aiq_af_grid **)(&af_grids); aiq_stats_input.num_af_grids = 1; aiq_stats_input.frame_af_parameters = NULL; aiq_stats_input.external_histograms = NULL; aiq_stats_input.num_external_histograms = 0; aiq_stats_input.camera_orientation = ia_aiq_camera_orientation_unknown; if (_pa_result) aiq_stats_input.frame_pa_parameters = _pa_result; #ifdef HAVE_AIQ_2_7 if (_sa_result) aiq_stats_input.frame_sa_parameters = _sa_result; #endif if (_ae_handler->is_started()) { #ifdef USE_HIST_GRID_WEIGHTING if (XCAM_AE_METERING_MODE_WEIGHTED_WINDOW == _ae_handler->get_metering_mode ()) { ia_aiq_ae_results* ae_result = _ae_handler->get_result (); if (XCAM_RETURN_NO_ERROR != _ae_handler->set_hist_weight_grid (&(ae_result->weight_grid))) { XCAM_LOG_ERROR ("ae handler set hist weight grid failed"); } } #endif aiq_stats_input.frame_ae_parameters = _ae_handler->get_result (); //_ae_handler->dump_hist_weight_grid (aiq_stats_input.frame_ae_parameters->weight_grid); } //if (_awb_handler->is_started()) // aiq_stats_input.frame_awb_parameters = _awb_handler->get_result(); if (!_adaptor->convert_statistics (stats->get_isp_stats(), &rgbs_grids, &af_grids)) { XCAM_LOG_WARNING ("ia isp adaptor convert 3a stats failed"); return false; } if (XCAM_AE_METERING_MODE_WEIGHTED_WINDOW == _ae_handler->get_metering_mode ()) { #ifdef USE_RGBS_GRID_WEIGHTING if (XCAM_RETURN_NO_ERROR != _ae_handler->set_RGBS_weight_grid(&rgbs_grids)) { XCAM_LOG_ERROR ("ae handler update RGBS weighted statistic failed"); } //_ae_handler->dump_RGBS_grid (*(aiq_stats_input.rgbs_grids)); #endif } XCAM_LOG_DEBUG ("statistics grid info, width:%u, height:%u, blk_r:%u, blk_b:%u, blk_gr:%u, blk_gb:%u", aiq_stats_input.rgbs_grids[0]->grid_width, aiq_stats_input.rgbs_grids[0]->grid_height, aiq_stats_input.rgbs_grids[0]->blocks_ptr->avg_r, aiq_stats_input.rgbs_grids[0]->blocks_ptr->avg_b, aiq_stats_input.rgbs_grids[0]->blocks_ptr->avg_gr, aiq_stats_input.rgbs_grids[0]->blocks_ptr->avg_gb); if (ia_aiq_statistics_set(get_handle (), &aiq_stats_input) != ia_err_none) { XCAM_LOG_ERROR ("Aiq set statistic failed"); return false; } return true; } XCamReturn AiqCompositor::convert_color_effect (IspInputParameters &isp_input) { AiqCommonHandler *aiq_common = _common_handler.ptr(); switch (aiq_common->get_color_effect()) { case XCAM_COLOR_EFFECT_NONE: isp_input.effects = ia_isp_effect_none; break; case XCAM_COLOR_EFFECT_SKY_BLUE: isp_input.effects = ia_isp_effect_sky_blue; break; case XCAM_COLOR_EFFECT_SKIN_WHITEN_LOW: isp_input.effects = ia_isp_effect_skin_whiten_low; break; case XCAM_COLOR_EFFECT_SKIN_WHITEN: isp_input.effects = ia_isp_effect_skin_whiten; break; case XCAM_COLOR_EFFECT_SKIN_WHITEN_HIGH: isp_input.effects = ia_isp_effect_skin_whiten_high; break; case XCAM_COLOR_EFFECT_SEPIA: isp_input.effects = ia_isp_effect_sepia; break; case XCAM_COLOR_EFFECT_NEGATIVE: isp_input.effects = ia_isp_effect_negative; break; case XCAM_COLOR_EFFECT_GRAYSCALE: isp_input.effects = ia_isp_effect_grayscale; break; default: isp_input.effects = ia_isp_effect_none; break; } return XCAM_RETURN_NO_ERROR; } XCamReturn AiqCompositor::apply_gamma_table (struct atomisp_parameters *isp_param) { if (_common_handler->_params.is_manual_gamma) { int i; if (isp_param->r_gamma_table) { isp_param->r_gamma_table->vamem_type = 1; //IA_CSS_VAMEM_TYPE_2 = 1; for (i = 0; i < XCAM_GAMMA_TABLE_SIZE; ++i) { // change from double to u0.12 isp_param->r_gamma_table->data.vamem_2[i] = (uint32_t) (_common_handler->_params.r_gamma[i] * 4096.0); } isp_param->r_gamma_table->data.vamem_2[256] = 4091; } if (isp_param->g_gamma_table) { isp_param->g_gamma_table->vamem_type = 1; //IA_CSS_VAMEM_TYPE_2 = 1; for (i = 0; i < XCAM_GAMMA_TABLE_SIZE; ++i) { // change from double to u0.12 isp_param->g_gamma_table->data.vamem_2[i] = (uint32_t) (_common_handler->_params.g_gamma[i] * 4096.0); } isp_param->g_gamma_table->data.vamem_2[256] = 4091; } if (isp_param->b_gamma_table) { isp_param->b_gamma_table->vamem_type = 1; //IA_CSS_VAMEM_TYPE_2 = 1; for (i = 0; i < XCAM_GAMMA_TABLE_SIZE; ++i) { // change from double to u0.12 isp_param->b_gamma_table->data.vamem_2[i] = (uint32_t) (_common_handler->_params.b_gamma[i] * 4096.0); } isp_param->b_gamma_table->data.vamem_2[256] = 4091; } } return XCAM_RETURN_NO_ERROR; } XCamReturn AiqCompositor::apply_night_mode (struct atomisp_parameters *isp_param) { static const struct atomisp_cc_config night_yuv2rgb_cc_config = { 10, { 1 << 10, 0, 0, /* 1.0, 0, 0 */ 1 << 10, 0, 0, /* 1.0, 0, 0 */ 1 << 10, 0, 0 } }; /* 1.0, 0, 0 */ static const struct atomisp_wb_config night_wb_config = { 1, 1 << 15, 1 << 15, 1 << 15, 1 << 15 }; /* 1.0, 1.0, 1.0, 1.0*/ if (isp_param->ctc_config) isp_param->ctc_config = NULL; *isp_param->wb_config = night_wb_config; *isp_param->yuv2rgb_cc_config = night_yuv2rgb_cc_config; return XCAM_RETURN_NO_ERROR; } double AiqCompositor::calculate_value_by_factor (double factor, double min, double mid, double max) { XCAM_ASSERT (factor >= -1.0 && factor <= 1.0); XCAM_ASSERT (min <= mid && max >= mid); if (factor >= 0.0) return (mid * (1.0 - factor) + max * factor); else return (mid * (1.0 + factor) + min * (-factor)); } XCamReturn AiqCompositor::limit_nr_levels (struct atomisp_parameters *isp_param) { #define NR_MIN_FACOTR 0.1 #define NR_MAX_FACOTR 6.0 #define NR_MID_FACOTR 1.0 SmartPtr aiq_common = _common_handler; if (!XCAM_DOUBLE_EQUAL_AROUND (aiq_common->_params.nr_level, 0.0)) { double nr_factor; nr_factor = calculate_value_by_factor ( aiq_common->_params.nr_level, NR_MIN_FACOTR, NR_MID_FACOTR, NR_MAX_FACOTR); if (isp_param->nr_config) { isp_param->nr_config->bnr_gain = XCAM_MIN (isp_param->nr_config->bnr_gain * nr_factor, 65535); isp_param->nr_config->ynr_gain = XCAM_MIN (isp_param->nr_config->ynr_gain * nr_factor, 65535); } if (isp_param->cnr_config) { isp_param->cnr_config->sense_gain_vy = XCAM_MIN (isp_param->cnr_config->sense_gain_vy * nr_factor, 8191); isp_param->cnr_config->sense_gain_vu = XCAM_MIN (isp_param->cnr_config->sense_gain_vu * nr_factor, 8191); isp_param->cnr_config->sense_gain_vv = XCAM_MIN (isp_param->cnr_config->sense_gain_vv * nr_factor, 8191); isp_param->cnr_config->sense_gain_hy = XCAM_MIN (isp_param->cnr_config->sense_gain_hy * nr_factor, 8191); isp_param->cnr_config->sense_gain_hu = XCAM_MIN (isp_param->cnr_config->sense_gain_hu * nr_factor, 8191); isp_param->cnr_config->sense_gain_hv = XCAM_MIN (isp_param->cnr_config->sense_gain_hv * nr_factor, 8191); } } if (!XCAM_DOUBLE_EQUAL_AROUND (aiq_common->_params.tnr_level, 0.0)) { double tnr_factor; tnr_factor = calculate_value_by_factor ( aiq_common->_params.tnr_level, NR_MIN_FACOTR, NR_MID_FACOTR, NR_MAX_FACOTR); if (isp_param->tnr_config) { isp_param->tnr_config->gain = XCAM_MIN (isp_param->tnr_config->gain * tnr_factor, 65535); if (XCAM_DOUBLE_EQUAL_AROUND (aiq_common->_params.tnr_level, 1.0)) { isp_param->tnr_config->gain = 65535; isp_param->tnr_config->threshold_y = 0; isp_param->tnr_config->threshold_uv = 0; } } XCAM_LOG_DEBUG ("set TNR gain:%u", isp_param->tnr_config->gain); } return XCAM_RETURN_NO_ERROR; } XCamReturn AiqCompositor::integrate (X3aResultList &results) { IspInputParameters isp_params; ia_aiq_pa_input_params pa_input; ia_aiq_pa_results *pa_result = NULL; #ifdef HAVE_AIQ_2_7 ia_aiq_sa_input_params sa_input; ia_aiq_sa_results *sa_result = NULL; #endif ia_err ia_error = ia_err_none; ia_binary_data output; AiqAeHandler *aiq_ae = _ae_handler.ptr(); AiqAwbHandler *aiq_awb = _awb_handler.ptr(); AiqAfHandler *aiq_af = _af_handler.ptr(); AiqCommonHandler *aiq_common = _common_handler.ptr(); struct atomisp_parameters *isp_3a_result = NULL; SmartPtr isp_results; XCAM_FAIL_RETURN ( ERROR, aiq_ae && aiq_awb && aiq_af && aiq_common, XCAM_RETURN_ERROR_PARAM, "handlers are not AIQ inherited"); xcam_mem_clear (pa_input); #ifndef HAVE_AIQ_2_7 pa_input.frame_use = _frame_use; pa_input.sensor_frame_params = &_frame_params; #endif if (aiq_ae->is_started()) pa_input.exposure_params = (aiq_ae->get_result ())->exposures[0].exposure; pa_input.color_gains = NULL; if (aiq_common->_params.enable_night_mode) { pa_input.awb_results = NULL; isp_params.effects = ia_isp_effect_grayscale; } else { pa_input.awb_results = aiq_awb->get_result (); } ia_error = ia_aiq_pa_run (_ia_handle, &pa_input, &pa_result); if (ia_error != ia_err_none) { XCAM_LOG_WARNING ("AIQ pa run failed"); // but not return error } _pa_result = pa_result; if (aiq_awb->get_mode_unlock () == XCAM_AWB_MODE_MANUAL) { if (XCAM_DOUBLE_EQUAL_AROUND (aiq_awb->_params.gr_gain, 0.0) || XCAM_DOUBLE_EQUAL_AROUND (aiq_awb->_params.r_gain, 0.0) || XCAM_DOUBLE_EQUAL_AROUND (aiq_awb->_params.b_gain, 0.0) || XCAM_DOUBLE_EQUAL_AROUND (aiq_awb->_params.gb_gain, 0.0)) { XCAM_LOG_DEBUG ("Zero gain would cause ISP division fatal error"); } else { #ifdef HAVE_AIQ_2_7 _pa_result->color_gains.gr = aiq_awb->_params.gr_gain; _pa_result->color_gains.r = aiq_awb->_params.r_gain; _pa_result->color_gains.b = aiq_awb->_params.b_gain; _pa_result->color_gains.gb = aiq_awb->_params.gb_gain; #else _pa_result->color_gains[0] = aiq_awb->_params.gr_gain; _pa_result->color_gains[1] = aiq_awb->_params.r_gain; _pa_result->color_gains[2] = aiq_awb->_params.b_gain; _pa_result->color_gains[3] = aiq_awb->_params.gb_gain; #endif } } #ifdef HAVE_AIQ_2_7 xcam_mem_clear (sa_input); sa_input.frame_use = _frame_use; sa_input.sensor_frame_params = &_frame_params; if (aiq_common->_params.enable_night_mode) { sa_input.awb_results = NULL; } else { sa_input.awb_results = aiq_awb->get_result (); } ia_error = ia_aiq_sa_run (_ia_handle, &sa_input, &sa_result); if (ia_error != ia_err_none) { XCAM_LOG_WARNING ("AIQ sa run failed"); // but not return error } _sa_result = sa_result; #endif isp_params.frame_use = _frame_use; isp_params.awb_results = aiq_awb->get_result (); if (aiq_ae->is_started()) isp_params.exposure_results = (aiq_ae->get_result ())->exposures[0].exposure; isp_params.gbce_results = aiq_common->get_gbce_result (); isp_params.sensor_frame_params = &_frame_params; isp_params.pa_results = pa_result; #ifdef HAVE_AIQ_2_7 isp_params.sa_results = sa_result; #endif isp_params.manual_brightness = (int8_t)(aiq_common->get_brightness_unlock() * 128.0); isp_params.manual_contrast = (int8_t)(aiq_common->get_contrast_unlock() * 128.0); isp_params.manual_saturation = (int8_t)(aiq_common->get_saturation_unlock() * 128.0); isp_params.manual_hue = (int8_t)(aiq_common->get_hue_unlock() * 128.0); isp_params.manual_sharpness = (int8_t)(aiq_common->get_sharpness_unlock() * 128.0); isp_params.manual_nr_level = (int8_t)(aiq_common->get_nr_level_unlock() * 128.0); convert_color_effect(isp_params); xcam_mem_clear (output); if (!_adaptor->run (&isp_params, &output)) { XCAM_LOG_ERROR("Aiq to isp adaptor running failed"); return XCAM_RETURN_ERROR_ISP; } isp_3a_result = ((struct atomisp_parameters *)output.data); apply_gamma_table (isp_3a_result); limit_nr_levels (isp_3a_result); if (aiq_common->_params.enable_night_mode) { apply_night_mode (isp_3a_result); } isp_results = generate_3a_configs (isp_3a_result); results.push_back (isp_results); return XCAM_RETURN_NO_ERROR; } SmartPtr AiqCompositor::generate_3a_configs (struct atomisp_parameters *parameters) { SmartPtr ret; X3aAtomIspParametersResult *x3a_result = new X3aAtomIspParametersResult (XCAM_IMAGE_PROCESS_ONCE); x3a_result->set_isp_config (*parameters); ret = x3a_result; return ret; } void AiqCompositor::set_ae_handler (SmartPtr &handler) { XCAM_ASSERT (!_ae_handler.ptr()); _ae_handler = handler; } void AiqCompositor::set_awb_handler (SmartPtr &handler) { XCAM_ASSERT (!_awb_handler.ptr()); _awb_handler = handler; } void AiqCompositor::set_af_handler (SmartPtr &handler) { XCAM_ASSERT (!_af_handler.ptr()); _af_handler = handler; } void AiqCompositor::set_common_handler (SmartPtr &handler) { XCAM_ASSERT (!_common_handler.ptr()); _common_handler = handler; } };