C/C++ OpenCV 矩陣運算

C/C++ OpenCV 矩陣運算詳解 💡

OpenCV 是一個強大的開源計算機視覺和機器學習庫,它提供了豐富的矩陣運算功能,這對于圖像處理和計算機視覺算法至關重要。本文將詳細介紹如何使用 C/C++ 和 OpenCV 進行常見的矩陣運算。


矩陣的創建與初始化

在進行矩陣運算之前,我們首先需要知道如何創建和初始化矩陣。OpenCV 提供了 cv::Mat 類來處理矩陣。

創建矩陣

有多種方法可以創建 cv::Mat 對象:

  • 使用構造函數:可以指定行數、列數、數據類型以及可選的初始值。

    #include <opencv2/opencv.hpp>
    #include <iostream>int main() {// 創建一個 3x3 的浮點型矩陣,所有元素初始化為 0cv::Mat matrix1 = cv::Mat::zeros(3, 3, CV_32F);std::cout << "Matrix1:\n" << matrix1 << std::endl;// 創建一個 2x4 的整型矩陣,所有元素初始化為 1cv::Mat matrix2 = cv::Mat::ones(2, 4, CV_8UC1); // 8位無符號單通道std::cout << "Matrix2:\n" << matrix2 << std::endl;// 創建一個具有特定尺寸和類型的矩陣,不初始化cv::Mat matrix3(4, 2, CV_64FC3); // 64位浮點型三通道// 使用 Scalar 初始化cv::Mat matrix4 = cv::Mat(3, 3, CV_32F, cv::Scalar(5.0));std::cout << "Matrix4:\n" << matrix4 << std::endl;// 通過 C/C++ 數組創建double data[] = {1, 2, 3, 4, 5, 6};cv::Mat matrix5 = cv::Mat(2, 3, CV_64F, data);std::cout << "Matrix5:\n" << matrix5 << std::endl;return 0;
    }
    
  • cv::Mat::create() 方法:如果矩陣已經存在,此方法會重新分配內存(如果需要)。

    cv::Mat matrix;
    matrix.create(4, 4, CV_32F);
    
  • cv::Mat::eye() 方法:創建單位矩陣。

    cv::Mat identityMatrix = cv::Mat::eye(3, 3, CV_64F);
    std::cout << "Identity Matrix:\n" << identityMatrix << std::endl;
    

訪問矩陣元素

可以使用 at() 方法或者直接通過指針訪問矩陣元素。at() 方法更安全,因為它會進行邊界檢查。

#include <opencv2/opencv.hpp>
#include <iostream>int main() {cv::Mat matrix = (cv::Mat_<double>(3,3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);std::cout << "Original Matrix:\n" << matrix << std::endl;// 使用 at() 方法訪問和修改元素double& elem = matrix.at<double>(0, 0); // 獲取 (0,0) 處的元素引用elem = 100.0;std::cout << "Modified Matrix (at()):\n" << matrix << std::endl;// 直接通過指針訪問 (效率更高,但不安全)// 注意:需要確保數據類型匹配double* p = matrix.ptr<double>(1); // 獲取第二行的指針p[1] = 200.0; // 修改第二行第二列的元素 (1,1)std::cout << "Modified Matrix (ptr):\n" << matrix << std::endl;return 0;
}

基本的算術運算 ?????

OpenCV 支持對矩陣進行各種基本的算術運算。這些運算可以是矩陣與標量之間的運算,也可以是矩陣與矩陣之間的運算。

矩陣與標量運算

可以直接使用標準的算術運算符:

