范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

目标跟踪(3)MultiTracker基于OpenCV(CPython)的多目标跟踪

  在这篇文章中,我们将介绍如何使用通过 MultiTracker 类实现的 OpenCV 的多对象跟踪 API。我们将共享C++ 和 Python 代码。
  1.为什么我们需要多目标跟踪
  大多数计算机视觉和机器学习的初学者都学习对象检测。如果您是初学者,您可能会想为什么我们需要对象跟踪。我们不能只检测每一帧中的对象吗?
  让我们来探究一下跟踪是有用的几个原因。
  首先,当在视频帧中检测到多个对象(例如人)时,跟踪有助于跨帧建立对象的身份。
  其次,在某些情况下,对象检测可能会失败,但仍可能跟踪对象,因为跟踪考虑了对象在前一帧中的位置和外观。
  第三,一些跟踪算法非常快,因为它们做的是局部搜索,而不是全局搜索。因此,我们可以通过每n帧进行目标检测,并在中间帧中跟踪目标,从而为我们的系统获得很高的帧率。
  那么,为什么不在第一次检测后无限期地跟踪对象呢?跟踪算法有时可能会丢失它正在跟踪的对象。例如,当对象的运动太大时,跟踪算法可能跟不上。许多现实世界的应用程序同时使用检测和跟踪。
  在本教程中,我们只关注跟踪部分。我们想要跟踪的对象将通过拖动它们周围的包围框来指定。2.MultiTracker: OpenCV的多对象跟踪器
  OpenCV 中的 MultiTracker 类提供了多目标跟踪的实现。它是一个简单的实现,因为它独立处理跟踪对象,而不对跟踪对象进行任何优化。
  让我们逐步查看代码,了解如何使用 OpenCV 的多目标跟踪 API。
  2.1 第 1 步:创建单一对象跟踪器
  多目标跟踪器只是单目标跟踪器的集合。我们首先定义一个函数,该函数接受一个跟踪器类型作为输入,并创建一个跟踪器对象。OpenCV有8种不同的跟踪器类型:BOOSTING, MIL, KCF,TLD, MEDIANFLOW, GOTURN, MOSSE, CSRT。
  如果您想使用 GOTURN 跟踪器,请务必阅读这篇文章并下载 caffe 模型。
  在下面的代码中,给定跟踪器类的名称,我们返回跟踪器对象。这将在稍后用于多目标跟踪器。
  Pythonfrom __future__ import print_function import sys import cv2 from random import randint trackerTypes = ["BOOSTING", "MIL", "KCF","TLD", "MEDIANFLOW", "GOTURN", "MOSSE", "CSRT"] def createTrackerByName(trackerType): # Create a tracker based on tracker name if trackerType == trackerTypes[0]: tracker = cv2.TrackerBoosting_create() elif trackerType == trackerTypes[1]: tracker = cv2.TrackerMIL_create() elif trackerType == trackerTypes[2]: tracker = cv2.TrackerKCF_create() elif trackerType == trackerTypes[3]: tracker = cv2.TrackerTLD_create() elif trackerType == trackerTypes[4]: tracker = cv2.TrackerMedianFlow_create() elif trackerType == trackerTypes[5]: tracker = cv2.TrackerGOTURN_create() elif trackerType == trackerTypes[6]: tracker = cv2.TrackerMOSSE_create() elif trackerType == trackerTypes[7]: tracker = cv2.TrackerCSRT_create() else: tracker = None print("Incorrect tracker name") print("Available trackers are:") for t in trackerTypes: print(t) return tracker
  C++**注意:**除了包含opencv2/opencv.hpp,还需要包含opencv2/tracking.hpp。 #include  #include  using namespace cv; using namespace std; vector trackerTypes = {"BOOSTING", "MIL", "KCF", "TLD", "MEDIANFLOW", "GOTURN", "MOSSE", "CSRT"}; // create tracker by name Ptr createTrackerByName(string trackerType) { Ptr tracker; if (trackerType == trackerTypes[0]) tracker = TrackerBoosting::create(); else if (trackerType == trackerTypes[1]) tracker = TrackerMIL::create(); else if (trackerType == trackerTypes[2]) tracker = TrackerKCF::create(); else if (trackerType == trackerTypes[3]) tracker = TrackerTLD::create(); else if (trackerType == trackerTypes[4]) tracker = TrackerMedianFlow::create(); else if (trackerType == trackerTypes[5]) tracker = TrackerGOTURN::create(); else if (trackerType == trackerTypes[6]) tracker = TrackerMOSSE::create(); else if (trackerType == trackerTypes[7]) tracker = TrackerCSRT::create(); else { cout << "Incorrect tracker name" << endl; cout << "Available trackers are: " << endl; for (vector::iterator it = trackerTypes.begin() ; it != trackerTypes.end(); ++it) std::cout << " " << *it << endl; } return tracker; }
  2.2 第 2 步:读取视频的第一帧
  多目标跟踪器需要两个输入一个视频帧我们要跟踪的所有对象的位置(边界框)。
  给定这些信息,跟踪器在所有后续帧中跟踪这些指定对象的位置。 在下面的代码中,我们首先使用 VideoCapture 类加载视频并读取第一帧。这将在稍后用于初始化 MultiTracker。
  Python# Set video to load videoPath = "videos/run.mp4" # Create a video capture object to read videos cap = cv2.VideoCapture(videoPath) # Read first frame success, frame = cap.read() # quit if unable to read the video file if not success: print("Failed to read video") sys.exit(1)
  C++// set default values for tracking algorithm and video string videoPath = "videos/run.mp4"; // Initialize MultiTracker with tracking algo vector bboxes; // create a video capture object to read videos cv::VideoCapture cap(videoPath); Mat frame; // quit if unabke to read video file if(!cap.isOpened()) { cout << "Error opening video file " << videoPath << endl; return -1; } // read first frame cap >> frame;
  2.3 第 3 步:在第一帧中定位对象
  接下来,我们需要在第一帧中定位我们想要跟踪的对象。该位置只是一个边界框。 OpenCV 提供了一个名为 selectROI 的函数,该函数会弹出一个 GUI 来选择边界框(也称为感兴趣区域 (ROI))。 在 C++ 版本中,selectROI 允许您获取多个边界框,但在 Python 版本中,它只返回一个边界框。所以,在 Python 版本中,我们需要一个循环来获取多个边界框。 对于每个对象,我们还选择一种随机颜色来显示边界框。 代码如下所示。
  Python## Select boxes bboxes = [] colors = [] # OpenCV 的 selectROI 函数不适用于在 Python 中选择多个对象 # 所以我们将循环调用这个函数,直到我们完成选择所有对象 while True: # 在对象上绘制边界框 # selectROI 的默认行为是从中心开始绘制框 # 当fromCenter设置为false时,可以从左上角开始画框 bbox = cv2.selectROI("MultiTracker", frame) bboxes.append(bbox) colors.append((randint(0, 255), randint(0, 255), randint(0, 255))) print("Press q to quit selecting boxes and start tracking") print("Press any other key to select next object") k = cv2.waitKey(0) & 0xFF if (k == 113): # q is pressed break print("Selected bounding boxes {}".format(bboxes))
  C++// Get bounding boxes for first frame // selectROI"s default behaviour is to draw box starting from the center // when fromCenter is set to false, you can draw box starting from top left corner bool showCrosshair = true; bool fromCenter = false; cout << " ========================================================== "; cout << "OpenCV says press c to cancel objects selection process" << endl; cout << "It doesn"t work. Press Escape to exit selection process" << endl; cout << " ========================================================== "; cv::selectROIs("MultiTracker", frame, bboxes, showCrosshair, fromCenter); // quit if there are no objects to track if(bboxes.size() < 1) return 0; vector colors; getRandomColors(colors, bboxes.size());
  getRandomColors 函数相当简单// Fill the vector with random colors void getRandomColors(vector& colors, int numColors) { RNG rng(0); for(int i=0; i < numColors; i++) colors.push_back(Scalar(rng.uniform(0,255), rng.uniform(0, 255), rng.uniform(0, 255))); }
  2.4 第 3 步:初始化 MultiTracker
  到目前为止,我们已经读取了第一帧并获得了对象周围的边界框。这就是我们初始化多目标跟踪器所需的所有信息。
  我们首先创建一个 MultiTracker 对象,并向其中添加与边界框一样多的单个对象跟踪器。在此示例中,我们使用 CSRT 单对象跟踪器,但您可以通过将下面的 trackerType 变量更改为本文开头提到的 8 个跟踪器之一来尝试其他跟踪器类型。 CSRT 跟踪器不是最快的,但在我们尝试的许多情况下它产生了最好的结果。
  您还可以使用包裹在同一个 MultiTracker 中的不同跟踪器,但当然,这没什么意义。
  MultiTracker 类只是这些单个对象跟踪器的包装器。正如我们从上一篇文章中知道的那样,单个对象跟踪器是使用第一帧初始化的,并且边界框指示我们想要跟踪的对象的位置。 MultiTracker 将此信息传递给它在内部包装的单个对象跟踪器。
  Python# Specify the tracker type trackerType = "CSRT" # Create MultiTracker object multiTracker = cv2.MultiTracker_create() # Initialize MultiTracker for bbox in bboxes: multiTracker.add(createTrackerByName(trackerType), frame, bbox)
  C++// Specify the tracker type string trackerType = "CSRT"; // Create multitracker Ptr multiTracker = cv::MultiTracker::create(); // Initialize multitracker for(int i=0; i < bboxes.size(); i++) multiTracker->add(createTrackerByName(trackerType), frame, Rect2d(bboxes[i]));
  2.5 第 4 步:更新 MultiTracker 并显示结果
  最后,我们的 MultiTracker 已准备就绪,我们可以在新帧中跟踪多个对象。我们使用 MultiTracker 类的 update 方法来定位新框架中的对象。每个跟踪对象的每个边界框都使用不同的颜色绘制。
  Python# Process video and track objects while cap.isOpened(): success, frame = cap.read() if not success: break # get updated location of objects in subsequent frames success, boxes = multiTracker.update(frame) # draw tracked objects for i, newbox in enumerate(boxes): p1 = (int(newbox[0]), int(newbox[1])) p2 = (int(newbox[0] + newbox[2]), int(newbox[1] + newbox[3])) cv2.rectangle(frame, p1, p2, colors[i], 2, 1) # show frame cv2.imshow("MultiTracker", frame) # quit on ESC button if cv2.waitKey(1) & 0xFF == 27: # Esc pressed break
  C++while(cap.isOpened()) { // get frame from the video cap >> frame; // Stop the program if reached end of video if (frame.empty()) break; //Update the tracking result with new frame multiTracker->update(frame); // Draw tracked objects for(unsigned i=0; igetObjects().size(); i++) { rectangle(frame, multiTracker->getObjects()[i], colors[i], 2, 1); } // Show frame imshow("MultiTracker", frame); // quit on x button if (waitKey(1) == 27) break; }
  3.完整代码
  C++#include  #include  using namespace cv; using namespace std; vector trackerTypes = {"BOOSTING", "MIL", "KCF", "TLD", "MEDIANFLOW", "GOTURN", "MOSSE", "CSRT"}; // 按名称创建跟踪器 Ptr createTrackerByName(string trackerType) { Ptr tracker; if (trackerType == trackerTypes[0]) tracker = TrackerBoosting::create(); else if (trackerType == trackerTypes[1]) tracker = TrackerMIL::create(); else if (trackerType == trackerTypes[2]) tracker = TrackerKCF::create(); else if (trackerType == trackerTypes[3]) tracker = TrackerTLD::create(); else if (trackerType == trackerTypes[4]) tracker = TrackerMedianFlow::create(); else if (trackerType == trackerTypes[5]) tracker = TrackerGOTURN::create(); else if (trackerType == trackerTypes[6]) tracker = TrackerMOSSE::create(); else if (trackerType == trackerTypes[7]) tracker = TrackerCSRT::create(); else { cout << "Incorrect tracker name" << endl; cout << "Available trackers are: " << endl; for (vector::iterator it = trackerTypes.begin() ; it != trackerTypes.end(); ++it) std::cout << " " << *it << endl; } return tracker; } // 用随机颜色填充vector void getRandomColors(vector &colors, int numColors) { RNG rng(0); for(int i=0; i < numColors; i++) colors.push_back(Scalar(rng.uniform(0,255), rng.uniform(0, 255), rng.uniform(0, 255))); } int main(int argc, char * argv[]) { cout << "Default tracking algoritm is CSRT" << endl; cout << "Available tracking algorithms are:" << endl; for (vector::iterator it = trackerTypes.begin() ; it != trackerTypes.end(); ++it) std::cout << " " << *it << endl;  // 设置跟踪器类型。更改此项以尝试不同的跟踪器。 string trackerType = "CSRT"; // 设置跟踪算法和视频的默认值 string videoPath = "videos/run.mp4";  // 使用跟踪算法初始化 MultiTracker vector bboxes; // 创建一个视频捕获对象来读取视频 cv::VideoCapture cap(videoPath); Mat frame; // 如果无法读取视频文件则退出 if(!cap.isOpened()) { cout << "Error opening video file " << videoPath << endl; return -1; } // 读取第一帧 cap >> frame;  // 在对象上绘制边界框 // selectROI 的默认行为是从中心开始绘制框 // 当fromCenter设置为false时,可以从左上角开始画框 bool showCrosshair = true; bool fromCenter = false; cout << " ========================================================== "; cout << "OpenCV says press c to cancel objects selection process" << endl; cout << "It doesn"t work. Press Escape to exit selection process" << endl; cout << " ========================================================== "; cv::selectROIs("MultiTracker", frame, bboxes, showCrosshair, fromCenter);  // 如果没有要跟踪的对象,则退出 if(bboxes.size() < 1) return 0;  vector colors; getRandomColors(colors, bboxes.size());  // 创建 multitracker Ptr multiTracker = cv::MultiTracker::create(); // 初始化 multitracker for(int i=0; i < bboxes.size(); i++) multiTracker->add(createTrackerByName(trackerType), frame, Rect2d(bboxes[i]));  // 处理视频和跟踪对象 cout << " ========================================================== "; cout << "Started tracking, press ESC to quit." << endl; while(cap.isOpened()) { // 从视频中获取帧 cap >> frame;  // 如果到达视频结尾,则停止程序 if (frame.empty()) break; // 用新帧更新跟踪结果 multiTracker->update(frame); // 绘制跟踪对象 for(unsigned i=0; igetObjects().size(); i++) { rectangle(frame, multiTracker->getObjects()[i], colors[i], 2, 1); }  // 显示 frame imshow("MultiTracker", frame);  // 按ESC退出 if (waitKey(1) == 27) break;  }  }
  Python#!/usr/bin/python from __future__ import print_function import sys import cv2 from random import randint trackerTypes = ["BOOSTING", "MIL", "KCF","TLD", "MEDIANFLOW", "GOTURN", "MOSSE", "CSRT"] def createTrackerByName(trackerType): # Create a tracker based on tracker name if trackerType == trackerTypes[0]: tracker = cv2.TrackerBoosting_create() elif trackerType == trackerTypes[1]: tracker = cv2.TrackerMIL_create() elif trackerType == trackerTypes[2]: tracker = cv2.TrackerKCF_create() elif trackerType == trackerTypes[3]: tracker = cv2.TrackerTLD_create() elif trackerType == trackerTypes[4]: tracker = cv2.TrackerMedianFlow_create() elif trackerType == trackerTypes[5]: tracker = cv2.TrackerGOTURN_create() elif trackerType == trackerTypes[6]: tracker = cv2.TrackerMOSSE_create() elif trackerType == trackerTypes[7]: tracker = cv2.TrackerCSRT_create() else: tracker = None print("Incorrect tracker name") print("Available trackers are:") for t in trackerTypes: print(t)  return tracker if __name__ == "__main__": print("Default tracking algoritm is CSRT  " "Available tracking algorithms are: ") for t in trackerTypes: print(t) trackerType = "CSRT" # Set video to load videoPath = "videos/run.mp4"  # Create a video capture object to read videos cap = cv2.VideoCapture(videoPath)  # Read first frame success, frame = cap.read() # quit if unable to read the video file if not success: print("Failed to read video") sys.exit(1) ## Select boxes bboxes = [] colors = [] # OpenCV"s selectROI function doesn"t work for selecting multiple objects in Python # So we will call this function in a loop till we are done selecting all objects while True: # draw bounding boxes over objects # selectROI"s default behaviour is to draw box starting from the center # when fromCenter is set to false, you can draw box starting from top left corner bbox = cv2.selectROI("MultiTracker", frame) bboxes.append(bbox) colors.append((randint(64, 255), randint(64, 255), randint(64, 255))) print("Press q to quit selecting boxes and start tracking") print("Press any other key to select next object") k = cv2.waitKey(0) & 0xFF if (k == 113): # q is pressed break  print("Selected bounding boxes {}".format(bboxes)) ## Initialize MultiTracker # There are two ways you can initialize multitracker # 1. tracker = cv2.MultiTracker("CSRT") # All the trackers added to this multitracker # will use CSRT algorithm as default # 2. tracker = cv2.MultiTracker() # No default algorithm specified # Initialize MultiTracker with tracking algo # Specify tracker type  # Create MultiTracker object multiTracker = cv2.MultiTracker_create() # Initialize MultiTracker for bbox in bboxes: multiTracker.add(createTrackerByName(trackerType), frame, bbox) # Process video and track objects while cap.isOpened(): success, frame = cap.read() if not success: break  # get updated location of objects in subsequent frames success, boxes = multiTracker.update(frame) # draw tracked objects for i, newbox in enumerate(boxes): p1 = (int(newbox[0]), int(newbox[1])) p2 = (int(newbox[0] + newbox[2]), int(newbox[1] + newbox[3])) cv2.rectangle(frame, p1, p2, colors[i], 2, 1) # show frame cv2.imshow("MultiTracker", frame)  # quit on ESC button if cv2.waitKey(1) & 0xFF == 27: # Esc pressed break
  参考目录
  https://learnopencv.com/multitracker-multiple-object-tracking-using-opencv-c-python/

明天元旦,记得吃这3道传统美食,新年吃寓意好,2022年好运相伴明天元旦,记得吃这3道吉祥菜,新年吃寓意好,2022年好运相伴。转眼明天就是元旦小长假啦,迎来了新的一年2022年,今晚的跨年你会和谁一起过呢?元旦,是每个新的一年的第一天,元有开明天元旦,要吃3道开运菜,做2件开运事,2022年好运连连民国以前,中国是没有元旦节的,相对于中国的传统节日,元旦是一个新的节日,但元旦这词在中国的古籍里早已有之,最早见于晋书,指的是正月一日,历史上说的元旦时间并不一致,如夏时期以春一月花滑组合隋文静韩聪信心足,决心复制前辈荣光隋文静韩聪本赛季连续夺冠,表现出很高的竞技状态。资料图新华社新京报讯(首席记者孙海光)在中国冰雪队伍中,迄今只有4支队伍拿到过冬奥会金牌,花样滑冰便是其中一支。即将到来的北京冬奥会TLG放出弹弓组合,GK十二分钟解决战斗,TLG虽败犹荣王者荣耀在结束了KPL秋季赛后,又迎来了高质量的挑战者杯。本次挑战者杯不仅邀请了KPL联赛队伍,还邀请了全国的顶尖战队。拖米组建的TLG战队在这次的挑战者杯中大放异彩,甚至有望晋级王者荣耀诸葛小明双排成上分陷阱,这五大组合真的会掉分大家好,我是秋豆。在游戏中,肯定有和朋友一起双排上分的经历。一般双排的收益都是112,但是有些热门的双排组合收益却是112,这就让人感到奇怪了。相信大家都有过被小明诸葛亮支配过的恐挑战者杯选拔赛大结局,GK拿出弹弓组合之后,TLG就已经输了苦瓜原创,翻版必究!苦瓜电竞让你体验不一样的电竞主播圈!王者荣耀挑战者杯赛事精彩依旧在火热进行中,关注赛程的都清楚,比赛已经来到了第四日的大战,也是来到了最终的大结局,最终晋级正赛十四款奢华大牌设计的游戏组合Herms象棋LouisVuitton足球桌Gucci电游SaintLaurent魔术方块令人乍舌的奢华玩味。GUCCIXBOX电游组合114Gucci联名持续不断,近期更是破天荒寒武纪生物介绍皮卡虫皮卡虫又被叫做皮凯亚虫,也叫做皮克鱼,来自于寒武纪,现在已灭绝。在1911年的时候对这种生物进行描述,并且同时发现了这种生物的化石。在化石上能够看到非常规则的结,所以属于多毛纲。头进入决赛圈,必须换这些武器,步枪狙击组合已经落后了大家好,我是你们的老朋友小信。决赛圈是吃鸡的关键圈,在进圈之前我们需要做好充分的准备才有更高的胜算。如何挑选一套合适自己的组合是非常重要的,小伙伴们知道比较受欢迎的组合有哪些吗?接地外生物与宇宙大爆炸之幻想宇宙万物都那么的井然有序经过诸多的理论猜想,人类完成了宇宙大爆炸的猜想,宇宙是否是经过大爆炸而产生的如井然有序的星体,各种星体又有各种引力,万球互相围绕转动的时间空间片区都差不多,花滑组合隋文静韩聪信心足,决心复制前辈荣光隋文静韩聪本赛季连续夺冠,表现出很高的竞技状态。资料图新华社新京报讯(首席记者孙海光)在中国冰雪队伍中,迄今只有4支队伍拿到过冬奥会金牌,花样滑冰便是其中一支。即将到来的北京冬奥会
灵耀X凌锋的夏日奇妙之旅夏天到了,天气好热,上班好累完全打不起精神出差在外,负重过大,动不动汗流浃背等等少年,也许你需要它的帮助晚上忘记给电脑充电,第二天要出差深夜11点主人结束了一天的工作,关上电脑刷起夏日太热不出门在家开空调有什么游戏玩?我会选择两个游戏,一个是星际争霸2和沙石镇时光星际争霸2星际争霸2一次1030分钟,很好玩。我玩的都是合作任务或是每周突变任务。暗黑破坏神不朽暗黑破坏神不朽不好玩,就是过任务,跟着2022年(七夕)购机指南!有哪些摩托罗拉手机值得购买?说到摩托罗拉相信对于许多80后和90后的小伙伴来说并不陌生,不过现在的摩托罗拉已经不是以前的摩托罗拉了。现在的摩托罗拉是联想旗下主打性价比的一个子品牌,如果大家在购买手机的过程当中夏日海滩借着夏天的名义,一起去看海吧!海滩是承载记忆的地方,有多少人在这里诉说心事,有多少人在这里写过名字。夏日和海滩是绝配。正午烈日炎炎,海边往往人声鼎沸。到了下午三四点钟,阳光不那么刺七夕好去处!到海拔1194米的避暑仙境露营,抬头见银河,是夏日清凉神地!阵阵晚风吹动着松涛,吹响这风铃声如天籁,站在这城市的寂静处,让一切喧嚣走远听着许巍唱的旅行,你是否也在蠢蠢欲动世界那么大,只想找个静谧之地安营扎寨,住在风景里就听夏夜晚风,观漫天星61岁蔡振华疑卸任退休!叱咤体坛多年功绩显著,执掌足协饱受争议根据官网显示,今年年初刚刚就任副主席的蔡振华,名字已经从领导班子中消失。根据国内大部分领导岗位的退休年龄规定,61岁的蔡振华基本已经卸任,正式开始退休生活。在中国体坛的众多上层领导追忆一次没能实现的壮举去全国各地拍摄毛主席塑像追忆一次没能实现的壮举去全国各地拍摄毛主席塑像作者江苏徐州孟宪达前段时间看到了几个自驾游的视频,忽然打开了我尘封了十几年的记忆。在十几年前,我曾经想有一次壮举,用我的残疾人用三轮车探秘森林里的游乐场寻宝小径,清凉的小众景区真香上周从延庆回程途中,顺路打卡了一个小众景区。景区清幽,几乎空无一人。大森林中的游乐场,古朴又好玩寻宝小径,有九个宝箱等着孩子去揭秘最让人惊喜的是,这里植被茂密,有相当长的一段路是在乐山乐水邓小平我不喜欢室内游泳池,喜欢在大自然里游泳,自由度大一些,有一股气势文曹应旺孔子说智者乐水。邓小平就属乐水派。他喜爱游泳,从年轻时代起就养成了洗冷水浴的习惯,几十年如一日,一直没有间断。1983年夏天,邓小平在东北和华北一些地区视察之后,来到大连市他们的故事旅游人自救导游做网红地接抵押房产沉浸文娱进退两难从业27年,经历过非典日本地震东南亚海啸欧洲罢工等事件对旅游业的影响,但周卫红从来没有像现在这样焦虑。今年上半年是我觉得旅游业受挫最严重的半年,疫情虽然持续了2年多,但我觉得今年的性价比超高的EHOMEWEI便携显示器,华为三星手机一秒变电脑在这个年代,智能手机掌上游戏机笔记本电脑,各种电子设备都是朝着轻巧便携前进。显示器也不例外。虽然是个新兴类目,但便携显示器已经逐渐往主流产品靠拢,越来越多的人都把它当成第二屏使用,