博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
OpenCV杂记 - Mat in C++
阅读量:2134 次
发布时间:2019-04-30

本文共 6081 字,大约阅读时间需要 20 分钟。

1 概述

Mat类是OpenCV表达二维图片的基础。

经过简单阅读有关Mat的,记录其中提出的几个关键要点。

 

  • OpenCV 的API函数,对图像的输出参数都会自动管理内存,调用者可不必亲自管理。但是如果调用者已经对输出参数配置好了合适大小的内纯,API会自动福永该内存而不是重新申请。
  • 在使用OpenCV提供的API时,大部分时间不需要考虑内存管理。
  • Mat的 = 运算符重载和拷贝构造函数,都不会复制Mat的数据区。若想获得数据区的副本,则需要使用 Mat::clone()和Mat::copyTo()函数。

OpenCV使用BGR格式存储三原色。

对于二维的Mat,可以使用 << 操作符将矩阵内容显示在屏幕上(插入流)。

Mat的数据格式使用预定义的类似一样的宏。其中8U代表8bit的无符号整数,C3代表每个pixel有三个通道,8UC3即表示每个pixcel共需要24 bit,3 byte的数据进行存储。注意,在C++代码中,若没有申明using namespace cv; 那么在使用CV_8UC3一类的宏时,不能在前面加 cv:: 作用域修饰,这会导致预编译器在做宏展开时报错

创建一个Mat可以使用如下形式的语句。

 

cv::Mat M(2,2, cv::CV_8UC3, cv::Scalar(0,0,255));

 

其中cv::Scalar是一个4元标量,这里仅使用了其前3个元素。

 

OpenCV提供类似于MATLAB的eye(), ones(), zeros()函数,但需要指定数据格式。

Mat的 << 操作符支持使用不同的格式输出,输出的格式涵盖python默认,numpy和c语言的格式。

 

经过学习Mat的一些特性,了解到Mat实际是为了处理图像而设计的一种矩阵表示方法,是图像表示成了矩阵,而不是用矩阵表示图像。这里我们不能假定Mat的所有特性与一般意义下的矩阵是一致的,这充分体现在Mat的数据格式和Mat的算术运算上。

 

2 获取单个数据

像std::vector那样提供了at()函数,但at()是一个模板函数,需要在使用是制定数据类型。

 

3 算数运算

对于一个提供矩阵表达和运算的库,应该测试一下某些关键函数的行为。这里测试了转置函数,加法,减法,数乘和矩阵乘法,确实发现了一些OpenCV的特性。

3.1 转置

与预想基本一致,Mat的转置函数t(), 返回一个类型的对象,可以参与其他算数运算或者赋值给另外一个Mat对象。当进行赋值时,例如B = A.t(); B将不共享A的存储,B与A的存储是独立的,互不影响。

 

3.2 加法,数乘

测试了标量数值与矩阵的加减法,也测试了矩阵之间的加减法。OpenCV重载了诸如 +, -, *, +=, -=, *=这类运算符,但仅限于数据类型一致的情况。数据类型不一致时需要使用函数接口来完成特定的运算。例如加法使用函数。

另外,对于矩阵加减法,数与矩阵加减法,在使用+, += 或者相应的减法运算符时,OpenCV会根据当前的数据格式进行自动截断。例如CV_8UC1类型的数据,最大值为255。所有加法运算结果超过255的元素,都将截断为255。这正是处理图像数据时需要的特性,而不能用于一般情况下的矩阵运算。当需要进行更一般的运算时,需要使用更大的数据深度,或者使用浮点数据类型。上述关于数据自动截断的特性,cv::add()函数同样具有。

 

20200508更新

当前本机OpenCV版本已更新至4.1+.

关于 函数,如下C++代码的和执行结果是合理的。

#include 
#include
int main( int argc, char** argv ) { std::cout << "Hello, TryOpenCV! " << std::endl; cv::Mat img( 2, 2, CV_8UC3 ); std::cout << "img: " << img << std::endl; cv::Mat img1; cv::add( img, cv::Scalar::all(255), img1 ); std::cout << "img1: " << img1 << std::endl; return 0;}

执行结果

Hello, TryOpenCV! img: [  0,   0,   0,   0,   0,   0;   0,   0,   0,   0,   0,   0]img1: [255, 255, 255, 255, 255, 255; 255, 255, 255, 255, 255, 255]

在使用Python的cv2进行类似操作时会有意想不到的效果。

import cv2import numpy as npimg = np.zeros((2, 2, 3), dtype=np.uint8)img1 = cv2.add(img, 255)

结果将是

array([[[255,   0,   0],        [255,   0,   0]],       [[255,   0,   0],        [255,   0,   0]]], dtype=uint8)

这里需要借鉴C++中cv::Scalar的概念。根据的描述(arithm_op函数),当进行Mat和实数的加法时,实数必须已CV_64F表示。在python环境中,这可以利用一个只有单个元素的NumPy array实现。

import cv2import numpy as npimg = np.zeros((2, 2, 3), dtype=np.uint8)s = np.array([255], dtype=np.float64)img1 = cv2.add(img, s)

运行将得到正确的结果。

array([[[255, 255, 255],        [255, 255, 255]],       [[255, 255, 255],        [255, 255, 255]]], dtype=uint8)

 

3.3 矩阵乘法

更加关心的是矩阵乘法。这里特指与通常情况下数学上定义的乘法一致的矩阵乘法,而非element-wise multiplication。类比于MATLAB,也就是 * 而不是 .* 。

