takephoto.c 13 KB

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