Преглед изворни кода

优化相机自动曝光的算法

niujiuru пре 4 дана
родитељ
комит
0ab904d087
1 измењених фајлова са 29 додато и 8 уклоњено
  1. 29 8
      hk_takephoto/takephoto.c

+ 29 - 8
hk_takephoto/takephoto.c

@@ -9,6 +9,17 @@ static unsigned int device_type =  MV_GIGE_DEVICE | MV_USB_DEVICE;
 // 制造厂商
 // 制造厂商
 static const char *manufacturer = "Hikrobot";
 static const char *manufacturer = "Hikrobot";
 
 
+// 触发模式
+static MV_CAM_TRIGGER_MODE setTriggerMode = MV_TRIGGER_MODE_ON; /// 默认: 打开
+
+// 网口相机
+#define GIGE_CAMERA_ANY_VENDOR  1 // 支持任意厂商的网口相机, 否则只支持海康机器人
+
+// 曝光取图
+#define AE_WAIT_MAX_MS     180000 // 最大曝光稳定等待的时间, 默认3分钟, 单位: ms
+#define AE_EXP_TIME_EPS_US     50 // 曝光时间稳定判定的误差, 防抖&区间, 单位: us
+#define AE_EXP_STABLE_FRAMES    4 // 相机处于自动曝光模式时, 累计曝光值稳定的帧数
+
 // 保存照片
 // 保存照片
 static int SavePhoto(HANDLE hCam, MV_FRAME_OUT *pFrame, EImgType imgType, const char *imgFile);
 static int SavePhoto(HANDLE hCam, MV_FRAME_OUT *pFrame, EImgType imgType, const char *imgFile);
 
 
@@ -20,7 +31,8 @@ typedef struct
   const char *saveImgPath;
   const char *saveImgPath;
   bool        isExposureAuto; // 自动曝光:是/否
   bool        isExposureAuto; // 自动曝光:是/否
   struct timespec expTime0;   // 曝光开始的时间
   struct timespec expTime0;   // 曝光开始的时间
-  float       lastExpTime;    // 上次的曝光时长
+  float       lastExpTime;    // 上次的曝光时长(us)
+  int         expStableCnt;   // 连续曝光稳定帧计数
   HANDLE      hESig;          // 任务结束的通知
   HANDLE      hESig;          // 任务结束的通知
   int         rCode;          // 任务结束返回值
   int         rCode;          // 任务结束返回值
 } PthotoProcCtx;
 } PthotoProcCtx;
@@ -31,7 +43,7 @@ static int PhotoProc(unsigned long wParam, unsigned long lParam) // 线程回调
 
 
   // 1, 触发一次拍照
   // 1, 触发一次拍照
 getp:
 getp:
-  ret = MV_CC_SetCommandValue(ctx->hCam, "TriggerSoftware");
+  ret = (setTriggerMode == MV_TRIGGER_MODE_ON) ? MV_CC_SetCommandValue(ctx->hCam, "TriggerSoftware") : MV_OK;
 
 
   // 2, 等待获取图像
   // 2, 等待获取图像
   if(MV_OK == ret)
   if(MV_OK == ret)
@@ -54,14 +66,17 @@ getp:
       if(MV_OK == ret) curExpTime = stExposureTime.fCurValue;
       if(MV_OK == ret) curExpTime = stExposureTime.fCurValue;
 
 
       struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now);
       struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now);
-      long elapsed = (now.tv_sec - ctx->expTime0.tv_sec)*1000 + (now.tv_nsec-ctx->expTime0.tv_nsec)/(1000*1000);
+      long elapsed = (now.tv_sec - ctx->expTime0.tv_sec)*1000 + (now.tv_nsec-ctx->expTime0.tv_nsec)/(1000*1000); // 计算时间差, 单位: ms
       sw_log_debug("[%s] +++GetOneFrame+++, 等待曝光完成, ExposureTime = %.2fus, ElapsedTime = %ldms", MODULE_NAME, curExpTime, elapsed);
       sw_log_debug("[%s] +++GetOneFrame+++, 等待曝光完成, ExposureTime = %.2fus, ElapsedTime = %ldms", MODULE_NAME, curExpTime, elapsed);
 
 
