Camera Hal OEM模块 ---- hdr算法流程

hdr是Camera中比较常见的功能,展锐平台下hdr的实现是在 oem 下的 cmr_hdr.c 文件中,本篇我们来介绍hdr的流程。

先来看下一个比较关键的结构体,class_hdr ,后面会经常看到对这个结构体的操作

struct class_hdr {
    struct ipm_common common;
    struct hdr_frame_addr hdr_addr[HDR_CAP_NUM];
    cmr_uint mem_size;
    cmr_uint width;
    cmr_uint height;
    cmr_uint is_inited;
    cmr_handle hdr_thread;
    struct img_addr dst_addr;
    ipm_callback reg_cb;
    struct ipm_frame_in frame_in;
    struct class_hdr_lib_context lib_cxt;
    cmr_uint ev_effect_frame_interval;
    void *dbg_info_addr;
    int32_t dbg_info_size;
};

在往下是函数声明:

//hdr 算法库使用的整体流程
static cmr_int hdr_open(cmr_handle ipm_handle, struct ipm_open_in *in,struct ipm_open_out *out, cmr_handle *class_handle);
static cmr_int hdr_close(cmr_handle class_handle);
static cmr_int hdr_transfer_frame(cmr_handle class_handle,struct ipm_frame_in *in,struct ipm_frame_out *out);

//跑hdr算法的核心
static cmr_int req_hdr_do(cmr_handle class_handle, struct img_addr *dst_addr,struct img_size frame_size);
static cmr_int hdr_arithmetic(cmr_handle class_handle,struct img_addr *dst_addr, cmr_u32 width,cmr_u32 height);      

//保存帧
static cmr_int hdr_save_frame(cmr_handle class_handle, struct ipm_frame_in *in);
static cmr_int req_hdr_save_frame(cmr_handle class_handle,struct ipm_frame_in *in);      

//hdr封装的对算法库接口的调用
static cmr_int hdr_sprd_adapter_init(struct class_hdr *hdr_handle);
static cmr_int hdr_sprd_adapter_deinit(struct class_hdr *hdr_handle);
static cmr_int hdr_sprd_adapter_fast_stop(struct class_hdr *hdr_handle);
static cmr_int hdr_sprd_adapter_process(struct class_hdr *hdr_handle,sprd_hdr_cmd_t cmd, void *param);        

关于 保存帧 的,我们在上一篇介绍 3dnr 的时候也提到了,3dnr是5帧数据送到算法中去跑的,本篇介绍的hdr是 3帧 数据跑的。跑算法前需要收集的数据帧数是算法库的要求,具体是在 cmr_common.h 中定义的。

#define CAP_4IN1_NUM 5
#define HDR_CAP_NUM 3
#define PRE_3DNR_NUM 2
#define CAP_3DNR_NUM 5

cmr_common.h 中还定义了许多libcamera下的宏和结构体,一般我会在当前算法的 .h 下面搜定义,搜不到就去cmr_common.h 中去搜,有机会可以在专门介绍下cmr_common.h中的那些关键定义。

好了,现在我们知道 hdr 是需要保存3帧数据的,从上面的函数声明开始看流程吧。

hdr_open

打开hdr

1,构建 struct class_hdr *hdr_handle = NULL;

class_hdr 结构体我们在开篇的时候就介绍了,关于hdr的数据都在这个结构体里面的了,所以在最开始open的时候就创建并且初始化了部分数据

hdr_handle = (struct class_hdr *)malloc(sizeof(struct class_hdr));

hdr_handle->common.ipm_cxt = (struct ipm_context_t *)ipm_handle;
hdr_handle->common.class_type = IPM_TYPE_HDR;
hdr_handle->common.receive_frame_count = 0;
hdr_handle->common.save_frame_count = 0;
hdr_handle->common.ops = &hdr_ops_tab_info;

hdr_handle->mem_size = size;

CMR_LOGD("hdr_handle->mem_size = 0x%lx", hdr_handle->mem_size);

hdr_handle->height = in->frame_size.height;
hdr_handle->width = in->frame_size.width;
hdr_handle->reg_cb = in->reg_cb;

