OpenCV cv::Mat之(二)像素数据操作
在上一篇中,揭开了cv::Mat的神秘面纱,了解了其作为图像数据容器的核心结构。本篇将聚焦于像素级的操作——通过多种姿势对图像数据进行读写操控每个像素,为更复杂的图像处理算法打下坚实的基础
1.Mat数据存储结构
首先了解一下Mat在内存中的存储结构,方便理解后续操作。
//内存排列查看-------------------------------------------------------------------------------------
Mat grayimage(3, 4, CV_8UC1);
Mat image(3, 4, CV_8UC3);
//直接赋值
image = Scalar(0, 0, 255);//B G R
grayimage = 127;
std::cout << "CV_8UC1:" << std::endl;
//输出mat基本信息
std::cout << "cols:" << grayimage.cols << " rows:" << grayimage.rows << " channel:" << grayimage.channels() << std::endl;
std::cout << grayimage << std::endl;
std::cout << "CV_8UC3:" << std::endl;
std::cout << image << std::endl;
上面代码创建了2个3*4的Mat数据。其中一个是灰度,另外一个是3通道。同时输出header头的基本信息。运行上面的代码可以查看如下图的结果。不管是单通道还是多通道,所有的数据都是按类似一维数组一样顺序存放。由于3通道的彩色图片一个像素是有红绿蓝3中信息的,所以其排列格式如下:
|BGR|BGR|BGR|BRG|
|BGR|BGR|BGR|BRG|
|BGR|BGR|BGR|BRG|
2.像素访问
2.1坐标定位法 at<>
最直观的像素访问方式,通过行列坐标精准定位.
注意:使用 at<>()
进行访问时,OpenCV 会自动进行边界检查,虽然使用简单安全,但在遍历整幅图像时效率相对较低。
// 灰度图像素访问(单通道)
uchar pixel = img.at<uchar>(row, col);
// BGR彩色图像素访问(三通道)
Vec3b bgr_pixel = img.at<Vec3b>(row, col);
// 访问各通道
uchar blue = bgr_pixel[0];
uchar green = bgr_pixel[1];
uchar red = bgr_pixel[2];
特点:
- 语法直观,适合随机访问
- 效率较低,不推荐用于全图遍历
- 需注意数据类型(uchar, float等)匹配:
CV_8U
对应uchar
,CV_32F
对应float
- Vec3b uchar
- Vec3f float
2.2 指针狂奔法(高效遍历)
for (int row = 0; row < img.rows; ++row) {
uchar* ptr = img.ptr<uchar>(row);
for (int col = 0; col < img.cols; ++col) {
// 灰度图:ptr[col] = 255 - ptr[col]; // 反色
// 彩色图:
ptr[3*col] = 255 - ptr[3*col]; // B
ptr[3*col+1] = 255 - ptr[3*col+1]; // G
ptr[3*col+2] = 255 - ptr[3*col+2]; // R
}
}
ptr[3col] = 255 - ptr[3col]中3表示通道数。
2.3. 安全卫士迭代器
STL风格迭代器,自动处理边界:
MatIterator_<Vec3b> it = img.begin<Vec3b>();
MatIterator_<Vec3b> end = img.end<Vec3b>();
for(; it != end; ++it) {
(*it)[0] = 255 - (*it)[0]; // B通道反色
(*it)[1] = 255 - (*it)[1]; // G通道
(*it)[2] = 255 - (*it)[2]; // R通道
}
3.参考代码
#include "stdio.h"
#include "opencv2/opencv.hpp"
#include <iostream>
using namespace cv;
int main()
{
//内存排列查看-------------------------------------------------------------------------------------
Mat grayimage(3, 4, CV_8UC1);
Mat image(3, 4, CV_8UC3);
//直接赋值
image = Scalar(0, 0, 255);//B G R
grayimage = 127;
std::cout << "CV_8UC1:" << std::endl;
//输出mat基本信息
std::cout << "cols:" << grayimage.cols << " rows:" << grayimage.rows << " channel:" << grayimage.channels() << std::endl;
std::cout << grayimage << std::endl;
std::cout << "CV_8UC3:" << std::endl;
std::cout << image << std::endl;
/*at操作--------------------------------------------------------------------------------------------------- */
cv::Mat srcImage=cv::imread("E:/image/opencv-logo.png");
// 判断图像是否读取成功
if (srcImage.empty())
{
std::cerr << "Error: Image not found." << std::endl;
return -1;
}
Mat rImage, gImage,bImage;
rImage = Mat::zeros(srcImage.rows,srcImage.cols, srcImage.type());
gImage = Mat::zeros(srcImage.rows, srcImage.cols, srcImage.type());
bImage = Mat::zeros(srcImage.rows, srcImage.cols, srcImage.type());
for (int i=0;i< srcImage.rows;i++)
{
for (int j = 0; j < srcImage.cols; j++)
{
if (srcImage.channels()==1)
{//灰度图
bImage.at<cv::Vec3b>(i, j)[0] = srcImage.at<uchar>(i, j);
gImage.at<cv::Vec3b>(i, j)[1] = srcImage.at<uchar>(i, j);
rImage.at<cv::Vec3b>(i, j)[2] = srcImage.at<uchar>(i, j);
}
else if (srcImage.channels() == 3)
{//rbg彩色图
cv::Vec3b rgb = srcImage.at<cv::Vec3b>(i, j);
bImage.at<cv::Vec3b>(i, j)[0] = rgb[0];
gImage.at<cv::Vec3b>(i, j)[1] = rgb[1];
rImage.at<cv::Vec3b>(i, j)[2] = rgb[2];
}
}
}
imshow("srcImage", srcImage);
imshow("bImage", bImage);
imshow("gImage", gImage);
imshow("rImage", rImage);
// 等待按键事件,按任意键关闭窗口
cv::waitKey(0);
destroyAllWindows();
//指针操作---------------------------------------------------------------------------------------------------
Mat vImage;
vImage = Mat::zeros(srcImage.rows, srcImage.cols, srcImage.type());
for (int i = 0; i < srcImage.rows; i++)
{
uchar* ptr = srcImage.ptr<uchar>(i);
uchar* dstPtr = vImage.ptr<uchar>(i);
for (int j = 0; j < srcImage.cols; j++)
{
dstPtr[3 * j] = 255 - ptr[3 * j];
dstPtr[3 * j+1] = 255 - ptr[3 * j+1];
dstPtr[3 * j+2] = 255 - ptr[3 * j+2];
}
}
imshow("原图", srcImage);
imshow("取反", vImage);
cv::waitKey(0);
destroyAllWindows();
//std 迭代器---------------------------------------------------------------------------------------------------
MatIterator_<Vec3b> it = srcImage.begin<Vec3b>();
MatIterator_<Vec3b> end = srcImage.end<Vec3b>();
for (; it != end; ++it) {
(*it)[0] = 255 - (*it)[0]; // B通道反色
(*it)[1] = 255 - (*it)[1]; // G通道
(*it)[2] = 255 - (*it)[2]; // R通道
}
imshow("取反", srcImage);
cv::waitKey(0);
destroyAllWindows();
return 0;
}