CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(basics)
#Eigen
include_directories("/usr/include/eigen3")
#opencv
find_package(OpenCV REQUIRED)
#添加头文件
include_directories(${OpenCV_INCLUDE_DIRS})
add_executable(imageBasics imageBasics.cpp)
target_link_libraries(imageBasics ${OpenCV_LIBS})
imageBasics.cpp
1. int argc,char **argv
argc 表示输入参数的个数 *argv 是指针数组 存放输入的参数(看下面例子会清晰点)
例如 make 以后再终端输入
./imageBasics ../ubuntu.png
argc = 2
argv[0] 是./imageBasics ,argv[1] 是 ../ubuntu.png
cout<
2. nullptr c++11的新特性
C++中,NULL却被明确定义为整常数0;
C中,NULL定义为void*指针值0 , 但同时,也允许将NULL定义为整常数0。
C++是强类型语言,void* 是无法隐式转换为别的指针类型的,而C语言可以,
因此C++引入了nullptr 表示真正的空指针
(27 封私信 / 81 条消息) C++ 11为什么引入nullptr? - 知乎 (zhihu.com)https://www.zhihu.com/question/55936870
3. CV_8UC1 CV_8UC2 CV_8UC3
上面三个值的结构如下:
CV_
1、bit_depth --- 比特数 --- 代表8bite,16bites,32bites,64bites
举个例子:如果你现在创建了一个存储--灰度图片的Mat对象,这个图像的大小为宽100,高100,那么,现在这张灰度图片中有10000个像素点,它每一个像素点在内存空间所占的空间大小是8bite,8位--所以它对应的就是CV_8
2、S|U|F
S--代表---signed int---有符号整形
U--代表--unsigned int--无符号整形
F--代表--float---------单精度浮点型
3、C
1--灰度图片--grayImg---是--单通道图像
2--RGB彩色图像---------是--3通道图像
3--带Alph通道的RGB图像--是--4通道图像 //带透明色的RGB图像
总结:
CV_8UC1---则可以创建----8位无符号的单通道---灰度图片------grayImg
#define CV_8UC1 CV_MAKETYPE(CV_8U,1)
CV_8UC3---则可以创建----8位无符号的三通道---RGB彩色图像---colorImg
#define CV_8UC3 CV_MAKETYPE(CV_8U,3)
CV_8UC4--则可以创建-----8位无符号的四通道---带透明色的RGB图像
#define CV_8UC4 CV_MAKETYPE(CV_8U,4)
OpenCvSharp中CV_8UC1,CV_8UC2等参数的解释_x1angzeeD.的博客-CSDN博客https://blog.csdn.net/qq_48705696/article/details/109745779
4. Opencv 成员函数的使用
data:是Mat对象的一个指针
uchar *data ;//!指向数据的指针
uchar类型的指针,指向Mat数据矩阵的首地址,可以理解为一个房屋的门牌号,因此我们可以直接通过data,读取图像中某一段空间的值。当然,就是我们想要的B,G,R的值。
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 }; 可见 0和1都代表8位, 2和3都代表16位,4和5代表32位,6代表64位;
depth值和type值有关(type值就是上面的CV_8UC1)
比如 CV_16SC2 是一个2通道的16位的有符号整数,depth值就是CV_16S 也就是3
step:
step表示矩阵第一行元素的字节数
对于一个二位数组,step数组只有两个值 step[0] 表示一行的数据大小 ,step[1]表示一个元素的数据大小 ;
step1(0):矩阵中一行有几个通道数
step1(1):一个元素有几个通道数(channel())
Mat::at:
返回对指定数组元素的引用
获得灰度图image_gray中 位于第v行 u列 像素值
image_gray.at(v,u);
获得彩色图color中 位于第v行 u列 像素的BGR 值
cv::Mat color;
color.at(v,u)[0]; //B
color.at(v,u)[1]; //G
color.at(v,u)[2]; //R
Mat::ptr: 返回指定矩阵行的指针
cv::Mat image = cv::Mat(400, 600, CV_8UC1); //宽400,长600 单通道图片
uchar *data00 = image.ptr(0);
uchar *data10 = image.ptr(1);
uchar data01 = image.ptr(0)[1];
cv::Mat image = cv::Mat(400, 600, CV_8UC3); //宽400,长600,3通道彩色图片
cv::Vec3b * data000 = image.ptr(0);
cv::Vec3b * data100 = image.ptr(1);
cv::Vec3b data001 = image.ptr(0)[1];
5.访问Mat图像中每个像素的值
Mat的存储形式
如果是灰度图,一般存放
单通道灰度图数据存放格式:
RGB三通道彩色图 数据存放格式:
访问图像中的像素 :
第一种: 迭代器遍历图像的像素
//使用迭代器Matlterator_遍历像素
cv::Mat tempImage = image.clone();
//初始化原图像迭代器
cv::MatIterator_ srcStart = image.begin();
cv::MatIterator_ srcEnd = image.end();
//初始化输出图像迭代器
cv::MatIterator_ tempStart = tempImage.begin();
cv::MatIterator_ tempEnd = tempImage.end();
for(;srcStart != srcEnd; srcStart++, tempStart++){
(*tempStart) [0] = (*srcStart)[0] /2; //这里对像素的处理最后使图片变暗
(*tempStart) [1] = (*srcStart)[1] /2;
(*tempStart) [2] = (*srcStart)[2] /2;
}
cv::imshow("迭代器",tempImage);
cv::waitKey(0);
Matlterator_是Mat数据操作的迭代器,:begin()表示指向Mat数据的起始迭代器,:end()表示指向Mat数据的终止迭代器。迭代器方法是一种更安全的用来遍历图像的方式,首先获取到数据图像的矩阵起始,再通过递增迭代实现移动数据指针。
第二种:数组遍历法
//使用at遍历像素(数组遍历法)
cv::Mat image2 = image.clone();
for(size_t v = 0 ; v< image.rows; v++){ //size_t 相当于 usigned int
for(size_t u = 0 ; u< image.cols ; u++){
//单通道
if(image.channels() == 1){
image2.at (v,u) = image.at (v,u) / 2;
}
//三通道
else{
image2.at (v,u) [0] = image.at (v,u) [0] /2 ;
image2.at (v,u) [1] = image.at (v,u) [1] /2 ;
image2.at (v,u) [2] = image.at (v,u) [2] /2 ;
}
}
}
cv::imshow("at遍历容器",image2);
cv::waitKey(0);
Mat类提供了一个at的方法用于取得图像上的点,它是一个模板函数,可以取到任何类型的图像上的点。
第三种:ptr指针遍历法
//使用ptr指针遍历图像
cv::Mat image3 = image.clone();
int height = image.rows;
int width = image.cols;
//判断图像是否连续 连续看成一行处理
if(image.isContinuous() && image3.isContinuous() ){
width = width * height * image.channels();
height = 1;
}
for(int i = 0; i < height ; i++){
uchar *inData = image.ptr (i);
uchar *outData = image3.ptr(i);
for(int j = 0 ; j
Mat提供了一个检测图像是否连续的函数isContinuous(),当图像连通时,我们就可以把图像完全展开,看成是一行。
opencv 几种不同遍历图像像素的方法详解_高祥xiang的博客-CSDN博客_opencv遍历图像像素简述我们在图像处理时经常会用到遍历图像像素点的方式,同样是遍历图像像素点,共有很多中方法可以做到;在这些方法中,有相对高效的,也有低效的;不是说低效的方法就不好,不同场景使用不同方法。方法下面将一一介绍这些遍历图像像素点的方法:方法一:数组遍历法1图像Mat中每个像素点,其实就是一个值(int、float、double、uchar等类型),而Mat是一个二维数组。1、单通道...https://blog.csdn.net/qq_27278957/article/details/84646948
6. steady_clock()
// 使用 std::chrono 来给算法计时
steady_clock是单调的时钟,相当于教练中的秒表,只会增长,适合用于记录程序耗时。
为了测量一段代码的执行时间,我们可以使用now()函数:
定义 start 和 end的时候可以写 auto(c++11新特性)
#include
chrono::steady_clock::time_point start = chrono::steady_clock::now()
auto start = chrono::steady_clock::now();
auto end = chrono::steady_clock::now();
auto diff = end - start;
cout<<"遍历图像用时: "< (diff).count()<<" s"<
如果单位是毫秒(ms):
cout << chrono::duration (diff).count() << " ms" << endl;
如果是纳秒:
cout << chrono::duration (diff).count() << " ns" << endl;
参考:(1条消息) 计时器使用steady_clock还是high_resolution_clock_caohongfei881的专栏-CSDN博客_high_resolution_clockhttps://blog.csdn.net/caohongfei881/article/details/100631743
7.关于cv::Mat拷贝
特别注意图片的拷贝,CV::Mat image_clone=image 这样赋值的话,若改变了image_clone,则image也会改变
所以应该用image_clone=image.clone()
这涉及到了Mat容器的深浅拷贝问题
深拷贝:
分配新内存的同时拷贝数据!当被赋值的容器被修改时,原始容器数据不会改变。
浅拷贝:
仅拷贝数据!当被赋值容器修改时,原始容器数据也会做同样改变。
Opencv中关于Mat的几种复制方式:
1、 b = a.clone();
2、 a.copyTo(b);
3、 b = a;
4、 b(a);
先给结论!先给结论!先给结论!
深拷贝是 b = a.clone(); 和 a.copyTo(b);
浅拷贝是 b = a;和 b(a);
参考:OpenCV中cv::Mat的深拷贝 浅拷贝问题_verystory的博客-CSDN博客_opencv 深拷贝https://blog.csdn.net/verystory/article/details/85070605
8. cv::Rect() 函数
Rect(int x, int y, int width, int height); 函数中的参数是要切割掉的窗口的大小、位置
参数含义 (左上角x坐标 左上角y坐标 矩形的宽 ,矩形的高)