匿名
未登录
登录
丢石头百科
搜索
查看“树莓派OpenCV系列教程3:IDE及图像存储的数据结构”的源代码
来自丢石头百科
名字空间
页面
讨论
更多
更多
页面选项
查看
查看源代码
历史
←
树莓派OpenCV系列教程3:IDE及图像存储的数据结构
因为以下原因,您没有权限编辑本页:
您所请求的操作仅限于该用户组的用户使用:
用户
您可以查看与复制此页面的源代码。
本章将首先讲解开发OpenCV的基本的IDE,以方便代码提示,同时方便阅读底层源码,接着,讲解图像存储的基本数据结构。 == 1 IDE == 之前两章开发OpenCV均未使用到IDE,比如开发Python的代码,用记事本即可写,需要时执行该脚本即可,当开发C++的代码则需要先Cmake生成Makefile,再make编译,最后执行,为提高效率,首先介绍常用的IDE的相关配置,以及如何将OpenCV集成到该工程中。 说明: * 关于Python中,目前采用的是OpenCV4,为什么却是import cv2呢? <blockquote style="display: block; padding: 15px 15px 15px 1rem; font-size: 0.9em; margin: 1em 0px; color: rgb(129, 145, 152); border-left: 6px solid rgb(220, 230, 240); background: rgb(242, 247, 251); overflow: auto; overflow-wrap: normal; word-break: normal;"> OpenCV1采用C语言编写,OpenCV2,OpenCV3,OpenCV4均采用C++编写,在Python中,cv1指代底 层算法为C语言的OpenCV版本,cv2指代底层算法为C ++语言的版本,所以,在Python中,import cv2可能是OpenCV2,OpenCV3,OpenCV4等版本。 </blockquote> * Python版本的OpenCV能不能看到底层的Python实现? <blockquote style="display: block; padding: 15px 15px 15px 1rem; font-size: 0.9em; margin: 1em 0px; color: rgb(129, 145, 152); border-left: 6px solid rgb(220, 230, 240); background: rgb(242, 247, 251); overflow: auto; overflow-wrap: normal; word-break: normal;"> 不能,因为底层实现为C++,OpenCV仅仅提供了Python接口,同时OpenCV还提供了C#,java,Andriod等的接口。 </blockquote> * C++版本的OpenCV,推荐用什么IDE呢? <blockquote style="display: block; padding: 15px 15px 15px 1rem; font-size: 0.9em; margin: 1em 0px; color: rgb(129, 145, 152); border-left: 6px solid rgb(220, 230, 240); background: rgb(242, 247, 251); overflow: auto; overflow-wrap: normal; word-break: normal;"> 因为工程是通过Cmake构建的,首先应该选择支持构建Cmake工程的IDE,在Linux(Raspbian)平台下,推荐Qt Creator,除了采用Cmake构建OpenCV工程外,Qt Creator还支持采用qmake构建OpenCV工程;此外,Qt creator还支持底层接口函数跳转,方便跳转到对应的函数中,方便OpenCV的算法研究,算法移植等工作。 </blockquote> === 1.1 Python3 === 若在树莓派平台采用Raspbian系统结合Python3开发OpenCV,由于Python仅提供相应的接口,无法查阅底层的实现,加上Python本身的简洁优雅,使用树莓派自带的Thonny Python IDE即可,如下图所示: [[File:20190901022724.png]] 点击Load,打开00_Test_OpenCV.py,点击Run,即可运行相应的Python脚本,此外,该IDE支持断点调试,如下图所示: [[File:20190901023321.png]] === 1.2 C++ === 在C++开发环境中,首选Qt Creator,方便添加依赖,同时支持跳转底层代码,开发与研究两不误;同时,结合Qt自带的UI界面,方便做成桌面应用;并且,支持添加树莓派自带bcm2835,wiringPi等库,方便进行底层开发;结合C ++的特点,向上支持应用开发,向下支持底层开发。 ==== 1.2.1 安装Qt Creator ==== <syntaxhighlight lang="python"> <syntaxhighlight lang="python"> sudo apt-get update sudo apt-get upgrade sudo apt-get install qt5-default sudo apt-get install qtcreator </syntaxhighlight></syntaxhighlight> 安装好之后,即可在菜单->编程界面中找到Qt Creator,如下图所示: [[File:20190901012530.png]] ==== 1.2.2 创建并运行Cmake项目 ==== 打开Qt Creator,新建工程,选择纯C++项目: [[File:20190901014412.png]] 选择合适的路径: [[File:20190901014532.png]] 选择Cmake: [[File:20190901014622.png]] 一般选择默认编译套件即可: [[File:20190901014720.png]] 默认的工程中,已经包含了Cmakelists.txt和main.cpp文件,现在修改Cmakelists.txt和main.cpp文件为《树莓派OpenCV系列教程1:开发环境搭建》的内容,如下图所示: [[File:20190901020105.png]] [[File:20190901020016.png]] 若Qt Creator提示有错误,一般Cmakelists.txt有误。 此时,点击Qt Creator左下角的绿色三角形,将运行程序,打开摄像头,并输出相关信息,如下图所示: [[File:20190901021139.png]] 程序设定了按下q键即可退出窗口,此时,按下q键即可退出。 ==== 1.2.3 其它使用说明 ==== 若想跳转到具体的函数,可按住Ctrl,再单击相应的函数即可,跳转到具体的函数之后,有相应的函数说明,方便查阅,如下图: [[File:20190901021813.png]] 并且,Qt Creator支持编程提示,错误提示等功能,能大幅提高效率,如下图所示: [[File:20190901022115.png]] == 2 图像存储的数据结构 == 任何图像处理算法,都是从操作每个像素点开始的。即使我们不会使用OpenCV提供的图像处理算法,只要了解图像处理算法的基本原理,也可以写出具有相同功能的程序。接下来,我们首先讲解图像存储的基本数据结构,接着,讲解如何访问图像中的具体某个像素点,将分为Python3和C++进行讲解。 === 2.1 Python3 === ==== 2.1.1 numpy基础 ==== 在Python中OpenCV图像读取(imread) 读入的数据格式是numpy的ndarray数据格式,此外,Python在数据计算领域火爆,numpy功不可没,所以,在讲解Python图像存储数据结构之前,有必要先讲解涉及到的numpy的相关操作: ===== ndarray初始化数组 ===== Python3: <syntaxhighlight lang="python"> <syntaxhighlight lang="python"> import numpy as np # One-dimensional array A1 = np.array([1, 2, 3]) # Two-dimensional array A2 = np.array([[1, 2, 3], [4, 5, 6]]) print('A1: \n%s'%A1) print('A2: \n%s'%A2) </syntaxhighlight></syntaxhighlight> 输出: <syntaxhighlight lang="python"> <syntaxhighlight lang="python"> A1: [1 2 3] A2: [[1 2 3] [4 5 6]] </syntaxhighlight></syntaxhighlight> ===== ndarray的属性 ===== <table style="display: table; width: 100%; text-align: left;"> <thead> <tr style="border-width: 1px 0px 0px; border-right-style: initial; border-bottom-style: initial; border-left-style: initial; border-right-color: initial; border-bottom-color: initial; border-left-color: initial; border-image: initial; border-top-style: solid; border-top-color: rgb(204, 204, 204); background-color: white;"> <th style="font-size: 1em; border: 1px solid rgb(204, 204, 204); padding: 0.5em 1em; text-align: left;font-weight: bold; background-color: rgb(240, 240, 240);">属性名称</th> <th style="font-size: 1em; border: 1px solid rgb(204, 204, 204); padding: 0.5em 1em; text-align: left;font-weight: bold; background-color: rgb(240, 240, 240);">含义</th> </tr> </thead> <tbody style="border: 0px;"> <tr style="border-width: 1px 0px 0px; border-right-style: initial; border-bottom-style: initial; border-left-style: initial; border-right-color: initial; border-bottom-color: initial; border-left-color: initial; border-image: initial; border-top-style: solid; border-top-color: rgb(204, 204, 204); background-color: white;"> <td style="font-size: 1em; border: 1px solid rgb(204, 204, 204); padding: 0.5em 1em; text-align: left;">ndarray.ndim</td> <td style="font-size: 1em; border: 1px solid rgb(204, 204, 204); padding: 0.5em 1em; text-align: left;">数组的维度,等于Rank</td> </tr> <tr style="border-width: 1px 0px 0px; border-right-style: initial; border-bottom-style: initial; border-left-style: initial; border-right-color: initial; border-bottom-color: initial; border-left-color: initial; border-image: initial; border-top-style: solid; border-top-color: rgb(204, 204, 204); background-color: white;background-color: rgb(248, 248, 248);"> <td style="font-size: 1em; border: 1px solid rgb(204, 204, 204); padding: 0.5em 1em; text-align: left;">ndarray.shape</td> <td style="font-size: 1em; border: 1px solid rgb(204, 204, 204); padding: 0.5em 1em; text-align: left;">(行数, 列数)</td> </tr> <tr style="border-width: 1px 0px 0px; border-right-style: initial; border-bottom-style: initial; border-left-style: initial; border-right-color: initial; border-bottom-color: initial; border-left-color: initial; border-image: initial; border-top-style: solid; border-top-color: rgb(204, 204, 204); background-color: white;"> <td style="font-size: 1em; border: 1px solid rgb(204, 204, 204); padding: 0.5em 1em; text-align: left;">ndarray.size</td> <td style="font-size: 1em; border: 1px solid rgb(204, 204, 204); padding: 0.5em 1em; text-align: left;">元素总个数 = 列数 * 行数</td> </tr> <tr style="border-width: 1px 0px 0px; border-right-style: initial; border-bottom-style: initial; border-left-style: initial; border-right-color: initial; border-bottom-color: initial; border-left-color: initial; border-image: initial; border-top-style: solid; border-top-color: rgb(204, 204, 204); background-color: white;background-color: rgb(248, 248, 248);"> <td style="font-size: 1em; border: 1px solid rgb(204, 204, 204); padding: 0.5em 1em; text-align: left;">ndarray.dtype</td> <td style="font-size: 1em; border: 1px solid rgb(204, 204, 204); padding: 0.5em 1em; text-align: left;">数组元素数据类型</td> </tr> <tr style="border-width: 1px 0px 0px; border-right-style: initial; border-bottom-style: initial; border-left-style: initial; border-right-color: initial; border-bottom-color: initial; border-left-color: initial; border-image: initial; border-top-style: solid; border-top-color: rgb(204, 204, 204); background-color: white;"> <td style="font-size: 1em; border: 1px solid rgb(204, 204, 204); padding: 0.5em 1em; text-align: left;">ndarray.itemsize</td> <td style="font-size: 1em; border: 1px solid rgb(204, 204, 204); padding: 0.5em 1em; text-align: left;">数组中每个元素,字节大小</td> </tr> </tbody> </table> Python3: <syntaxhighlight lang="python"> <syntaxhighlight lang="python"> A2 = np.array([[1, 2, 3], [4, 5, 6]]) print('A2.ndim = %d' % A2.ndim) print('A2.shape') print(A2.shape) print('A2.size = %d' % A2.size) print('A2.dtype = %s'%A2.dtype) print('A2.itemsize = %d'%A2.itemsize) </syntaxhighlight></syntaxhighlight> 输出: <syntaxhighlight lang="python"> <syntaxhighlight lang="python"> A2.ndim = 2 A2.shape (2, 3) A2.size = 6 A2.dtype = int64 A2.itemsize = 8 </syntaxhighlight></syntaxhighlight> ===== ndarray的切片操作 ===== 对数组进行切片操作,指的是获取数组的其中某一个子区域,具体切片操作,详情见如下图: <h6 id="-" style="margin: 1.5em 0px; font-weight: bold;font-size: 1em; color: rgb(9, 187, 7);">一维数组的切片操作</h6> [[File:20190901214035.png]] 其中: A = np.arange(10)表示生成0到9,步长为1的一维数组。 A[0:3:1]是A[0:3]及A[:3]的完整写法,表示取一维数组A中索引从0到3(但不包含3),步长为1的元素。 A[-1:5:-1]中:这里start=-1代表最后一个元素,表示取一维数组A中索引从最后一个到第5个(但不包含5),步长为-1的元素。 A[ : : -1]表示将一维数组A中的元素逆序取出 同理: A[ : : 1]表示将一维数组A中的元素顺序取出 <h6 id="-" style="margin: 1.5em 0px; font-weight: bold;font-size: 1em; color: rgb(9, 187, 7);">多维数组的切片操作</h6> 对于多维数组的切片操作,中间需要使用逗号进行分隔,如下图所示: [[File:20190901221239.png]] 对于图像数据结构ndarray的切片操作,可参考上图。 ==== 2.1.2 OpenCV中图像数据结构ndarray ==== 下图是OpenCV中BGR格式的数据结构: [[File:20190901221911.png]] 第一维度 : Height 高度, 对应这张图片的 nRow行数 第二维度 : Width 宽度, 对应这张图片的nCol 列数 第三维度: Value BGR三通道的值. BGR 分别代表:B: Blue 蓝色,G: Green 绿色,R: Red 红色 ==== 2.1.3 从ndarray中取出像素点值 ==== 注意,由于历史原因,OpenCV存储图像的数据结构采用的BGR格式,而非RGB,下面,将读取一幅图片,并把这幅图片的像素分片打印出来。 <syntaxhighlight lang="python"> <syntaxhighlight lang="python"> import cv2 img = cv2.imread('color.jpg') cv2.imshow('image',img) print(img[100:102,100:102]) cv2.waitKey(0) cv2.destroyAllWindows() </syntaxhighlight></syntaxhighlight> 输出结果如下图所示: [[File:20190902000858.png]] 通过对图像数据结构ndarray的切片操作,打印出了4个像素点的值,需要注意的是,每个点的像素值是以(B,G,R)的形式存储。 === 2.2 C++ === 与Python不同,在OpenCV4版本中(OpenCV1例外),提供了Mat类作为图像容器,该对象利用了内存管理(非严格意义上的),可以避免在退出程序前忘记释放内存造成的内存泄露。 总而言之,Mat就是一个类,由两个数据部分组成:矩阵头(包含矩阵尺寸、存储方法、存储地址等信息)和一个存储所有像素值的矩阵(根据所选存储方法的不同,矩阵可以是不同的维数)的指针。 ==== 2.2.1 创建矩阵及输出矩阵的常用方法 ==== 当使用拷贝构造函数,或对矩阵进行复制时,只复制信息头和矩阵指针,而不复制矩阵。 来看下面这段代码: <syntaxhighlight lang="python"> <syntaxhighlight lang="python"> Mat A,C A = imread("1.jpg",CV_LOAD_IMAGE_COLOR); Mat B(A); C = A; </syntaxhighlight></syntaxhighlight> 在以上代码中,构造函数Mat B(A),赋值操作C=A,均只是复制矩阵A的信息头和矩阵指针,而不复制矩阵。 如果需要复制矩阵进行操作(实际不建议大量复制矩阵,因为图像一般比较占内存),可使用以下操作: <syntaxhighlight lang="python"> <syntaxhighlight lang="python"> Mat A,B,C; A = imread("1.jpg",CV_LOAD_IMAGE_COLOR); B = A.clone(); A.copyTo(C); </syntaxhighlight></syntaxhighlight> 这样一来,B和C均复制了A的图像矩阵。 此外,可直观地使用以下方法创建矩阵: <syntaxhighlight lang="python"> <syntaxhighlight lang="python"> Mat M(2,2,CV_8UC3,Scalar(0,0,255)); cout << "M = " << endl << " " << M << endl << endl; </syntaxhighlight></syntaxhighlight> 输出结果如下: <syntaxhighlight lang="python"> <syntaxhighlight lang="python"> M= [0,0,255,0,0,255; 0,0,255,0,0,255] </syntaxhighlight></syntaxhighlight> 下面将通过一个综合示例来演示创建矩阵及矩阵的输出方法: <syntaxhighlight lang="python"> <syntaxhighlight lang="python"> #include "opencv2/core/core.hpp" #include "opencv2/highgui/highgui.hpp" #include <iostream></iostream> using namespace std; using namespace cv; int main(int,char**) { //Create Mat I = Mat::eye(4, 4, CV_64F); I.at<double>(1,1) = CV_PI; //Display cout << "I=\n" << I << ";\n" << endl; //Create Mat r = Mat(3, 4, CV_8UC3); randu(r, Scalar::all(0), Scalar::all(255)); //Display cout << "(OpenCV default Style)=\n" << r << ";" << endl << endl; cout << "(Python Style)=\n" << format(r, Formatter::FMT_PYTHON) << ";" << endl << endl; cout << "(Numpy Style)=\n" << format(r, Formatter::FMT_NUMPY)<< ";" << endl << endl; cout << "(Comma Style)=\n" << format(r, Formatter::FMT_CSV)<< ";" << endl<< endl; cout << "(C Style)=\n" << format(r, Formatter::FMT_C) << ";" << endl << endl; //Create Point2f p(6, 2); //Display cout << "Two Dimension Point p =\n" << p << ";\n" << endl; //Create Point3f p3f(8, 2, 0); //Display cout << "Three Dimension Point p3f =\n" << p3f << ";\n" << endl; //Create vector<float> v; v.push_back(3); v.push_back(5); v.push_back(7); //Display cout << "Point based on vector shortvec =\n" << Mat(v) << ";\n"<<endl class="hljs-comment" style="color: rgb(150, 152, 150);">//Create vector </point2f> points(10); for (size_t i = 0; i < points.size(); ++i) { points[i] = Point2f((float)(i * 5), (float)(i % 7)); } //Display cout << "Two Dimension points =\n" << points<<";"; return 0; } </endl></syntaxhighlight></syntaxhighlight> 相应的输出结果如下所示: <syntaxhighlight lang="python"> <syntaxhighlight lang="python"> I= [1, 0, 0, 0; 0, 3.141592653589793, 0, 0; 0, 0, 1, 0; 0, 0, 0, 1]; (OpenCV default Style)= [ 91, 2, 79, 179, 52, 205, 236, 8, 181, 239, 26, 248; 207, 218, 45, 183, 158, 101, 102, 18, 118, 68, 210, 139; 198, 207, 211, 181, 162, 197, 191, 196, 40, 7, 243, 230]; (Python Style)= [[[ 91, 2, 79], [179, 52, 205], [236, 8, 181], [239, 26, 248]], [[207, 218, 45], [183, 158, 101], [102, 18, 118], [ 68, 210, 139]], [[198, 207, 211], [181, 162, 197], [191, 196, 40], [ 7, 243, 230]]]; (Numpy Style)= array([[[ 91, 2, 79], [179, 52, 205], [236, 8, 181], [239, 26, 248]], [[207, 218, 45], [183, 158, 101], [102, 18, 118], [ 68, 210, 139]], [[198, 207, 211], [181, 162, 197], [191, 196, 40], [ 7, 243, 230]]], dtype='uint8'); (Comma Style)= 91, 2, 79, 179, 52, 205, 236, 8, 181, 239, 26, 248 207, 218, 45, 183, 158, 101, 102, 18, 118, 68, 210, 139 198, 207, 211, 181, 162, 197, 191, 196, 40, 7, 243, 230 ; (C Style)= { 91, 2, 79, 179, 52, 205, 236, 8, 181, 239, 26, 248, 207, 218, 45, 183, 158, 101, 102, 18, 118, 68, 210, 139, 198, 207, 211, 181, 162, 197, 191, 196, 40, 7, 243, 230}; Two Dimension Point p = [6, 2]; Three Dimension Point p3f = [8, 2, 0]; Point based on vector shortvec = [3; 5; 7]; Two Dimension points = [0, 0; 5, 1; 10, 2; 15, 3; 20, 4; 25, 5; 30, 6; 35, 0; 40, 1; 45, 2]; </syntaxhighlight></syntaxhighlight> ==== 2.2.2 从Mat中取出像素点 ==== 本节,将介绍C++中常用的从Mat类的实例化对象中取出像素点的3种方法,并且,每种方法均对颜色空间进行缩减,即: 0~100范围的像素值为0; 100~200范围的像素值为100; 200~255范围的像素值为200。 并且,每种方法均统计了运行时间。 ===== 用指针访问像素 ===== <syntaxhighlight lang="python"> <syntaxhighlight lang="python"> #include <opencv2 core="" core.hpp=""></opencv2> #include <opencv2 highgui="" highgui.hpp=""></opencv2> #include <iostream></iostream> using namespace std; using namespace cv; void colorReduce(Mat& inputImage, Mat& outputImage, int div); int main( ) { Mat srcImage = imread("color.jpg"); imshow("srcImage",srcImage); Mat dstImage; dstImage.create(srcImage.rows,srcImage.cols,srcImage.type()); double time0 = static_cast<double>(getTickCount()); colorReduce(srcImage,dstImage,100); time0 = ((double)getTickCount() - time0)/getTickFrequency(); cout<<"This function waste time:"<<time0 class="hljs-string" style="color: rgb(181, 189, 104);">" second"<<endl class="hljs-string" imshow="" style="color: rgb(181, 189, 104);">"dstImage",dstImage); waitKey(0); } void colorReduce(Mat& inputImage, Mat& outputImage, int div) { outputImage = inputImage.clone(); int rowNumber = outputImage.rows; //Number of columns * Number of channels = number of elements per line int colNumber = outputImage.cols*outputImage.channels(); for(int i = 0;i < rowNumber;i++) { //Get the first address of the i-th line uchar* data = outputImage.ptr<uchar>(i); for(int j = 0;j < colNumber;j++) { data[j] = data[j]/div*div; } } } </uchar></endl></time0></syntaxhighlight></syntaxhighlight> 在该程序中,先获取每一行的元素的个数,在双重遍历中,先获取第i行的首地址,然后通过指针获取第i的第j个元素,再对该元素进行处理。 该函数的运行效果如下图所示: [[File:20190902023319.png]] 可见,遍历所有像素点并进行处理的时间为0.02秒左右 ===== 用迭代器访问像素 ===== 用迭代器访问像素点的操作如下程序所示: <syntaxhighlight lang="python"> <syntaxhighlight lang="python"> #include <opencv2 core="" core.hpp=""></opencv2> #include <opencv2 highgui="" highgui.hpp=""></opencv2> #include <iostream></iostream> using namespace std; using namespace cv; void colorReduce(Mat& inputImage, Mat& outputImage, int div); int main( ) { Mat srcImage = imread("color.jpg"); imshow("srcImage",srcImage); Mat dstImage; dstImage.create(srcImage.rows,srcImage.cols,srcImage.type()); double time0 = static_cast<double>(getTickCount()); colorReduce(srcImage,dstImage,100); time0 = ((double)getTickCount() - time0)/getTickFrequency(); cout<<"This function waste time:"<<time0 class="hljs-string" style="color: rgb(181, 189, 104);">" second"<<endl class="hljs-string" imshow="" style="color: rgb(181, 189, 104);">"dstImage",dstImage); waitKey(0); } void colorReduce(Mat& inputImage, Mat& outputImage, int div) { outputImage = inputImage.clone(); //Start position iterator Mat_<vec3b>::iterator it = outputImage.begin<vec3b>(); //End position iterator Mat_<vec3b>::iterator itend = outputImage.end<vec3b>(); for(;it != itend;++it) { (*it)[0] = (*it)[0]/div*div; (*it)[1] = (*it)[1]/div*div; (*it)[2] = (*it)[2]/div*div; } } </vec3b></vec3b></vec3b></vec3b></endl></time0></syntaxhighlight></syntaxhighlight> 该函数运行效果如下图所示: [[File:151712kbvhjdhd1sh1hvvb.png]] 在该方法中,直接使用迭代器进行处理,采用迭代器访问相对于数组越界的可能性,还是非常安全的,经实测,该方法遍历所有像素点并进行处理的时间为0.04秒左右。 可见,采用迭代器访问像素点的方法比采用指针访问像素点的方法慢了近一倍,因此,为提高处理速度,建议采用指针访问像素点。
返回至
树莓派OpenCV系列教程3:IDE及图像存储的数据结构
。
导航
导航
首页
最近更改
随机页面
MediaWiki帮助
首页
首页
树莓派
主机
配件包
外壳
键鼠
电源
扩展板
显示屏
墨水屏
摄像模块
通信模块
继电器
电机驱动板
游戏机
产品分类
树莓派
Arduino
micro:bit
STM32
Espressif
WiFi模块
蓝牙模块
无线模块
LoRa模块
4G模块
GSM
GPRS
以太网
导航模块
北斗卫星
GPS
LCD
墨水屏
OLED
摄像头
USB模块
串口模块
RS232
RS485
CAN
传感器
温度模块
湿度模块
气压模块
继电器
电机模块
指纹模块
电平转换
音频模块
编程器
Wiki工具
Wiki工具
特殊页面
页面工具
页面工具
用户页面工具
更多
链入页面
相关更改
页面信息
页面日志