OpenCV3学习笔记

本文详细介绍了OpenCV3中的核心数据类型Mat和SparseMat,重点讲解了Mat类的图像读写、创建、色彩空间转换、像素操作、图像滤波、形态学操作等基础操作。通过实例展示了如何进行图像的线性混合、对比度和亮度增强、图像边缘检测等,帮助读者深入理解OpenCV3的基本功能。
摘要由CSDN通过智能技术生成

一、OpenCV数据类型

  • 第一类是直接从C++中继承的基础数据类型(如int和float等)。这些类型包括简单的数组和矩阵,一些简单的几何概念,比如点、矩形、大小等;
  • 第二类是辅助对象。这同时也代表些对象代表更抽象的概念, 比如垃圾收集指针类、用于数据切片的范围对象 (range objects) 以及抽象的终止条件类等;
  • 第三类可以称为大型数组类型。这些对象原本目的是涵盖数组或一些其他的原语、 程序集或更常见的基础数据类型。这一类的典型代表是cv::Mat类,该类用来代表任意维度的包含任意基础元素的数组。存储图片对象是cv::Mat类的特殊用途。

为快速上手,先从第三类的Mat类开始。

1. 大型数组类型

比较具有代表性的是 cv::Mat和cv::SparseMat 类型。

  • cv::Mat针对的是密集连续性的存储,大多数的图像数据被存储为这种类,即使数据为空,预留的存储空间仍然存在;

  • cv::SparseMat针对的是稀疏的存储方式,只有数据不为0才保留空间,否则不会预留。显然cv::SparseMat存储更为节省空间,典型使用cv::SparseMat的例如直方图数据的存储。

1.1 Mat类

Mat 是一个类,由两个数据部分组成:

  • 矩阵头(包含矩阵尺寸,存储方法,存储地址等信息)

  • 一个指向存储所有像素值的矩阵(根据所选存储方法的不同矩阵可以是不同的维数)的指针。

1.2 SparseMat类

二、Mat类基础操作

1. 图像的读写

1.1 读取

imread功能是加载图像文件成为一个Mat对象,其中:
第一个参数为图像文件的路径;
第二个参数,表示加载的图像是什么类型,支持常见的三个参数值:

  • IMREAD_UNCHANGED (<0) 表示加载原图,不做任何改变
  • IMREAD_GRAYSCALE (=0)表示把原图作为灰度图像加载进来
  • IMREAD_COLOR (>0) 表示把原图作为RGB图像加载进来

注意:OpenCV支持JPG、PNG、TIFF等常见格式图像文件加载。

同时用到的函数还有:

  • namedWindow:功能是创建一个OpenCV窗口,它是由OpenCV自动创建与释放,你无需取销毁它。常见用法是:
    namedWindow(“Window Title”, WINDOW_AUTOSIZE)。
    — WINDOW_AUTOSIZE,会自动根据图像大小,显示窗口大小,不能人为改变窗口大小;
    — WINDOW_NORMAL,跟QT集成的时候会使用,允许修改窗口大小。
  • destroyWindow:功能是销毁指定的窗口。
  • imshow:根据窗口名称显示图像到指定的窗口上去,第一个参数是窗口名称,第二参数是Mat对象
//加载图像
#include <iostream>
#include <opencv.hpp>

using namespace std;
using namespace cv;

int main()
{
   
    Mat image = imread("C://1.jpg");			//加载图像
    if (image.empty())
    {
   
        return -1;
    }
    namedWindow("Example1", WINDOW_AUTOSIZE);	//创建窗口
    imshow("Example1", image);					//显示图像
    waitKey(0);
    destroyWindow("Example1");					//销毁窗口
    return 0;
}

在这里插入图片描述

//加载为灰度图
#include <iostream>
#include <opencv.hpp>

using namespace std;
using namespace cv;

int main()
{
   
    Mat image = imread("C://1.jpg", 0);		//IMREAD_GRAYSCALE (=0)表示把原图作为灰度图像加载进来
    if (image.empty())
    {
   
        return -1;
    }
    namedWindow("Example1", WINDOW_AUTOSIZE);
    imshow("Example1", image);
    waitKey(0);
    destroyWindow("Example1");
    return 0;
}

