opencv中 对像素(pixel)进行操作 (::at ::ptr matiterator )

本文详细介绍了OpenCV中三种常见的像素操作方法:使用cv::Mat::at直接访问像素,使用cv::Mat::ptr获取行指针,以及使用Mat迭代器遍历图像。通过实例展示了如何利用这些方法实现对图像像素的具体操作。

opencv中 对像素(pixel)进行操作

1. cv:Mat::at

图片来自于RIP TUTORIAL:

基础结构:
CV_{U|S|F}C(<number_of_channels>)
例如:
CV_8UC1 8-bit 1-channel 灰度图
CV_32FC3 for 32-bit floating point 3-channel 彩色图

Vec结构:
在这里插入图片描述类型包括: uchar(b) short(s) int(i) float(f) double(d)
例如: Vec3i 表示 int vector of 3 channels

Mat结构:
在这里插入图片描述
在这里插入图片描述
表示单通道(1 channel):
mat.at(i,j) (int)mat.at(i,j)
表示多通道 (n channels):
mat.at<Vec2b/Vec3b>(i,j)[k] (int)mat.at(i,j)[k]

使用cv::Mat 函数 描述像素:
通常使用: cv::Mat::at(r,c) T:类型 r: 行 c: 列

通常, opencv 属于行主导(row major)顺序, 所以 如果像素用 (x,y)描述,
函数为: image.at<…>(y,x) 注意顺序
也可以用 image.at<…>(cv::Point(x,y))

2. cv::Mat::ptr

CV_8UC1 可以表示为 uchar* ptr = image.ptr®;
CV_32FC3 可以表示为 cv::Vec3f* ptr = image.ptrcv::Vec3f®;
使用 ptr[c] 去处理列

for(int r = 0; r < image.rows; r++) {
        // 行r起始位置获得指针
        cv::Vec3b* ptr = image.ptr<cv::Vec3b>(r);

        for(int c = 0; c < image.cols; c++) {
            // ptr[c] 表示列
            ptr[c] = cv::Vec3b(ptr[c][2], ptr[c][1], ptr[c][0]);
        }
    }

3. Matiterator

举例: 移除红绿通道

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp> 


