尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
MFC集成HALCON实现笔记本摄像头实时采集显示全流程
做视觉上位机的朋友应该都有同感HALCON的算法封装确实省心可真到了把它塞进一个MFC窗口程序、调用笔记本摄像头做实时显示这一步很多细节得靠自己一点点磨。这篇是HALCON学习笔记系列的第五篇记录我用MFC对话框程序调用笔记本内置摄像头的完整过程从工程配置到采集线程再到退出处理都会过一遍所有代码来自一个实际跑通的模拟项目X。整个方案的核心只有两条HALCON通过DirectShow接口打开摄像头抓图再用自己的HWindow窗口把图像实时显示到MFC控件里。听起来简单做起来坑不少尤其是线程安全和窗口销毁这两块网上能查到的资料讲得都很含糊这篇笔记尽量把细节摊开讲适合正在用MFC搭界面、想集成HALCON采集链路的同学参考。1. 整体架构拆解MFC界面与HALCON采集各管一段1.1 为什么坚持用MFC做视觉上位机界面很多人一上来就问都什么年代了还用MFC换成C#、Qt不香吗这个问题的答案不在技术潮流里而在工业现场的存量生态里。我做这个项目时周边的运动控制卡、IO板卡、串口设备厂商提供的例程清一色是MFC仓库里维护的上位机也都是MFC直接在这套体系里加视觉模块比推倒重来要现实得多。HALCON本身是C库MFC也是C框架两者集成不用跨语言边界内存管理和异常处理都是同一套规则出问题排查起来也直观。另一个原因是HALCON在不同界面框架下的嵌入方式差异巨大。C#里可以用HWindowControl控件拖拽一下就行MFC里则是把HALCON窗口挂到某个子控件的HWND上代码可控性更强。如果你以后要做的项目涉及运动平台联动、IO触发拍照MFC配合windows消息机制反而顺手因为HALCON的抓图可以通过外部触发和回调跟硬件信号配合这套东西在MFC里写起来没有额外包装。1.2 采集链路的关键节点与线程模型先把我跑通的方案画在脑子里整个系统里跑两个线程。MFC主线程负责界面事件处理按钮、窗口缩放、状态栏刷新另一个是采集线程专职干“取帧—显示—上报”这三件事。相机对象只在采集线程里打开和使用主线程不直接碰相机句柄两个线程之间只用线程标志位和PostMessage通信。这样分层的好处很明显鼠标拖动窗口、点击按钮的时候抓图循环不会停下来反过来当摄像头帧率不稳导致某帧耗时偏长界面也不会卡死。说白了这就是一个典型的生产者消费者模型采集线程是生产者UI线程消费的是状态消息而不是每一帧图像本身。还有一个细节HALCON的显示窗口是谁创建的就该由谁使用。你可以把HWindow句柄传给另一个线程去DispObj但跨线程使用GDI对象有随机崩溃的风险。我最终的做法是在采集线程里创建显示窗口并负责显示主线程只负责窗口位置和大小的调整。这样分工清楚调试时也能一眼定位问题出在哪一端。2. 工程配置HALCON库引入MFC工程的前置准备2.1 HALCON版本、VS版本和DirectShow接口的匹配我用的环境是HALCON 17.12 Progressive、VS2017、Windows 10系统工程目标平台x64。这个组合不是唯一选择HALCON 18以降的版本配VS2019也没问题但有一个建议安装什么版本的HALCON就去匹配对应VS版本的MFC工程尽量别跨太大版本。HALCON的老库和最新VS在某些链接选项上会有警告虽然多数能强拧过去但没必要给自己挖坑。摄像头接口上笔记本内置摄像头在Windows下最通用的就是DirectShow接口HALCON从较老的版本一直保留着这个采集接口兼容性还算稳定。要注意的是DirectShow下设备名不是“摄像头”这种中文名而是Windows枚举出来的设备字符串比如“Integrated Camera”所以程序里不能把这个名字写死否则换一台电脑就崩。这也是后面要解决的问题。2.2 include、lib与运行库设置MFC工程引入HALCON库并不复杂但路径和依赖项要配好。我在VS里是这样设置的配置属性 - VC目录 - 包含目录: $(HALCONROOT)\include $(HALCONROOT)\include\halconcpp 配置属性 - VC目录 - 库目录: $(HALCONROOT)\lib\x64-win64 链接器 - 输入 - 附加依赖项: halconcpp.lib halcon.libHALCON安装时一般会自动配置HALCONROOT环境变量用它做相对路径换机器不用改工程。如果你的环境变量没生效可以在系统环境变量里手动补一下或者直接用绝对路径先跑通再回头优化。注意你自己安装的HALCON可能是32位版本那库目录就应该是x86-win32平台也要切到Win32两边必须一致这点属于最常规但最容易犯的错。2.3 字符集和其他容易被忽略的工程开关除了路径工程属性里还有几个开关必须检查我网格出最关键的三个。首先是目标平台。新建MFC工程默认可能是Win32如果HALCON装的是x64链接时一堆“无法解析的外部符号”扑面而来实际上不是代码问题就是位数不匹配。先统一成x64再回头找其他问题。其次是字符集。MFC工程默认可能是UnicodeHALCON的HTuple内部用的是char*。当你把设备名从std::string转成HTuple传给OpenFramegrabber时按常规写就行但当你从返回的HTuple里取设备名时要注意拿到的字符编码和界面显示字符串的转换关系。我在工程里保留了Unicode设置取设备名时统一转成UTF-8再塞进下拉框避免中文环境下的乱码。再一个是运行库。配置属性 - C/C - 代码生成 - 运行库我选的是“多线程DLL (/MD)”。如果用/MT静态链接HALCON的DLL和MFC运行时可能产生两套C运行库某些情况下会导致内存句柄在不同堆之间传递轻则报警告重则崩溃。这个开关不显眼但花点时间确认一下后面能省很多事。3. 界面与HALCON窗口绑定让图像显示区真正工作起来3.1 对话框显示区的创建与属性设置MFC侧的工作从对话框模板开始。我在资源编辑器里拖了一个Picture Control控件ID设为IDC_STATIC_DISPLAY属性Type选“Rectangle”勾上Sunken边框。为什么不直接用一个普通Static Text因为Picture Control本质上也是Static窗口但它更适合做图像显示区域视觉上有明确的边框和底衬HALCON窗口挂上去之后层次感更清楚。有一个细节容易忽略Picture Control的Visible属性必须勾上否则OpenWindow虽然能创建成功但画面死活不显示。具体原因和Windows窗口的Z序、可见性状态有关HALCON在父窗口不可见时创建子窗口虽然不报错窗口也建了但永远看不到画面。这个坑我踩过两次都是排查了半天才发现是属性问题。显示区域的初始尺寸尽量接近笔记本摄像头的输出比例。内置摄像头一般是4:3或16:9我在对话框里按4:3预留了一个比较大的区域这样画面铺进去不会被裁切得太明显。实际尺寸后面还会随窗口缩放但初始比例能减少第一次打开时的拉伸感。3.2 OpenWindow的父窗口参数与初始化代码对话框初始化后在OnInitDialog里把HALCON显示窗口建出来核心是OpenWindow的FatherWindow参数。这个参数要填的就是Picture Control的HWNDHALCON会在这个控件上创建一个自己的子窗口后续DispObj都画在这个子窗口上。代码长这样BOOL CDemoDlg::OnInitDialog() { CDialogEx::OnInitDialog(); CRect rc; GetDlgItem(IDC_STATIC_DISPLAY)-GetClientRect(rc); HTuple hWin; try { HOperatorSet::OpenWindow(0, 0, rc.Width(), rc.Height(), (Hlong)GetDlgItem(IDC_STATIC_DISPLAY)-GetSafeHwnd(), visible, , hWin); m_hWin hWin; } catch (const HalconCpp::HException e) { AfxMessageBox(e.ErrorMessage().Text()); } return TRUE; }把OpenWindow放在OnInitDialog里而不是放在采集线程里是为了保证窗口创建时有稳定的界面上下文。等采集线程启动后它拿到的是已经创建好的m_hWin只负责往里画图。OpenWindow的“visible”参数表示窗口创建后立即可见不要改成“false”不然还要额外处理窗口显示状态没必要。3.3 窗口缩放与图像自适应SetWindowExtents和SetPart界面搭好之后会遇到一个很实际的问题对话框一缩放显示区变了HALCON窗口不会自动跟着变。解决办法是重载OnSize在对话框大小变化时同步调整HALCON窗口的像素尺寸void CDemoDlg::OnSize(UINT nType, int cx, int cy) { CDialogEx::OnSize(nType, cx, cy); if (m_hWin ! -1 GetDlgItem(IDC_STATIC_DISPLAY) ! nullptr IsWindow(GetDlgItem(IDC_STATIC_DISPLAY)-GetSafeHwnd())) { CRect rc; GetDlgItem(IDC_STATIC_DISPLAY)-GetWindowRect(rc); ScreenToClient(rc); HOperatorSet::SetWindowExtents(m_hWin, 0, 0, rc.Width(), rc.Height()); } }注意OnSize在对话框初始化早期也会被调用那时候控件还没创建好所以一定要做空指针和IsWindow判断。图像内容自适应是另一层问题。SetWindowExtents管的是窗口在屏幕上的大小SetPart管的才是窗口内显示图像的坐标范围。第一次拿到图像后我建议主动做一次SetPart把整个图像铺到窗口里HTuple imgW, imgH; HOperatorSet::GetImageSize(hoImage, imgW, imgH); HOperatorSet::SetPart(m_hWin, 0, 0, imgH - 1, imgW - 1);如果不显式设置PartHALCON有时会沿用上一次窗口的显示区域切换分辨率或摄像头之后画面就会被裁切。加一行SetPart之后无论相机分辨率怎么变图像都能以完整视野显示出来。4. 采集线程实现笔记本摄像头持续抓图的关键代码4.1 设备名获取与OpenFramegrabber调用打开相机之前先要确定设备名。HALCON提供了info_framegrabber算子可以枚举当前系统里的DirectShow设备HTuple information, devices; HOperatorSet::InfoFramegrabber(DirectShow, device, information, devices); // devices 是一个HTuple字符串元组每个元素是一个设备名 for (Hlong i 0; i devices.Length(); i) { m_cameraNameList.Add(devices[i].S()); }这一步非常关键。笔记本内置摄像头的设备名在不同厂商、不同型号之间都不一样有叫“Integrated Camera”的有叫“HD Webcam”的还有带品牌型号的。直接在代码里写死一个名字换台机器就抓瞎。我是在对话框初始化时枚举设备名填进一个下拉框用户自己选或者程序自动选第一个设备。整个工程的可移植性一下子就上来了。拿到设备名后打开相机的核心调用是OpenFramegrabberHlong fgHandle -1; HOperatorSet::OpenFramegrabber(DirectShow, 0, 0, 0, 0, 0, 0, default, -1, default, -1, 0, default, HTuple(m_cameraName.c_str()), 0, -1, -1, fgHandle); m_fgHandle fgHandle;OpenFramegrabber的参数很多一共17个输入加1个输出非常容易写错。以DirectShow为例前两个数字是水平和垂直分辨率选择填0表示让驱动自己协商一个合适格式如果填具体值HALCON会尝试匹配摄像头支持的格式。后面的ImageWidth和ImageHeight也可以用来指定输出分辨率比如想强制640x480就把这两个参数填成640和480没有匹配格式时会报错。实际使用中笔记本摄像头分辨率比较固定我推荐先用0让系统自动匹配稳定优先。4.2 GrabImageStart与GrabImageAsync的组合用法打开相机后最推荐的做法是先调用GrabImageStart启动一轮采集然后循环里用GrabImageAsync取帧void CDemoDlg::CameraWork() { try { HTuple fgHandle; HOperatorSet::OpenFramegrabber(...); // 上面的代码 m_fgHandle fgHandle; HOperatorSet::GrabImageStart(fgHandle, -1); HObject hoImage; while (m_bRunning) { HOperatorSet::GrabImageAsync(hoImage, fgHandle, -1); HOperatorSet::DispObj(hoImage, m_hWin); if ((m_frameCounter % 30) 1) { ::PostMessage(m_hWnd, WM_CAM_UPDATE, 0, 0); } } } catch (const HalconCpp::HException e) { ::PostMessage(m_hWnd, WM_CAM_ERROR, 0, 0); m_bRunning FALSE; } }GrabImageStart的作用是做一次预抓取往采集管线的缓冲里提前放一帧之后GrabImageAsync会等下一帧就绪并把上一帧缓冲释放掉。这个机制带来的实际效果就是采集循环和相机曝光/传输可以部分重叠比每次都同步等待的GrabImage更能跑的满帧。这里有个容易写错的地方必须连续调用GrabImageStart再循环GrabImageAsync。如果漏掉StartGrabImageAsync在部分设备上会直接报错“抓取状态不对”在另一部分设备上则表现为长时间阻塞。更隐蔽的错误是有人把GrabImageAsync的参数-1当成超时时间以为可以控制等待时长实际上这个参数在DirectShow接口下的行为是留给驱动判断用的填-1就是完全等待下一帧并不具备传统意义的上限超时功能。你真想防摄像头掉线卡死要靠外层try/catch和线程标志位配合而不是指望这个参数。4.3 线程内刷UI的正确方式PostMessage而不是操作控件采集线程里最容易犯的错误就是顺手操作界面控件。比如直接用SetDlgItemText去刷新帧率、直接弹MessageBox提示错误。刚开始我也这么干程序表现时好时坏经常运行几分钟后随机闪退奇怪的是Debug版本又很难复现。后来才明白MFC控件归主线程所有跨线程调用等于强行访问UI线程的资源Windows的消息泵一旦在操作控件的半路插进来处理其他消息两个线程就会在内部窗口数据结构上打架。正确的做法是采集线程只PostMessage把要干的事告诉主线程让主线程在消息响应函数里更新界面。上面代码里每30帧向主窗口发一个WM_CAM_UPDATE主线程收到后在OnCamUpdate里刷新帧率显示。错误消息也是一样用另一个自定义消息WM_CAM_ERROR传回去主线程负责弹窗提示。这样采集线程和UI线程各干各的跨线程的全部走消息稳定性和可排查性都高很多。5. 退出与资源释放线程、相机、窗口的销毁顺序5.1 不要用TerminateThread协作式停止线程程序退出时怎么关线程是MFC和HALCON集成中最容易崩的环节。最粗暴的办法是TerminateThread直接终结采集线程但这是万万不能用的线程被强杀时HALCON内部的相机对象可能正处于半开半关状态下次打开同一设备时会得到莫名其妙的“设备被占用”错误更危险的是线程持有的某些锁可能永远不会释放整个进程可能直接死锁。正确做法是协作式停止。用一个线程标志位m_bRunning主线程在退出时把它置为FALSE采集线程在每轮循环开头检查这个标志发现要退出就自己跳出循环然后清理相机资源后自然结束。代码片段// 主线程退出通知 m_bRunning FALSE; if (m_hThread ! nullptr) { WaitForSingleObject(m_hThread, 5000); }只要摄像头还在正常出帧采集线程最多一帧时间就会响应退出。这里有个隐含前提标志位需要是volatile或者用原子操作。我习惯用volatile BOOL配合InterlockedExchange或者把整个循环条件写严谨一点避免编译器优化导致主线程改了标志、采集线程读不到新值。5.2 关闭流程的代码顺序与防御措施退出时资源销毁的先后顺序非常讲究。我最终确定的OnDestroy流程是先停线程、再等线程结束、然后关相机、最后关显示窗口。void CDemoDlg::OnDestroy() { // 1. 通知采集线程退出 m_bRunning FALSE; // 2. 等待线程真正结束设一个超时防止异常卡死 if (m_hThread ! nullptr) { WaitForSingleObject(m_hThread, 5000); } // 3. 关闭抓图器 if (m_fgHandle ! -1) { HOperatorSet::CloseFramegrabber(m_fgHandle); m_fgHandle -1; } // 4. 最后才关闭显示窗口 if (m_hWin ! -1) { HOperatorSet::CloseWindow(m_hWin); m_hWin -1; } CDialogEx::OnDestroy(); }之所以要把关闭显示窗口放在最后是因为采集线程退出之前可能正在执行DispObj。如果先关窗口或者提前释放控件线程再去画图就是典型的访问已销毁的GDI对象必定崩溃而且这种崩溃没有固定重现路径非常难查。不要小看这个顺序我见过太多工程退出了对话框、任务还在后台跑的情况点一次关闭按钮CPU直接满一格原因就是线程活着、窗口没了。另外如果你的采集中途因为设备拔除等异常自己退出了CameraWork里的try/catch会先关掉相机、把句柄置-1OnDestroy里的CloseFramegrabber因为有m_fgHandle判断就不会重复关闭导致错误。5.3 异常处理HALCON C接口的try/catchHALCON的C接口在算子失败时不是返回错误码而是直接抛异常。如果程序里不做任何捕获默认行为是调用terminate连个像样的错误提示都看不到调试时很容易一头雾水。我的建议是整个采集流程包一层大try/catchcatch HalconCpp::HException把错误信息通过PostMessage发回主线程同时重置工作线程状态。catch (const HalconCpp::HException e) { CString strErr e.ErrorMessage().Text(); ::PostMessage(m_hWnd, WM_CAM_ERROR, 0, (LPARAM)new CString(strErr)); m_bRunning FALSE; }这里有个细节错误信息字符串如果直接在线程里弹框又回到跨线程操作UI的问题了。正确做法是PostMessage携带指针主线程在处理消息时弹框并负责释放内存。这一套处理下来即使采集链路中途挂掉程序也能稳定退回到待机界面不至于整个上位机崩掉。6. 常见问题排查速查表与个人经验补充6.1 六个高频问题与对应处理我整理了这套方案里最常遇到的六个问题及排查思路现象可能原因处理办法OpenFramegrabber报找不到设备设备名写错或摄像头驱动异常用InfoFramegrabber枚举设备名确认设备在设备管理器中正常打开成功但画面一直是黑屏Picture Control不可见或OpenWindow父窗口绑定错误确认控件Visible属性核对传入OpenWindow的HWND程序退出时立即崩溃线程未停止就关闭窗口或相机严格按OnDestroy四步顺序释放资源画面卡顿、掉帧明显用同步GrabImage导致采集和显示串行阻塞改GrabImageStart GrabImageAsync组合摄像头报告被占用其他软件浏览器、会议软件占用了摄像头关闭占用程序代码中保证上次进程彻底退出后再启动链接报错或运行时报DLL错误平台位数和HALCON安装版本不匹配统一为x64或x86检查lib目录路径其中黑屏和退出崩溃是咨询朋友时问得最多的两个。黑屏问题往往不是OpenWindow没成功而是父控件在Z序上被其他控件盖住了或者控件本身尺寸为0。退出崩溃十有八九就是线程销毁顺序错了。我建议遇到问题时不要急着改代码先在界面上把现象看清楚再沿着线程生命周期这条线去查往往能省下一晚上时间。6.2 一个小技巧先让HDevelop把参数跑明白最后分享一个效率技巧调试这类相机问题时强烈建议先用HDevelop把流程跑通再写MFC代码。HDevelop里有采集助手选择相机、调整分辨率、测试抓图全部可视化操作。它生成的open_framegrabber代码是经过验证的参数抄到MFC工程里基本能直接用。这一步能帮我过滤掉至少一半的“参数没填对”问题。我在模拟项目X里就是这么干的HDevelop里确认相机设备名和输出格式然后把生成的调用代码复制到采集线程一次就打开成功。后面换了一台不同品牌的笔记本同样先用HDevelop枚举设备名再更新程序里的下拉列表几乎没有改其他代码。如果将来要接工业相机思路也一样把OpenFramegrabber第一个参数从“DirectShow”换成“GenICam”采集循环和显示部分的代码完全可以复用。6.3 更具韧性的采集循环设计再补一点我自己踩过坑之后加上的设计。采集循环里除了抓图和显示一定要安排一个心跳或者帧计数上报机制。我是在每30帧向上位机发一次状态消息主线程在界面状态栏同步显示当前帧率和相机状态。一开始觉得这个功能无所谓直到有一次相机在运行中途掉线程序虽然没有崩溃但画面卡在最后一帧界面看起来像死机了一样。有了帧计数上报主线程可以通过停更来判断相机无输出进而弹窗提示或者自动重连。另外如果摄像头长时间不出帧GrabImageAsync可能会一直阻塞。针对这种场景我后来做了一个简单的看门狗主线程每隔一段时间检查上次收到帧计数消息的时间超过阈值就认为采集线程异常主动提示用户重启相机。这个逻辑完全在UI线程里做不碰采集线程的锁和状态实现成本很低但工业现场使用时会让人安心很多。说回整体的体会MFC调用HALCON这件事真正的难点不在HALCON的算子而在窗口生命周期和线程安全这两条线。我一开始图省事直接在采集线程里SetDlgItemText刷新帧率结果半小时崩一次改成PostMessage之后整个下午跑下来都稳稳的。如果你也卡在某些莫名其妙的问题上先往线程和窗口这两个方向去怀疑多数时候都是它们在捣鬼。这套框架跑通之后换相机、换电脑、换工程核心结构都没再大改过希望这篇笔记也能帮你少走几段弯路。
RELATED

