OpenCV Mat UMat GpuMat Matx HostMem InputArray等設計哲學

一、概覽:

GpuMat對應于cuda;HostMem 可以看作是一種特殊的Mat,其存儲對應cuda在主機分配的鎖頁內存,可以不經顯示download upload自動轉變成GpuMat(但是和GpuMat并無繼承關系);UMat對應于opencl的存儲 Matx指代常量Mat,編譯時即確定:InputArray則是一種代理模式。 注意,InputAray和Mat UMat GpuMat Matx等無繼承關系!!

二、然后我們通過幾個點來深入了解一下opencv為何這么設計,以及一些細節。

一、為何一些數據結構之間有時候可以轉換有時候不可以

首先要知道opencv的數據結構本質是要管理一塊存儲,也許是主機內存也許是cuda顯存 也許是opencl存儲 那么,無論是Mat Matx 還是其他數據結構,本質上都是一個header+一個數據指針,不同數據結構之間并無繼承關系。那么有一個情況需要解釋,比如HostMem和Mat同樣是主機內存,那么可以HostMem就會有從HostMem轉變為Mat的構造函數,同時因為HostMem是cuda分配的,如果是帶有deviceMapped的主機內存(opencv管這叫shared HostMem),也可以調用轉變為GpuMat的構造函數,需要強調,這些構造函數本質上是轉移了data指針并創造了一個新的header。

二、為何InputArray不是一個基類?

對于許多OpenCV的C++開發者來說,第一次在函數簽名中遇到 cv::InputArraycv::OutputArray 時,心中難免會產生疑問:“這到底是什么類型?為什么我不直接傳遞 cv::Mat?” 當我們進一步發現,MatGpuMat 甚至 std::vector 都可以被傳遞給一個 InputArray 參數時,這種好奇心會變得更加強烈。

這背后,隱藏著OpenCV設計者們關于性能、靈活性和擴展性的深刻思考。本文將結合我們對OpenCV數據結構的理解,深入探討這個看似“奇怪”卻極其精妙的設計選擇。

1、舞臺上的演員們:OpenCV的數據江湖

在深入探討設計哲學之前,我們必須先認識一下舞臺上的主要“演員們”。它們的核心任務都是管理一塊內存,但這塊內存的“家”卻各不相同。

  • cv::Mat: 最家喻戶曉的明星。它是一個通用的N維數組容器,主要負責管理主機(CPU)內存。它是OpenCV圖像處理的基石。
  • cv::cuda::GpuMat: CUDA陣營的先鋒。它專門管理在NVIDIA GPU顯存中的數據,是進行CUDA加速運算的主體。
  • cv::cuda::HostMem: GpuMat 的得力助手。它可以看作是一種特殊的Mat,其數據存儲在由CUDA分配的**主機端鎖頁內存(Pinned Memory)**中。這種內存的特殊之處在于,它可以被GPU直接訪問(DMA),從而極大地加速了主機與設備之間的數據傳輸,甚至可以實現數據流的并發。
  • cv::UMat: OpenCL陣營的代表,透明計算的未來。它是一個更為抽象的容器,其管理的內存可能在CPU上,也可能在GPU、DSP或其他OpenCL設備上。UMat 的美妙之處在于它能根據計算上下文自動處理數據同步,對開發者隱藏了復雜的內存遷移操作。
  • cv::Matx: 輕量級的“便簽條”。它是一個小尺寸、固定大小的矩陣,其內存通常直接在**棧(Stack)**上分配。由于大小在編譯時就已確定,避免了堆內存分配的開銷,非常適合用于表示3D點、像素值等小型數據。
  • std::vector: 來自C++標準庫的“外援”。無論是std::vector<Point>還是std::vector<float>,它們都是OpenCV算法中常見的數據結構。

關鍵點:這些數據結構,尤其是 MatGpuMatUMatstd::vector,彼此之間并無繼承關系。它們是獨立的、為了不同目的而設計的類。

2、核心挑戰:如何讓一個函數“通吃”所有數據類型?

現在,問題來了。假設我們要寫一個函數,比如計算數組的均值。我們希望這個函數既能處理CPU上的Mat,也能處理GPU上的GpuMat,甚至還能處理一個std::vector<float>

