数字图像基本OP:OpenCV中的访问与操作像素值方法

我们知道,在计算机中一幅图像以数组矩阵的数据结构被存储下来,这里记录总结OpenCV中遍历访问每一个像素值的方法。

C++ API中,访问像素值有以下几种方法:

  • 数组索引方法
  • 指针方法
  • 迭代器方法

而在Python中,之前说过,图像存储与NumPy中的ndarray完全一致,因此访问像素值方法也是如此。

1.数组方法访问像素值

1.1 数组方法介绍

OpenCV提供了at()函数,它是一个模板函数,返回指定位置矩阵元素的引用,常用的有:

  • 数组方法访问RGB图像:Mat.at<Vec3b>(h, w)
  • 数组方法访问灰度图:Mat.at<uchar>(h, w)

如上面的函数所示,数组方法访问图像指定坐标处的像素值主要是调用Mat对象的at方法,其中(h,w)好理解,就是要访问的像素值得二维坐标,尖括号里的内容是访问方式,因为RGB图像中有3个通道各自的像素值,因此以Vec3b的方式来访问,即3个字节元素的vector向量,而灰度图只有一个元素,类型为uchar字节数据,因此以uchar方式访问。

1.2 读入图像并获取长宽等信息

  • Mat.rows返回图像的高度,即行数。
  • Mat.cols返回图像的长度,即列数。
  • Mat.channels()返回图像的通道数,3为RGB图像,1为灰度图像。
Mat input_image = imread("test_images/opencv.jpg", 1);
if (input_image.empty())
{
	cout << "read input error!" << endl;
	return -1;
}
imshow("input", input_image);

int height = input_image.rows;
int width = input_image.cols;
int channel = input_image.channels();

1.2 数组方法遍历访问像素值并取反

//数组方法遍历
for (int h = 0; h < height; h++)
{
	for (int w = 0; w < width; w++)
	{
		if (channel == 3)//彩色图像
		{
			//bgr 是一个vector,包含三个通道的值
			Vec3b bgr = input_image.at<Vec3b>(h, w);
			bgr[0] = 255 - bgr[0];
			bgr[1] = 255 - bgr[1];
			bgr[2] = 255 - bgr[2];
			input_image.at<Vec3b>(h, w) = bgr;
		}
		else if (channel == 1)//灰度图像
		{
			int value = input_image.at<uchar>(h, w);
			input_image.at<uchar>(h, w) = 255 - value;
		}
	}
}
imshow("result", input_image);
  • 需要注意的是,at()方法代码可读性高,但是代码运行效率较差,所以当需要大规模遍历像素的的时候不推荐使用。

2.指针方法访问像素值

2.1 指针方法介绍

  • 得到每一行的首地址:Mat.ptr<type>(h)

OpenCV提供了指针方法来访问操作像素值,h是行数,得到每一行的首地址后可以按照这个地址往后对像素值进行遍历。Mat.ptr<type>(h)返回Mat对象的第h行的头指针,如下图所示:

对于单通道图像gary,就像上面的图所示:

  • gray.ptr<uchar>(0)指向第0行首元素。
  • gray.ptr<uchar>(1)指向第1行首元素。
  • gray.ptr<uchar>(2)指向第2行首元素。

可以使用

  • gray.ptr<uchar>(0)[0]来访问gray[0][0]。
  • gray.ptr<uchar>(0)[1]来访问gray[1][0]。
  • gray.ptr<uchar>(0)[2]来访问gray[0][2]。

对于三通道图像,bgr.pt<uchar>(i)同样指向第i行的首元素,但是与单通道的图像不一样,三通道的图像中一个像素点有BGR三个像素值,可以看下面的图来区别单通道和三通道Mat在内存中的实际分布情况(假设内存地址从左往右增加):

单通道内存分布:

在这里插入图片描述