相关推荐

VScode 插件推荐-html篇:把 settings.json 改到 TaoToken 的 AI 补全配置清单

VScode 插件推荐-html篇:把 settings.json 改到 TaoToken 的 AI 补全配置清单

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/12 9:03:02
激光雷达点云投影到相机图像的完整实现与避坑指南

激光雷达点云投影到相机图像的完整实现与避坑指南

做这个系列做到第四篇,前面已经啃完传感器标定的原理部分,这篇就把激光雷达3D点云映射到相机图像这件事彻底跑通。说白了,就是让雷达"看到"的世界,精准地"画"到照片上——每个3D点落在图像的哪个像素位置&…

📅 2026/10/12 9:03:02
GitHub开源项目日报 · 2026年3月19日 · AI编程工具与机器人仿真受关注:用TaoToken统一Key跑通本地AI编程工作流

GitHub开源项目日报 · 2026年3月19日 · AI编程工具与机器人仿真受关注:用TaoToken统一Key跑通本地AI编程工作流

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/12 9:03:02
MORE NEWS

更多资讯

📰

大模型落地、具身智能与端侧AI:科技产业趋势全景观察

长假里科技圈其实没打烊。我习惯每天早晚各刷一遍行业消息,把值得展开说的东西单独挑出来写一写。今天这份科技爆文日报,想聊五个方向:大模型从卷参数转向卷应用落地,具身智能集中放料、机器人赛道进入手眼配合的深水区&#xff0…