OpenCV强制规定,Mat之间的乘法只能在如下4种数据格式上进行:CV_32FC1, CV_32FC2, CV_64FC1 以及 CF_64FC2。并且两个Mat必须具有相同的数据格式。这个规定体现在OpenCV的源码上,以现在手上的版本(3.4.1)为例,该限制出现在 modules/core/src/matmul.cpp: 1558位置,如图所示。

 

 

在进行矩阵乘法前,可能需要对现有Mat对象进行数据格式的转换,这通过Mat的convertTo()成员函数实现。converTo()函数可以用于转换自身的数据格式,例如一个Mat对象A,可以这样:

 

A.convertTo(A, CV_64FC1);

 

另外,在运用convertTo()时,像进行加法和数乘一样,OpenCV也会进行自动数据截断。

 

矩阵乘法也重载了 *= 运算符。可以 A *= A; 这样使用。

今天先到这里,上一波简单代码。

 

cv::Mat A = ( cv::Mat_
(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9 ); std::cout << "A = " << std::endl << A << std::endl << std::endl; // Test the t() function. std::cout << "A.t() = " << std::endl << A.t() << std::endl << std::endl; std::cout << "After transposition, A = " << std::endl << A << std::endl << std::endl; // Test the t() function with assignment. cv::Mat B = A.t(); std::cout << "B = " << std::endl << B << std::endl << std::endl; B.at
(1,2) = 10; std::cout << "After assigning value to B." << std::endl; std::cout << "A = " << std::endl << A << std::endl; std::cout << "B = " << std::endl << B << std::endl; // Test arithmetic operations. cv::Mat C = A.clone(); C += 1; std::cout << "C += 1 -> " << std::endl << C << std::endl << std::endl; C -= 1; std::cout << "C -= 1 -> " << std::endl << C << std::endl << std::endl; C *= 10; std::cout << "C *= 10 -> " << std::endl << C << std::endl << std::endl; C = A.clone(); C += A; std::cout << "C += A -> " << std::endl << C << std::endl << std::endl; C -= A; std::cout << "C -= A -> " << std::endl << C << std::endl << std::endl; C = A.clone(); C += 250; std::cout << "C += 250 -> " << std::endl << C << std::endl << std::endl; C = A.clone(); C -= 250; std::cout << "C -= 250 -> " << std::endl << C << std::endl << std::endl; C = A.clone(); C *= 100; std::cout << "C *= 100 -> " << std::endl << C << std::endl << std::endl; cv::Mat D; A.convertTo(D, CV_32FC1); // Only CV_32FC1, CV_32FC2, CV_64FC1 and, CV_64FC2 could be used to do matrix multiplication. std::cout << "D = " << std::endl << D << std::endl << std::endl; D *= D; std::cout << "D *= D -> " << std::endl << D << std::endl << std::endl; cv::Mat DD = A.clone(); std::cout << "DD.type() = " << DD.type() << std::endl; std::cout << "DD = " << std::endl << DD << std::endl << std::endl; DD.convertTo(DD, CV_64FC1); std::cout << "DD.type() = " << DD.type() << std::endl; std::cout << "DD = " << std::endl << DD << std::endl << std::endl; DD *= 100; std::cout << "DD *= 100 -> " << std::endl << DD << std::endl << std::endl; DD.convertTo(DD, CV_8UC1); std::cout << "DD convert to CV_8UC1 -> "<< std::endl << DD << std::endl << std::endl; DD = A.clone(); DD.convertTo(DD, CV_64FC1); DD -= 250; std::cout << "DD convert to CV_64FC1, -= 250 -> " << std::endl << DD << std::endl << std::endl; DD.convertTo(DD, CV_8UC1); std::cout << "DD convert to CV_8UC1 -> " << std::endl << DD << std::endl << std::endl; cv::Mat E;// E = D + A; // This will cause a runtime exception.// std::cout << "E.type() = " << E.type() << std::endl << std::endl; cv::add( A, D, E, cv::Mat(), CV_32SC1 ); std::cout << "E = A + D -> " << E << std::endl << std::endl; std::cout << "E.type() = " << E.type() << std::endl << std::endl; B = 250; cv::add(B, D, E, cv::Mat(), CV_32SC1); std::cout << "B = 250, E = B + D with CV_32SC1 -> " << std::endl << E << std::endl << std::endl; cv::add(B, D, E, cv::Mat(), CV_8UC1); std::cout << "B = 250, E = B + D with CV_8UC1 -> " << std::endl << E << std::endl << std::endl;

 

 

 

你可能感兴趣的文章
【MachineLearning】数据挖掘中的分类和聚类的区别
查看>>
【LEETCODE】292-Nim Game
查看>>
【LEETCODE】237-Delete Node in a Linked List
查看>>
【LEETCODE】206-Reverse Linked List
查看>>
【LEETCODE】203-Remove Linked List Elements
查看>>
【LEETCODE】234-Palindrome Linked List
查看>>
【LEETCODE】141-Linked List Cycle
查看>>
【LEETCODE】142-Linked List Cycle II
查看>>
【LEETCODE】92-Reverse Linked List II
查看>>
【LEETCODE】283-Move Zeroes
查看>>
【LEETCODE】217-Contains Duplicate
查看>>
【LEETCODE】219-Contains Duplicate II
查看>>
【LEETCODE】220-Contains Duplicate III
查看>>
【LEETCODE】171-Excel Sheet Column Number
查看>>
【LEETCODE】169-Majority Element
查看>>
【LEETCODE】191-Number of 1 Bits
查看>>
【LEETCODE】13-Roman to Integer
查看>>
【LEETCODE】83-Remove Duplicates from Sorted List
查看>>
【LEETCODE】70-Climbing Stairs
查看>>
【LEETCODE】198-House Robber
查看>>