一個遵循傳統面向對象(OOP)思路的開發者可能會立刻想到:繼承!

我們可以設計一個抽象基類 Array,然后讓 MatGpuMat 等都公有繼承自它:

// 一個看似很美的“繼承”方案(但OpenCV沒有采納)
class Array {
public:virtual ~Array() {}virtual int getRows() const = 0;// ... 其他通用接口
};class Mat : public Array { /*...*/ };
class GpuMat : public Array { /*...*/ };// 函數簽名
void calculateMean(const Array& arr);

然而,這個方案存在三個對于高性能計算庫而言幾乎是致命的缺陷。

  1. 性能的枷鎖——虛函數開銷:為了實現多態,基類中的函數必須是虛函數。這意味著每個對象都需要額外存儲一個虛函數表指針,并且每次函數調用都需要一次間接尋址。在像素級的海量循環中,這種微小的開銷會被無限放大,違背了OpenCV追求極致性能的初衷。

  2. 靈活性的噩夢——侵入式設計:這個方案最大的問題是,它要求所有被處理的類型都必須從Array繼承。我們不可能去修改C++標準庫,讓std::vector繼承自我們的Array!我們也無法讓一個C風格的原始數組指針繼承一個類。這種“侵入式”的設計會極大地限制庫的通用性。

  3. 穩定性的隱患——脆弱的ABI:對于一個被全球開發者使用的庫,保持二進制接口(ABI)的穩定至關重要。一旦基類Array的結構(如增刪虛函數)發生改變,所有依賴它的、已編譯的程序都可能需要重新編譯,這是一場災難。

3、OpenCV的答案:優雅的代理模式(Proxy Pattern)

面對上述挑戰,OpenCV的設計者們給出了一個非凡的答案:代理模式InputArrayOutputArray 就是這個模式的實現者。

InputArray 不是一個基類,而是一個輕量級的“代理”或“適配器”。

它本身不擁有數據,而是像一個經紀人一樣,持有對“真正”數據(Mat, GpuMat, vector…)的引用或指針,并對外提供一個統一的接口。

這種設計是如何工作的呢?

模式一:轉發共同能力

當函數需要執行一個通用操作時(比如獲取尺寸),它會調用InputArray的接口,例如arr.size()InputArray內部會判斷自己當前代理的是哪位“明星”(Mat? GpuMat?),然后將這個調用轉發給實際對象的對應方法。

// 函數實現者視角
void myFunction(cv::InputArray arr) {// 無需關心 arr 到底是 Mat 還是 GpuMat// InputArray 會自動將調用轉發給它代理的對象的 .size() 方法cv::Size sz = arr.size(); // ...
}```這實現了多態的好處,卻沒有虛函數的性能開銷,也無需修改任何原始類。#### 模式二:直接獲取特有能力當需要執行某個特定類型才有的操作時(比如將`GpuMat`傳入一個自定義的CUDA核函數),`InputArray`也提供了一個“逃生通道”。你可以從它那里獲取到原始對象的引用。```cpp
// 函數實現者視角
void myCudaFunction(cv::InputArray arr) {// 確認代理的是GpuMat后,獲取其可寫引用cv::cuda::GpuMat& d_mat = arr.getGpuMatRef(); // 現在可以調用 GpuMat 的所有特有方法了my_cuda_kernel<<<...>>>(d_mat.ptr<float>(), ...);
}

這保證了設計的靈活性和功能的完整性,我們不會因為使用了代理而丟失對底層對象的完全控制。

結論:一場工程智慧的勝利

現在,我們可以清晰地回答最初的問題了。

OpenCV之所以不采用傳統的繼承體系,而是設計出InputArray這樣的代理類,是為了在一套API中,同時實現三個看似矛盾的目標:

  1. 極致的性能:避免了虛函數帶來的開銷。
  2. 無與倫比的靈活性:通過非侵入式的設計,使其能夠適配MatGpuMatUMatstd::vector等眾多類型,而無需它們做出任何改變。
  3. 堅如磐石的穩定性:代理類本身結構穩定,易于擴展以支持新類型,而不會破壞二進制兼容性。