2, hdr_thread_create

创建hdr的thred 消息处理对象

ret = cmr_thread_create(&class_handle->hdr_thread,
                                CAMERA_HDR_MSG_QUEUE_SIZE, hdr_thread_proc,
                                (void *)class_handle, "hdr");

3,hdr_sprd_adapter_init

将上述创建的 class_hdr 结构体传入算法接口中去初始化算法

ret = hdr_sprd_adapter_init(hdr_handle);

static cmr_int hdr_sprd_adapter_init(struct class_hdr *hdr_handle) {
	//获取hdr的效果参数
	sprd_hdr_init_param_t hdr_param;
    ret = ipm_in->ipm_isp_ioctl(oem_handle, COM_ISP_GET_HDR_PARAM,
                                    &isp_cmd_parm);
	hdr_param.tuning_param = isp_cmd_parm.hdr_param.param_ptr;
    hdr_param.tuning_param_size= isp_cmd_parm.hdr_param.param_size;	
	//将hdr效果参数传给hdr算法库去做init
	hdr_handle->lib_cxt.lib_handle =
        sprd_hdr_adpt_init(max_width, max_height, (void *)&hdr_param);

}

hdr_close

close的操作与open是相对的,open的时候打开或者创建了什么,close的时候就要去关闭、销毁什么

1,hdr_sprd_adapter_fast_stop(hdr_handle);

fast_stop 很好理解,就是停止hdr算法,其对应的算法接口如下:

ret = sprd_hdr_adpt_ctrl(hdr_handle->lib_cxt.lib_handle,SPRD_HDR_FAST_STOP_CMD, NULL);

2,hdr_thread_destroy(hdr_handle)

thread_destory也是常规的操作,销毁在hdr_open的时候创建的消息处理器

static cmr_int hdr_thread_destroy(struct class_hdr *class_handle) {
    if (class_handle->is_inited) {
        ret = cmr_thread_destroy(class_handle->hdr_thread);
        class_handle->hdr_thread = 0;

        class_handle->is_inited = 0;
    }
}

3, hdr_sprd_adapter_deinit(hdr_handle);

这里也是直接调用算法接口的deinit

ret = sprd_hdr_adpt_deinit(hdr_handle->lib_cxt.lib_handle);

hdr_sprd_adapter_deinit 和 sprd_hdr_adpt_deinit ,名字很像,看着有点晕哈,可以这样理解,以hdr开头的是当前cmr_hdr定义的函数,以 sprd 开头的是算法库的统一接口,我们在上篇介绍 3dnr 的文章中也能看到 sprd 开头的函数,比如 sprd_mfnr_adpt_init。

hdr_transfer_frame

此函数会被调用3次,每次传入一帧数据,类似上面的 3dnr 中 threednr_transfer_frame 会被调用5次。
我们分3部分来讲:

  1. 打印帧数据的地址
  2. 保存帧数据
  3. 需要的3帧数据都拿到之后的流程
