图像处理mat类型

2015-06-27 17:49:33 LZY272942518 阅读数 3436
  • Mat对象创建与使用

    掌握OpenCV核心模块,熟练使用相关API 理解各个API背后的相关算法原理,每个参数意义 有能力解决使用应用场景问题,大量工程代码经验分享 掌握图像处理与视频分析,图像分析与测量编码与开发技巧

    191人学习 贾志刚
    免费试看

注:此博客原本由本人于2013-7-10发表于http://blog.sina.com.cn/s/blog_87ce045d0101f61r.html,现在新浪博客不再使用。此博客为第二版,附上了主函数的源码。

最近在做一个字符识别项目,用的是德国产Basler工业摄像头。它的显示采集图像的例程中,定义了一个指针,指向图像的某像素,通过对指针的操作来显示图像。我需要对采集的图像进行基于opencv的处理,而opencv函数能处理的数据类型却是Mat型。怎么转换呢?

首先是第一种方法,定义一个指针变量din,将原指针赋给din,然后操作din,构建出一个完整的Mat。代码如下:
          //定义用于存放摄像头图像的Mat
                Mat Image(cv::Size(FrameWidth,FrameHeight),CV_8UC1);
          //将摄像头采集的图像转换为Mat数据格式
uchar* din = (uchar *)(Img.GetBuffer());

  for(int j=0; j<FrameHeight; j++){

uchar* out= Image.ptr<uchar>(j);
for(int i=0; i<FrameWidth; i++){
out[i]= din[j*FrameWidth+i];
}
}

主函数完整代码如下,加上了Basler摄像头的初始化相关函数:

int main(int argc, char* argv[])
        // Create an instant camera object with the camera device found first.
        CInstantCamera camera( CTlFactory::GetInstance().CreateFirstDevice());

        // Print the model name of the camera.
        cout << "Using device " << camera.GetDeviceInfo().GetModelName() << endl;

        // The parameter MaxNumBuffer can be used to control the count of buffers
        // allocated for grabbing. The default value of this parameter is 10.
        camera.MaxNumBuffer = 5;

  <span style="white-space:pre">	</span>Mat Image(cv::Size(FrameWidth,FrameHeight),CV_8UC1);<span style="white-space:pre">	</span>

        // This smart pointer will receive the grab result data.
        CGrabResultPtr ptrGrabResult;

        // Camera.StopGrabbing() is called automatically by the RetrieveResult() method
        // when c_countOfImagesToGrab images have been retrieved.

	CPylonImage Img;
	while(1)
	{
	    camera.StartGrabbing( 1);

            // Wait for an image and then retrieve it. A timeout of 5000 ms is used.
            camera.RetrieveResult( 5000, ptrGrabResult, TimeoutHandling_ThrowException);
	    // Image grabbed successfully?
            if (ptrGrabResult->GrabSucceeded())
            {
                // Access the image data.
		Img.AttachGrabResultBuffer(ptrGrabResult);
		//将摄像头采集的图像转换为Mat数据格式
		uchar* din = (uchar *)(Img.GetBuffer());				
		for(int j=0; j<FrameHeight; j++){
			uchar* out= Image.ptr<uchar>(j);
			for(int i=0; i<FrameWidth; i++){
				out[i]= din[j*FrameWidth+i];
			}
		}
	    }
	    else
            {
                cout << "Error: " << ptrGrabResult->GetErrorCode() << " " << ptrGrabResult->GetErrorDescription();
            }
	}
}


然后是第二种方法,这种方法来自于http://blog.csdn.net/yang_xian521/article/details/7107786,代码如下: 
         //将摄像头采集的图像转换为Mat数据格式
       uchar* din = (uchar *)(Img.GetBuffer());
               Mat Image(FrameHeight,FrameWidth,CV_8UC1,din);
可以看出,第二种方法相当简单!
2017-04-11 09:28:11 u011574296 阅读数 3543
  • Mat对象创建与使用

    掌握OpenCV核心模块,熟练使用相关API 理解各个API背后的相关算法原理,每个参数意义 有能力解决使用应用场景问题,大量工程代码经验分享 掌握图像处理与视频分析,图像分析与测量编码与开发技巧

    191人学习 贾志刚
    免费试看

 

一、Mat类的定义:OpenCV3 参考文档:cv::Mat Class

 

Mat类的对象用于表示一个多维度的单通道或者多通道稠密数组,它可以用来存储以下东西:

real or complex-valued vectors or matrices(实数或复数的向量或者矩阵)

grayscale or color images (灰度图或者彩色图)

voxel volumes (立体元素)

vector fields (矢量场)

point clouds (点云)

tensors (张量)

histograms (直方图) 

 

矩阵 (M) 中数据元素的地址计算公式:

addr(Mi0,i1,…im-1) = M.data + M.step[0] * i0 + M.step[1] * i1 + … + M.step[m-1] * im-1

(其中 m = M.dims ,M的维度)


data:Mat对象中的一个指针,指向内存中存放矩阵数据的一块内存 (uchar* data),数据存储方式是按像素存储,不是按通道存储
dims:Mat所代表的矩阵的维度,如 3 * 4 的矩阵为 2 维, 3 * 4 * 5 的为3维
channels:通道数,Mat.channels()得到的是矩阵中的每一个像素拥有的值的个数

depth:深度,即每一个像素的位数(bits),在opencv的Mat.depth()中得到的是一个0–6的数字,enum { CV_8U=0, CV_8S=1, CV_16U=2, CV_16S=3, CV_32S=4, CV_32F=5, CV_64F=6 };

step:是一个数组,定义了矩阵的布局,另外注意 step1 = step / elemSize1,M.step[m-1] 总是等于 elemSize,M.step1(m-1)总是等于 channels;

elemSize : 矩阵中每一个像素的数据大小,如果Mat中的数据的数据类型是 CV_8U 则elemSize = 1;CV_8UC3 则elemSize = 3,记住另外有个 elemSize1 表示的是矩阵中数据类型的大小,即 elemSize / channels 的大小

 

考虑二维情况(stored row by row)按行存储

一个 3 X 4 的矩阵,M.rows == 3; M.cols == 4;二维矩阵,那么维度为 2 (M.dims == 2);

假设其数据类型为 CV_8U,也就是单通道的 uchar 类型,sizeof(uchar) = 1,那么每一个像素大小为 1

(M.elemSize() == 1, M.elemSize1() == 1);

M.depth() == 0, M.channels() == 1;

因为是二维矩阵,step 数组只有两个值, step[0] 和 step[1] 分别代表一行的数据大小和一个像素的数据大小

则 M.step[0] == 4, M.step[1] == 1;

 

假设上面的矩阵数据类型是 CV_8UC3,也就是三通道

M.dims == 2;

M.channels() == 3;

M.depth() == 0;

M.elemSize() == 3 (每一个像素包含3个uchar值)

M.elemSize1() == 1 (elemSize / channels)

M.step[0] == M.cols * M.elemSize() == 12,

M.step[1] == M.channels() * M.elemSize1() == M.elemSize() == 3;

 

考虑三维情况(stored plane by plane)按面存储

一个 3 X 4 X 6 的矩阵,假设其数据类型为 CV_16SC4,也就是 short 类型
M.dims == 3 ; M.channels() == 4 ; M.elemSize1() == sizeof(short) == 2 ;

M.rows == M.cols == –1;

M.elemSize() == M.elemSize1() * M.channels() == 2 * 4 == 8;

M.step[M.dims-1]==M.elemSize() == 8;

M.step[0] == 4 * 6 * M.elemSize() == 192;

M.step[1] == 6 * M.elemSize() == 48;

M.step[2] == M.elemSize() == 8;

M.step1(0) == M.step[0] / M.elemSize() == 192 / 2 == 96 (第一维度(即面的像素个数) * 通道数);

M.step1(1) == M.step[1] / M.elemSize() == 48 / 2 == 24(第二维度(即行的像素个数/列宽) * 通道数);

M.step1(2) == M.step[2] / M.elemSize() == M.channels() == 4(第三维度(即像素) * 通道数);

 

 

二、创建Mat类对象的方式:

1.构造函数

(1)方式一 :制定行数列数,但是不初始化赋值

Mat::Mat(int rows, int cols, int type)
创建行数为 rows,列数为col,类型为type的图像;

Mat::Mat(Size size, int type)
创建大小为 size,类型为type的图像;

 

type的类型有CV_8UC1,CV_16SC1,…,CV_64FC4等。里面的8U表示8位无符号整数,16S表示16位有符号整数,64F表示64位浮点数(即double类型);C后面的数表示通道数,例如C1表示一个通道的图像,C4表示4个通道的图像,以此类推。