InputArray的設計哲學,是典型的用組合(代理是一種組合形式)優于繼承的工程實踐。它或許在初學時帶來一絲困惑,但一旦理解其背后的深意,你便會由衷地贊嘆這種設計的優雅與強大。它不僅僅是一個技術選擇,更是OpenCV作為一個高性能、高通用性計算庫的立身之本。

InputArray除了可以作為通用接口接受不同數據結構外,還有什么作用?

兩個層面:抽象接口的轉發具體對象的直接訪問。這兩種模式是相輔相成的。

模式一:轉發/代理 (Forwarding/Delegation) - 處理“共同能力”

當一個操作是所有或大多數數組類型(Mat, UMat, GpuMat, vector…)都應該具備的通用能力時,_OutputArray 類會為這個操作提供一個自己的成員函數。

最典型的“共同能力”就是:

  • 創建/分配內存 (create, createSameSize)
  • 釋放/清空 (release, clear)
  • 賦值 (setTo, assign, move)

工作流程:

  1. 函數實現者調用 OutputArray 的方法,例如 dst.create(size, type)
  2. _OutputArray 內部會檢查它當前“代理”的是哪種具體對象(Mat? GpuMat?)。
  3. 然后,它將這個調用**轉發(Forward)**給它所代理的那個具體對象的相應方法。
    • 如果 dst 包裹的是一個 Mat,它內部會調用 the_mat.create(size, type)
    • 如果 dst 包裹的是一個 GpuMat,它內部會調用 the_gpumat.create(size, type)

這么做的好處是多態性代碼復用。函數實現者無需寫 if-else 來判斷 dst 的具體類型,只需面向 OutputArray 這個統一的抽象接口編程即可。這使得一個函數(如 cv::cvtColor)可以無縫地同時支持 MatUMatGpuMat 作為輸出。


模式二:直接獲取 (Direct Access) - 處理“特有能力”