#include <opencv2/opencv.hpp>
#include <iostream>int main() {cv::Mat matrixA = (cv::Mat_<double>(2,2) << 1, 2, 3, 4);double scalar = 5.0;cv::Mat result;// 加法result = matrixA + scalar;std::cout << "MatrixA + Scalar:\n" << result << std::endl;// 減法result = matrixA - scalar;std::cout << "MatrixA - Scalar:\n" << result << std::endl;result = scalar - matrixA;std::cout << "Scalar - MatrixA:\n" << result << std::endl;// 乘法result = matrixA * scalar;std::cout << "MatrixA * Scalar:\n" << result << std::endl;// 除法result = matrixA / scalar;std::cout << "MatrixA / Scalar:\n" << result << std::endl;return 0;
}

矩陣與矩陣運算 (逐元素)

對于加法、減法和逐元素的乘法、除法,可以使用重載的運算符或 OpenCV 提供的函數。

  • 加法 (+cv::add())
  • 減法 (-cv::subtract())
  • 逐元素乘法 (cv::multiply())
  • 逐元素除法 (cv::divide())
#include <opencv2/opencv.hpp>
#include <iostream>int main() {cv::Mat matrixA = (cv::Mat_<double>(2,2) << 1, 2, 3, 4);cv::Mat matrixB = (cv::Mat_<double>(2,2) << 5, 6, 7, 8);cv::Mat result;// 加法result = matrixA + matrixB;// 或者 cv::add(matrixA, matrixB, result);std::cout << "MatrixA + MatrixB:\n" << result << std::endl;// 減法result = matrixA - matrixB;// 或者 cv::subtract(matrixA, matrixB, result);std::cout << "MatrixA - MatrixB:\n" << result << std::endl;// 逐元素乘法cv::multiply(matrixA, matrixB, result);// 注意: matrixA * matrixB 是矩陣乘法,而不是逐元素乘法std::cout << "Element-wise multiplication (A .* B):\n" << result << std::endl;// 逐元素除法cv::divide(matrixA, matrixB, result);std::cout << "Element-wise division (A ./ B):\n" << result << std::endl;return 0;
}

矩陣乘法 (標準線性代數乘法)

使用 * 運算符可以執行標準的矩陣乘法 (m x n 矩陣乘以 n x p 矩陣得到 m x p 矩陣)。或者使用 cv::gemm() 函數 (通用矩陣乘法)。

#include <opencv2/opencv.hpp>
#include <iostream>int main() {cv::Mat matrixA = (cv::Mat_<double>(2,3) << 1, 2, 3, 4, 5, 6);cv::Mat matrixB = (cv::Mat_<double>(3,2) << 7, 8, 9, 10, 11, 12);cv::Mat result;// 矩陣乘法result = matrixA * matrixB;std::cout << "MatrixA * MatrixB (Matrix Multiplication):\n" << result << std::endl;// 使用 cv::gemm()// gemm(src1, src2, alpha, src3, beta, dst, flags)// dst = alpha*src1*src2 + beta*src3cv::Mat matrixC = cv::Mat::zeros(2, 2, CV_64F);cv::gemm(matrixA, matrixB, 1.0, cv::Mat(), 0.0, result, 0); // result = 1.0 * matrixA * matrixBstd::cout << "MatrixA * MatrixB (using gemm):\n" << result << std::endl;return 0;
}

注意: 參與矩陣乘法的兩個矩陣的維度必須兼容 (第一個矩陣的列數等于第二個矩陣的行數)。


其他重要的矩陣運算 ??

OpenCV 還提供了許多其他有用的矩陣運算函數。

轉置 (cv::transpose()Mat::t())

#include <opencv2/opencv.hpp>
#include <iostream>int main() {cv::Mat matrixA = (cv::Mat_<double>(2,3) << 1, 2, 3, 4, 5, 6);cv::Mat transposedMatrix;cv::transpose(matrixA, transposedMatrix);// 或者 transposedMatrix = matrixA.t();std::cout << "Original MatrixA:\n" << matrixA << std::endl;std::cout << "Transposed MatrixA:\n" << transposedMatrix << std::endl;return 0;
}

逆矩陣 (cv::invert()Mat::inv())

只有方陣且非奇異矩陣(行列式不為零)才有逆矩陣。

