【和春笋一起学C++】OpenCV中数组和指针运用实例

**前言:**前面学习了数组和指针在C++中的处理原理,本文通过自己编写一个图像处理的函数实例来加深对数组和指针的理解。为什么是图像处理呢,因为图像数据是一个二维矩阵,相当于一个二维数组,前面学习了一维数组,现在看看指针是怎么来处理二维数组的。

目录

遍历OpenCV中图像像素方法一

遍历OpenCV中图像像素方法二

总结


**正文:**我们首先使用opencv图像处理库读入一张图像,然后对图像进行二值化(opencv库中已经集成了此功能,此处自己编写仅仅为了巩固指针和数组的知识)。

遍历OpenCV中图像像素方法一

代码如下:

cpp 复制代码
#include <iostream>
#include <cstring>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int main()
{
	Mat srcImage = imread("test.jpg");
	Mat srcGray;
	cvtColor(srcImage, srcGray, COLOR_BGR2GRAY);
	Mat outputImage = srcGray.clone();
	int rows = outputImage.rows;
	int cols = outputImage.cols*outputImage.channels();
	int threshold = 50;
	for (int i = 0; i < rows; i++)
	{
		uchar* data = outputImage.ptr<uchar>(i);///获取第i行的首地址
		for (int j = 0; j < cols; j++)
		{
			if (data[j] < threshold)
			{
				data[j] = 255;
			}
			else
			{
				data[j] = 0;
			}
		}
	}
	imshow("src", srcImage);
	imshow("binary", outputImage);
	waitKey();
	return 0;
}

该方法中最重要的是这条语句:uchar* data = outputImage.ptr<uchar>(i);Mat类中的ptr函数返回的是某一行的首地址,ptr是一个模板函数,代码中它返回的是第i行的首地址。

第一个循环体内获取每行的首地址,第二个循环体内获取每行的每个像素值,并做阈值划分。前面文章《使用new创建动态数组》讲过,访问数组中的元素既可以用指针访问,也可以用数组的方括号表示法来访问,此处用的是方括号表示法,data[j]代表的是第i行中的第(j+1)个像素值,通过双层循环对图像中每一行的每个像素值做阈值划分,就完成了图像的二值化。

也可以通过指针的方式来访问图像中的每个像素值,将上面代码中的循环体换成以下代码,运行的结果是一样的。

cpp 复制代码
for (int i = 0; i < rows; i++)
	{
		uchar* data = outputImage.ptr<uchar>(i);///获取第i行的首地址
		for (int j = 0; j < cols; j++)
		{
			if ((*(data+j)) < threshold)
			{
				*(data + j) = 255;
			}
			else
			{
				*(data + j) = 0;
			}
		}
	}

遍历OpenCV中图像像素方法二

方法一中通过获取每行的首地址来访问每行的每个像素值。也可以直接通过二维数组的整个数组的首地址来访问每个像素值。

其实,当opencv读入一张图像时,图像数据(每个像素的像素值)在内存中的存储是连续的。当opencv读入一张图片时,计算机将图像中每行的像素值一行一行的连接起来,存储在一块连续的内存块中。假如一张图片大小为500*600(row=500,col=600),且为单通道图像,则第600个数据为图像第一行的最后一个像素值,第601个数据为图像第二行的第1个像素值。

在opencv中可以通过Mat类中的函数isContinuous来判断数据是否连续存储。图像数据的连续存储有助于提升图像的扫描速度,相当于将图像中的每行一行一行的连接起来,形成一个长行。此时,遍历每个像素只需要一个指针或一个一维数组。

代码如下:

cpp 复制代码
#include <iostream>
#include <cstring>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int main()
{
	Mat srcImage = imread("test.jpg");
	Mat srcGray;
	cvtColor(srcImage, srcGray, COLOR_BGR2GRAY);
	Mat outputImage = srcGray.clone();
	int rows = outputImage.rows;
	int cols = outputImage.cols*outputImage.channels();
	int threshold = 50;
    if (outputImage.isContinuous() == false)
	{
		cout << "图像数据非连续存储" << endl;
		return 0;
	}
	else
	{
		cout << "图像数据为连续存储" << endl;
	}
	uchar* data = outputImage.data;
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			if (data[i*cols+j] < threshold)
			{
				data[i*cols + j] = 255;
			}
			else
			{
				data[i*cols + j] = 0;
			}
		}
	}
	imshow("src", srcImage);
	imshow("binary", outputImage);
	waitKey();
	return 0;
}

方法二中最重要的是这条语句:uchar* data = outputImage.data; Mat类中的data指针指向的就是图像数据二维数组的首地址,该方法用的是数组的方括号表示法来访问每个像素。同理,循环体中也可以换成指针的方式来访问每个像素。循环体中的代码换成指针的方式如下:

cpp 复制代码
for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			if (*(data+i*cols + j) < threshold)
			{
				*(data + i * cols + j) = 255;
			}
			else
			{
				*(data + i * cols + j) = 0;
			}
		}
	}

总结

opencv中遍历图像像素的方法有十几种,本文通过4种遍历图像像素的方式编写了一个简单的图像二值化函数,既巩固了指针和数组的知识,又掌握了遍历opencv中图像像素的方法。

相关推荐
Hoshino _Ai6 小时前
OpenCV图像认知(三)
人工智能·opencv·计算机视觉
azoo7 小时前
Canny边缘检测(cv2.Canny())
人工智能·opencv·计算机视觉
PyAIExplorer10 小时前
图像梯度处理与边缘检测:OpenCV 实战指南
人工智能·opencv·计算机视觉
九章云极AladdinEdu10 小时前
华为昇腾NPU与NVIDIA CUDA生态兼容层开发实录:手写算子自动转换工具链(AST级代码迁移方案)
人工智能·深度学习·opencv·机器学习·华为·数据挖掘·gpu算力
Blue桃之夭夭10 小时前
基于OpenCV的实时人脸检测系统实现指南 ——Python+Haar级联分类器从环境搭建到完整部署
人工智能·python·opencv
C语言小火车10 小时前
野指针:C/C++内存管理的“幽灵陷阱”与系统化规避策略
c语言·c++·学习·指针
presenttttt12 小时前
用Python和OpenCV从零搭建一个完整的双目视觉系统(四)
开发语言·python·opencv·计算机视觉
jndingxin1 天前
OpenCV 人脸分析----人脸识别的一个经典类cv::face::EigenFaceRecognizer
人工智能·opencv·计算机视觉
PyAIExplorer1 天前
图像轮廓检测与绘制:OpenCV 实战指南
人工智能·opencv·计算机视觉
AndrewHZ1 天前
【图像处理基石】如何检测到画面中的ppt并对其进行增强?
图像处理·人工智能·pytorch·opencv·目标检测·计算机视觉·图像增强