OpenCV (一)Mat基本操作以及灰度图转化 您所在的位置:网站首页 opencvbitmap转mat OpenCV (一)Mat基本操作以及灰度图转化

OpenCV (一)Mat基本操作以及灰度图转化

2024-07-11 13:49| 来源: 网络整理| 查看: 265

前言

开始写OpenCV这篇文章的时候,不由想到,我的大学计算机图形学的第一门实操课程就是灰度转化,拉普拉斯锐化等。其中灰度图的转化,是计算机图形学基础中基础,这里就顺着OpenCV的灰度的转化,来看看OpenCV一些基础的api。

本文地址:https://www.jianshu.com/p/7963c7dbaf92

正文 Mat

先来看看OpenCV,基础对象Mat,矩阵。什么是矩阵,实际上没有必要解释,一般人都能够明白数学意义上矩阵的含义。

OpenCV把每一个M * N的宽高图像,看成M*N的矩阵。矩阵的每一个单元就对应着图像中像素的每一个点。

我们如果放大图中某个部分,就会发现如下情况

image.png

图像实际上就如同矩阵一样每个单元由一个像素点构成。

因为OpenCV的Mat每一个像素点,包含的数据不仅仅只有一个单纯的数字。每一个像素点中包含着颜色通道数据。

稍微解释一下颜色通道,我们可以把世间万物肉眼能识别的颜色由3种颜色(R 红色,G 绿色,B 蓝色)经过调节其色彩饱和度组成的。也就是说通过控制RGB三种的色值大小(0~255)来调配新的颜色。

当我们常见的灰度图,一般是单个颜色通道,因为只用黑白两种颜色。我们常见的图片,至少是三色通道,因为需要RGB三种颜色通道。

我们常见Android的Bitmap能够设置ARGB_8888的标志位就是指能够通过A(透明通道),R,G,B来控制图片加载的颜色通道。

OpenCV为了更好的控制这些数据。因此采用了数学上的矩阵的概念。当OpenCV要控制如RGB三色通道的Mat,本质上是一个M * N * 3的三维矩阵。

但是实际上,我们在使用OpenCV的Mat的时候,我们只需要关注每个图片的像素,而每个像素的颜色通道则是看成Mat中每个单元数据的内容即可

我们先来看看Mat的构造方法

Mat(); Mat(int rows, int cols, int type); Mat(Size size, int type); Mat(int rows, int cols, int type, const Scalar& s); Mat(Size size, int type, const Scalar& s); ... Mat(const Mat& m); Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP); Mat(Size size, int type, void* data, size_t step=AUTO_STEP); Mat(int ndims, const int* sizes, int type, void* data, const size_t* steps=0); ... Mat(const Mat& m, const Range& rowRange, const Range& colRange=Range::all()); Mat(const Mat& m, const Rect& roi); Mat(const Mat& m, const Range* ranges); Mat(const Mat& m, const std::vector& ranges);

现阶段,实际上我们值得我们注意的是构造函数:

1.是通过Size或者rows和cols来设置Mat大小的方法。相当于创建一个固定宽高大小的黑色原始图片。

举个例子:

Mat mat(20,30,CV_8UC1,Scalar(255));

这个mat矩阵将会制造一个高20,宽30,一个1字节的颜色通道(也是Mat中每一个像素数据都是1字节的unchar类型的数据),同时颜色是白色的图片。

记住一点,在OpenCV中,rows是高,cols是宽。和我们传统印象是颠倒过来的。

在这里面我们能够看到一个特殊的宏CV_8UC1。实际上这是指代OpenCV中图片带的是多少颜色通道的意思。

CV_8uc1 单颜色通道 8位 CV_8uc2 2颜色通道 16位 CV_8uc3 3颜色通道 24位 CV_8uc4 4颜色通道 32位

这4个宏十分重要,要时刻记住。

2.设置Range,来裁剪Mat中的数据范围,相当于把图片一部分裁剪出来。记得这种方式。被裁减出来的像素数据还是指向了原来Mat的像素数据。也就是说当修改了被裁减的Mat中的像素数据,同时也会更改了原来的Mat数据。如下图: 带Range构造函数的原理.png

当我们需要把Mat 中的数据拷贝一份出来,我们应该调用下面这个api:

src.copyTo(mat);

这样就能拷贝一份像素数据到新的Mat中。之后操作新的Mat就不会影响原图。

Mat矩阵的创建原理

实际上,在本文中,我们能够看到OpenCV是这么调用api读取图片的数据转化为Mat矩阵。

Mat src = imread("/Users/yjy/Desktop/learningMaterials/study/test.jpg");

OpenCV会通过imread去读图片文件,并且转化为Mat矩阵。

Mat imread( const String& filename, int flags ) { CV_TRACE_FUNCTION(); /// create the basic container Mat img; /// load the data imread_( filename, flags, img ); /// optionally rotate the data if EXIF' orientation flag says so if( !img.empty() && (flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED ) { ApplyExifOrientation(filename, img); } /// return a reference to the data return img; }

能看见imread,是调用imread_把图片中的数据拷贝的img这个Mat对象中。接着会做一次图片的颠倒。这个方面倒是和Glide很相似。 文件:modules/imgcodecs/src/loadsave.cpp

static bool imread_( const String& filename, int flags, Mat& mat ) { /// Search for the relevant decoder to handle the imagery ImageDecoder decoder; #ifdef HAVE_GDAL if(flags != IMREAD_UNCHANGED && (flags & IMREAD_LOAD_GDAL) == IMREAD_LOAD_GDAL ){ decoder = GdalDecoder().newDecoder(); }else{ #endif decoder = findDecoder( filename ); #ifdef HAVE_GDAL } #endif /// if no decoder was found, return nothing. if( !decoder ){ return 0; } int scale_denom = 1; if( flags > IMREAD_LOAD_GDAL ) { if( flags & IMREAD_REDUCED_GRAYSCALE_2 ) scale_denom = 2; else if( flags & IMREAD_REDUCED_GRAYSCALE_4 ) scale_denom = 4; else if( flags & IMREAD_REDUCED_GRAYSCALE_8 ) scale_denom = 8; } /// set the scale_denom in the driver decoder->setScale( scale_denom ); /// set the filename in the driver decoder->setSource( filename ); try { // read the header to make sure it succeeds if( !decoder->readHeader() ) return 0; } catch (const cv::Exception& e) { std::cerr


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有