takephoto.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  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, index = 0; SImgMark imgMark = { 0 }; HANDLE hCam = NULL; PthotoProcCtx ctx = { 0 };
  80. MV_CC_DEVICE_INFO_LIST devList = { 0 }; MV_CC_DEVICE_INFO *pDevInfo; 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. char name[MAX_LINE_CHARS]; // 存放相机制造商名字
  110. findp:
  111. pDevInfo = devList.pDeviceInfo[index++];
  112. if(!pDevInfo)
  113. {
  114. ret = -4;
  115. sw_log_error("[%s] unexpected internal error, unable to obtain detailed information about the industrial camera!!", MODULE_NAME);
  116. goto end_p;
  117. }
  118. name[0] = '\0'; if(0) ;
  119. else if(pDevInfo->nTLayerType == MV_USB_DEVICE /*U口相机*/)
  120. {
  121. strcpy(name, (const char *)pDevInfo->SpecialInfo.stUsb3VInfo.chManufacturerName);
  122. strcpy(imgMark.camModelName, (const char *)pDevInfo->SpecialInfo.stUsb3VInfo.chModelName);
  123. strcpy(imgMark.camSerialNum, (const char *)pDevInfo->SpecialInfo.stUsb3VInfo.chSerialNumber);
  124. }
  125. else if(pDevInfo->nTLayerType == MV_GIGE_DEVICE/*G口相机*/)
  126. {
  127. strcpy(name, (const char *)pDevInfo->SpecialInfo.stGigEInfo.chManufacturerName);
  128. strcpy(imgMark.camModelName, (const char *)pDevInfo->SpecialInfo.stGigEInfo.chModelName);
  129. strcpy(imgMark.camSerialNum, (const char *)pDevInfo->SpecialInfo.stGigEInfo.chSerialNumber);
  130. }
  131. if(xstrcasecmp(name, manufacturer) != 0)
  132. {
  133. if(index < devList.nDeviceNum) goto findp;
  134. ret = -5;
  135. sw_log_error("[%s] 没有找到 \"%s\" 的相机!!", MODULE_NAME, manufacturer);
  136. goto end_p;
  137. }
  138. // 3, 打开相机, 设置其触发模式
  139. ret = MV_CC_CreateHandleWithoutLog(&hCam, pDevInfo);
  140. if(MV_OK == ret) ret = MV_CC_OpenDevice(hCam, MV_ACCESS_Exclusive, 0);
  141. if(MV_OK == ret && pDevInfo->nTLayerType == MV_USB_DEVICE /*U口相机*/)
  142. {
  143. ret = MV_USB_SetTransferWays(hCam, 1); // 设置传输通道个数(RTU硬件资源有限, 需要降低通道数)
  144. }
  145. if(MV_OK == ret && pDevInfo->nTLayerType == MV_GIGE_DEVICE/*G口相机*/)
  146. { // 探测网络最佳包大小(只对GigE相机有效)
  147. int size = MV_CC_GetOptimalPacketSize(hCam);
  148. if(size > 0) ret = MV_CC_SetIntValueEx(hCam, "GevSCPSPacketSize", size);
  149. else ret = -6;
  150. }
  151. if(MV_OK != ret)
  152. {
  153. sw_log_error("[%s] 打开相机时发生错误, errCode=0x%x!!", MODULE_NAME, ret);
  154. goto end_p;
  155. }
  156. ret = MV_CC_SetEnumValue(hCam, "TriggerMode", 1); // 允许触发模式
  157. if(MV_OK == ret) ret = MV_CC_SetEnumValue(hCam, "TriggerSource", MV_TRIGGER_SOURCE_SOFTWARE); // 设置软件触发
  158. if(MV_OK != ret)
  159. {
  160. sw_log_error("[%s] 设置相机时发生错误, errCode=0x%x!!", MODULE_NAME, ret);
  161. goto end_p;
  162. }
  163. // 4, 开始拍照, 等待完成后输出
  164. ret = MV_CC_GetExposureAutoMode(hCam, &exposureMode);
  165. if(MV_OK != ret)
  166. {
  167. sw_log_error("[%s] 获取曝光模式时出错, errCode=0x%x!!", MODULE_NAME, ret);
  168. goto end_p;
  169. }
  170. if(exposureMode.nCurValue != MV_EXPOSURE_AUTO_MODE_OFF) { ctx.isExposureAuto = true; clock_gettime(CLOCK_MONOTONIC, &ctx.expTime0); }
  171. else ctx.isExposureAuto = false;
  172. ret = MV_CC_StartGrabbing(hCam);
  173. if(MV_OK != ret)
  174. {
  175. sw_log_error("[%s] 相机取流时发生错误, errCode=0x%x!!", MODULE_NAME, ret);
  176. goto end_p;
  177. }
  178. ctx.hCam = hCam;
  179. ctx.saveImgType = imgType;
  180. ctx.saveImgPath = saveImgPath;
  181. ctx.hESig = sw_signal_create();
  182. if(!ctx.hESig)
  183. {
  184. ret = -7;
  185. sw_log_error("[%s] SIG信号量创建失败!!", MODULE_NAME);
  186. goto end_p;
  187. }
  188. ctx.rCode = MV_OK;
  189. HANDLE hThrd = sw_thrd_create("PhotoProc", THREAD_DEFAULT_PRIORITY, THREAD_DEFAULT_STACK_SIZE, PhotoProc, (unsigned long)&ctx, 0);
  190. if(!hThrd)
  191. {
  192. ret = -8;
  193. sw_signal_destroy(ctx.hESig);
  194. sw_log_error("[%s] 拍照线程-创建失败!!", MODULE_NAME);
  195. goto end_p;
  196. }
  197. sw_thrd_resume(hThrd);
  198. ret = sw_signal_wait(ctx.hESig, timeout*1000); // 阻塞等待拍照任务结束或超时
  199. if(0 == ret) ret = ctx.rCode;
  200. else
  201. {
  202. ret = -9;
  203. sw_log_error("[%s] 拍照过程-等待超时!!", MODULE_NAME);
  204. }
  205. sw_thrd_destroy(hThrd, WAITTHRD_SAFEEXIT_TIMEOUT);
  206. sw_signal_destroy(ctx.hESig);
  207. // 5, 成功拍照, 输出相机的信息
  208. if(MV_OK == ret)
  209. {
  210. MVCC_FLOATVALUE fv = { 0 };
  211. MV_CC_GetFloatValue(hCam, "ExposureTime", &fv);
  212. imgMark.imgExposureTime = fv.fCurValue;
  213. if(pImgMark) memcpy(pImgMark, &imgMark, sizeof(SImgMark));
  214. }
  215. // 6, 任务结束, 释放相关的资源
  216. end_p:
  217. if(hCam)
  218. {
  219. MV_CC_StopGrabbing(hCam);
  220. MV_CC_CloseDevice(hCam);
  221. MV_CC_DestroyHandle(hCam);
  222. }
  223. MV_CC_Finalize();
  224. flock(fd, LOCK_UN);
  225. close(fd);
  226. sw_file_delete(lockFile);
  227. return ret;
  228. }
  229. // 保存照片
  230. static int SavePhoto(HANDLE hCam, MV_FRAME_OUT *pFrame, EImgType imgType, const char *imgFile)
  231. {
  232. MV_SAVE_IMAGE_PARAM_EX3 saveParams = { 0 }; MVCC_INTVALUE_EX iv;
  233. char *filename1 = (char *)imgFile, *filename2 = NULL; int ret; char ext[5];
  234. if(!hCam || !pFrame || !filename1 || strlen(filename1) <= 0) return -15;
  235. switch(imgType)
  236. {
  237. case IMG_TYPE_BMP: // 保存为.bmp格式
  238. imgType = MV_Image_Bmp;
  239. strcpy(ext, ".bmp");
  240. break;
  241. case IMG_TYPE_JPG: // 保存为.jpg格式
  242. imgType = MV_Image_Jpeg;
  243. strcpy(ext, ".jpg");
  244. break;
  245. default: return -16;
  246. }
  247. ret = MV_CC_GetIntValueEx(hCam, "PayloadSize", &iv);
  248. if(MV_OK != ret) return ret;
  249. saveParams.pData = pFrame->pBufAddr;
  250. saveParams.nDataLen = pFrame->stFrameInfo.nFrameLen;
  251. saveParams.enPixelType = pFrame->stFrameInfo.enPixelType;
  252. saveParams.nHeight = pFrame->stFrameInfo.nHeight;
  253. saveParams.nWidth = pFrame->stFrameInfo.nWidth;
  254. saveParams.enImageType = imgType;
  255. saveParams.nJpgQuality = 90; // JPG编码质量(50-99], 其它格式无效, 影响JPG图像文件大小
  256. saveParams.iMethodValue = 1;
  257. if(imgType == IMG_TYPE_JPG) saveParams.nBufferSize = (unsigned int)(iv.nCurValue * 1);
  258. else saveParams.nBufferSize = (unsigned int)(iv.nCurValue * 4);
  259. saveParams.pImageBuffer = (unsigned char *)sw_heap_malloc(saveParams.nBufferSize);
  260. if(!saveParams.pImageBuffer) return -17;
  261. ret = MV_CC_SaveImageEx3(hCam, &saveParams);
  262. if(MV_OK != ret) goto end_p;
  263. if(!xstrcasestr(filename1, "right", ext))
  264. {
  265. filename2 = (char *)sw_heap_malloc(strlen(filename1) +sizeof(ext));
  266. if(filename2) sprintf(filename2, "%s%s", filename1, ext);
  267. else { ret = -18; goto end_p; }
  268. ret = sw_file_update(filename2, "wb", (const char *)saveParams.pImageBuffer, saveParams.nImageLen);
  269. sw_heap_free(filename2);
  270. }
  271. else
  272. {
  273. ret = sw_file_update(filename1, "wb", (const char *)saveParams.pImageBuffer, saveParams.nImageLen);
  274. }
  275. if(ret == saveParams.nImageLen) ret = MV_OK;
  276. else ret = -19;
  277. end_p:
  278. sw_heap_free(saveParams.pImageBuffer);
  279. return ret;
  280. }
  281. // 获取系统当前USBFS内存大小(MB), 成功返回: >=0, 失败返回: <0值
  282. int GetSysUsbfsMemCurrentSize()
  283. {
  284. const char *usbfsFile = "/sys/module/usbcore/parameters/usbfs_memory_mb";
  285. char buf[MAX_LINE_CHARS]; int val = -1;
  286. FILE *fp = fopen(usbfsFile, "r");
  287. if(fp)
  288. {
  289. if(fgets(buf, sizeof(buf), fp)) sscanf(buf, "%d", &val);
  290. fclose(fp);
  291. }
  292. return val;
  293. }
  294. // 设置系统新的USBFS内存大小(MB), 成功返回: 0值, 失败返回: <0值
  295. int SetSysUsbfsMemSize(int val)
  296. {
  297. const char *usbfsFile = "/sys/module/usbcore/parameters/usbfs_memory_mb";
  298. char buf[MAX_LINE_CHARS]; sprintf(buf, "%d\n", val);
  299. if(val >= 0 && sw_file_update(usbfsFile, "w", buf, strlen(buf)) == strlen(buf)) return 0;
  300. else return -1;
  301. }
  302. // 获取当前已连接的海康相机的数量, 成功返回: >=0, 失败返回: <0值
  303. int GetHKCameraCount()
  304. {
  305. MV_CC_DEVICE_INFO_LIST devList = { 0 };
  306. int ret;
  307. ret = MV_CC_Initialize();
  308. if(MV_OK != ret)
  309. {
  310. sw_log_error("[%s] 相机SDK初始化失败, errCode=0x%x!!", MODULE_NAME, ret);
  311. goto end_p;
  312. }
  313. ret = MV_CC_EnumDevices(device_type, &devList);
  314. if(MV_OK != ret)
  315. {
  316. sw_log_error("[%s] 枚举相机,执行错误, errCode=0x%x!!", MODULE_NAME, ret);
  317. ret = -1; goto end_p;
  318. }
  319. ret = devList.nDeviceNum;
  320. if(ret > 0)
  321. {
  322. int index = 0, cnt = 0; MV_CC_DEVICE_INFO *pDevInfo; char name[MAX_LINE_CHARS];
  323. findp:
  324. pDevInfo = devList.pDeviceInfo[index++];
  325. name[0] = '\0'; if(0) ;
  326. else if(pDevInfo && pDevInfo->nTLayerType == MV_USB_DEVICE /*U口相机*/) strcpy(name, (const char *)pDevInfo->SpecialInfo.stUsb3VInfo.chManufacturerName);
  327. else if(pDevInfo && pDevInfo->nTLayerType == MV_GIGE_DEVICE/*G口相机*/) strcpy(name, (const char *)pDevInfo->SpecialInfo.stGigEInfo.chManufacturerName);
  328. if(xstrcasecmp(name, manufacturer) == 0) cnt++;
  329. if(index < devList.nDeviceNum) goto findp;
  330. else ret = cnt;
  331. }
  332. end_p:
  333. MV_CC_Finalize();
  334. return ret;
  335. }