如果你需要更多的通道数,需要用宏CV_8UC(n),例如:Mat M(3,2, CV_8UC(5));      //创建行数为3,列数为2,通道数为5的图像

 

(2)方式二 :指定行数和列数,使用Scalar初始化赋值

Mat::Mat(int rows, int cols, int type, const Scalar& s)

创建行数为rows,列数为col,类型为type的图像,并将所有元素初始化为值s;

Mat::Mat(Size size, int type, const Scalar& s)
创建大小为 size,类型为type的图像,并将所有元素初始化为值s

 

Mat m(3, 5, CV_32FC1, Scalar(1));   // m为3*5的矩阵,float型的单通道,把每个点都初始化为1

cout<<m;


输出为:
[1, 1, 1, 1, 1;
  1, 1, 1, 1, 1;
  1, 1, 1, 1, 1]
 

Mat m(3, 5, CV_32FC2, Scalar(1, 2));    // m为3*5的矩阵,float型的2通道,把每个点都初始化为1 2
cout<<m;

输出为:
[1, 2, 1, 2, 1, 2, 1, 2, 1, 2;
  1, 2, 1, 2, 1, 2, 1, 2, 1, 2;
  1, 2, 1, 2, 1, 2, 1, 2, 1, 2]

 

Mat m(3, 5, CV_32FC3, Scalar(1, 2, 3));// m为3*5的矩阵,float型的3通道,把每个点都初始化为1 2 3
cout << m

输出为:
[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3;
  1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3;
  1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]

 

(3)方式三:使用已有的的mat对象赋值,其实就是对已有的mat对象取了别名

Mat::Mat(const Mat& m)
将 m 赋值给新创建的对象,此处不会对图像数据进行复制, m 和新对象共用图像数据;

 

(4)方式四:指定行数和列数,使用已有数据源的数据地址初始化赋值

Mat::Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP)

创建行数为 rows,列数为cols,类型为type的图像,此构造函数不创建图像数据所需内存,而是直接使用data所指内存,图像的行步长由step指定。

Mat::Mat(Size size, int type, void* data, size_t step=AUTO_STEP)
创建大小为 size,类型为type的图像,此构造函数不创建图像数据所需内存,而是直接使用data所指内存,图像的行步长由step指定。

uchar *data = new uchar[15];
for (int i = 0; i < 15; i++)
{
   data[i] = 2;
}
Mat m(3, 5, CV_8UC1, data);
cout << m;


输出为:
[2, 2, 2, 2, 2;
  2, 2, 2, 2, 2;
  2, 2, 2, 2, 2]


如果接着

delete [] data;
cout << m;


输出为:
[-1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144;
  -1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144;
  -1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144]
可见,这里只是进行了浅拷贝,当数据源不在的时候,Mat里的数据也就是乱码了。

 

uint *data = new uint[24];
for (int i =1; i < =24; i++)
{
   data[i] = i;
}
Mat m(2, 4, CV_16UC3, data);
cout << m;

 

输出为: 

[1, 2, 3, 4, 5,6,7,8,9,10,11,12;

13,14,15,16, 17, 18, 19, 20,21,22,23,24]

可知,数据存储方式是按像素存储,不是按通道存储

uchar *data = new uchar[15];
for (int i = 0; i < 15; i++)
{
   data[i] = 2;
}
Mat m(3, 5, CV_8UC1, data);
cout << m;


输出为:
[2, 2, 2, 2, 2;
  2, 2, 2, 2, 2;
  2, 2, 2, 2, 2]


如果接着

delete [] data;
cout << m;


输出为:
[-1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144;
  -1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144;
  -1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144]


可见,这里只是进行了浅拷贝!!!!!!!   当数据源不在的时候,Mat里的数据也就是乱码了

 

uint *data = new uint[24];
for (int i =1; i < =24; i++)
{
   data[i] = i;
}
Mat m(2, 4, CV_16UC3, data);
cout << m;

 

输出为: 

[1, 2, 3, 4, 5,6,7,8,9,10,11,12;

13,14,15,16, 17, 18, 19, 20,21,22,23,24]

可知,数据存储方式是按像素存储,不是按通道存储

 

(5)方式五:使用已有mat对象的部分区域初始化赋值

Mat::Mat(const Mat& m, const Range& rowRange, const Range& colRange)
创建的新图像为 m 的一部分,具体的范围由 rowRange 和 colRange 指定,此构造函数也不进行图像数据的复制操作,新图像与m共用图像数据;