#include <opencv2/opencv.hpp>
#include <iostream>int main() {cv::Mat matrixA = (cv::Mat_<double>(2,2) << 4, 7, 2, 6);cv::Mat inverseMatrix;// method 可以是 DECOMP_LU, DECOMP_SVD, DECOMP_CHOLESKY (對于對稱正定矩陣)double det = cv::determinant(matrixA);if (det != 0) {cv::invert(matrixA, inverseMatrix, cv::DECOMP_LU);// 或者 inverseMatrix = matrixA.inv(cv::DECOMP_LU);std::cout << "Original MatrixA:\n" << matrixA << std::endl;std::cout << "Inverse MatrixA:\n" << inverseMatrix << std::endl;// 驗證 A * A_inv = Istd::cout << "A * A_inv:\n" << matrixA * inverseMatrix << std::endl;} else {std::cout << "MatrixA is singular, cannot compute inverse." << std::endl;}return 0;
}

行列式 (cv::determinant())

計算方陣的行列式。

#include <opencv2/opencv.hpp>
#include <iostream>int main() {cv::Mat matrixA = (cv::Mat_<double>(3,3) << 1, 2, 3, 0, 1, 4, 5, 6, 0);double det = cv::determinant(matrixA);std::cout << "MatrixA:\n" << matrixA << std::endl;std::cout << "Determinant of MatrixA: " << det << std::endl;return 0;
}

跡 (cv::trace())

計算方陣的跡(主對角線元素之和)。

#include <opencv2/opencv.hpp>
#include <iostream>int main() {cv::Mat matrixA = (cv::Mat_<double>(3,3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);cv::Scalar traceValue = cv::trace(matrixA);std::cout << "MatrixA:\n" << matrixA << std::endl;std::cout << "Trace of MatrixA: " << traceValue[0] << std::endl; // trace返回一個Scalarreturn 0;
}

范數 (cv::norm())

計算矩陣的范數(例如 L1 范數、L2 范數、無窮范數)。

#include <opencv2/opencv.hpp>
#include <iostream>int main() {cv::Mat matrixA = (cv::Mat_<double>(1,3) << 3, -4, 0); // 也可以是多維矩陣double normL1 = cv::norm(matrixA, cv::NORM_L1);double normL2 = cv::norm(matrixA, cv::NORM_L2);double normInf = cv::norm(matrixA, cv::NORM_INF);std::cout << "MatrixA: " << matrixA << std::endl;std::cout << "L1 Norm: " << normL1 << std::endl;   // |3| + |-4| + |0| = 7std::cout << "L2 Norm: " << normL2 << std::endl;   // sqrt(3^2 + (-4)^2 + 0^2) = 5std::cout << "Infinity Norm: " << normInf << std::endl; // max(|3|, |-4|, |0|) = 4cv::Mat matrixB = (cv::Mat_<double>(2,2) << 1, 2, 3, 4);double frobeniusNorm = cv::norm(matrixB, cv::NORM_L2); // 對于矩陣,NORM_L2 是 Frobenius 范數std::cout << "MatrixB:\n" << matrixB << std::endl;std::cout << "Frobenius Norm of MatrixB: " << frobeniusNorm << std::endl;return 0;
}

矩陣操作 🛠?

改變形狀 (Mat::reshape())

在不改變數據的情況下改變矩陣的維度。