三通道内存分布:
在这里插入图片描述
因此像gray.ptr<uchar>(0)[0]那样类型设为uchar来操作时,bgr.ptr<uchar>(0)[n]得到的是一个像素点内某个通道的值,因为三通道图像一个像素的有三个值。

所以对于三通道图像,可以使用bgr.ptr<Vec3b>(0)[n]>来获得一个像素点的值,这个时候vec3b是一个包含三个值的向量

2.2 指针方法遍历访问像素值并取反

//指针方法遍历
for (int h = 0; h < height; h++)
{
	uchar* current_row = input_image.ptr<uchar>(h);
	uchar* flag_row = current_row;
	for (int w = 0; w < width; w++)
	{
		int b = 0, g = 0, r = 0;
		if (channel == 3)
		{
			b = *current_row++;
			g = *current_row++;
			r = *current_row++;

			*flag_row++ = 255 - b;
			*flag_row++ = 255 - g;
			*flag_row++ = 255 - r;
		}
		else if (channel == 1)
		{
			int value = *current_row++;
			*flag_row++ = 255 - value;
		}
	}
}
imshow("result", input_image);
  • 指针方法访问速度较快,因此推荐使用。但是要注意C/C++的指针操作并不进行类型和越界检查,如果指针访问出错,可能会让程序出现段错误。

3.迭代器方法遍历访问像素值并取反

//迭代器方法	
if (channel == 3)
{
	Mat_<Vec3b>::iterator ite = input_image.begin<Vec3b>(),
		end = input_image.end<Vec3b>();

	for (; ite != end; ++ite)
	{
		(*ite)[0] = 255 - (*ite)[0];
		(*ite)[1] = 255 - (*ite)[1];
		(*ite)[2] = 255 - (*ite)[2];
	}
}
else if (channel == 1)
{
	Mat_<uchar>::iterator ite = input_image.begin<uchar>(),
		end = input_image.end<uchar>();
	for (; ite != end; ++ite)
	{
		*ite = 255 - *ite;
	}
}

imshow("result", input_image);

4.运行结果分析

完整代码:
image_pixels_access.cpp

运行结果:

在这里插入图片描述
三种方法的运行时间评估:
在OpenCV中,可以用下面的方法来测试运行时间:

double start = static_cast<double>(getTickCount());
//需要测试时间的代码
/*
...
*/
double end= ((double)getTickCount() - start) / getTickFrequency();

//以毫秒输出
cout << "所用时间为:" << time/1000 << "ms" << endl;

输出时间:

array time: 3.50392e-05ms
pointer time: 1.007e-06ms
iterator time: 6.63757e-05ms

可以看出,指针方法遍历像素值得方法是最快的,几乎比另外两个方法快了一个数量级。

5. Python方法访问像素值

5.1 得到图像矩阵维度信息

numpy中,可以使用ndarrayshape方法打印出数组的维度信息,该方法返回一个元组,若是RGB图像,则元组长度为3,里面的三个元素分别为行列以及通道数,若元组长度为2,则为灰度图像,两个元素为行列数,没有通道数,如下面的代码,分别以RGB个灰度图方式读入图像后输出shape:

input = cv.imread("./test_images/opencv.jpg",1)
shape = input.shape
print(shape)

输出:

(273, 508, 3)
input = cv.imread("./test_images/opencv.jpg",0)
shape = input.shape
print(shape)

输出:

(273, 508)

5.2 通过坐标来索引像素值

for row in range(shape[0]):
    for col in range(shape[1]):
        if len(shape)==3 and shape[2]==3: #RGB
            b,g,r = input[row,col]
            b = 255-b
            g = 255-g 
            r = 255-r
            input[row,col] = [b,g,r]
        elif len(shape)==2 : #灰度图
            input[row,col] = 255-input[row,col]

从上面的遍历代码可以看出,在RGB图像中对于每一个像素点索引得到的是包含三个元素的列表,对灰度图索引得到的就是一个值。

完整代码:
image_pixels_access.py

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值