扫码加入

  • 正文
  • 相关推荐
申请入驻 产业图谱

STM32N6的开发日记(6):用ISP中间件点亮IMX335相机的专业画质

9小时前
301
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

STM32N6作为意法半导体推出的首款集成自研神经处理单元的STM32产品以“MCU+NPU”的异构架构重新定义了边缘AI算力边界,是意法半导体的MCU最前沿技术栈,不过由于其高难度技术应用以及需要的极其深厚的STM32使用经验以及神经网络基础概念,因此上手难度非常的高。

自从STM32N6发布以来,博主有幸获得一块STM32N6570-DK开发板,闲暇之余陆陆续续折腾如何开发。因此将会陆陆续续发表一些使用STM32N6的使用笔记,以供将来的使用者参考。

回顾学习历程,踩了很多很多的坑,在后续使用STM32N6的文章中也会向大家陆续介绍这些点。

上一期我们介绍了如何在 STM32N6 上使用 DCMIPP 读取 IMX335 摄像头的数据,并通过 LCD 显示屏实时显示摄像头图像。这已经是一个非常重要的基础步骤,让我们成功地把IMX335的原始Bayer数据流送进了MCU并显示了出来。

但是,正如大家所知,IMX335是一款专业级5MP CMOS图像传感器,它直接输出的RAW Bayer数据如果不经过适当的图像信号处理(ISP),画面通常会呈现出严重的偏色、对比度不足、噪声明显、动态范围有限等问题。直接显示的画面往往是“绿绿的”或“偏粉”,这正是典型的未经过demosaic、白平衡、gamma校正等处理的 RAW 图表现。

要让IMX335真正发挥出专业水准的画质,我们必须对图像进行完整的ISP(Image Signal Processing)处理链路,包括但不限于:黑电平校正(Black Level Compensation)、坏点校正(Defective Pixel Correction)、去马赛克(Demosaicing)——从Bayer RGGB格局还原RGB、自动白平衡(AWB)、色彩校正(Color Correction Matrix, CCM)、伽马校正(Gamma Correction)、色彩增强/饱和度调整、降噪(Noise Reduction)、锐化(Sharpening)、镜头畸变校正(Lens Shading Correction / 周边光量补偿)、自动曝光(AE)与自动增益(AGC)控制

幸运的是,STM32N6系列集成了功能强大的硬件ISP模块,并且ST官方提供了配套的STM32 ISP中间件(STM32-MW-ISP),可以帮助我们快速实现上述大部分处理功能,而无需从零编写复杂的ISP算法。

本期重点:让IMX335的画面“正常且好看”

1、STM32_ISP_IQTune

ST提供了专业的ISP软件:STM32_ISP_IQTune来帮我我们设置CMOS相机参数:

我们可以从ST官网找到STM32_IQTune的Windows版本和用于STM32N6的程序。

利用STM32CubeProgrammer将ISP程序烧入到STM32N6570-DK开发板中,将开发板的USB线连接到PC上。

接着我们打开STM32_ISP_IQTune软件,可以连接到STM32N6570-DK开发板上:

进入配置界面,我们可以读取到STM32N6570-DK官方推荐的ISP参数,这里我们可以点击左边的Start tuning from scratch重置参数(非专业不推荐)。

设置完各个参数之后,调节到我们满意的图像界面,接着我们就可以导出我们的参数配置:

导出成.h版本后,就可以获得参数.h文件

2、代码修改

int32_tIMX335_Init(I2C_HandleTypeDef *hi2c, uint16_t DevAddr);int32_tIMX335_ReadID(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, uint32_t *Id);int32_tIMX335_SetGain(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, int32_t gain);int32_tIMX335_SetExposure(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, int32_t exposure);int32_tIMX335_SetFrequency(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, int32_t frequency);int32_tIMX335_SetFramerate(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, int32_t framerate);int32_tIMX335_MirrorFlipConfig(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, uint32_t Config);int32_tIMX335_GetSensorInfo(IMX335_SensorInfo_t *Info);int32_tIMX335_SetTestPattern(I2C_HandleTypeDef *hi2c, uint16_t DevAddr, int32_t mode);

上一期我们实现了IMX335的功能函数,包括传感器的基本初始化、ID 读取、增益/曝光控制、时钟频率与帧率设置、镜像翻转、测试图案输出,以及获取传感器基本信息等操作。

接着我们使用ST提供的ISP库,将库文件(.c/.h/.a)添加到我们的工程中:

ISP的API对接