#include <opencv2/opencv.hpp>
#include <iostream>int main() {cv::Mat matrixA = (cv::Mat_<double>(2,3) << 1, 2, 3, 4, 5, 6);std::cout << "Original MatrixA (2x3):\n" << matrixA << std::endl;// 改變為一個通道,3行 (列數自動計算)cv::Mat reshaped1 = matrixA.reshape(1, 3);std::cout << "Reshaped to 3 rows (1 channel):\n" << reshaped1 << std::endl;std::cout << "Reshaped1 channels: " << reshaped1.channels() << ", rows: " << reshaped1.rows << ", cols: " << reshaped1.cols << std::endl;// 改變為 3 通道,2 行 (假設原始數據可以這樣組織)// 注意:reshape(cn, rows) cn是新的通道數// 原始矩陣是單通道,6個元素。如果reshape(3, 2),則變成2行1列,3通道。// (1,2,3) 像素1// (4,5,6) 像素2cv::Mat matrixB = cv::Mat::arange(1, 7).reshape(1, 2); // 2行3列,單通道matrixB = matrixB.reshape(3, 2); // 2行1列,3通道std::cout << "Original MatrixB (2x3, 1 channel then reshaped to 2x1, 3 channels):\n" << matrixB << std::endl;std::cout << "MatrixB value at (0,0) Ch0: " << matrixB.at<cv::Vec3b>(0,0)[0] << std::endl; // 假設是 CV_8UC3// arange 默認是 CV_32Scv::Mat matrixC = (cv::Mat_<int>(1,6) << 1,2,3,4,5,6);std::cout << "Original MatrixC (1x6, 1 channel):\n" << matrixC << std::endl;cv::Mat reshapedC = matrixC.reshape(3, 2); // 2行1列,3通道std::cout << "ReshapedC (2x1, 3 channels):\n" << reshapedC << std::endl;// 訪問 reshapedC.at<cv::Vec3i>(0,0)[0] 等return 0;
}

注意: reshape() 不會復制數據。新的矩陣頭指向原始數據。

合并與拼接 (cv::hconcat(), cv::vconcat())

  • cv::hconcat(): 水平拼接矩陣 (列數增加)
  • cv::vconcat(): 垂直拼接矩陣 (行數增加)
#include <opencv2/opencv.hpp>
#include <iostream>int main() {cv::Mat matrixA = cv::Mat::ones(2, 2, CV_64F);cv::Mat matrixB = cv::Mat::zeros(2, 2, CV_64F);cv::Mat matrixC = cv::Mat::eye(2, 2, CV_64F) * 2;cv::Mat horizontalConcat;cv::hconcat(matrixA, matrixB, horizontalConcat); // 可以傳遞多個矩陣cv::hconcat(horizontalConcat, matrixC, horizontalConcat);std::cout << "Horizontal Concatenation:\n" << horizontalConcat << std::endl;cv::Mat matrixD = cv::Mat::ones(2, 2, CV_64F) * 3;cv::Mat matrixE = cv::Mat::ones(2, 2, CV_64F) * 4;cv::Mat verticalConcat;std::vector<cv::Mat> matrices_to_stack = {matrixD, matrixE};cv::vconcat(matrices_to_stack, verticalConcat); // 可以傳遞一個Mat的vector// 或者 cv::vconcat(matrixD, matrixE, verticalConcat);std::cout << "Vertical Concatenation:\n" << verticalConcat << std::endl;return 0;
}

總結 🎉

OpenCV 提供了非常全面且易于使用的矩陣運算功能。通過 cv::Mat 類及其相關函數,可以高效地執行從基本算術運算到復雜線性代數運算的各種操作。熟練掌握這些運算是進行圖像處理和計算機視覺算法開發的基礎。記得查閱 OpenCV 官方文檔以獲取更詳細的信息和更多高級功能。

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

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

相關文章

基于大模型的 UI 自動化系統

基于大模型的 UI 自動化系統 下面是一個完整的 Python 系統,利用大模型實現智能 UI 自動化,結合計算機視覺和自然語言處理技術,實現"看屏操作"的能力。 系統架構設計 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…

USB擴展器與USB服務器的2個主要區別

在現代辦公和IT環境中&#xff0c;連接和管理USB設備是常見需求。USB擴展器&#xff08;常稱USB集線器&#xff09;與USB服務器&#xff08;如朝天椒USB服務器&#xff09;是兩類功能定位截然不同的解決方案。前者主要解決物理接口數量不足的“近身”連接擴展問題&#xff0c;而…