在这里插入图片描述

1.2 写入

imwrite功能是保存图像文件到指定目录路径,有三个参数:

  • 第一个参数表示需要写入的路径&文件名,必须要加上后缀,比如“123.png”;

  • 第二个参数表示Mat类型的图像数据,是你要保存的对象。

  • 第三个参数表示为特定格式保存的参数编码,它有一个默认值std::vector< int >(),所以一般情况下不用写。

注意:只有8位、16位的PNG、JPG、Tiff文件格式而且是单通道或者三通道的BGR的图像才可以通过这种方式保存,保存PNG格式的时候可以保存透明通道的图片

#include <iostream>
#include <opencv.hpp>

using namespace std;
using namespace cv;

int main()
{
   
    Mat image = imread("C://1.jpg");
    if (image.empty())
    {
   
        return -1;
    }

    Mat image_out;
    cvtColor(image, image_out, COLOR_BGR2HLS);     //转换为灰度图

    imwrite("D://1_out.png", image_out);	//将转换后image_out的写入到"D://1_out.png"

    namedWindow("Output Window", WINDOW_AUTOSIZE);
    imshow("Output Window", image_out);
    waitKey(0);
    destroyWindow("Output Window");
    return 0;
}

2. 创建一个Mat类

  • Mat对象构造函数:Mat()
  • Mat对象构造方法:A.create()

赋值一个全为0的Mat:img = Scalar(0)。

//三种建立Mat类的方式
#include <opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char** args) {
   

	Mat M;
	M.create(4, 3, CV_32FC3);
	M = Scalar((126.3928), (12.3334), (246.12));
	cout << "M = " << endl << M << endl << endl;

	Mat A = Mat(3, 2, CV_16SC3);
	A = Scalar(1, 2, 3);
	cout << endl << "A = " << endl << A << endl << endl;

	Mat D = (Mat_<double>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
	cout << "D = " << endl << D << endl << endl;

	waitKey(0);
	return 0;
	}
/*
M =
[126.3928, 12.3334, 246.12, 126.3928, 12.3334, 246.12, 126.3928, 12.3334, 246.12;
 126.3928, 12.3334, 246.12, 126.3928, 12.3334, 246.12, 126.3928, 12.3334, 246.12;
 126.3928, 12.3334, 246.12, 126.3928, 12.3334, 246.12, 126.3928, 12.3334, 246.12;
 126.3928, 12.3334, 246.12, 126.3928, 12.3334, 246.12, 126.3928, 12.3334, 246.12]


A =
[1, 2, 3, 1, 2, 3;
 1, 2, 3, 1, 2, 3;
 1, 2, 3, 1, 2, 3]

D =
[0, -1, 0;
 -1, 5, -1;
 0, -1, 0]


C:\Users\CK Yang\source\repos\LXCV01\x64\Debug\LXCV01.exe (进程 18808)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
*/
}

创建Mat类型的函数十分灵活:

#include <opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char** args) {
   

	Mat M;
	M.create(4, 3, CV_32FC3);
	M = Scalar((126.3928), (12.3334), (246.12));
	cout << "M = " << endl << M << endl << endl;

	Mat A = Mat(3, 2, CV_16SC3);
	A = Scalar(1, 2, 3);
	cout << endl << "A = " << endl << A << endl << endl;

	Mat B = Mat(Size(3, 4), CV_8SC3);
	B = Scalar(2, 3, 4);
	cout << "B = " << endl << B << endl << endl;

	Mat C = Mat(Size(2, 3), CV_16SC2, Scalar(2, 3, 4));
	cout << "C = " << endl << C << endl << endl;
	
	Mat D = (Mat_<double>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
	cout << "D = " << endl << D << endl << endl;

	
	waitKey(0);
	return 0;
	}
/*
A =
[1, 2, 3, 1, 2, 3;
 1, 2, 3, 1, 2, 3;
 1, 2, 3, 1, 2, 3]

B =
[  2,   3,   4,   2,   3,   4,   2,   3,   4;
   2,   3,   4,   2,   3,   4,   2,   3,   4;
   2,   3,   4,   2,   3,   4,   2,   3,   4;
   2,   3,   4,   2,   3,   4,   2,   3,   4]

C =
[2, 3, 2, 3;
 2, 3, 2, 3;
 2, 3, 2, 3]

D =
[0, -1, 0;
 -1, 5, -1;
 0, -1, 0]

*/