static cmr_int hdr_transfer_frame(cmr_handle class_handle,struct ipm_frame_in *in,struct ipm_frame_out *out){
	addr = &in->dst_frame.addr_vir;
	//1 打印帧数据的地址
    CMR_LOGD("ipm_frame_in.private_data 0x%lx  before (in->dst)addr 0x%lx  (in->src)addr 0x%lx", 
        (cmr_int)in->private_data, addr->addr_y, (&in->src_frame.addr_vir)->addr_y);
    //2 保存帧数据
	ret = req_hdr_save_frame(class_handle, in);
	//3 需要的3帧数据都拿到之后的流程
    if (hdr_handle->common.save_frame_count ==
       (HDR_CAP_NUM + hdr_handle->ev_effect_frame_interval)) {
       CMR_LOGD("HDR enable = %d", hdr_enable);
       cmr_bzero(&out->dst_frame, sizeof(struct img_frm));
       sensor_ioctl = hdr_handle->common.ipm_cxt->init_in.ipm_sensor_ioctl;
       isp_ioctl = hdr_handle->common.ipm_cxt->init_in.ipm_isp_ioctl;
       oem_handle = hdr_handle->common.ipm_cxt->init_in.oem_handle;
       hdr_handle->frame_in = *in;
       ret = req_hdr_do(class_handle, addr, size); // 关键点*****
       if (ret != CMR_CAMERA_SUCCESS) {
           CMR_LOGE("req_hdr_do fail");
       }
       CMR_LOGE("after addr 0x%lx", addr->addr_y);
       out->dst_frame = in->dst_frame;
       out->private_data = in->private_data;

       struct sensor_exp_info sensor_info;
       sensor_id = hdr_handle->common.ipm_cxt->init_in.sensor_id;
       get_sensor_info = hdr_handle->common.ipm_cxt->init_in.get_sensor_info;
       get_sensor_info(oem_handle, sensor_id, &sensor_info);

       if (CAM_IMG_FMT_BAYER_MIPI_RAW == sensor_info.image_format) {
           isp_param1.cmd_value = hdr_enable;
           ret = isp_ioctl(oem_handle, COM_ISP_SET_HDR, (void *)&isp_param1);
       } else {
           sn_param.cmd_value = OEM_EV_LEVEL_2;
           ret =
               sensor_ioctl(oem_handle, COM_SN_SET_HDR_EV, (void *)&sn_param);
       }
       if (ret) {
           CMR_LOGE("HDR failed to set ev.");
       }
   }
}

1. 打印帧数据的地址

我们看到log显示3帧数的 in->src不同, in->dst 都是一样的,并且第一帧的src 与 dst 是相同的。

Line 99341: 06-06 12:31:33.356   471  3159 D cmr_hdr : 328, hdr_transfer_frame: ipm_frame_in.private_data 0xeed40010  before (in->dst)addr 0xe4e1e000  (in->src)addr 0xe4e1e000
Line 99507: 06-06 12:31:33.422   471  3159 D cmr_hdr : 328, hdr_transfer_frame: ipm_frame_in.private_data 0xeed40010  before (in->dst)addr 0xe4e1e000  (in->src)addr 0xe3b8c000
Line 99605: 06-06 12:31:33.467   471  3159 D cmr_hdr : 328, hdr_transfer_frame: ipm_frame_in.private_data 0xeed40010  before (in->dst)addr 0xe4e1e000  (in->src)addr 0xe28fa000

2. 保存帧数据

req_hdr_save_frame,以req开头的都是发送一个message,关键逻辑是在message的处理函数 hdr_save_frame中。

static cmr_int hdr_save_frame(cmr_handle class_handle,struct ipm_frame_in *in) {
	hdr_handle->lib_cxt.ev[0] = in->ev[0];
    hdr_handle->lib_cxt.ev[1] = in->ev[1];
    hdr_handle->lib_cxt.hdr_callback = in->hdr_callback;
    hdr_handle->lib_cxt.ae_exp_gain_info = in->ae_exp_gain_info;
  
    if (hdr_handle->mem_size >= in->src_frame.buf_size &&
        NULL != (void *)in->src_frame.addr_vir.addr_y) {
        hdr_handle->hdr_addr[frame_sn].addr_phy.addr_y = in->src_frame.addr_phy.addr_y;
        hdr_handle->hdr_addr[frame_sn].addr_phy.addr_u = in->src_frame.addr_phy.addr_u;
        hdr_handle->hdr_addr[frame_sn].addr_phy.addr_v = in->src_frame.addr_phy.addr_v;

        hdr_handle->hdr_addr[frame_sn].addr_vir.addr_y = in->src_frame.addr_vir.addr_y;
        hdr_handle->hdr_addr[frame_sn].addr_vir.addr_u = in->src_frame.addr_vir.addr_u;
        hdr_handle->hdr_addr[frame_sn].addr_vir.addr_v = in->src_frame.addr_vir.addr_v;

        hdr_handle->hdr_addr[frame_sn].fd = in->src_frame.fd;
    }
}

hdr_save_frame的主要逻辑是将参数in->src_frame的地址保存到 hdr_handle->hdr_addr 数组中。

3. 需要的3帧数据都拿到之后的流程