Nuxt.js 中的路由配置詳解

Nuxt.js 通過其內置的路由系統簡化了應用的路由配置&#xff0c;使得開發者可以輕松地管理頁面導航和 URL 結構。路由配置主要涉及頁面組件的組織、動態路由的設置以及路由元信息的配置。 自動路由生成 Nuxt.js 會根據 pages 目錄下的文件結構自動生成路由配置。每個文件都會對…

驗證負載均衡與彈性伸縮

什么是彈性伸縮&#xff08;Auto Scaling&#xff09;&#xff1f; 彈性伸縮是指 云計算平臺根據實時負載自動調整計算資源&#xff08;如服務器實例、容器Pod&#xff09;數量&#xff0c;以確保系統在高峰時保持穩定&#xff0c;在低谷時節省成本。 什么時候會觸發彈性伸縮&…

區分viewmodel和model職責的方法

gpt回答挺好的&#xff0c;我就分享一下。 1. 最經典的一句話區分 Model&#xff08;Repository/數據層&#xff09;&#xff1a;只負責**“數據獲取/存儲/持久化”和“核心業務算法”**&#xff0c;不依賴UI層和Android框架&#xff0c;可以脫離界面獨立存在。 ViewModel&…

C語言數據結構筆記3:Union聯合體+結構體取8位Bool量

本文銜接上文要求&#xff0c;新增8位bool量的獲取方式。 目錄 問題提出&#xff1a; Union聯合體struct結構體(方式1)&#xff1a; Union聯合體struct結構體(方式2)&#xff1a; BYTE方式讀取&#xff1a; 問題提出&#xff1a; 在STM32單片機的編程中&#xff0c;無法定義Boo…

三種讀寫傳統xls格式文件開源庫libxls、xlslib、BasicExcel的比較

最近準備讀寫傳統xls格式文件&#xff0c;而不是較新的xlsx&#xff0c;詢問DeepSeek有哪些開源庫&#xff0c;他給出了如下的簡介和建議&#xff0c;還給出了相應鏈接&#xff0c;不過有的鏈接已失效。最后還不忘提醒&#xff0c;現在該用xlsx格式了。 以下是幾個可以處理傳統…

從測試角度看待CI/CD,敏捷開發

什么是敏捷開發&#xff1f; 是在高強度反饋的情況下&#xff0c;短周期&#xff0c;不斷的迭代產品&#xff0c;滿足用戶需求&#xff0c;搶占更多的市場 敏捷開發是什么&#xff1f; 是一種產品快速迭代的情況下&#xff0c;降低出錯的概率&#xff0c;具體會落實到公司的…

figma MCP + cursor如何將設計稿生成前端頁面

一、準備工作 figma MCP需要通過figma key來獲取設計稿權限&#xff0c;key的生成步驟如下 1. 打開figma網頁版/APP&#xff0c;進入賬戶設定 2. 點擊生成token 3. 填寫內容生成token(一定要確認復制了&#xff0c;不然關閉彈窗后就不會顯示了) 二、配置MCP 4. 進入到cursor…

git互聯GitHub 使用教程

一、下載git Git 公司 右鍵 git config --global user.name "name" git config --global user.email "email" ssh-keygen -t rsa -C email &#xff1a;生成的ssh密鑰需要到github 網站中保存ssh 二、GitHub新建repository 三、本地git互聯GitHub 找…

“輕量應用服務器” vs. “云服務器CVM”:小白入門騰訊云,哪款“云機”更適合你?(場景、配置、價格對比解析)

更多云服務器知識&#xff0c;盡在hostol.com 當你第一次踏入騰訊云這個“數字百貨大樓”&#xff0c;面對琳瑯滿目的“云產品”&#xff0c;是不是有點眼花繚亂&#xff0c;特別是看到“輕量應用服務器”和“云服務器CVM”這兩位都號稱能幫你“安家落戶”的“云主機”時&…

