OpenCV2:幼儿园篇 第四章 访问图像
一.行/列访问
1.单行/单列访问
Mat Mat::row(int i) const
Mat Mat::col(int j) const
2.多行/多列访问
Range(start,end);
Range::all(); // 表示所有行或列
1 2 | Mat A; Mat B=A(Range::(5,9),Range(1,3)); //表示5-9行(不包括9),1-3列(不包括3) |
二.区域访问
1.CRect
1 2 3 4 5 6 7 8 9 10 11 12 | //在图像的右下角定义一个ROI cv::Mat imageROI(image,cv::Rect(image.cols-logo.cols, image.rows-logo.rows, logo.cols, logo.rows)); 或者 cv::Mat imageROI=image(cv::Rect(image.cols-logo.cols, image.rows-logo.rows, logo.cols, logo.rows)); |
2.CSize
三.像素访问
1.at()访问
使用at()函数代码可读性高,效率不是很高,使用at()函数必须用两个循环实现
注意访问顺序,先访问行(外循环rows)(指针在高的第一行)
再访问列(内循环cols)(指针移动宽的每一行)
template <typename _Tp> inline _Tp& Mat::at(int i0, int i1)
template <typename _Tp> inline const _Tp& Mat::at(int i0, int i1) const
template <typename _Tp> inline _Tp& Mat::at(Point pt)
template <typename _Tp> inline const _Tp& Mat::at(Point pt) const
template <typename _Tp> inline _Tp& Mat::at(int i0)
template <typename _Tp> inline const _Tp& Mat::at(int i0) const
template <typename _Tp> inline _Tp& Mat::at(int i0, int i1, int i2)
template <typename _Tp> inline const _Tp& Mat::at(int i0, int i1, int i2) const
template <typename _Tp> inline _Tp& Mat::at(const int* idx)
template <typename _Tp> inline const _Tp& Mat::at(const int* idx) const
template <typename _Tp, int n> _Tp& Mat::at(const Vec<int, n>& idx)
template <typename _Tp, int n> inline const _Tp& Mat::at(const Vec<int, n>& idx) const
at()函数是模板函数,可以自定义类型
其中at<uchar>表示单通道图像,at<cv::Vec3b>表示三通道图像
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | //遍历像素值 #include <iostream> #include "opencv2/opencv.hpp" using namespace std; int main( int argc, char * argv[]) { cv::Mat grayim(600, 800, CV_8UC1); cv::Mat colorim(600, 800, CV_8UC3); //遍历所有像素,并设置像素值 for ( int i = 0;i < grayim.rows; ++i) for ( int j = 0; j < grayim.cols; ++j) grayim.at<uchar>(i,j) = (i+j)%255; //遍历所有像素,并设置像素值 for ( int i=0; i<colorim.rows; ++i) for ( int j=0; j<colorim.cols; ++j) { cv::Vec3b pixel; pixel[0] = i%255; //Blue pixel[1] = j%255; //Green pixel[2] = 0; //Red colorim.at<cv::Vec3b>(i,j) = pixel; } cv::imshow( "grayim" ,grayim); cv::imshow( "colorim" ,colorim); cv::waitKey(0); return 0; } |
2.ptr()访问
注意at()函数返回的是一个uchar变量类型,而ptr()函数返回的是一个uchar*指针类型
ptr()函数和at()函数都需要两个循环来遍历像素.虽然它返回的是指针,索引速度比较快,但是指针不进行类型以及越界检查
它的访问顺序是跟at()函数有点像,先获取首个行指针,再遍历列指针
inline uchar* Mat::ptr(int y)
inline const uchar* Mat::ptr(int y) const
template<typename _Tp> inline _Tp* Mat::ptr(int y)
template<typename _Tp> inline const _Tp* Mat::ptr(int y) const
inline uchar* Mat::ptr(int i0, int i1)
intline const uchar* Mat::ptr(int i0, int i1) const
template<typename _Tp> inline _Tp* Mat::ptr(int i0, int i1)
template<typename _Tp> inline const _Tp* Mat::ptr(int i0, int i1) const
inline uchar* Mat::ptr(int i0, int i1, int i2)
inline const uchar* Mat::ptr(int i0, int i1, int i2) const
template<typename _Tp> inline _Tp* Mat::ptr(int i0, int i1, int i2)
template<typenmae _Tp> inline const _Tp* Mat::ptr(int i0, int i1, int i2) const
inline uchar* Mat::ptr(const int* idx)
inline const uchar* Mat::ptr(const int* idx) const
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #include <iostream> #include "opencv2/opencv.hpp" using namespace std; int main( int argc, char * argv[]) { cv::Mat grayim(600,800,CV_8UC1); cv::Mat colorim(600,800,CV_8UC3); //遍历所有像素,并设置像素值 for ( int i = 0; i < grayim.rows; ++i) { //获取第i行首像素指针 uchar* p = grayim.ptr<uchar>(i); //对第i的每个像素操作 for ( int j = 0;j < grayim.cols; ++j) p[j] = (i+j)%255; } //遍历所有像素,并设置像素值 for ( int i = 0; i<colorim.rows; ++i) { //获取第i行首像素指针 cv::Vec3b* p=colorim.ptr<cv::Vec3b>(i); for ( int j = 0; j < colorim.cols; ++j) { p[j][0] = i%255; //Blue p[j][1] = j%255; //Green p[j][2] = 0; //Red } } imshow( "grayim" , grayim); imshow( "colorim" , colorim); cv::waitKey(0); return 0; } |
3.迭代器访问
迭代器访问有点像at()函数访问,虽然它只需要一个循环,但是它的可读性没有at()函数清楚
MatIterator_是一个类模板,而at()函数是函数模板
MatIterator_类似于STL中的迭代器,根据迭代器开头和结尾一直遍历
template<typename _Tp>
class MatIterator_ : public MatConstIterator_<_Tp>
{
public:
typedef _Tp* pointer;
typedef _Tp& reference;
typedef std::random_access_iterator_tag iterator_category;
MatIterator_();
MatIterator_(Mat_<_Tp>* _m);
MatIterator_(Mat_<_Tp>* _m, int _row, int _col = 0);
MatIterator_(const Mat_<_Tp>* _m, Point _pt);
MatIterator_(const Mat_<_Tp>* _m, const int* _idx);
MatIterator_(const MatIterator_& it);
MatIterator_& operator = (const MatIterator_<_Tp>& it);
_Tp& operator *() const;
_Tp& operator [](ptrdiff_t i) const;
MatIterator_& operator += (ptrdiff_t ofs);
MatIterator_& operator -= (ptrdiff_t ofs);
MatIterator_& operator -- ();
MatIterator_ operator -- (int);
MatIterator_& operator ++ ();
MatIterator_ operator ++ (int);
};
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | #include <iostream> #include "opencv2/opencv.hpp" using namespace std; int main( int argc, char * argv[]) { cv::Mat grayim(600, 800, CV_8UC1); cv::Mat colorim(600, 800, CV_8UC3); //遍历所有像素,并设置像素值 cv::MatIterator_<uchar> grayit, grayend; for ( grayit = grayim.begin<uchar>(), grayend = grayim.end<uchar>(); grayit != grayend; ++grayit) *grayit = rand ()%255; //遍历所有像素,并设置像素值 cv::MatIterator_<cv::Vec3b> colorit,colorend; for ( colorit = colorim.begin<cv::Vec3b>(), colorend = colorim.end<cv::Vec3b>(); colorit != colorend; ++colorit) { (*colorit)[0] = rand ()%255; //Blue (*colorit)[1] = rand ()%255; //Green (*colorit)[2] = rand ()%255; //Red } //显示结果 cv::imshow( "grayim" , grayim); cv::imshow( "colorim" , colorim); cv::waitKey(0); return 0; } |
4.模板访问
1 2 3 | //用Mat_模板操作图像 cv::Mat_<uchar> im2(image); im2(50,100)=0; // 访问第50行,第100列处那个值 |
5.查找表访问
LUT(Look Up Table,查找表)是一张像素灰度值的映射表,它将采样到的灰度值经过图像处理(替换 反转 赋值 阈值 二值化 灰度变化等),利用映射关系变换成相应的灰度值
OpenCV中LUT查找表包含256个元素,应用于单通道或相同类型的多通道数据,用于减少图像映射的时间复杂度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | cv::Mat inverseColor6(cv::Mat srcImage) { int row = srcImage.rows; int col = srcImage.cols; cv::Mat tempImage = srcImage.clone(); // 建立LUT 反色table uchar LutTable[256]; for ( int i = 0; i < 256; ++i) LutTable[i] = 256 - i; cv::Mat lookUpTable(1, 256, CV_8U); uchar* pData = lookUpTable.data; // 建立映射表 for ( int i = 0; i <256; ++i) pData[i] = LutTable[i]; // 应用搜索表进行查找 cv::LUT(srcImage, lookUpTable, tempImage); return tempImage; } |
四.例子
1.减色
(1)指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | //test.cpp void colorReduce(cv::Mat image, int div =64){ int nl=image.rows; //行数 //每行的元素数量 int nc=image.cols*image.channels(); for ( int j=0;j<nl;j++){ //取得行j的地址 uchar* data=image.ptr<uchar>(j); for ( int i=0;i<nc;i++){ //处理每个像素 data[i]=data[i]/ div * div + div /2; } } } |
(2)迭代器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | void colorReduce(cv::Mat image, int div =64){ //在初始位置获得迭代器 cv::Mat_<cv::Vec3b>::iterator it=image.begin<cv::Vec3b>(); //获得结束位置 cv::Mat_<cv::Vec3b>::iterator itend=image.end<cv::Vec3b>(); //循环遍历所有像素 for (;it!=itend;++it){ //处理每个像素 (*it)[0]=(*it)[0]/ div * div + div /2; (*it)[1]=(*it)[1]/ div * div + div /2; (*it)[2]=(*it)[2]/ div * div + div /2; } } |
2.锐化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | void sharpen( const cv::Mat& image,cv::Mat& result){ //判断是否需要分配图像数据,如果需要,就分配 result.create(image.size(),image.type()); int nchannels=image.channels(); //获得通道数 //处理所有行(除了第一行和最后一行) for ( int j=1;j<image.rows-1;j++){ const uchar* previous=image.ptr< const uchar>(j-1); //上一行 const uchar* current=image.ptr< const uchar>(j); //当前行 const uchar* next=image.ptr< const uchar>(j+1); //下一行 uchar* output=result.ptr<uchar>(j); //输出行 for ( int i=nchannels;i<(image.cols-1)*nchannels;i++){ *output++=cv::saturate_cast<uchar>( 5*current[i]-current[i-nchannels]-current[i+nchannels-previous[i]-next[i]]); } } //把未处理的像素设为0 result.row(0).setTo(cv::Scalar(0)); result.row(result.rows-1).setTo(cv::Scalar(0)); result.col(0).setTo(cv::Scalar(0)); result.col(result.cols-1).setTo(cv::Scalar(0)); } int main() { cv::Mat imag1=cv::imread( "a.jpg" ); cv::Mat result; sharpen(imag1,result); cv::namedWindow( "Image" ); cv::imshow( "Image" ,result); cv::waitKey(0); system ( "pause" ); return 0; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)