📰

Qt多线程选型:QThread与QtConcurrent的区别、坑点与最佳实践

做 Qt 开发绕不开多线程,但只要涉及线程,十个人里八个都踩过坑。界面卡死、数据错乱、信号丢失、崩溃退出……很多问题的根源不是业务逻辑,而是没有系统理解 QThread 和 QtConcurrent 这两个工具的定位和用法。我这两年帮别人排查过不少类似问…

📰

哈希算法在系统安全中的关键作用:从密码存储到完整性校验

你有没有遇到过这样的情况:下载一个软件安装包时,官网页面会贴出一串长长的十六进制字符,比如e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855。大多数人都把这串字符当成可有可无的“官网装饰”,但你有没有想…

📰

Java函数复写深度解析:从语法规则到Android实战避坑

1. 从一次编译报错说起:为什么函数复写值得单独拎出来讲刚接触Java4Android那会儿,我写了一个继承自父类的子类,想当然地给某个方法换了个参数类型,以为这就是"复写"。结果运行起来,程序走的还是父类的逻辑&…

📰

JSP+MySQL构建BBS论坛:核心架构与完整开发实战

简介:基于JSP与MySQL构建的BBS论坛项目源码包,面向正在学习Java Web开发的学生与开发者,完整演示了论坛系统的核心业务流程:用户注册登录、板块管理、主题发帖、回复互动、关键词搜索以及后台权限控制等。包内共493个文件&#xf…

📰

深度学习实践三部曲:Keras回调、自编码器异常检测与TensorFlow Serving部署

1. 为什么持续啃 DLology:一个翻译项目背后的技术筛选逻辑1.1 DLology 这个博客,到底强在哪先交代一下背景。我一直在做的这个 DLology 博客中文翻译系列,已经推进到第四期了。很多朋友问我,深度学习资料那么多,为什么…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