camera-qsc-crosstalk校准数据XTALK回写

问题背景

手机越做越紧凑,需要模组和芯片尺寸越做越小,在尺寸一定的基础上,高像素和大像素,对于手机摄像头来说,一直是一对矛盾的存在。

  • 高像素:带来高分辨率画质
  • 大像素:带来暗态下高感光度和低噪声的画质。

以上两者都非常重要,为了两者兼得4cell1感光芯片应运而生,也称之为"Tetra cell","Quad bayer","four cell"等,该芯片基于经典的Bayer阵列,将每一种颜色以4个pixel排列组合,成功可以让一款摄像头在高像素和大像素之间自由切换。现如今4cell1感光芯片兼具了常态的高像素和暗态的高感光低噪声的表现,已经成为目前手机摄像头的主力军。
同时也带来问题,如下图,左边实拍图比右图多了许多齿狀格子等伪色。主要原因是芯片本身硬件感光和Remosaic造成的,为了优化这种现象,'CrossTalk校准'登场,去除由于信号差别造成的格子,锯齿状等色块干扰。
什么是crosstalk?

在感光芯片中,crosstalk(串扰)现象是指相邻像素之间的信号互相影响导致的一种干扰。当一个像素受到光照并转换成电信号时,其附近的像素可能会因为光电效应或其他物理机制而感应到部分光强,这不属于它们自身的光学响应,而是由于彼此之间产生的非线性相互作用。这种串扰可能导致图像质量下降,尤其是在高密度像素阵列如CMOS传感器中更为明显,因为它增加了错误信息的传播机会。为了减少crosstalk,设计者通常会采用各种技术,比如优化像素布局、增加隔离层等措施。
解决办法:crosstalk(串扰)校准。

Crosstalk校准工具,通常将全图分成多个ROI方块,计算各像素通道的能量并确定其补偿数据,芯片再使用这些校准数据让原本不均匀状态的能量分布变得更为平衡。

问题分析

一般是模组厂家在模组出厂的时候,会使用上述的crosstalk工具将校准补偿数据烧录在eeprom里面。在调试模组成像效果的时候,需要从eeprom中读取crosstalk数据 (QSC),然后写入感光芯片中的特定寄存器 中进行校准。
注意:

4cell1芯片还会涉及到PD补偿,坏点补偿等其他的内容,而整个流程,通常需要先补偿PD点,再做Crosstalk校准,然后再Remosaic成为正常的Bayer去应用。针对软件的Remosaic,这其中所有的步骤,通常都封装在芯片厂商提供的一个算法库里面,手机端调用该算法库,去实现4cell1芯片,兼具常态时候的高分辨率和暗态时候的高感光度,然后你就可以美美地拍照了。


复制代码
                        版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/qq_41897352/article/details/117336884

当向sensor寄存器(0x53c0 ~ 0x54df)写入Crosstalk补偿数据时候,sensor要么在sleep mode(0x0100 = 0)要么crosstalk disable(寄存器0x5000[6] = 0)

关键代码

  1. 在文件camxeepromdriver.xsd添加Crosstalk对应的QSC数据属性可以模仿SPC的填写方法,如在eeprom的xml文件模仿SPC的数据格式填写如下。
c 复制代码
    <QSC>
       <!--Specifies the availability of the specified data and type of endianness
            element for operation  -->
       <QSCData description="Memory Type">
         <!--Specifies whether a particular OTP data available or not -->
         <isAvailable>true</isAvailable>
         <!--Type of the Endianness
             Valid values are: BIG, LITTLE -->
         <endianness>BIG</endianness>
       </QSCData>
       <QSCSettings>
           <!--Specifies memory offset value and mask information -->
           <dataOffset>
             <!--Offset value in the OTP memory -->
             <offset>0x1798</offset>
             <!--mask value which will be applied to obtain required data bits -->
             <mask>0xFF</mask>
           </dataOffset>
           <!--register address value to write QSC data -->
           <QSCAddress>0x53c0</QSCAddress>
           <!--size of the QSC data settings -->
           <settingsSize>288</settingsSize>
       </QSCSettings>
       <!--Register address / data size in bytes -->
       <addressType range="[1,4]">2</addressType>
       <!--Register address / data size in bytes -->
       <dataType range="[1,4]">1</dataType>
       <!--Delay in milli seconds. Delay is 0 if not explicitly provided -->
       <delay>0</delay>
     </QSC>
  1. 在仓库camx中,先将EEPROM中的crosstalk校准数据读取出来,主要实现在src/core/camxeepromdata.cpp中的FormatQSCData();FormatQSCData函数在构造函数EEPROMData::EEPROMData()中调用。FormatQSCData函数是模仿者FormatSPCData函数写的。如下:
cpp 复制代码
 
  /// EEPROMData::FormatQSCData
  
  VOID EEPROMData::FormatQSCData()
  {
      QSCInfo*             pQSCFormatInfo = &(m_pEEPROMDriverData->formatInfo.QSC);
      QSCCalibrationData*  pQSCData       = &(m_pSensorInfoTable->moduleCaps.OTPData.QSCCalibration);
      MemoryInfo           dataOffset     = { 0 };
  
      pQSCData->isAvailable = pQSCFormatInfo->QSCData.isAvailable;
  
      if (TRUE == pQSCFormatInfo->QSCData.isAvailable)
      {
          UINT32  totalRegCount = 0;
          for (UINT16 i = 0; i < pQSCFormatInfo->QSCSettingsCount; ++i)
          {
              totalRegCount += pQSCFormatInfo->QSCSettings[i].settingsSize;
          }
  
          pQSCData->settings.regSettingCount = totalRegCount;
          pQSCData->settings.regSetting      = static_cast<RegisterSetting*>(
                                                           CAMX_CALLOC(sizeof(RegisterSetting) * totalRegCount));
  
          if (NULL != pQSCData->settings.regSetting)
          {
              RegisterData* pRegData = static_cast<RegisterData*>(CAMX_CALLOC(sizeof(RegisterData) * totalRegCount));
  
              if (NULL != pRegData)
              {
                  RegisterSetting* pCurRegOffset = pQSCData->settings.regSetting;
  
                  for (UINT16 i = 0; i < pQSCFormatInfo->QSCSettingsCount; ++i)
                  {
                      /// copy to local structures so that original offset values wont be modified because of increments
					  
                      Utils::Memcpy(&dataOffset, &pQSCFormatInfo->QSCSettings[i].dataOffset, sizeof(MemoryInfo));
  
                      UINT32 dataSize = MaskLengthInBytes(dataOffset.mask);
                      if (NULL != pQSCData->settings.regSetting)
                      {
                          for (UINT16 index = 0; index < pQSCFormatInfo->QSCSettings[i].settingsSize; index++, pCurRegOffset++)
                          {
                              (pCurRegOffset)->registerAddr    = pQSCFormatInfo->QSCSettings[i].QSCAddress + index;
                              (pCurRegOffset)->registerData    = pRegData++;
                              (pCurRegOffset)->registerData[0] =
                                  FormatDataTypeInteger((&dataOffset), pQSCFormatInfo->QSCData.endianness);
                              dataOffset.offset                += static_cast<UINT16>(dataSize);
                              (pCurRegOffset)->regAddrType     = pQSCFormatInfo->addressType;
                              (pCurRegOffset)->regDataType     = pQSCFormatInfo->dataType;
                              (pCurRegOffset)->operation       = OperationType::WRITE;
  
                              if (0 != pQSCFormatInfo->delay)
                              {
                                  (pCurRegOffset)->delayUsExists = TRUE;
                                  (pCurRegOffset)->delayUsID     = 0;
                                  (pCurRegOffset)->delayUs       = pQSCFormatInfo->delay;
                              }
                          }
                      }
                  }
  
              }
              else
              {
                  CAMX_LOG_ERROR(CamxLogGroupSensor, "Calloc failed");
              }
          }
          else
          {
              CAMX_LOG_ERROR(CamxLogGroupSensor, "Calloc failed");
          }
      }
  }