-      if((curExpTime != ctx->lastExpTime) && elapsed < 3*60*1000)
+      if(fabsf(curExpTime - ctx->lastExpTime) <= AE_EXP_TIME_EPS_US) ctx->expStableCnt++;
+      else ctx->expStableCnt = 0;
+
+      if(ctx->expStableCnt < AE_EXP_STABLE_FRAMES && elapsed < AE_WAIT_MAX_MS)
       {
       {
         MV_CC_FreeImageBuffer(ctx->hCam, &frame);
         MV_CC_FreeImageBuffer(ctx->hCam, &frame);
-        ctx->lastExpTime = curExpTime;
-        goto getp; // 继续取图像
+        ctx->lastExpTime = curExpTime; if(0 == ctx->expStableCnt) ctx->expStableCnt = 1;
+        sw_thrd_delay(1); goto getp; // 继续取图像
       }
       }
     }
     }
 
 
@@ -73,6 +88,7 @@ getp:
   switch(ret)
   switch(ret)
   {
   {
   case MV_E_NODATA: // 无数据时, 线程继续运行
   case MV_E_NODATA: // 无数据时, 线程继续运行
+    sw_log_debug("[%s] +++GetOneFrame+++, 无数据, 继续等待...", MODULE_NAME);
     ret = 1; break;
     ret = 1; break;
   default: // 成功或发生其他错误,线程结束运行
   default: // 成功或发生其他错误,线程结束运行
     ctx->rCode = ret; sw_signal_give(ctx->hESig); ret = -1; break;
     ctx->rCode = ret; sw_signal_give(ctx->hESig); ret = -1; break;
@@ -150,6 +166,11 @@ findp:
     strcpy(name, (const char *)pDevInfo->SpecialInfo.stGigEInfo.chManufacturerName);
     strcpy(name, (const char *)pDevInfo->SpecialInfo.stGigEInfo.chManufacturerName);
     strcpy(imgMark.camModelName, (const char *)pDevInfo->SpecialInfo.stGigEInfo.chModelName);
     strcpy(imgMark.camModelName, (const char *)pDevInfo->SpecialInfo.stGigEInfo.chModelName);
     strcpy(imgMark.camSerialNum, (const char *)pDevInfo->SpecialInfo.stGigEInfo.chSerialNumber);
     strcpy(imgMark.camSerialNum, (const char *)pDevInfo->SpecialInfo.stGigEInfo.chSerialNumber);
+    if(GIGE_CAMERA_ANY_VENDOR && 0 != strcmp(name, manufacturer))
+    {
+      sw_log_debug("[%s] 发现 \"%s\" 厂商的网口相机", MODULE_NAME, name);
+      setTriggerMode = MV_TRIGGER_MODE_OFF; strcpy(name, manufacturer); // 关闭触发模式, 视为海康的网口相机
+    }
   }
   }
 
 
   if(xstrcasecmp(name, manufacturer) != 0)
   if(xstrcasecmp(name, manufacturer) != 0)
@@ -179,8 +200,8 @@ findp:
     goto end_p;
     goto end_p;
   }
   }
 
 
-  ret = MV_CC_SetEnumValue(hCam, "TriggerMode", 1); // 允许触发模式
-  if(MV_OK == ret) ret = MV_CC_SetEnumValue(hCam, "TriggerSource", MV_TRIGGER_SOURCE_SOFTWARE); // 设置软件触发
+  ret = MV_CC_SetEnumValue(hCam, "TriggerMode", setTriggerMode); // 设置触发模式
+  if(MV_OK == ret && setTriggerMode == MV_TRIGGER_MODE_ON) ret = MV_CC_SetEnumValue(hCam, "TriggerSource", MV_TRIGGER_SOURCE_SOFTWARE); // 设置软件触发
   if(MV_OK != ret)
   if(MV_OK != ret)
   {
   {
     sw_log_error("[%s] 设置相机时发生错误, errCode=0x%x!!", MODULE_NAME, ret);
     sw_log_error("[%s] 设置相机时发生错误, errCode=0x%x!!", MODULE_NAME, ret);