Mat::Mat(const Mat& m, const Rect& roi)
创建的新图像为 m 的一部分,具体的范围 roi 指定,此构造函数也不进行图像数据的复制操作,新图像与m共用图像数据。
 

2.create()函数创建对象

Mat M;

M.create(3,2,CV_8UC2);

需要注意的时,使用create()函数无法设置图像像素的初始值。

Mat M(2,2,CV_8UC3);   //构造函数创建图像
M.create(3,2, CV_8UC2);   //释放内存重新创建图像


如果create( )函数指定的参数与图像之前的参数相同,则不进行实质的内存申请操作;如果参数不同,则减少原始数据内存的索引,并重新申请内存
 

 

三、Mat类对象之间的拷贝

Mat这个类有两部分数据:

一个是matrix header,这部分的大小是固定的,包含矩阵的大小,存储的方式,矩阵存储的地址等等。

另一个部分是一个指向矩阵包含像素值的指针

Mat A, C;   

A = imread(argv[1], CV_LOAD_IMAGE_COLOR); 

Mat B(A);

C = A; 

copy这样的操作只是copy了矩阵的matrix header和那个指针,而不是矩阵的本身,也就意味着两个矩阵的数据指针指向的是同一个地址,需要开发者格外注意。比如上面这段程序,A、B、C指向的是同一块数据,他们的header不同,但对于A的操作同样也影响着B、C的结果。当我不再使用A的时候就把内存释放了,那时候再操作B和C岂不是很危险,不用担心,OpenCV的大神为我们已经考虑了这个问题,是在最后一个Mat不再使用的时候才会释放内存,咱们就放心用就行了。

 

如果想建立互不影响的Mat,是真正的复制操作,需要使用函数clone()或者copyTo()

说到数据的存储,这一直就是一个值得关注的问题,Mat_<uchar>对应的是CV_8U,Mat_<char>对应的是CV_8S,Mat_<int>对应的是CV_32S,Mat_<float>对应的是CV_32F,Mat_<double>对应的是CV_64F,对应的数据深度如下:

• CV_8U - 8-bit unsigned integers ( 0..255 )

• CV_8S - 8-bit signed integers ( -128..127 )

• CV_16U - 16-bit unsigned integers ( 0..65535 )

• CV_16S - 16-bit signed integers ( -32768..32767 )

• CV_32S - 32-bit signed integers ( -2147483648..2147483647 )

• CV_32F - 32-bit floating-point numbers ( -FLT_MAX..FLT_MAX, INF, NAN )

• CV_64F - 64-bit floating-point numbers ( -DBL_MAX..DBL_MAX, INF, NAN )

这里还需要注意一个问题,很多OpenCV的函数支持的数据深度只有8位和32位的,所以要少使用CV_64F,但是vs的编译器又会把float数据自动变成double型,有些不太爽。

 

参考:https://www.douban.com/note/265479171/

2018-07-23 20:44:08 Du_Shuang 阅读数 2781
  • Mat对象创建与使用

    掌握OpenCV核心模块,熟练使用相关API 理解各个API背后的相关算法原理,每个参数意义 有能力解决使用应用场景问题,大量工程代码经验分享 掌握图像处理与视频分析,图像分析与测量编码与开发技巧

    191人学习 贾志刚
    免费试看

Mat数据类型是opencv独有图像数据类型,暗示一般通过摄像头获取的图形数据都是BYTE数据类型,如果我们需要用opencv对该图像进行处理,那么我们就必须要学会这两种数据类型之间的转换。
1.BYTE数据类型转换为Mat数据类型
其实opencv自带还是进行这种转换
Mat image=Mat(height,width,CV_8UC3,image1)
这是Mat的一个构造函数,第一个参数为图像的高,宽,第三个为图像的通套数和数据位,第四个参数就是我们的BYTE*数据了。

2.Mat数据类型转BYTE类型
其实这个OPENCV也有线程的函数
image.data
上面这个属性返回的就是uchar也就是BYTE类型的图像数据,我们只需要用memcpy()函数将该数据复制到我们创建的BYTE数据里面就ok了。

推荐博客:https://blog.csdn.net/qq_35971623/article/details/78471875

2017-11-01 15:06:01 qq_32215819 阅读数 4359
  • Mat对象创建与使用

    掌握OpenCV核心模块,熟练使用相关API 理解各个API背后的相关算法原理,每个参数意义 有能力解决使用应用场景问题,大量工程代码经验分享 掌握图像处理与视频分析,图像分析与测量编码与开发技巧

    191人学习 贾志刚
    免费试看