同理,也需要模仿DumpWBData函数来实现DumpQSCData

其他地方,可以参考模仿AWB,SPC的写法在SensorNode::LoadSensorConfigCmds()函数中添加,即可打通流程。

具体的说,crosstalk补偿写入是发生在配流阶段,详细可以参考
CamX-Chi 高通Android Camera HAL框架学习2-configure_streams

验证测试

验证高像素模式下修改前后的预览成像和拍照成像在手机上肉眼无法观察到明显差异。

那么如何保证已经正确地将EEPROM中的crosstalk校准数据写入了感光芯片对应的寄存器中呢?

  1. 首先验证确保从EEPROM中crosstalk校准数据读取正确,这可以通过checksum和dump数据来分别验证。
cpp 复制代码
  
  /// EEPROMDataDump::DumpQSCData
  
  VOID EEPROMDataDump::DumpQSCData()
  {
      FILE*              pQSCDumpFile            = NULL;
      CHAR               fileName[FILENAME_MAX] = { 0 };
      QSCCalibrationData* pQSCData                = &(m_pSensorInfoTable->moduleCaps.OTPData.QSCCalibration);
  
      if (TRUE == pQSCData->isAvailable)
      {
          OsUtils::SNPrintF(fileName, sizeof(fileName), "%s/%s_%s_%s", ConfigFileDirectory,
                            m_pEEPROMDriverData->slaveInfo.EEPROMName, "qsc", "OTP.txt");
          pQSCDumpFile = OsUtils::FOpen(fileName, "w");
      }
  
      if (pQSCDumpFile != NULL)
      {

        for (UINT16 index = 0; index < pQSCData->settings.regSettingCount; index++)
        {
            OsUtils::FPrintF(pQSCDumpFile,
                             "addr = 0x%x : value  = 0x%x\n",
                             pQSCData->settings.regSetting[i].registerAddr,
                             pQSCData->settings.regSetting[i].registerData[0]);
        }

  
        OsUtils::FClose(pQSCDumpFile);
      }
  }
  1. 如何确保将crosstalk校准数据准确地写入了sensor对应的寄存器0x53c0 ~ 0x54df中,最简单的思路就是去读取写入后寄存器的值。遗憾的是,这些寄存器都是只写寄存器。
    于是,想到找效果同事去验证下Xtalk补偿回写前后高像素模式拍照图片对比。使用效果专用软件发现,补偿后的图片在放大后的效果字体更加清晰,说明补偿有不错的收益。

代码评审问题

  1. Xtalk补偿回写是在什么时候写入的?是在使用高像素模式的时候进行回写补偿吗?
  2. sensor寄存器0x5380(值0x0f)默认打开 Xtalk补偿回写,有没有副作用?

参考文章

https://blog.csdn.net/qq_41897352/article/details/117336884

相关推荐
CYRUS_STUDIO9 小时前
利用 Linux 信号机制(SIGTRAP)实现 Android 下的反调试
android·安全·逆向
CYRUS_STUDIO9 小时前
Android 反调试攻防实战:多重检测手段解析与内核级绕过方案
android·操作系统·逆向
黄林晴13 小时前
如何判断手机是否是纯血鸿蒙系统
android
火柴就是我13 小时前
flutter 之真手势冲突处理
android·flutter
法的空间13 小时前
Flutter JsonToDart 支持 JsonSchema
android·flutter·ios
循环不息优化不止14 小时前
深入解析安卓 Handle 机制
android
恋猫de小郭14 小时前
Android 将强制应用使用主题图标,你怎么看?
android·前端·flutter
jctech14 小时前
这才是2025年的插件化!ComboLite 2.0:为Compose开发者带来极致“爽”感
android·开源
用户20187928316714 小时前
为何Handler的postDelayed不适合精准定时任务?
android
叽哥14 小时前
Kotlin学习第 8 课:Kotlin 进阶特性:简化代码与提升效率
android·java·kotlin