最近在系统地学习OpenCV,将学习的过程在此做一个记录,主要以代码+注释的方式
记录学习过程。
cv::Mat有两个必不可少的组成部分:一个头部和一个数据块。头部包含了矩阵的所有相关信息(大小、通道数量、数据类型等);数据块包含了图像中所有像素的值。头部有一个指向数据块的指针,即data属性。cv::Mat有一个很重要的属性,即只有在明确要求时,内存块才会被复制。实际上,大多数操作仅仅复制了cv::Mat的头部,因此多个对象会同时指向同一个数据块。
下面的程序可用来测试cv::Mat数据结构的不同属性(代码+注释):
#include <iostream>#include <opencv2/core/core.hpp>#include <opencv2/highgui/highgui.hpp>// 测试函数, 它创建一个图像cv::Mat function() { // 创建图像 cv::Mat ima(500, 500, CV_8U, 50); // 返回图像 return ima;} int main() { // 定义图像窗口 cv::namedWindow("Image 1"); cv::namedWindow("Image 2"); cv::namedWindow("Image 3"); cv::namedWindow("Image 4"); cv::namedWindow("Image 5"); cv::namedWindow("Image"); /*我们需要指定每个矩阵元素的类型,这里我们用CV_8U表示每个像素 对应1字节,用字母U表示无符号,你也可用字母S表示有符号。对于 彩色图像,你应该用三通道类型(CV_8UC3),也可以定义16位和 32位的整数(有符号或无符号),例如CV_16SC3。我们甚至可以使 用32位和64位的浮点数(例如CV_32F)。*/ // 创建一个240行 × 320列的新图像 cv::Mat image1(240, 320, CV_8U, 100); cv::imshow("Image", image1); // 显示图像 cv::waitKey(0); // 等待按键 /*我们可以随时用create方法分配或重新分配图像的数据块,如果图 像已经分配,首先其原来的内容会被释放。*/ // 重新分配一个新的图像 image1.create(200, 200, CV_8U); image1 = 200; cv::imshow("Image", image1); // 显示图像 cv::waitKey(0); // 等待按键 /*图像(或矩阵)的每个元素都可以包含多个值(例如彩色图像中的三 个通道),因此OpenCV引入了一个简单的数据结构cv::Scalar, 用于在调用函数时传递像素值。该结构通常包含一个或三个值。*/ // 创建一个红色的图像 // 通道次序为BGR cv::Mat image2(240, 320, CV_8UC3, cv::Scalar(0, 0, 255)); // 或者: // cv::Mat image2(cv::Size(320,240),CV_8UC3); // image2= cv::Scalar(0,0,255); cv::imshow("Image", image2); // 显示图像 cv::waitKey(0); // 等待按键 // 读入一个图像 cv::Mat image3 = cv::imread("puppy.bmp"); /*一旦没有了指向cv::Mat对象的引用, 分配的内存就会被自动释 放。 这一点非常方便, 因为它避免了C++动态内存分配中经常发生的 内存泄漏问题。 这是OpenCV 2中一个关键的机制, 它的实现方法是 通过cv::Mat实现计数引用和浅复制。 因此, 当在两个图像之间赋 值时, 图像数据( 即像素) 并不会被复制, 此时两个图像都指向同一 个内存块。 这同样适用于图像间的值传递或值返回。 由于维护了一个 引用计数器, 因此只有当图像的所有引用都将释放或赋值给另一个图 像时, 内存才会被释放;*/ // 所有这些图像都指向同一个数据块。 //下面的两个图像,对其中的任何一个做转换都会影响到其他图像。 cv::Mat image4(image3); image1 = image3; /*如果要对图像内容做一个深复制, 你可以使用copyTo方法, 在此情况下 目标图像会调用create方法。 另一个生成图像副本的方法是clone, 即创建 一个完全相同的新图像*/ // 这些图像是源图像的副本图像 image3.copyTo(image2); cv::Mat image5 = image3.clone(); // 转换图像用来测试 cv::flip(image3, image3, 1); // 检查哪些图像在处理过程中受到了影响 cv::imshow("Image 3", image3); cv::imshow("Image 1", image1); cv::imshow("Image 2", image2); cv::imshow("Image 4", image4); cv::imshow("Image 5", image5); cv::waitKey(0); // 等待按键 // 从函数中获取一个灰度图像 cv::Mat gray = function(); cv::imshow("Image", gray); // 显示图像 cv::waitKey(0); // 等待按键 // 作为灰度图像读入 image1 = cv::imread("puppy.bmp", CV_LOAD_IMAGE_GRAYSCALE); /*如果你需要把一个图像复制到另一个图像中, 而两者的数据类型不一 定相同, 那就要使用convertTo方法.需要注意的是, 这两个图像的通道数量 必须相同。*/ image1.convertTo(image2, CV_32F, 1 / 255.0, 0.0); cv::imshow("Image", image2); // 显示图像 cv::waitKey(0); // 等待按键 return 0;}程序运行结果:从左往下,image1至image5。
新闻热点
疑难解答