當一個操作是某個具體類(比如 GpuMat特有的能力,而其他類(如 Mat)沒有這個能力時,_OutputArray 接口中就不會包含這個操作。

例如:

  • 直接訪問 GpuMatstep 成員進行指針運算。
  • 調用 Mat 特有的 push_back() 方法。
  • GpuMat 傳遞給一個需要 cudaStream_t 參數的自定義CUDA核函數。

在這種情況下,函數實現者就必須先“揭開”OutputArray 的代理面紗,拿到它背后包裹的那個原始對象。

工作流程:

  1. 函數實現者首先需要知道或判斷 OutputArray 代理的是哪種類型。
  2. 然后調用 get...Ref() 方法,如 dst.getMatRef()dst.getGpuMatRef(),來獲取一個可寫的引用
  3. 拿到這個引用后,就可以像操作一個普通的 MatGpuMat 對象一樣,調用它所有特有的方法和成員。
    這么做的好處是靈活性完整性。它提供了一個“逃生通道”,確保了即使 OutputArray 的抽象接口沒有覆蓋某個功能,開發者依然可以使用具體類的全部能力,不會因為使用了代理類而丟失功能。

總結對比

行為模式調用方式適用場景設計目的
轉發/代理直接調用 dst.create(...), dst.setTo(...)OutputArray 的方法。處理所有數組類型都支持的通用操作抽象與多態:隱藏具體實現,讓函數可以處理多種數據類型。
直接獲取先調用 dst.getMatRef() 等獲取具體引用,再調用該引用的特有方法。處理某個特定數組類型才有的專屬操作靈活性與完整性:不限制開發者使用具體類的全部功能。

OpenCV 的 Input/Output 代理類設計,是一個在高度抽象(為了易用和通用)和完全控制(為了性能和功能完整性)之間取得精妙平衡的典范。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/90437.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/90437.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/90437.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ATR2652SGNSS全頻段低噪聲放大器

ATR2652S是一款具有高增益、低噪聲系數的低噪聲放大器芯片。支持GNSS全頻段信號&#xff0c;同時GNSS 的兩個頻段可以應用于GNSS雙頻導航接收機中。 采用先進的 SiGe 工藝設計和制作&#xff0c;工藝穩定&#xff0c;低噪聲放大器在 GNSS 整個頻段內可以獲得非常好的射頻性能&a…

大數據中心——解讀60頁IDC云數據中心機房運維服務解決方案【附全文閱讀】

該方案主要面向云數據中心運營管理者、IT 運維人員、企業決策者等&#xff0c;旨在解決云資源和業務網絡管理難題&#xff0c;提升 IT 資源掌控能力。方案核心是 EVM VirtualViz 仿真可視化系統&#xff0c;它整合多源數據&#xff0c;提供 3D 仿真展示&#xff0c;實現數據中心…

環境變量-進程概念(7)

文章目錄Linux 真實調度算法1. queue[140]2. bitmap[5] 位圖3. nr_active4. 活躍進程與過期進程環境變量1. 基本概念2. 命令行參數3. PATH 環境變量4. 環境變量具體操作Linux 真實調度算法 下圖是Linux2.6內核中進程隊列的數據結構&#xff0c;也有Linux2.6內核進程O(1)調度算…

為什么數組可以做到時間復雜度為O(1)的隨機訪問

這個問題涉及數組底層結構與內存尋址機制 一、數組元素在內存中連續存儲 數組在內存中會開辟一塊連續地址空間。假設數組A為int類型&#xff0c;共有n個元素&#xff0c;每個元素大小為4字節&#xff0c;那么他們在內存中的存儲結構可能如下&#xff1a;內存地址數組元素A0x100…

《使用Qt Quick從零構建AI螺絲瑕疵檢測系統》——5. 集成OpenCV:讓程序擁有“視力”

目錄一、概述1.1 背景介紹&#xff1a;賦予應用“視力”1.2 學習目標二、集成OpenCV2.1 安裝OpenCV2.2 在Qt項目中配置CMake三、項目數據集介紹與準備四、圖像的橋梁&#xff1a;ImageProvider與格式轉換五、加載、轉換并顯示圖像六、總結與展望一、概述 1.1 背景介紹&#xf…

智慧駕駛疲勞檢測算法的實時性優化

智慧駕駛疲勞檢測&#xff1a;從技術突破到場景革命全球每年因疲勞駕駛引發的交通事故占比超20%&#xff0c;夜間及長途駕駛場景中這一比例更高。當駕駛員出現疲勞甚至暈倒等危險駕駛行為時&#xff0c;傳統檢測手段因依賴單一傳感器或受環境干擾&#xff0c;存在誤報率高、響應…

USRP X440

產品概述 USRP X440 是 Ettus Research 推出的高性能、多通道、寬帶軟件定義無線電&#xff08;SDR&#xff09;系統。基于 Xilinx Zynq UltraScale RFSoC 架構&#xff0c;它提供高密度、相干性的信號收發能力&#xff0c;幫助您快速構建雷達、電子戰&#xff08;EW&#xff0…

[特殊字符] GitHub 2025年7月月度精選項目 Top5

&#x1f680; GitHub 2025年7月月度精選項目 Top5 本月GitHub有哪些值得關注的優質開源項目&#xff1f;我從數千個新項目中&#xff0c;精選了5個有趣 實用 可演示的倉庫 無論你是開發者、AI愛好者、工具控&#xff0c;還是正在做副業產品&#xff0c;這篇文章都值得收藏&a…

微服務架構下的自動化測試策略調優經驗分享

微服務架構下,自動化測試策略需針對分布式特性、服務自治性和高耦合風險進行針對性調整的關鍵調整方向及實施方法: 一、??測試策略重構:分層與契約驅動?? 1. ??測試金字塔升級為鉆石模型?? ??調整邏輯??:傳統金字塔中UI測試占比過高,而微服務需強化契約測試與…

圖論:并查集

入門 久聞并查集的大名&#xff0c;今天來一探究竟&#xff0c;到底什么是并查集&#xff0c;并查集有什么用&#xff1f; 并查集(Disjoint Set Union, DSU)是一種處理不相交集合的合并及查詢問題的數據結構。 其實并查集的作用主要就有兩個&#xff1a; 1、將兩個元素添加到…

告別靜態文檔!Oracle交互式技術架構圖讓數據庫學習“活“起來

&#x1f5fa;? 當數據庫架構圖學會"互動" 想象一下&#xff0c;你正在學習Oracle數據庫架構&#xff0c;面對密密麻麻的靜態文檔和復雜的組件關系圖&#xff0c;是不是常常感到&#xff1a; 像在迷宮里找路&#xff0c;不知道組件間如何協作&#xff1f;想深入了…

day62-可觀測性建設-全鏈路監控zabbix+grafana

&#x1f31f;監控api接口 &#x1f50d;監控zabbix-api接口 生成API tokens命令行測試 curl -s -X POST -H "Content-Type: application/json-rpc" -d {"jsonrpc": "2.0","method": "host.get","params": {&quo…

通過Deepseek找工作

推送的結果如下,對應的AI提示詞在底部: 計算機方向遠程工作職位匯總 整合全球遠程技術崗位 | 支持全地域遠程辦公 | 涵蓋開發、安全、云計算等方向 覆蓋方向:8+個技術領域 薪資范圍:10K-40K/月 工作模式:100%遠程 遠程技術職位列表 職位名稱 技能要求 經驗要求 薪資…

vscode文件顏色,只顯示自己更改的文件顏色、剛git下來的庫,vscode打開后,顯示所有文件都被修改了

問題&#xff1a;git新的庫&#xff0c;然后我用vscode打開&#xff0c;默認顯示所有的文件都更改了&#xff0c;但是我打開他們修改的對比&#xff0c;沒有顯示任何有被修改的地方&#xff0c;是怎么回事 linux/wsl下這么設置就可以了&#xff1a;git config core.autocrlf in…

基于ENMeval包的MaxEnt模型參數優化總結

MaxEnt模型參數優化1. MaxEnt模型優化&#xff1a;增加RM&#xff0c;降低模型過擬合風險&#xff0c;簡易模型&#xff0c;平滑響應曲線&#xff0c;增強模型可解釋性和轉移性&#xff08;生物入侵&#xff09;2. 默認參數&#xff1a;FCLQHP&#xff0c;RM12.1. 基于優化的 M…

Docker實踐:使用Docker部署blog輕量級博客系統

Docker實踐&#xff1a;使用Docker部署blog輕量級博客系統一、blog系統介紹1.1 blog介紹1.2 個人博客系統介紹1.3 個人博客使用場景二、本地環境介紹2.1 本地環境規劃2.2 本次實踐介紹三、本地環境檢查3.1 檢查Docker服務狀態3.2 檢查Docker版本3.3 檢查docker compose 版本四、…

專題:2025電商增長新勢力洞察報告:區域裂變、平臺壟斷與銀發平權|附260+報告PDF、原數據表匯總下載

原文鏈接&#xff1a;https://tecdat.cn/?p43416 當茂名果農對著鏡頭用方言喊出“荔枝現摘現發”&#xff0c;2小時賣出83萬元&#xff1b;當65歲的上海阿姨通過“子女代付”買到人生第一臺智能冰箱——2025年的電商戰場&#xff0c;正在上演三重革命&#xff1a;新興市場的增…

數字化轉型-AI落地金字塔法則

前言 人工智能必須要跟傳統產業結合&#xff0c;融入傳統產業&#xff0c;才能落地&#xff0c;才能產生巨大的倍增個幾何級效果&#xff01;&#xff01; AI不應該停留在工具層面&#xff0c;AI不僅僅是工具&#xff0c;不僅僅是硬件和軟件&#xff0c;而是軟硬結合。人工智能…

SQL Server 字段類型選型指南:什么數據用什么字段

目錄 一、數值型數據 二、日期與時間數據 三、字符串與文本數據 四、布爾值與狀態碼 五、二進制與文件數據 六、唯一標識符&#xff08;GUID&#xff09; 七、枚舉與代碼表設計 八、存儲優化小結 九、總結 在數據庫設計中&#xff0c;字段類型&#xff08;數據類型&am…

酷暑來襲,科技如何讓城市清涼又潔凈?

烈日下的身影&#xff0c;不該被“炙烤”的擔當又是一年盛夏&#xff0c;城市的血管在高溫下脈動&#xff0c;柏油馬路仿佛要融化&#xff0c;空氣中彌漫著灼熱的氣息。此刻&#xff0c;你是否曾留意過那些身影&#xff1f;在烈日下&#xff0c;他們依舊堅守崗位&#xff0c;用…