在使用VS+QT+OpenCV做图像处理的过程中,对OpenCV中的Mat类型的数据格式一直很头疼CV_8UC4,CV_32F等等格式的输出如果是在使用namewindow+imshow的组合进行弹窗输出时确实是没有什么大问题,但是当需要把图片转成QImage输出到控件上时就会出大问题了,因为不同图片格式的Mat在转成QImage时需要使用不同的参数。

但是好在Mat类型在自身的不同格式间转换时没有什么阻碍的。所以就此整理出以下Mat类型输出到QT控件的方法。


一、输出灰度图像,输出灰度图像在格式转换前后通常来说不会有太大的影响,仅有的影响也就只有因为QImage可能会只显示图片的左上角某一部分(长三分之一,宽三分之一),在我看来应该是图像中每个像素数据所占的位数不一样导致的。所以通常只需要在转成QImage前把图片使用mat.convertTo(mat,CV_8UC3){也可以用其他格式}转换格式后再转成QImage(转成QImage的方法在下面会给出)。


二、输出RGB图,输出RGB图在通常时和输出灰度图是一样的(这个通常情况指的是读取的是RGB图,输出这个读取的RGB图),但是如果对RGB图进行了三个通道的拆分后,对RGB通道分别处理以后再重新合成RGB图像的话(使用vector<Mat> vmat读取B G R三个通道的数据并分别处理,最后使用merge(vmat,mat)组成输出的图像),需要在merge后再次使用convertTO转变mat的格式,然后才能统一输出,至于转成什么格式,推荐是CV_8UC1,CV_8UC3,CV_8UC4,原因的话,因为本人对QImage的数据格式不了解,只能参照其他博主的QImage和Mat类型的格式转换方法,在之前搜索的资料中看到过两个博主的代码是可用的,根据自己的使用体验推荐下面这段代码

[cpp] view plain copy
  1. QImage MatToQImage(const cv::Mat& mat)    
  2. {    
  3.     // 8-bits unsigned, NO. OF CHANNELS = 1    
  4.     if(mat.type() == CV_8UC1)    
  5.     {    
  6.         QImage image(mat.cols, mat.rows, QImage::Format_Indexed8);    
  7.         // Set the color table (used to translate colour indexes to qRgb values)    
  8.         image.setColorCount(256);    
  9.         for(int i = 0; i < 256; i++)    
  10.         {    
  11.             image.setColor(i, qRgb(i, i, i));    
  12.         }    
  13.         // Copy input Mat    
  14.         uchar *pSrc = mat.data;    
  15.         for(int row = 0; row < mat.rows; row ++)    
  16.         {    
  17.             uchar *pDest = image.scanLine(row);    
  18.             memcpy(pDest, pSrc, mat.cols);    
  19.             pSrc += mat.step;    
  20.         }    
  21.         return image;    
  22.     }    
  23.     // 8-bits unsigned, NO. OF CHANNELS = 3    
  24.     else if(mat.type() == CV_8UC3)    
  25.     {    
  26.         // Copy input Mat    
  27.         const uchar *pSrc = (const uchar*)mat.data;    
  28.         // Create QImage with same dimensions as input Mat    
  29.         QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_RGB888);    
  30.         return image.rgbSwapped();    
  31.     }    
  32.     else if(mat.type() == CV_8UC4)    
  33.     {    
  34.         qDebug() << "CV_8UC4";    
  35.         // Copy input Mat    
  36.         const uchar *pSrc = (const uchar*)mat.data;    
  37.         // Create QImage with same dimensions as input Mat    
  38.         QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_ARGB32);    
  39.         return image.copy();    
  40.     }    
  41.     else    
  42.     {    
  43.         qDebug() << "ERROR: Mat could not be converted to QImage.";    
  44.         return QImage();    
  45.     }    
  46. }

[cpp] view plain copy
  1. cv::Mat QImageToMat(QImage image)  
  2. {  
  3.     cv::Mat mat;  
  4.     switch (image.format())  
  5.     {  
  6.     case QImage::Format_ARGB32:  
  7.     case QImage::Format_RGB32:  
  8.     case QImage::Format_ARGB32_Premultiplied:  
  9.         mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine());  
  10.         break;  
  11.     case QImage::Format_RGB888:  
  12.         mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine());  
  13.         cv::cvtColor(mat, mat, CV_BGR2RGB);  
  14.         break;  
  15.     case QImage::Format_Indexed8:  
  16.         mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine());  
  17.         break;  
  18.     }  
  19.     return mat;  
  20. }  

