在 opencv 的编程中,遍历访问图像元素是经常遇到的操作,掌握其方法非常重要,无论是 Mat 类的像素访问,还是 IplImage 结构体的访问的方法,都必须扎实掌握,毕竟,图像处理本质上就是对像素的各种操作,访问元素就是各种图像处理算法的第一步。
首先先看看图像的是怎么存储的。
单通道图像
多通道图像
在大多数图像处理任务中, 执行计算时你都需要对图像的所有像素进行扫描。 当需要访问的像素数量非常庞大, 你必须采用高效的方式来执行这个任务来提高效率。 如果你需要高效扫描大图片的数据,那么请使用指针方式。
- #include<opencv2\opencv.hpp>
- #include<opencv2\highgui\highgui.hpp>
- using namespace std;
- using namespace cv;
- int main(int argc, char** argv)
- {
- Mat img = imread("lena.jpg",1); //载入灰度图
- Mat img1 = img.clone();
- int div = 64;
- /* 方法1:用指针访问 */
- int channels = img1.channels(); //获取通道数
- int rows = img1.rows;
- int cols = img.cols* channels; //注意,是列数*通道数
- for (int i = 0; i < rows; i++)
- {
- uchar* p = img1.ptr<uchar>(i); //获取第i行的首地址
- for (int j = 0; j < cols; j++)
- {
- //在这里操作具体元素
- p[i] = p[i] / div*div + div / 2;
- }
- }
- imshow("lean", img1);
- waitKey(0);
- return 0;
- }
在面向对象编程时, 我们通常用迭代器对数据集合进行循环遍历。 标准模板库(STL) 对每个集合类都定义了对应的迭代器类, OpenCV 也提供了 cv::Mat 的迭代器类, 并且与 C++ STL 中的标准迭代器兼容。
- #include<opencv2\opencv.hpp>
- #include<opencv2\highgui\highgui.hpp>
- using namespace std;
- using namespace cv;
- int main(int argc, char** argv)
- {
- Mat img = imread("lena.jpg",1); //载入灰度图
- Mat img1 = img.clone();
- int div = 64;
- /* 方法2:用迭代器访问 */
- /******************多通道的可以这么写***************/
- Mat_<Vec3b>::iterator it = img1.begin<Vec3b>(); //获取起始迭代器
- Mat_<Vec3b>::iterator it_end = img1.end<Vec3b>(); //获取结束迭代器
- for (; it != it_end; it++)
- {
- //在这里分别访问每个通道的元素
- (*it)[0] = (*it)[0] / div*div + div / 2;
- (*it)[1] = (*it)[1] / div*div + div / 2;
- (*it)[1] = (*it)[1] / div*div + div / 2;
- }
- imshow("lean", img1);
- /******************单通道的可以这么写***************/
- Mat img2;
- cvtColor(img, img2, COLOR_RGB2GRAY); //转化为单通道灰度图
- Mat_<uchar>::iterator it2 = img2.begin<uchar>(); //获取起始迭代器
- Mat_<uchar>::iterator it_end2 = img2.end<uchar>(); //获取结束迭代器
- for (; it2 != it_end2; it2++)
- {
- //在这里分别访问每个通道的元素
- *it2 = *it2 / div*div + div / 2;
- }
- imshow("lena2", img2);
- waitKey(0);
- return 0;
- }
- }
若要从图像的第二行开始,程序该怎么修改? 我们可以用
- image.begin<cv::Vec3b>()+image.cols
初始化 cv::Mat 迭代器。 获得集合结束位置的方法也类似, 只是改用 end 方法。 但是, 用 end 方法得到的迭代器已经超出了集合范围, 因此必须在结束位置停止迭代过程。 结束的迭代器也能使用数学计算, 例如, 如果你想在最后一行前就结束迭代, 可使用
- image.end<cv::Vec3b>()-image.cols
- #include<opencv2\opencv.hpp>
- #include<opencv2\highgui\highgui.hpp>
- using namespace std;
- using namespace cv;
- int main(int argc, char** argv)
- {
- Mat img = imread("lena.jpg",1);
- Mat img1 = img.clone();
- int div = 64;
- /* 方法3:用at访问 */
- /****************访问多通道元素*********************/
- int rows = img1.rows;
- int cols = img1.cols;
- for (int i = 0; i < rows; i++)
- {
- for (int j = 0; j < cols; j++)
- {
- //在这里访问每个通道的元素,注意,成员函数at(int y,int x)的参数
- img1.at<Vec3b>(i,j)[0] = img1.at<Vec3b>(i, j)[0] / div*div + div / 2;
- img1.at<Vec3b>(i, j)[1] = img1.at<Vec3b>(i, j)[1] / div*div + div / 2;
- img1.at<Vec3b>(i, j)[2] = img1.at<Vec3b>(i, j)[2] / div*div + div / 2;
- }
- }
- imshow("lena", img1);
- /****************访问单通道元素*********************/
- Mat img2;
- cvtColor(img, img2, COLOR_RGB2GRAY);
- for (int i = 0; i < rows; i++)
- {
- for (int j = 0; j < cols; j++)
- {
- //在这里访问每个通道的元素,注意,成员函数at(int y,int x)的参数
- img2.at<uchar>(i, j) = img2.at<uchar>(i, j) / div*div + div / 2;
- }
- }
- imshow("lena2", img2);
- waitKey(0);
- return 0;
- }
- #include<opencv2\opencv.hpp>
- #include<opencv2\highgui\highgui.hpp>
- using namespace std;
- using namespace cv;
- int main(int argc, char** argv)
- {
- /*访问单通道元素*/
- IplImage* img = cvCreateImage(cvSize(640, 480), IPL_DEPTH_8U, 1); //单通道图像
- CvScalar s;
- double tmp;
- for (int i = 0; i < img->height; i++)
- {
- for (int j = 0; j < img->width; j++)
- {
- //可以在这里访问元素
- tmp = cvGet2D(img, i, j).val[0];
- cvSet2D(img, i, j, 255); //第三个参数是要设置的值
- }
- }
- cvShowImage("img", img);
- /*访问多通道元素*/
- IplImage* img2 = cvCreateImage(cvSize(640, 480), IPL_DEPTH_32F, 3);
- double tmpb, tmpg, tmpr;
- for (int i = 0; i < img->height; i++)
- {
- for (int j = 0; j < img->width; j++)
- {
- tmpb = cvGet2D(img, i, j).val[0];
- tmpg = cvGet2D(img, i, j).val[1];
- tmpr = cvGet2D(img, i, j).val[2];
- cvSet2D(img2, i, j, CvScalar(255,255,255)); //第三个参数是要设置的值,三个通道一起设置
- }
- }
- cvShowImage("img2", img2);
- waitKey(0);
- return 0;
- }
追求高效率地访问元素请使用该方法。
- #include<opencv2\opencv.hpp>
- #include<opencv2\highgui\highgui.hpp>
- using namespace std;
- using namespace cv;
- int main(int argc, char** argv)
- {
- /*访问多通道元素*/
- IplImage* img = cvCreateImage(cvSize(640, 480), IPL_DEPTH_8U, 3);
- uchar* data = (uchar *)img->imageData;
- int step = img->widthStep / sizeof(uchar);
- int channels = img->nChannels;
- uchar b, g, r;
- for (int i = 0; i < img->height; i++)
- {
- for (int j = 0; j < img->width; j++)
- {
- //获得元素的值
- b = data[i*step + j*channels + 0];
- g = data[i*step + j*channels + 1];
- r = data[i*step + j*channels + 2];
- //修改元素的值
- data[i*step + j*channels + 0] = 255;
- }
- }
- cvShowImage("img", img);
- /*访问单通道元素*/
- IplImage* img2 = cvCreateImage(cvSize(640, 480), IPL_DEPTH_8U, 1);
- uchar* data2 = (uchar *)img2->imageData;
- int step2 = img2->widthStep / sizeof(uchar);
- uchar v;
- for (int i = 0; i < img2->height; i++)
- {
- for (int j = 0; j < img2->width; j++)
- {
- //获得元素的值
- v = data2[i*step2 + j];
- //修改元素的值
- data2[i*step2 + j] = 255;
- }
- }
- cvShowImage("img2", img2);
- waitKey(0);
- return 0;
- }
来源: http://www.cnblogs.com/skyfsm/p/7082914.html