takephoto.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. #include "takephoto.h"
  2. // 模块名称
  3. static const char MODULE_NAME[] = "HrTakePhoto";
  4. // 相机类型
  5. static unsigned int device_type = interfaceTypeGige | interfaceTypeUsb3;
  6. // 制造厂商
  7. static const char * const manufacturer = "Huaray Technology";
  8. // 触发模式
  9. static const char * const setTriggerMode = "On"; /// 默认: 打开, 可关闭: "Off"
  10. // 曝光取图
  11. #define AE_WAIT_MAX_MS 180000 // 最大曝光稳定等待的时间, 默认3分钟, 单位: ms
  12. #define AE_EXP_TIME_EPS_US 50 // 曝光时间稳定判定的误差, 防抖&区间, 单位: us
  13. #define AE_EXP_STABLE_FRAMES 4 // 相机处于自动曝光模式时, 累计曝光值稳定的帧数
  14. // 保存照片
  15. // 拍照回调
  16. typedef struct
  17. {
  18. HANDLE hCam; // 打开相机的句柄
  19. uint32_t camType; // 相机类型:U、网
  20. char manuName[MAX_LINE_CHARS]; //制造厂商名称
  21. DHImgType saveImgType; // 保存图像的类型
  22. const char *saveImgPath; // 保存图像的路径
  23. bool isExposureAuto; // 自动曝光:是/否
  24. struct timespec expTime0; // 曝光开始的时间
  25. float lastExpTime; // 上次的曝光时长(us)
  26. int expStableCnt; // 连续曝光稳定帧计数
  27. HANDLE hESig; // 任务结束的通知
  28. int rCode; // 任务结束返回值
  29. } PthotoProcCtx;
  30. // 非标准的"Huaray Technology"制造商名称, 但也是华睿的相机, 如: "Machine Vision"
  31. static const char * const huaray_manu_aliases[] = { "Machine Vision", NULL };
  32. static bool is_huaray_manu_alias(const char *name)
  33. {
  34. for(int i = 0; i < huaray_manu_aliases[i] != NULL; i++)
  35. {
  36. if(xstrcasecmp(name, huaray_manu_aliases[i]) == 0) return true;
  37. }
  38. return false;
  39. }
  40. static void OnFrameReceived(IMV_Frame *pFrame, void *pUser) // 数据帧的回调, 完成一次拍照任务
  41. {
  42. PthotoProcCtx *ctx = (PthotoProcCtx *)pUser;
  43. if(NULL == pFrame) return;
  44. sw_log_debug("[%s] +++GetOneFrame+++, Width[%u], Height[%u], FrameNum[%llu], FrameLen[%u], PixelType[0x%08X]", MODULE_NAME,
  45. pFrame->frameInfo.width, pFrame->frameInfo.height, pFrame->frameInfo.blockId, pFrame->frameInfo.size,
  46. (unsigned int)pFrame->frameInfo.pixelFormat);
  47. }
  48. static int FrameSoftTrigger(unsigned long wParam, unsigned long lParam) // 线程回调函数, 触发一次拍照执行
  49. {
  50. PthotoProcCtx *ctx = (PthotoProcCtx *)wParam;
  51. int ret = IMV_ExecuteCommandFeature(ctx->hCam, "TriggerSoftware");
  52. if(IMV_OK != ret) ret = 1; // 软触发失败后, 马上再次触发
  53. else ret = 1000; // 软触发成功后-延时一秒, 等待下次的触发
  54. return ret;
  55. }
  56. // 单次执行相机拍照, 并保存到文件, 成功返回: 0值, 失败返回:非0值
  57. // "imgType" - 获取图像类型
  58. // "saveImgPath" - 保存的文件名
  59. // "timeout" - 等待超时时间, 单位:秒: < 0 表示无超时;
  60. // 无论超时怎么设置, 首次尝试拍照一定会执行
  61. // , 但时间不确定; 拍照成功、超时或发生错误
  62. // 时会自动结束任务(相机持续无数据的时间).
  63. // "pImgMark" - 输出本次拍照图像的水印信息, 可以设置NULL
  64. int DH_TakePhoto(DHImgType imgType, const char *saveImgPath, int timeout, DHImgMark *pImgMark)
  65. {
  66. int fd; char runDir[MAX_PATH_CHARS] = { 0 }, lockFile[MAX_PATH_CHARS+32] = { 0 };
  67. int ret, index = 0; DHImgMark imgMark = { 0 }; HANDLE hCam = NULL; PthotoProcCtx ctx = { 0 };
  68. IMV_DeviceList devList = { 0 }; IMV_DeviceInfo *pDevInfo = NULL; IMV_String exposureMode = { 0 };
  69. // 1, 占用锁定, 避免同时间拍照
  70. xGetSelfRunningInfo(runDir, NULL);
  71. if(runDir[strlen(runDir)-1] == '/') sprintf(lockFile, "%s%s", runDir, "status/");
  72. else sprintf(lockFile, "%s/%s", runDir, "status/");
  73. if(!sw_dir_exists(lockFile)) sw_dir_create(lockFile);
  74. strcat(lockFile, "dh_takephoto.lock");
  75. fd = open(lockFile, O_CREAT | O_RDWR | __O_CLOEXEC, 0666);
  76. if(-1 == fd) { return -1; }
  77. if(-1 == flock(fd, LOCK_EX | LOCK_NB)) { close(fd); return -2; }
  78. // 2, 查找相机, 获取其设备信息
  79. ret = IMV_EnumDevices(&devList, device_type);
  80. if(IMV_OK != ret)
  81. {
  82. sw_log_error("[%s] 枚举相机,执行错误, errCode=%d!!", MODULE_NAME, ret);
  83. goto end_p;
  84. }
  85. if(devList.nDevNum < 1)
  86. {
  87. ret = -3;
  88. sw_log_error("[%s] 没有相机,数量=%u!!", MODULE_NAME, devList.nDevNum);
  89. goto end_p;
  90. }
  91. findp:
  92. pDevInfo = &devList.pDevInfo[index++];
  93. if(!pDevInfo)
  94. {
  95. ret = -4;
  96. sw_log_error("[%s] unexpected internal error, failed to obtain camera information!!", MODULE_NAME);
  97. goto end_p;
  98. }
  99. if(pDevInfo->nCameraType == typeU3vCamera) ctx.camType = interfaceTypeUsb3;
  100. else if(pDevInfo->nCameraType == typeGigeCamera) ctx.camType = interfaceTypeGige;
  101. else { // SDK出错, 我只枚举了U口和G口相机, 不可能出现其他类型
  102. ret = -5;
  103. sw_log_error("[%s] unexpected internal error, unknown camera type(%u)!!", MODULE_NAME, pDevInfo->nCameraType);
  104. goto end_p;
  105. }
  106. strcpy(ctx.manuName, pDevInfo->vendorName);
  107. strcpy(imgMark.camModelName, pDevInfo->modelName);
  108. strcpy(imgMark.camSerialNum, pDevInfo->serialNumber);
  109. if(xstrcasecmp(ctx.manuName, manufacturer) != 0 && !is_huaray_manu_alias(ctx.manuName))
  110. {
  111. if(index < devList.nDevNum) goto findp;
  112. ret = -6;
  113. sw_log_error("[%s] 没有找到 \"%s\" 的相机!!", MODULE_NAME, manufacturer);
  114. goto end_p;
  115. }
  116. // 3, 打开相机, 设置其触发模式
  117. unsigned int cameraIndex = (index - 1);
  118. ret = IMV_CreateHandle(&hCam, modeByIndex, (void*)&cameraIndex);
  119. if(IMV_OK == ret) ret = IMV_Open(hCam);
  120. if(IMV_OK != ret)
  121. {
  122. sw_log_error("[%s] 打开相机时发生错误, errCode=%d!!", MODULE_NAME, ret);
  123. goto end_p;
  124. }
  125. if(IMV_OK == ret && xstrcasecmp(setTriggerMode, "On") == 0) ret = IMV_SetEnumFeatureSymbol(hCam, "TriggerSelector", "FrameStart"); // 设置触发条件
  126. if(IMV_OK == ret && xstrcasecmp(setTriggerMode, "On") == 0) ret = IMV_SetEnumFeatureSymbol(hCam, "TriggerSource", "Software"); // 设置软件触发
  127. if(IMV_OK == ret) ret = IMV_SetEnumFeatureSymbol(hCam, "TriggerMode", setTriggerMode); // 设置触发模式
  128. if(IMV_OK != ret)
  129. {
  130. sw_log_error("[%s] 设置相机时发生错误, errCode=%d!!", MODULE_NAME, ret);
  131. goto end_p;
  132. }
  133. // 4, 开始拍照, 等待完成后输出
  134. // 5, 成功拍照, 输出相机的信息
  135. // 6, 任务结束, 释放相关的资源
  136. end_p:
  137. if(hCam)
  138. {
  139. IMV_StopGrabbing(hCam);
  140. IMV_Close(hCam);
  141. IMV_DestroyHandle(hCam);
  142. }
  143. flock(fd, LOCK_UN);
  144. close(fd);
  145. sw_file_delete(lockFile);
  146. return ret;
  147. }
  148. // 获取系统当前USBFS内存大小(MB), 成功返回: >=0, 失败返回: <0值
  149. int DH_GetSysUsbfsMemCurrentSize()
  150. {
  151. const char *usbfsFile = "/sys/module/usbcore/parameters/usbfs_memory_mb";
  152. char buf[MAX_LINE_CHARS]; int val = -1;
  153. FILE *fp = fopen(usbfsFile, "r");
  154. if(fp)
  155. {
  156. if(fgets(buf, sizeof(buf), fp)) sscanf(buf, "%d", &val);
  157. fclose(fp);
  158. }
  159. return val;
  160. }
  161. // 设置系统新的USBFS内存大小(MB), 成功返回: 0值, 失败返回: <0值
  162. int DH_SetSysUsbfsMemSize(int val)
  163. {
  164. const char *usbfsFile = "/sys/module/usbcore/parameters/usbfs_memory_mb";
  165. char buf[MAX_LINE_CHARS]; sprintf(buf, "%d\n", val);
  166. if(val >= 0 && sw_file_update(usbfsFile, "w", buf, strlen(buf)) == strlen(buf)) return 0;
  167. else return -1;
  168. }
  169. // 获取当前已连接的海康相机的数量, 成功返回: >=0, 失败返回: <0值
  170. int DH_GetCameraCount()
  171. {
  172. IMV_DeviceList devList = { 0 };
  173. int ret;
  174. ret = IMV_EnumDevices(&devList, device_type);
  175. if(IMV_OK != ret)
  176. {
  177. sw_log_error("[%s] 枚举相机,执行错误, errCode=%d!!", MODULE_NAME, ret);
  178. return -1;
  179. }
  180. ret = devList.nDevNum;
  181. if(ret > 0)
  182. {
  183. int index = 0, cnt = 0; IMV_DeviceInfo *pDevInfo; char name[MAX_LINE_CHARS];
  184. findp:
  185. pDevInfo = &devList.pDevInfo[index++];
  186. name[0] = '\0';
  187. if(pDevInfo && (pDevInfo->nCameraType == typeU3vCamera || pDevInfo->nCameraType == typeGigeCamera)) strcpy(name, pDevInfo->vendorName);
  188. if(xstrcasecmp(name, manufacturer) == 0 || is_huaray_manu_alias(name)) cnt++;
  189. if(index < devList.nDevNum) goto findp;
  190. else ret = cnt;
  191. }
  192. return ret;
  193. }