在图片格式转换完成后,使用
ui.QLabel->setPixmap(QPixmap::fromImage(QImage));
ui.QLabel->resize(ui.QLabel->pixmap()->size());
完成QImage输出到QLabel的操作

本文代码片段来源:http://blog.csdn.net/dancing_night/article/details/51545524,如有侵犯,请联系本人好及时删除。

2016-11-27 12:48:37 liumangmao1314 阅读数 11718
  • Mat对象创建与使用

    掌握OpenCV核心模块,熟练使用相关API 理解各个API背后的相关算法原理,每个参数意义 有能力解决使用应用场景问题,大量工程代码经验分享 掌握图像处理与视频分析,图像分析与测量编码与开发技巧

    191人学习 贾志刚
    免费试看

opencv中对Mat类型图像感兴趣(ROI)轮廓外接矩形并截取保存结果

最近自己在用opencv做图像实验时,要对轮廓外接矩形,网上大多是对IplImage类型图像做处理,而现在opencv中Mat取代了IplImage类型的图像,IplImage类型存储比Mat类型复杂,而且不如Mat类型图像访问方便,比如IplImage类型图像访问每个点的像素时,要计算步长如srcimage->imageData[i+j*srcimage->widthStep],看起来就很是繁琐,Mat类型图像srcimage.at<uchar>(i,j)即可,很方便。
话不多说,进入正题。

IplImage类型的图像用cvSetImageRIO()函数即可,具体使用代码如下:

cvSetImageROI(frame,((CvContour*)c)->rect);//frame为源图像,((CvContour*)c)->rect就是CvSeq *c;
Mat类型图像处理如下:
大致步骤:
1、用findContours()检测轮廓,具体参数可以上百度或借阅资料参考;
2、用approxPolyDP()求出多边形近似,参数如下的代码;
3、利用boundingRect()得到每个轮廓外接矩形的数据结构信息,存在boundRect[i]中;
4、image(boundRect[i])得到每个轮廓外接矩形的结果图像,可以存储到指定文件夹。
具体看代码(一些主要语句):
//轮廓检测  
    cv::vector<vector<Point>> contours;//定义轮廓集合  
    cv::vector<Vec4i> hierarchy;  
    cv::findContours(thinDoublexy,contours,hierarchy,CV_RETR_TREE,CV_CHAIN_APPROX_SIMPLE,Point(0,0));//thinDoublexy为需要查找轮廓的图像,其他参数借阅资料。  
    Mat drawing=Mat::zeros(thinDoublexy.size(),CV_8UC3);  
    cv::vector<vector<Point>> conpoint(contours.size());  
    cv::vector<Rect> boundRect(contours.size());  
    for(int i=0;i<contours.size();i++)  
    {  
        approxPolyDP(Mat(contours[i]),conpoint[i],3,true);//多边形近似  
        boundRect[i]=boundingRect(Mat(conpoint[i]));//得到轮廓外接矩形数据结构  
        cv::drawContours(drawing,contours,i,255,2,8,hierarchy,0,Point());//画出轮廓得到图像drawing  
        if(boundRect[i].area()>4) //有些轮廓就是一个点,要舍去  
        {  
            cv::Mat imageROI=image(boundRect[i]);//根据轮廓外接矩形信息进行截取RIO感兴趣部分图像  
            std::stringstream ss;//int转换为string  
            std::string str;  
            ss<<i;  
            ss>>str;
	 string tempname = pathWrite+"\\" + str + "result.jpg";<span style="font-family: Arial, Helvetica, sans-serif;">//pathWrite变量是文件夹路径 
            imwrite(tempname.c_str(),imageROI);//存储图像至指定文件夹  
            //cout<<tempname<<endl;  
            //imshow(tempname,imageROI);  
        }  
    }
上述代码亲测有效,运行结果如下,只是我做一下说明:代码是部分代码,只是实现Mat类型图像的轮廓外接矩形并保存结果图像的功能,要想代码跑起来,肯定需要加一些定义部分的代码。我自己也是初学者,如果你发现不对的地方,敬请指正,谢谢!

运行结果:

源图像


运行结果