#define IMX335_I2C_ADDRESS    (0x1A << 1)staticint32_t isp_gain     = 0;    // mdb 单位 (×1000)staticint32_t isp_exposure = 0;    // 微秒 usint NbMainFrames = 0;ISP_HandleTypeDef hcamera_isp;/** * @brief ISP Middleware helper: 获取传感器信息 */ISP_StatusTypeDef GetSensorInfoHelper(uint32_t Instance, ISP_SensorInfoTypeDef *SensorInfo){    (void)Instance;  // 未使用参数    IMX335_SensorInfo_t info = {0};    if (IMX335_GetSensorInfo(&info) != IMX335_OK)    {        return ISP_ERR_SENSORINFO;      }    strncpy(SensorInfo->name, info.name, ISP_SENSOR_INFO_MAX_LENGTH - 1);    SensorInfo->name[ISP_SENSOR_INFO_MAX_LENGTH - 1] = '';    SensorInfo->bayer_pattern  = info.bayer_pattern;      SensorInfo->color_depth    = info.color_depth;     // 10    SensorInfo->width          = info.width;           // 2592    SensorInfo->height         = info.height;          // 1944    SensorInfo->gain_min       = info.gain_min;        // mdb 单位    SensorInfo->gain_max       = info.gain_max;    SensorInfo->exposure_min   = info.exposure_min;    // us    SensorInfo->exposure_max   = info.exposure_max;    return ISP_OK;}/** * @brief ISP Middleware helper: 设置传感器模拟增益 */ISP_StatusTypeDef SetSensorGainHelper(uint32_t Instance, int32_t Gain){    (void)Instance;    isp_gain = Gain;    if (IMX335_SetGain(&hi2c1, IMX335_I2C_ADDRESS, Gain) != IMX335_OK)    {        return ISP_ERR_SENSORGAIN;    }    return ISP_OK;}/** * @brief ISP Middleware helper: 获取当前增益值(软件缓存) */ISP_StatusTypeDef GetSensorGainHelper(uint32_t Instance, int32_t *Gain){    (void)Instance;    *Gain = isp_gain;    return ISP_OK;}/** * @brief ISP Middleware helper: 设置传感器曝光时间(单位:微秒) */ISP_StatusTypeDef SetSensorExposureHelper(uint32_t Instance, int32_t Exposure){    (void)Instance;
    isp_exposure = Exposure;    if (IMX335_SetExposure(&hi2c1, IMX335_I2C_ADDRESS, Exposure) != IMX335_OK)    {        return ISP_ERR_SENSOREXPOSURE;    }    return ISP_OK;}/* * @brief ISP Middleware helper: 获取当前曝光值(软件缓存) */ISP_StatusTypeDef GetSensorExposureHelper(uint32_t Instance, int32_t *Exposure){    (void)Instance;    *Exposure = isp_exposure;    return ISP_OK;}

接着将ISP的API和我们的IMX335的API对接,包括获取各类信息,设置各类参数的函数。

voidHAL_DCMIPP_PIPE_VsyncEventCallback(DCMIPP_HandleTypeDef *hdcmipp, uint32_t Pipe){  UNUSED(hdcmipp);/* Update the frame counter and call the ISP statistics handler */switch (Pipe)  {case DCMIPP_PIPE0 :      ISP_IncDumpFrameId(&hcamera_isp);break;case DCMIPP_PIPE1 :      ISP_IncMainFrameId(&hcamera_isp);      ISP_GatherStatistics(&hcamera_isp);      NbMainFrames++;break;case DCMIPP_PIPE2 :      ISP_IncAncillaryFrameId(&hcamera_isp);
break;  }}

添加HAL_DCMIPP_PIPE_VsyncEventCallback函数用来为DCMIPP实现VSYNC中断,在每帧画面接收完成后就会调用这个中断,更新帧计数、采集 ISP 统计数据、为自动曝光/白平衡等算法提供实时输入。

NbMainFrames用于帧计数。

voidDCMIPP_ISP_Init(void){    ISP_AppliHelpersTypeDef appliHelpers = {0};    ISP_StatAreaTypeDef statArea = {0};    // 填充 ISP middleware 需要的回调函数    appliHelpers.GetSensorInfo    = GetSensorInfoHelper;    appliHelpers.SetSensorGain    = SetSensorGainHelper;    appliHelpers.GetSensorGain    = GetSensorGainHelper;    appliHelpers.SetSensorExposure = SetSensorExposureHelper;    appliHelpers.GetSensorExposure = GetSensorExposureHelper;    // 统计区域(全画幅)    statArea.X0    = 0;    statArea.Y0    = 0;    statArea.XSize = 2592;    statArea.YSize = 1944;    // 初始化 ISP 中间件    if (ISP_Init(&hcamera_isp, &hdcmipp, 0, &appliHelpers, &statArea, ISP_IQParamCacheInit[0]) != ISP_OK)    {        Error_Handler();    }
    // 启动 DCMIPP CSI PIPE    if (HAL_DCMIPP_CSI_PIPE_Start(&hdcmipp, DCMIPP_PIPE1, DCMIPP_VIRTUAL_CHANNEL0,                                  (uint8_t *)lcd_bg_buffer, DCMIPP_MODE_CONTINUOUS) != HAL_OK)    {        Error_Handler();    }    // 启动 ISP 处理    if (ISP_Start(&hcamera_isp) != ISP_OK)    {        Error_Handler();    }    while (NbMainFrames < 60)    {        if (ISP_BackgroundProcess(&hcamera_isp) != ISP_OK)        {            HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_10);        }    }}

 

接着开始STM32的ISP处理,统计60帧的数据送入ISP处理算法。

3、小结

至此,我们终于完成了从“原始 Bayer 数据”到“可观看彩色图像”的完整链路:IMX335 传感器 → DCMIPP 数据接收 → ISP 中间件处理 → LCD 显示输出。

通过对接 IMX335 的控制接口与 ST 提供的 ISP 中间件,我们实现了传感器信息获取、增益/曝光的动态设置,并在每一帧到来时通过 VSYNC 中断可靠地更新帧计数和采集统计数据。这一系列工作,让画面从最初的“绿油油”“偏粉”“噪点严重”逐步走向了“能看”“基本正常”的阶段。

当然,目前的画面效果仍然主要依赖于 ST 官方示例中的默认IQ参数,距离“专业级”“讨喜”“适合实际应用”的水准还有一定距离。真正的画质飞跃,通常需要在 STM32_ISP_IQTune 软件中进行针对性的调参:调整黑电平、去马赛克算法、白平衡参考点、gamma 曲线、降噪强度、锐化程度等,并反复对比实拍效果,才能让 IMX335 在不同光照、不同场景下都呈现出令人满意的表现。

但无论如何,走到这一步已经是一个非常重要的里程碑——我们不再只是“把数据搬运到屏幕上”,而是真正拥有了对图像信号处理链路的控制能力。这为后续更高级的功能奠定了基础。

相关推荐