takephoto.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. #include "takephoto.h"
  2. // 模块名称
  3. static const char MODULE_NAME[] = "TakePhoto";
  4. // 保存照片
  5. static int SavePhoto(HANDLE hCam, MV_FRAME_OUT *pFrame, EImgType imgType, const char *imgFile);
  6. // 拍照回调
  7. typedef struct
  8. {
  9. HANDLE hCam;
  10. EImgType saveImgType;
  11. const char *saveImgPath;
  12. bool isExposureAuto; // 自动曝光:是/否
  13. struct timespec expTime0; // 曝光开始的时间
  14. float lastExpTime; // 上次的曝光时长
  15. HANDLE hESig; // 任务结束的通知
  16. int rCode; // 任务结束返回值
  17. } PthotoProcCtx;
  18. static int PhotoProc(unsigned long wParam, unsigned long lParam) // 线程回调函数,执行一次拍照任务
  19. {
  20. PthotoProcCtx *ctx = (PthotoProcCtx *)wParam; MV_FRAME_OUT frame = { 0 }; int ret;
  21. // 1, 触发一次拍照
  22. getp:
  23. ret = MV_CC_SetCommandValue(ctx->hCam, "TriggerSoftware");
  24. // 2, 等待获取图像
  25. if(MV_OK == ret)
  26. {
  27. memset(&frame, 0, sizeof(MV_FRAME_OUT));
  28. ret = MV_CC_GetImageBuffer(ctx->hCam, &frame, 1000);
  29. }
  30. // 3, 导出图像文件
  31. if(MV_OK == ret)
  32. {
  33. sw_log_debug("[%s] +++GetOneFrame+++, Width[%d], Height[%d], FrameNum[%d], FrameLen[%d], PixelType[0x%08X]", MODULE_NAME, \
  34. frame.stFrameInfo.nWidth, frame.stFrameInfo.nHeight, frame.stFrameInfo.nFrameNum, frame.stFrameInfo.nFrameLen, \
  35. (unsigned int)frame.stFrameInfo.enPixelType);
  36. if(ctx->isExposureAuto)
  37. { // 自动曝光模式下, 等待曝光稳定或超时
  38. MVCC_FLOATVALUE stExposureTime = { 0 }; float curExpTime = 0;
  39. ret = MV_CC_GetExposureTime(ctx->hCam, &stExposureTime);
  40. if(MV_OK == ret) curExpTime = stExposureTime.fCurValue;
  41. struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now);
  42. long elapsed = (now.tv_sec - ctx->expTime0.tv_sec)*1000 + (now.tv_nsec-ctx->expTime0.tv_nsec)/(1000*1000);
  43. sw_log_debug("[%s] +++GetOneFrame+++, 等待曝光完成, ExposureTime = %.2fus, ElapsedTime = %ldms", MODULE_NAME, curExpTime, elapsed);
  44. if((curExpTime != ctx->lastExpTime) && elapsed < 3*60*1000)
  45. {
  46. MV_CC_FreeImageBuffer(ctx->hCam, &frame);
  47. ctx->lastExpTime = curExpTime;
  48. goto getp; // 继续取图像
  49. }
  50. }
  51. ret = SavePhoto(ctx->hCam, &frame, ctx->saveImgType, ctx->saveImgPath);
  52. MV_CC_FreeImageBuffer(ctx->hCam, &frame);
  53. }
  54. // 4, 控制线程退出
  55. switch(ret)
  56. {
  57. case MV_E_NODATA: // 无数据时, 线程继续运行
  58. ret = 1; break;
  59. default: // 成功或发生其他错误,线程结束运行
  60. ctx->rCode = ret; sw_signal_give(ctx->hESig); ret = -1; break;
  61. }
  62. return ret;
  63. }
  64. // 单次执行相机拍照, 并保存到文件, 成功返回: 0值, 失败返回:非0值
  65. // "imgType" - 获取图像类型
  66. // "saveImgPath" - 保存的文件名
  67. // "timeout" - 等待超时时间, 单位:秒: < 0 表示无超时;
  68. // 无论超时怎么设置, 首次尝试拍照一定会执行
  69. // , 但时间不确定; 拍照成功、超时或发生错误
  70. // 时会自动结束任务(相机持续无数据的时间).
  71. // "pImgMark" - 输出本次拍照图像的水印信息, 可以设置NULL
  72. int TakePhoto(EImgType imgType, const char *saveImgPath, int timeout, SImgMark *pImgMark)
  73. {
  74. int fd; char runDir[MAX_PATH_CHARS] = { 0 }, lockFile[MAX_PATH_CHARS+32] = { 0 };
  75. int ret; SImgMark imgMark = { 0 }; HANDLE hCam = NULL; PthotoProcCtx ctx = { 0 };
  76. MV_CC_DEVICE_INFO_LIST devList = { 0 }; MV_CC_DEVICE_INFO *pDevInfo = NULL; MVCC_ENUMVALUE exposureMode = { 0 };
  77. // 1, 占用锁定, 避免同时间拍照
  78. xGetSelfRunningInfo(runDir, NULL);
  79. if(runDir[strlen(runDir)-1] == '/') sprintf(lockFile, "%s%s", runDir, "status/");
  80. else sprintf(lockFile, "%s/%s", runDir, "status/");
  81. if(!sw_dir_exists(lockFile)) sw_dir_create(lockFile);
  82. strcat(lockFile, "takephoto.lock");
  83. fd = open(lockFile, O_CREAT | O_RDWR | __O_CLOEXEC, 0666);
  84. if(-1 == fd) { return -1; }
  85. if(-1 == flock(fd, LOCK_EX | LOCK_NB)) { close(fd); return -2; }
  86. // 2, 查找相机, 获取其设备信息
  87. ret = MV_CC_Initialize();
  88. if(MV_OK != ret)
  89. {
  90. sw_log_error("[%s] 相机SDK初始化失败, errCode=0x%x!!", MODULE_NAME, ret);
  91. goto end_p;
  92. }
  93. ret = MV_CC_EnumDevices(MV_USB_DEVICE, &devList);
  94. if(MV_OK != ret)
  95. {
  96. sw_log_error("[%s] USB口枚举相机失败, errCode=0x%x!!", MODULE_NAME, ret);
  97. goto end_p;
  98. }
  99. if(devList.nDeviceNum != 1)
  100. {
  101. ret = -3;
  102. sw_log_error("[%s] USB口相机数量错误, deviceNum=%u!!", MODULE_NAME, devList.nDeviceNum);
  103. goto end_p;
  104. }
  105. pDevInfo = devList.pDeviceInfo[0];
  106. if(!pDevInfo)
  107. {
  108. ret = -4;
  109. sw_log_error("[%s] unexpected internal error, unable to obtain detailed information about the industrial camera!!", MODULE_NAME);
  110. goto end_p;
  111. }
  112. strcpy(imgMark.camModelName, (const char *)pDevInfo->SpecialInfo.stUsb3VInfo.chModelName);
  113. strcpy(imgMark.camSerialNum, (const char *)pDevInfo->SpecialInfo.stUsb3VInfo.chSerialNumber);
  114. // 3, 打开相机, 设置其触发模式
  115. ret = MV_CC_CreateHandleWithoutLog(&hCam, pDevInfo);
  116. if(MV_OK == ret) ret = MV_CC_OpenDevice(hCam, MV_ACCESS_Exclusive, 0);
  117. if(MV_OK == ret) ret = MV_USB_SetTransferWays(hCam, 1); // 设置传输通道个数(RTU硬件资源有限, 需要降低通道数)
  118. if(MV_OK != ret)
  119. {
  120. sw_log_error("[%s] USB口相机打开失败, errCode=0x%x!!", MODULE_NAME, ret);
  121. goto end_p;
  122. }
  123. ret = MV_CC_SetEnumValue(hCam, "TriggerMode", 1); // 允许触发模式
  124. if(MV_OK == ret) ret = MV_CC_SetEnumValue(hCam, "TriggerSource", MV_TRIGGER_SOURCE_SOFTWARE); // 设置软件触发
  125. if(MV_OK != ret)
  126. {
  127. sw_log_error("[%s] USB口相机设置失败, errCode=0x%x!!", MODULE_NAME, ret);
  128. goto end_p;
  129. }
  130. // 4, 开始拍照, 等待完成后输出
  131. ret = MV_CC_GetExposureAutoMode(hCam, &exposureMode);
  132. if(MV_OK != ret)
  133. {
  134. sw_log_error("[%s] 获取-曝光模式失败, errCode=0x%x!!", MODULE_NAME, ret);
  135. goto end_p;
  136. }
  137. if(exposureMode.nCurValue != MV_EXPOSURE_AUTO_MODE_OFF) { ctx.isExposureAuto = true; clock_gettime(CLOCK_MONOTONIC, &ctx.expTime0); }
  138. else ctx.isExposureAuto = false;
  139. ret = MV_CC_StartGrabbing(hCam);
  140. if(MV_OK != ret)
  141. {
  142. sw_log_error("[%s] USB口相机取流失败, errCode=0x%x!!", MODULE_NAME, ret);
  143. goto end_p;
  144. }
  145. ctx.hCam = hCam;
  146. ctx.saveImgType = imgType;
  147. ctx.saveImgPath = saveImgPath;
  148. ctx.hESig = sw_signal_create();
  149. if(!ctx.hESig)
  150. {
  151. ret = -5;
  152. sw_log_error("[%s] SIG信号量创建失败!!", MODULE_NAME);
  153. goto end_p;
  154. }
  155. ctx.rCode = MV_OK;
  156. HANDLE hThrd = sw_thrd_create("PhotoProc", THREAD_DEFAULT_PRIORITY, THREAD_DEFAULT_STACK_SIZE, PhotoProc, (unsigned long)&ctx, 0);
  157. if(!hThrd)
  158. {
  159. ret = -6;
  160. sw_signal_destroy(ctx.hESig);
  161. sw_log_error("[%s] 拍照线程-创建失败!!", MODULE_NAME);
  162. goto end_p;
  163. }
  164. sw_thrd_resume(hThrd);
  165. ret = sw_signal_wait(ctx.hESig, timeout*1000); // 阻塞等待拍照任务结束或超时
  166. if(0 == ret) ret = ctx.rCode;
  167. else
  168. {
  169. ret = -7;
  170. sw_log_error("[%s] 拍照过程-等待超时!!", MODULE_NAME);
  171. }
  172. sw_thrd_destroy(hThrd, WAITTHRD_SAFEEXIT_TIMEOUT);
  173. sw_signal_destroy(ctx.hESig);
  174. // 5, 成功拍照, 输出相机的信息
  175. if(MV_OK == ret)
  176. {
  177. MVCC_FLOATVALUE fv = { 0 };
  178. MV_CC_GetFloatValue(hCam, "ExposureTime", &fv);
  179. imgMark.imgExposureTime = fv.fCurValue;
  180. if(pImgMark) memcpy(pImgMark, &imgMark, sizeof(SImgMark));
  181. }
  182. // 6, 任务结束, 释放相关的资源
  183. end_p:
  184. if(hCam)
  185. {
  186. MV_CC_StopGrabbing(hCam);
  187. MV_CC_CloseDevice(hCam);
  188. MV_CC_DestroyHandle(hCam);
  189. }
  190. MV_CC_Finalize();
  191. flock(fd, LOCK_UN);
  192. close(fd);
  193. sw_file_delete(lockFile);
  194. return ret;
  195. }
  196. // 保存照片
  197. static int SavePhoto(HANDLE hCam, MV_FRAME_OUT *pFrame, EImgType imgType, const char *imgFile)
  198. {
  199. MV_SAVE_IMAGE_PARAM_EX3 saveParams = { 0 }; MVCC_INTVALUE_EX iv;
  200. char *filename1 = (char *)imgFile, *filename2 = NULL; int ret; char ext[5];
  201. if(!hCam || !pFrame || !filename1 || strlen(filename1) <= 0) return -8;
  202. switch(imgType)
  203. {
  204. case IMG_TYPE_BMP: // 保存为.bmp格式
  205. imgType = MV_Image_Bmp;
  206. strcpy(ext, ".bmp");
  207. break;
  208. case IMG_TYPE_JPG: // 保存为.jpg格式
  209. imgType = MV_Image_Jpeg;
  210. strcpy(ext, ".jpg");
  211. break;
  212. default: return -9;
  213. }
  214. ret = MV_CC_GetIntValueEx(hCam, "PayloadSize", &iv);
  215. if(MV_OK != ret) return ret;
  216. saveParams.pData = pFrame->pBufAddr;
  217. saveParams.nDataLen = pFrame->stFrameInfo.nFrameLen;
  218. saveParams.enPixelType = pFrame->stFrameInfo.enPixelType;
  219. saveParams.nHeight = pFrame->stFrameInfo.nHeight;
  220. saveParams.nWidth = pFrame->stFrameInfo.nWidth;
  221. saveParams.enImageType = imgType;
  222. saveParams.nJpgQuality = 90; // JPG编码质量(50-99], 其它格式无效, 影响JPG图像文件大小
  223. saveParams.iMethodValue = 1;
  224. if(imgType == IMG_TYPE_JPG) saveParams.nBufferSize = (unsigned int)(iv.nCurValue * 1);
  225. else saveParams.nBufferSize = (unsigned int)(iv.nCurValue * 4);
  226. saveParams.pImageBuffer = (unsigned char *)sw_heap_malloc(saveParams.nBufferSize);
  227. if(!saveParams.pImageBuffer) return -10;
  228. ret = MV_CC_SaveImageEx3(hCam, &saveParams);
  229. if(MV_OK != ret) goto end_p;
  230. if(!xstrcasestr(filename1, "right", ext))
  231. {
  232. filename2 = (char *)sw_heap_malloc(strlen(filename1) +sizeof(ext));
  233. if(filename2) sprintf(filename2, "%s%s", filename1, ext);
  234. else { ret = -11; goto end_p; }
  235. ret = sw_file_update(filename2, "wb", (const char *)saveParams.pImageBuffer, saveParams.nImageLen);
  236. sw_heap_free(filename2);
  237. }
  238. else
  239. {
  240. ret = sw_file_update(filename1, "wb", (const char *)saveParams.pImageBuffer, saveParams.nImageLen);
  241. }
  242. if(ret == saveParams.nImageLen) ret = MV_OK;
  243. else ret = -12;
  244. end_p:
  245. sw_heap_free(saveParams.pImageBuffer);
  246. return ret;
  247. }
  248. // 获取系统当前USBFS内存大小(MB), 成功返回: >=0, 失败返回: -1值
  249. int GetSysUsbfsMemCurrentSize()
  250. {
  251. const char *usbfsFile = "/sys/module/usbcore/parameters/usbfs_memory_mb";
  252. char buf[MAX_LINE_CHARS]; int val = -1;
  253. FILE *fp = fopen(usbfsFile, "r");
  254. if(fp)
  255. {
  256. if(fgets(buf, sizeof(buf), fp)) sscanf(buf, "%d", &val);
  257. fclose(fp);
  258. }
  259. return val;
  260. }
  261. // 设置系统新的USBFS内存大小(MB), 成功返回: 0值, 失败返回: -1值
  262. int SetSysUsbfsMemSize(int val)
  263. {
  264. const char *usbfsFile = "/sys/module/usbcore/parameters/usbfs_memory_mb";
  265. char buf[MAX_LINE_CHARS]; sprintf(buf, "%d\n", val);
  266. if(val >= 0 && sw_file_update(usbfsFile, "w", buf, strlen(buf)) == strlen(buf)) return 0;
  267. else return -1;
  268. }
  269. // 获取当前连接在USB口上的相机数, 失败返回: <0值, 成功返回: >=0
  270. int GetUsbCameraCount()
  271. {
  272. MV_CC_DEVICE_INFO_LIST devList = { 0 };
  273. int ret;
  274. ret = MV_CC_Initialize();
  275. if(MV_OK != ret)
  276. {
  277. sw_log_error("[%s] 相机SDK初始化失败, errCode=0x%x!!", MODULE_NAME, ret);
  278. goto end_p;
  279. }
  280. ret = MV_CC_EnumDevices(MV_USB_DEVICE, &devList);
  281. if(MV_OK != ret)
  282. {
  283. sw_log_error("[%s] USB口枚举相机失败, errCode=0x%x!!", MODULE_NAME, ret);
  284. goto end_p;
  285. }
  286. ret = devList.nDeviceNum;
  287. end_p:
  288. MV_CC_Finalize();
  289. return ret;
  290. }