还可通过imread(“C:/2.jpg”);的方式建立图像的Mat

#include <opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char** args)
	{
   
	Mat image = imread("C:/2.jpg");

	if (image.empty()) 
	{
   
		return -1;
	}

	namedWindow("My Image", WINDOW_AUTOSIZE);
	imshow("My Image", image);

	Mat M;
	M.create(4, 3, CV_32FC3);
	M = Scalar((126.3928), (12.3334), (246.12));
	cout << "M = " << endl << " " << M << endl << endl;
	uchar* firstRow = M.ptr<uchar>(0);
	printf("%d\n", *firstRow);

	Mat C = (Mat_<double>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
	cout << "C = " << endl << " " << C << endl << endl;

	waitKey(0);
	cvDestroyAllWindows();
	return 0;
}

Mat的type和depth:
:CV_32FC3——表示的是元素类型是一个32位的浮点数,通道为3。
在这里插入图片描述

  • 数据类型中:
    U(unsigned integer)表示的是无符号整数,
    S(signed integer)是有符号整数,
    F(float)是浮点数。
  • 通道数中:C1,C2,C3,C4则表示通道数是1,2,3,4。

type一般是在创建Mat对象时设定,如果要取得Mat的元素类型,则无需使用type,使用depth:
depth是矩阵中元素的一个通道的数据类型,这个值和type是相关的。
例如: type为 CV_16SC2,一个2通道的16位的有符号整数。那么,depth则是CV_16S。

  • depth:将type的值去掉通道信息就是depth值:CV_8U、CV_16S、CV_32F、CV_64F。
  • elemSize:矩阵一个元素占用的字节数,例如:type是CV_16SC3,那么elemSize = 3 * 16 / 8 = 6
    bytes
  • elemSize1:矩阵元素一个通道占用的字节数,例如:type是CV_16CS3,那么elemSize1 = 16 / 8 = 2
    bytes = elemSize / channels

常用方法:

  • void copyTo(Mat):完全复制;
  • Mat clone():完全复制;
  • void convertTo(Mat, int type):转换Mat为指定type;
  • int type():查看Mat的type;
  • size():查看Mat的size;
  • int channels():查看Mat的channels;
  • int depth():查看Mat的depth;
  • bool empty():查看Mat是否为empty;
  • uchar* ptr(i=0):像素指针。

注:

部分复制:一般情况下只会复制Mat对象的头和指针部分,不会复制数据部分

  • Mat A= imread(imgFilePath);
  • Mat B(A)

完全复制:如果想把Mat对象的头部和数据部分一起复制,可以通过如下两个API实现

  • Mat F = A.clone();
  • Mat G;
    A.copyTo(G);
#include <opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char** args) {
   

	Mat M;
	M.create(4, 3, CV_32FC3);
	M = Scalar((126.3928), (12.3334), (246.12));
	cout << "M = " << endl << M << endl << endl;

	Mat A = Mat(3, 2, CV_16SC3);
	A = Scalar(1, 2, 3);
	cout << endl << "A = " << endl << A << endl << endl;

	Mat B = Mat(Size(3, 4), CV_8SC3);
	B = Scalar(2, 3, 4);
	cout << "B = " << endl << B << endl << endl;

	Mat E;
	A.copyTo(E);
	cout << "E = " << endl << E << endl << endl;

	Mat F;
	A.convertTo(F, CV_32FC3);
	cout << "F = " << endl << F << endl << endl;

	Mat G = B.clone();
	cout << "G = " << endl << G << endl << endl;

3. 色彩空间转换

cvtColor( image, gray_image, COLOR_BGR2GRAY )
cvtColor功能是把图像从一个彩色空间转换到另外一个色彩空间,有三个参数:

  • 第一个参数表示源图像;

  • 第二参数表示色彩空间转换之后的图像;

  • 第三个参数表示源和目标色彩空间如:COLOR_BGR2HLS 、COLOR_BGR2GRAY 等。

#include <iostream>
#include <opencv.hpp>

using namespace std;
using namespace cv;

int main()
{
   
    Mat image = imread("C://1.jpg");
    if (image.empty())
    {
   
        return -1;
    }

    Mat image_out;
    cvtColor(image, image_out, COLOR_BGR2HLS);     //转换色彩空间为COLOR_BGR2HLS

    namedWindow("Output Window", WINDOW_AUTOSIZE);
    imshow("Output Window", image_out);
    waitKey(0);
    destroyWindow("Output Window");
    return 0;
}

在这里插入图片描述

4. 图像像素指针&掩膜操作(Mask)的实现

Mat.ptr(int i=0) 功能是获取像素矩阵的指针,索引 i 表示行数,从0开始计数。

  • 获得当前行指针const uchar* current= myImage.ptr< uchar>(row);
  • 获取当前像素点P(row, col)的像素值 p(row, col) = current[col]。

注意:掩膜Mask也被称之为Kernel。

实现一个下图的掩膜操作,功能是提高图像的对比度:
在这里插入图片描述
这能干嘛?我下载的某个PDF文档看着比较瞎眼,就可以用这个来让它看着没那么瞎眼。
在这里插入图片描述

saturate_cast< uchar>()是像素范围处理函数,功能是确保RGB值的范围在0~255之间。

  • saturate_cast(-100),返回 0;
  • saturate_cast(288),返回255;
  • saturate_cast(100),返回100。
#include <iostream>
#include <opencv.hpp>

using namespace std;
using namespace cv;

int main()
{
   
    Mat image = imread("C://2.png");
    Mat image_output = Mat::zeros(image.size(), image.type());	//创立一个元素全为0的Mat类型作为输出图像,size和type与输入图像保持一致。

    if (image.empty())
    {
   
        return -1;
    }

    namedWindow("Input",WINDOW_AUTOSIZE);
    imshow("Input", image);
    
    int cols = image.cols * image.channels();
    int rows = image.rows;

    for (int row = 1; row < (rows - 1); row++)
    {
   
        const uchar* current = image.ptr<uchar>(row);		//获取当前行指针
        const uchar* next = image.ptr<uchar>(row + 1);		//获取下一行指针
        const uchar* previous = image.ptr<uchar>(row - 1);	//获取上一行指针

        uchar* output = image_output.ptr<uchar>(row);		//获取想要输出的图像的行指针

        for (int col = 1 * image.channels(); col < cols; col++)		//为什么要* image.channels()?思考下彩色图像显示的原理。
        {
   
            output[col] = saturate_cast<uchar> ( current[col] * 5 - (current[col] - image.channels()) - (current[col] + image.channels()) - next[col] - previous[col] );
            //掩膜操作
        }
    }

    namedWindow("Output", WINDOW_AUTOSIZE);
    imshow("Output", image_output);
    
    waitKey(0);
    destroyWindow("Output");
    destroyWindow("INput");

    return 0;
}

在这里插入图片描述
除了利用图像像素指针进行掩膜操作,还能用OpenCV的api完成同样的操作,操作步骤:

  1. 定义掩膜:Mat kernel = (Mat_(3,3) << 0, -1, 0, -1, 5, -1, 0, -1,
    0);
  2. 调用filter2D( image, image_output, src.depth(), kernel):
    其中image和 image_output是Mat类型变量;
    src.depth表示位图深度,有32、24、8等,根据图像属性变化;
    kernel是刚才定义的掩膜。
#include <iostream>
#include <opencv.hpp>

using namespace std;
using namespace cv;

int main()
{
   
    Mat image = imread("C://2.png");
    Mat image_output = Mat::zeros(image.size(), image.type());

    if (image.empty())
    {
   
        return -1;
    }

    namedWindow("Input", WINDOW_AUTOSIZE);
    imshow("Input", image);

    Mat kernel = (Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);	//定义掩膜
    filter2D(image, image_output, image.depth(), kernel);	//调用filter2D

    namedWindow("Output", WINDOW_AUTOSIZE);
    
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值