像素值的读写
我们需要读取某个像素值,或者设置某个像素值;在更多的时候,我们需要对整个图像里的所有像素进行遍历。OpenCV 提供了多种方法来实现图像的遍历。
at()函数
函数at()用于读取矩阵中的某个像素,或者对某个像素进行赋值操作。
uchar value = grayim.at<uchar>(i,j);//读出第 i 行第 j 列像素值
grayim.at<uchar>(i,j)=128; //将第 i 行第 j 列像素值设置为 128
如果要对图像进行遍历,可以参考下面的例程。这个例程创建了两个图像,
分别是单通道的 grayim 以及 3 个通道的 colorim,然后对两个图像的所有像素值
进行赋值,最后现实结果。
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
int main(int argc, char* argv[])
{
Mat grayim(600, 800, CV_8UC1);
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)
{
Vec3b pixel;
pixel[0] = i % 255; //Blue
pixel[1] = j % 255; //Green
pixel[2] = 0; //Red
colorim.at<Vec3b>(i, j) = pixel;
}
//显示结果
imshow("grayim", grayim);
imshow("colorim", colorim);
waitKey(0);
return 0;
}
#include"opencv2/opencv.hpp"
#include<iostream>
using namespace std;
using namespace cv;
int main() {
Mat src;
src = imread("D:/111.jpg");
if (src.empty()) {
cerr << "open error" << endl;
return -1;
}
imshow("src", src);
Mat g_src1;
cvtColor(src, g_src1, CV_BGR2GRAY);
imshow("g_src1", g_src1);
Mat c_src;
src.copyTo(c_src);
imshow("c_src",c_src);
int height = src.rows;
int weight = src.cols;
int channals = src.channels();//3
for (int row = 0; row < height; row++) {
for (int col = 0; col < weight; col++) {
if (channals == 1) {
int date=g_src1.at<uchar>(row, col);
g_src1.at<uchar>(row, col) = 255 - date;
}
else if(channals==3 ){
int b = c_src.at<Vec3b>(row, col)[0];
int g = c_src.at<Vec3b>(row, col)[1];
int r = c_src.at<Vec3b>(row, col)[2];
c_src.at<Vec3b>(row, col)[0] = 0;
c_src.at<Vec3b>(row, col)[1] = g;
c_src.at<Vec3b>(row, col)[2] = b;
//把三通道的值转化成单通道
//取最大的值作为灰度值
g_src1.at<uchar>(row, col) = max(r, max(g, b));
}
}
}
//把三通道位运算取反
Mat dst;
bitwise_not(src, dst);
imshow("c_src_1", c_src);
imshow("dst", dst);
waitKey(0);
return 0;
}
使用迭代器
如果你熟悉 C++的 STL 库,那一定了解迭代器(iterator)的使用。迭代器可以方便地遍历所有元素。Mat 也增加了迭代器的支持,以便于矩阵元素的遍历。下面的例程功能跟上一节的例程类似,但是由于使用了迭代器,而不是使用行数和列数来遍历,所以这儿没有了 i 和 j 变量,图像的像素值设置为一个随机数。
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
int main(int argc, char* argv[])
{
Mat grayim(600, 800, CV_8UC1);
Mat colorim(600, 800, CV_8UC3);
//遍历所有像素,并设置像素值
MatIterator_<uchar> grayit, grayend;
for (grayit = grayim.begin<uchar>(), grayend =grayim.end<uchar>(); grayit != grayend; ++grayit)
*grayit = rand() % 255;
//遍历所有像素,并设置像素值
MatIterator_<Vec3b> colorit, colorend;
for (colorit = colorim.begin<Vec3b>(), colorend =colorim.end<Vec3b>(); colorit != colorend; ++colorit)
{
(*colorit)[0] = rand() % 255; //Blue
(*colorit)[1] = rand() % 255; //Green
(*colorit)[2] = rand() % 255; //Red
}
//显示结果
imshow("grayim", grayim);
imshow("colorim", colorim);
waitKey(0);
return 0;
}
通过数据指针
使用 IplImage 结构的时候,我们会经常使用数据指针来直接操作像素。通过指针操作来访问像素是非常高效的,但是你务必十分地小心。C/C++中的指针操作是不进行类型以及越界检查的,如果指针访问出错,程序运行时有时候可能看上去一切正常,有时候却突然弹出“段错误”(segment fault)。
当程序规模较大,且逻辑复杂时,查找指针错误十分困难。对于不熟悉指针的编程者来说,指针就如同噩梦。如果你对指针使用没有自信,则不建议直接通过指针操作来访问像素。虽然 at()函数和迭代器也不能保证对像素访问进行充分的检查,但是总是比指针操作要可靠一些。如果你非常注重程序的运行速度,那么遍历像素时,建议使用指针。
下面的例程演示如何使用指针来遍历图像中的所有像素。
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
int main(int argc, char* argv[])
{
Mat grayim(600, 800, CV_8UC1);
Mat colorim(600, 800, CV_8UC3);
//遍历所有像素,并设置像素值
for (int i = 0; i < grayim.rows; ++i)
{
//获取第 i 行首像素指针
uchar * p = grayim.ptr<uchar>(i);
//对第 i 行的每个像素(byte)操作
for (int j = 0; j < grayim.cols; ++j)
p[j] = (i + j) % 255;
}
//遍历所有像素,并设置像素值
for (int i = 0; i < colorim.rows; ++i)
{
//获取第 i 行首像素指针
Vec3b * p = colorim.ptr<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);
waitKey(0);
return 0;
}