int main(int argc, char **argv)
{

// Create a container
cv::Mat im; 

//Create a vector
cv::Vec3b *vec;

// Create an mat iterator
cv::MatIterator_<cv::Vec3b> it;

// Read the image in color format
im = cv::imread("orig1.jpg", 1);

// iterate through each pixel
for(it = im.begin<cv::Vec3b>(); it != im.end<cv::Vec3b>(); ++it)
{
    // Erase the green and red channels 
    (*it)[1] = 0;
    (*it)[2] = 0;
}


// Create a new window
cv::namedWindow("Resulting Image");

// Show the image
cv::imshow("Resulting Image", im);

// Wait for a key
cv::waitKey(0);

return 0;
}
<think> 我们需要讨论如何读取cv::Mat中的数值。根据引用,我们知道cv::MatOpenCV中用于存储图像和矩阵数据的类。读取其中的数值有多种方法,取决于具体需求(如读取单个元素、行、列或区域)。 关键点: 1. 使用`at`方法:通过指定行和列(对于二维矩阵)或使用迭代器。 2. 使用指针:通过ptr方法获取行指针,然后遍历列。 3. 使用迭代器:OpenCV提供MatIterator_和MatConstIterator_用于安全遍历。 4. 使用范围(Range)提取子矩阵,然后读取(如引用[3]所示)。 注意事项: - 需要知道矩阵的通道数(单通道或多通道)和数据类型(uchar, float等)。 - 对于多通道矩阵,每个元素可能有多个值(例如,RGB图像每个像素有3个值)。 根据引用[4]中提供的类型表,我们可以通过mat.type()获取矩阵类型,从而知道数据类型和通道数。 步骤: 1. 确定矩阵的类型(type)和通道数(channels)。 2. 根据数据类型选择正确的访问方法(例如,对于单通道float矩阵,使用mat.at<float>(i,j))。 下面分别介绍几种方法: ### 方法1:使用`at`方法访问单个元素 ```cpp // 假设mat是一个单通道的float矩阵 float value = mat.at<float>(i, j); // i是行索引,j是列索引 // 对于多通道矩阵(例如3通道uchar,即CV_8UC3) cv::Vec3b pixel = mat.at<cv::Vec3b>(i, j); // 然后可以访问每个通道的值 uchar blue = pixel[0]; uchar green = pixel[1]; uchar red = pixel[2]; ``` ### 方法2:使用指针访问 ```cpp // 对于单通道 for (int i = 0; i < mat.rows; ++i) { float* row_ptr = mat.ptr<float>(i); // 获取第i行的指针 for (int j = 0; j < mat.cols; ++j) { float value = row_ptr[j]; } } // 对于多通道(例如3通道float) for (int i = 0; i < mat.rows; ++i) { cv::Vec3f* row_ptr = mat.ptr<cv::Vec3f>(i); for (int j = 0; j < mat.cols; ++j) { cv::Vec3f pixel = row_ptr[j]; float blue = pixel[0]; float green = pixel[1]; float red = pixel[2]; } } ``` ### 方法3:使用迭代器 ```cpp // 单通道float矩阵 cv::Mat_<float>::iterator it = mat.begin<float>(); cv::Mat_<float>::iterator end = mat.end<float>(); for (; it != end; ++it) { float value = *it; } // 多通道(例如3通道uchar) cv::Mat_<cv::Vec3b>::iterator it = mat.begin<cv::Vec3b>(); cv::Mat_<cv::Vec3b>::iterator end = mat.end<cv::Vec3b>(); for (; it != end; ++it) { cv::Vec3b pixel = *it; uchar blue = pixel[0]; // ... } ``` ### 方法4:使用范围提取子矩阵(引用[3]) ```cpp // 提取第3行到第7行(不包括第7行)和第2列到第5列(不包括第5列) cv::Mat roi = mat(cv::Range(3,7), cv::Range(2,5)); // 然后可以用上述任一方法读取roi中的值 ``` ### 完整示例 假设我们有一个3通道的uchar图像(CV_8UC3),我们想读取(10,20)处的像素值: ```cpp cv::Mat image = cv::imread("image.jpg"); // 默认读取为3通道uchar cv::Vec3b pixel = image.at<cv::Vec3b>(10, 20); std::cout << "B: " << (int)pixel[0] << ", G: " << (int)pixel[1] << ", R: " << (int)pixel[2] << std::endl; ``` ### 注意事项 - 使用`at`方法时,要确保模板类型与矩阵的数据类型匹配,否则会出错。可以通过mat.type()查看类型(引用[4]中的表)。 - 使用指针和迭代器时,注意边界,不要越界访问。 - 多通道矩阵的元素访问使用`cv::Vec<T, n>`(n是通道数),例如`cv::Vec3b`(3通道uchar)、`cv::Vec3f`(3通道float)等。 ### 相关问题 1. 如何获取cv::Mat的总元素数(包括多通道)? 2. 如何修改cv::Mat中的特定元素? 3. 如何将cv::Mat转换为Eigen矩阵? 4. 如何遍历cv::Mat的每个像素并修改? 以上方法涵盖了cv::Mat的基本读取操作,用户可以根据需要选择合适的方式。 </think> ### C++读取`cv::Mat`中数值的方法 #### 1. **直接使用`at`方法访问元素(推荐)** ```cpp cv::Mat img = cv::imread("image.jpg"); int row = 10, col = 20; // 单通道图像(灰度图) uchar pixel_value = img.at<uchar>(row, col); // 三通道图像(BGR格式) cv::Vec3b pixel = img.at<cv::Vec3b>(row, col); uchar blue = pixel[0]; // 蓝色通道值 uchar green = pixel[1]; // 绿色通道值 uchar red = pixel[2]; // 红色通道值 ``` #### 2. **使用指针高效遍历** ```cpp for (int r = 0; r < img.rows; ++r) { uchar* row_ptr = img.ptr<uchar>(r); // 获取行指针 for (int c = 0; c < img.cols; ++c) { // 单通道 uchar value = row_ptr[c]; // 三通道(BGR顺序) uchar b = row_ptr[c * 3]; uchar g = row_ptr[c * 3 + 1]; uchar r = row_ptr[c * 3 + 2]; } } ``` #### 3. **使用迭代器安全访问** ```cpp cv::MatIterator_<cv::Vec3b> it = img.begin<cv::Vec3b>(); cv::MatIterator_<cv::Vec3b> end = img.end<cv::Vec3b>(); for (; it != end; ++it) { cv::Vec3b pixel = *it; uchar blue = pixel[0]; uchar green = pixel[1]; uchar red = pixel[2]; } ``` #### 4. **提取特定区域数据** ```cpp // 提取2-5行,3-7列的子区域 cv::Mat roi = img( cv::Range(2, 5), // 行范围 [2,5) cv::Range(3, 7) // 列范围 [3,7) ); // 访问子区域中的像素 uchar val = roi.at<uchar>(0, 0); // 相对坐标(0,0)对应原图(2,3) ``` #### 5. **类型判断与安全访问** ```cpp // 检查矩阵类型(引用[4]) if (img.type() == CV_8UC1) { // 单通道uchar uchar val = img.at<uchar>(row, col); } else if (img.type() == CV_32FC3) { // 三通道float cv::Vec3f pixel = img.at<cv::Vec3f>(row, col); float r = pixel[2]; } ``` #### 完整示例 ```cpp #include <opencv2/opencv.hpp> int main() { cv::Mat img = cv::imread("image.jpg"); // 默认读取为CV_8UC3 // 方法1:at访问 cv::Vec3b center_pixel = img.at<cv::Vec3b>(img.rows/2, img.cols/2); std::cout << "中心像素(BGR): " << (int)center_pixel[0] << ", " << (int)center_pixel[1] << ", " << (int)center_pixel[2] << std::endl; // 方法2:指针遍历 double sum = 0; for (int r = 0; r < img.rows; ++r) { uchar* p = img.ptr<uchar>(r); for (int c = 0; c < img.cols; ++c) { sum += p[c*3 + 1]; // 绿色通道求和 } } std::cout << "绿色通道平均值: " << sum/(img.rows*img.cols) << std::endl; return 0; } ``` ### 关键注意事项 1. **通道顺序** OpenCV默认使用BGR顺序而非RGB,访问时: - `pixel[0]`: 蓝色通道 - `pixel[1]`: 绿色通道 - `pixel[2]`: 红色通道 2. **数据类型匹配** 必须根据`mat.type()`选择正确的数据类型: - `CV_8U`: `uchar` - `CV_32F`: `float` - `CV_64F`: `double` - 多通道使用`cv::Vec<T,N>`(N为通道数)[^4] 3. **性能建议** - 单像素访问:`at()`方法 - 整行遍历:`ptr()`指针方法 - 全图遍历:迭代器 ### 相关问题 1. 如何修改`cv::Mat`中的特定像素值? 2. 不同数据类型的`cv::Mat`如何相互转换? 3. 如何高效实现`cv::Mat`的深拷贝与浅拷贝? 4. 多通道矩阵如何分离成单通道矩阵? 5. `cv::Mat`与Eigen矩阵如何相互转换?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值