05.OpenCV cv::Mat之(二)像素数据操作

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对应ucharCV_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通道
}

https://www.notion.so

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;
}

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值