MongoDB學習和應用(高效的非關系型數據庫)

一丶 MongoDB簡介 對于社交類軟件的功能&#xff0c;我們需要對它的功能特點進行分析&#xff1a; 數據量會隨著用戶數增大而增大讀多寫少價值較低非好友看不到其動態信息地理位置的查詢… 針對以上特點進行分析各大存儲工具&#xff1a; mysql&#xff1a;關系型數據庫&am…

Qt學習及使用_第1部分_認識Qt---Qt簡介

前言 學以致用,通過QT框架的學習,一邊實踐,一邊探索編程的方方面面. 參考書:<Qt 6 C開發指南>(以下稱"本書") 標識說明:概念用粗體傾斜.重點內容用(加粗黑體)---重點內容(紅字)---重點內容(加粗紅字), 本書原話內容用深藍色標識,比較重要的內容用加粗傾斜下劃線…

Python語法基礎篇(包含類型轉換、拷貝、可變對象/不可變對象,函數,拆包,異常,模塊,閉包,裝飾器)

Python語法基礎篇&#xff08;二&#xff09; 類型轉換拷貝可變對象與不可變對象可變對象不可變對象 函數拆包異常模塊閉包裝飾器 &#x1f439;&#x1f439;&#x1f439;&#x1f439;&#x1f439;一只正在努力學習計算機技術的小倉鼠&#xff0c;尾部有課程鏈接哦~&#x…

錄制mp4

目錄 單線程保存mp4 多線程保存mp4 rtsp ffmpeg錄制mp4 單線程保存mp4 import cv2 import imageiocv2.namedWindow(photo, 0) # 0窗口大小可以任意拖動&#xff0c;1自適應 cv2.resizeWindow(photo, 1280, 720) url "rtsp://admin:aa123456192.168.1.64/h264/ch1/main…

ISBN書號查詢接口如何用PHP實現調用?

一、什么是ISBN書號查詢接口 ISBN數據查詢接口是一項圖書信息查詢服務。它基于全球通用的ISBN編碼系統&#xff0c;幫助用戶快速獲取圖書的詳細信息&#xff0c;包括書名、作者、出版社、出版時間、價格、封面等關鍵字段。 該接口廣泛應用于電商平臺、圖書館管理系統、二手書…

Redis底層數據結構之深入理解跳表(2)

上一篇文章中我們詳細講述了跳表的增添、查找和修改的操作&#xff0c;這篇文章我們來講解一下跳表在多線程并發時的安全問題。在Redis中&#xff0c;除了網絡IO部分和大文件的后臺復制涉及到多線程外&#xff0c;其余任務執行時全部都是單線程&#xff0c;這也就意味著在Redis…

Go語言依賴管理與版本控制-《Go語言實戰指南》

在現代軟件開發中&#xff0c;項目的第三方依賴和版本控制扮演著至關重要的角色。Go 語言自 Go 1.11 引入 Modules&#xff08;模塊化管理&#xff09;以來&#xff0c;已經實現了內建的依賴管理機制&#xff0c;徹底擺脫了傳統 GOPATH 模式的限制。 本章將深入探討如何使用 Go…

Appium+python自動化(十一)- 元素定位- 下

1、 List定位 List顧名思義就是一個列表&#xff0c;在python里面也有list這一個說法&#xff0c;如果你不是很理解什么是list&#xff0c;這里暫且理解為一個數組或者說一個集合。首先一個list是一個集合&#xff0c;那么他的個數也就成了不確定性&#xff0c;所以這里需要用復…

stress 服務器壓力測試的工具學習

一、stress 工具介紹 tress 是一種工具&#xff0c;可以對符合 POSIX 標準的操作系統施加可配置數量的 CPU、內存、I/O 或磁盤壓力&#xff0c;并報告其檢測到的任何錯誤。 stress 不是一個基準測試。它是由系統管理員用來評估其系統擴展性的工具&#xff0c;由內核程序員用來…