save的逻辑走完了,在回到 hdr_transfer_frame 中。有一个 if 判断

if (hdr_handle->common.save_frame_count == (HDR_CAP_NUM + hdr_handle->ev_effect_frame_interval)) 

HDR_CAP_NUM 宏定义 = 3
hdr_handle->ev_effect_frame_interval = 0
即当 save_frame_count == 3 ,该 if 条件满足。
if 满足会调用 req_hdr_do,又是一个req开头的函数,显然是有对应的实现逻辑,即 hdr_arithmetic

hdr_arithmetic 的流程:

3.1 取出保存的3帧数据的虚拟地址
static cmr_int hdr_arithmetic(cmr_handle class_handle,struct img_addr *dst_addr, cmr_u32 width,cmr_u32 height) {
	//这是我们在 hdr_save_frame 中保存的3帧数据的虚拟地址
    temp_addr0 = (cmr_u8 *)hdr_handle->hdr_addr[0].addr_vir.addr_y;
    temp_addr1 = (cmr_u8 *)hdr_handle->hdr_addr[1].addr_vir.addr_y;
    temp_addr2 = (cmr_u8 *)hdr_handle->hdr_addr[2].addr_vir.addr_y;

}
3.2 构建 sprd_hdr_param 参数

sprd_hdr_param 参数
取 hdr_handle->hdr_addr[0] 和 hdr_handle->hdr_addr[1] 的地址为 input 参数
取 hdr_handle->hdr_addr[0] 为 output 参数

    if ((NULL != temp_addr0) && (NULL != temp_addr1) && (NULL != temp_addr2)) {
#ifndef CONFIG_SPRD_HDR_LIB_VERSION_2
        ret = HDR_Function(temp_addr0, temp_addr2, temp_addr1, temp_addr0,
                           height, width, p_format);
#else
        sprd_hdr_param.input[0].format = SPRD_CAMALG_IMG_NV21;
        sprd_hdr_param.input[0].addr[0] = (void *)hdr_handle->hdr_addr[0].addr_vir.addr_y;
        sprd_hdr_param.input[0].addr[1] = (void *)hdr_handle->hdr_addr[0].addr_vir.addr_u;
        sprd_hdr_param.input[0].addr[2] = (void *)hdr_handle->hdr_addr[0].addr_vir.addr_v;
        sprd_hdr_param.input[0].ion_fd = hdr_handle->hdr_addr[0].fd;
        sprd_hdr_param.input[0].offset[0] = 0;
        sprd_hdr_param.input[0].offset[1] = width * height;
        sprd_hdr_param.input[0].width = width;
        sprd_hdr_param.input[0].height = height;
        sprd_hdr_param.input[0].stride = width;
        sprd_hdr_param.input[0].size = width * height * 3 / 2;
        sprd_hdr_param.ev[0] = hdr_handle->lib_cxt.ev[0];
        sprd_hdr_param.detect_out = hdr_handle->lib_cxt.hdr_callback;
        CMR_LOGI("sprd_hdr_param.callback %p", sprd_hdr_param.callback);

        sprd_hdr_param.input[1].format = SPRD_CAMALG_IMG_NV21;
        sprd_hdr_param.input[1].addr[0] = (void *)hdr_handle->hdr_addr[1].addr_vir.addr_y;
        sprd_hdr_param.input[1].addr[1] = (void *)hdr_handle->hdr_addr[1].addr_vir.addr_u;
        sprd_hdr_param.input[1].addr[2] = (void *)hdr_handle->hdr_addr[1].addr_vir.addr_v;
        sprd_hdr_param.input[1].ion_fd = hdr_handle->hdr_addr[1].fd;
        sprd_hdr_param.input[1].offset[0] = 0;
        sprd_hdr_param.input[1].offset[1] = width * height;
        sprd_hdr_param.input[1].width = width;
        sprd_hdr_param.input[1].height = height;
        sprd_hdr_param.input[1].stride = width;
        sprd_hdr_param.input[1].size = width * height * 3 / 2;
        sprd_hdr_param.ev[1] = hdr_handle->lib_cxt.ev[1];

        sprd_hdr_param.output.format = SPRD_CAMALG_IMG_NV21;
        sprd_hdr_param.output.addr[0] = (void *)hdr_handle->hdr_addr[0].addr_vir.addr_y;
        sprd_hdr_param.output.addr[1] = (void *)hdr_handle->hdr_addr[0].addr_vir.addr_u;
        sprd_hdr_param.output.addr[2] = (void *)hdr_handle->hdr_addr[0].addr_vir.addr_v;
        sprd_hdr_param.output.ion_fd = hdr_handle->hdr_addr[0].fd;
        sprd_hdr_param.output.offset[0] = 0;
        sprd_hdr_param.output.offset[1] = width * height;
        sprd_hdr_param.output.width = width;
        sprd_hdr_param.output.height = height;
        sprd_hdr_param.output.stride = width;
        sprd_hdr_param.output.size = width * height * 3 / 2;

        ret = hdr_sprd_adapter_process(hdr_handle, SPRD_HDR_PROCESS_CMD,
                                       (void *)&sprd_hdr_param);
3.3 执行 hdr_sprd_adapter_process
case SPRD_HDR_PROCESS_CMD:
        ret =
            sprd_hdr_adpt_ctrl(hdr_handle->lib_cxt.lib_handle,
                               SPRD_HDR_PROCESS_CMD, (sprd_hdr_param_t *)param);
        break;
3.4 获取hdr合成结果的 exit 信息
ret = sprd_hdr_adpt_ctrl(hdr_handle->lib_cxt.lib_handle, SPRD_HDR_GET_EXIF_INFO_CMD,
                                    (void *)&get_exif_param);

hdr_arithmetic 的流程到这里就完成了,主要是取出我们之前保存的帧数据地址,给hdr算法去跑,算法跑完之后在去拿exif信息。

注:不知道大家有没有注意到,我们在hdr_save_frame中保存的是3帧数据,但是这里向算法里面送的时候只取了2帧数据,hdr_handle->hdr_addr[2]没有用到,不清楚平台这样设计的含义是什么。

hdr拍照的完整log 如下,与我们上述介绍的代码流程也是一致的:

	open
	Line 98694: 06-06 12:31:33.107   471  3159 D cmr_hdr : 174, hdr_open: in->frame_size.width = 4160,in->frame_size.height = 3120
	Line 98695: 06-06 12:31:33.107   471  3159 D cmr_hdr : 184, hdr_open: hdr_handle->mem_size = 0x1291200
	Line 98740: 06-06 12:31:33.134   471  3159 D cmr_hdr : 913, hdr_sprd_adapter_init: max width*height = [4160 * 3120]
	Line 98749: 06-06 12:31:33.134   471  3159 E cmr_hdr : 920, hdr_sprd_adapter_init: failed to get isp hdr param 1
	Line 98752: 06-06 12:31:33.134   471  3159 D cmr_hdr : 927, hdr_sprd_adapter_init: hdr_param 0x0,size 0
	Line 98845: 06-06 12:31:33.141   471  3352 D cmr_hdr : 696, hdr_thread_proc: HDR pre_proc
	Line 98846: 06-06 12:31:33.141   471  3352 D cmr_hdr : 438, hdr_frame_proc: HDR enable = 1 image_format = 48
	第一帧
	Line 99341: 06-06 12:31:33.356   471  3159 D cmr_hdr : 328, hdr_transfer_frame: ipm_frame_in.private_data 0xeed40010  before (in->dst)addr 0xe4e1e000  (in->src)addr 0xe4e1e000
	Line 99342: 06-06 12:31:33.356   471  3352 D cmr_hdr : 700, hdr_thread_proc: HDR save frame
	Line 99343: 06-06 12:31:33.356   471  3352 D cmr_hdr : 643, hdr_save_frame: ev: -0.800000, 0.800000, 0xea369278,0xea379870
	Line 99344: 06-06 12:31:33.356   471  3352 D cmr_hdr : 654, hdr_save_frame:  HDR frame_sn 0, y_addr 0xe4e1e000 save_frame_count 1 ev_effect_frame_interval 0
	第二帧
	Line 99507: 06-06 12:31:33.422   471  3159 D cmr_hdr : 328, hdr_transfer_frame: ipm_frame_in.private_data 0xeed40010  before (in->dst)addr 0xe4e1e000  (in->src)addr 0xe3b8c000
	Line 99508: 06-06 12:31:33.423   471  3352 D cmr_hdr : 700, hdr_thread_proc: HDR save frame
	Line 99509: 06-06 12:31:33.423   471  3352 D cmr_hdr : 643, hdr_save_frame: ev: -0.800000, 0.800000, 0xea369278,0xea379870
	Line 99510: 06-06 12:31:33.423   471  3352 D cmr_hdr : 654, hdr_save_frame:  HDR frame_sn 1, y_addr 0xe3b8c000 save_frame_count 2 ev_effect_frame_interval 0
	第三帧
	Line 99605: 06-06 12:31:33.467   471  3159 D cmr_hdr : 328, hdr_transfer_frame: ipm_frame_in.private_data 0xeed40010  before (in->dst)addr 0xe4e1e000  (in->src)addr 0xe28fa000
	Line 99606: 06-06 12:31:33.467   471  3352 D cmr_hdr : 700, hdr_thread_proc: HDR save frame
	Line 99607: 06-06 12:31:33.467   471  3352 D cmr_hdr : 643, hdr_save_frame: ev: -0.800000, 0.800000, 0xea369278,0xea379870
	Line 99608: 06-06 12:31:33.467   471  3352 D cmr_hdr : 654, hdr_save_frame:  HDR frame_sn 2, y_addr 0xe28fa000 save_frame_count 3 ev_effect_frame_interval 0
	三帧数据拿到之后的处理
	Line 99609: 06-06 12:31:33.467   471  3159 D cmr_hdr : 338, hdr_transfer_frame: HDR enable = 0
	Line 99610: 06-06 12:31:33.468   471  3352 D cmr_hdr : 712, hdr_thread_proc: out private_data 0xeed40010
	Line 99611: 06-06 12:31:33.468   471  3352 D cmr_hdr : 715, hdr_thread_proc: CMR_EVT_HDR_START addr 0xe4e1e000 4160 3120
	Line 99612: 06-06 12:31:33.468   471  3159 E cmr_hdr : 348, hdr_transfer_frame: after addr 0xe4e1e000
	Line 99613: 06-06 12:31:33.468   471  3352 D cmr_hdr : 716, hdr_thread_proc: HDR thread proc start 
	
	Line 99615: 06-06 12:31:33.468   471  3352 D cmr_hdr : 528, hdr_arithmetic: width 4160, height 3120 temp_addr0 0xe4e1e000 temp_addr1 0xe3b8c000 temp_addr2 0xe28fa000
	Line 99616: 06-06 12:31:33.468   471  3352 I cmr_hdr : 549, hdr_arithmetic: sprd_hdr_param.callback 0x0
	Line 102466: 06-06 12:31:35.357   471  3352 I cmr_hdr : 584, hdr_arithmetic: exif_info 0xf61c1180
	Line 102468: 06-06 12:31:35.358   471  3352 I cmr_hdr : 592, hdr_arithmetic: exif_info size = 120
	Line 102469: 06-06 12:31:35.358   471  3352 E cmr_hdr : 607, hdr_arithmetic: dst_addr->addr_y 0xe4e1e000  temp_addr0 0xe4e1e000
	Line 102472: 06-06 12:31:35.358   471  3352 D cmr_hdr : 614, hdr_arithmetic: hdr done.
	//hdr完成,close
	Line 102473: 06-06 12:31:35.358   471  3352 D cmr_hdr : 732, hdr_thread_proc: HDR thread proc done 
	Line 102474: 06-06 12:31:35.358   471  3352 D cmr_hdr : 738, hdr_thread_proc: hdr_dbg_info_addr 0xf61c1180 hdr_dbg_info_size 120
	Line 103027: 06-06 12:31:35.455   471  3124 D cmr_hdr : 231, hdr_close: E

关于hdr算法的流程就介绍到这里

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值