【OpenCV】OpenCV中遍历图像像素的几种方式对比

本文详细介绍了OpenCV中遍历图像像素的五种方法,包括数组遍历、at(i,j)、指针遍历、迭代器遍历,并通过实例和耗时测试比较了它们的效率和安全性。指针法在效率上占优,而迭代器法则确保了安全。" 3092867,444168,使用JavaScript实现回车键模拟Tab键切换,"['javascript', 'HTML', '表单交互']
摘要由CSDN通过智能技术生成


前言

Mat 类是OpenCV中的一个基本数据类型,它是一个n维密集数组类
Mat 类表示一个 n 维密集数值单通道或多通道数组。它可用于存储实数或复值向量和矩阵、灰度或彩色图像、体素体积、向量场、点云、张量、直方图。本文将总结几种遍历像素的不同方法,在需要处理大量图片的场景,不同的遍历方法,速度上会有显著差异。

一、Mat数据类型

Mat包含两个数据部分的类:矩阵头(包含诸如矩阵大小,用于存储的方法,存储的矩阵地址等信息)和指向包含像素值矩阵(根据选择的存储方法而有不同的维度)的指针.矩阵头大小是个常量,不同大小的图像的矩阵大小各不相同,通常矩阵大小要比图像大小大几个数量级。
Mat的数据类型及取值范围
在这里插入图片描述当Mat为单通道灰度图时,其在内存中的排布如下:
在这里插入图片描述

当Mat为多通道图像时,以3通道为例,其在内存中的排布方式如下:
在这里插入图片描述
通常情况内存足够大的话图像的每一行是连续存放的,也就是在内存上图像的所有数据存放成一行,这中情况在访问时可以提供很大方便。OpenCV中提供了 isContinuous()函数来判断图像数组是否为连续的。

二、Mat数据的遍历方法

1.数组遍历法

代码如下:

#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

int main()
{
   
	Mat src = imread("./2.png",0);
	int width = src.cols;
	int height = src.rows;
	//单通道
	for (int i = 0; i < height; i++)
	{
   
		for (int j = 0; j < width; j++)
		{
   
			src.data[i * width + j]+=50;
		}
	}
	
	//三通道
	/*for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			int index = i * width + j;
			src.data[3 * index + 0]+=50;
			src.data[3 * index + 1]+= 50;;
			src.data[3 * index + 2]+=50;
			
		}
	}
*/
	imwrite("test.png", src);
	return 0;
}

原图
在这里插入图片描述
结果图
在这里插入图片描述

2. at(i,j)

Mat类提供了一个at的方法用于取得图像上的点,它是一个模板函数,可以取到任何类型的图像上的点。此typename的定义如下:
在这里插入图片描述

测试代码如下:

#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

int main()
{
   
	Mat src = imread("./2.png", -1);

	//灰度图
	if (1 == src.channels())
	{
   
		for (int y = 0; y < src.rows; y++)
		{
   
			for (int x = 0; x < src.cols; x++)
			{
   
				src.at<uchar>(y, x) = 255 - src.at<uchar>(y, x);
			}
		}
	}
	else if(3 == src.channels())
	{
   
		//彩色图
		for (int y = 0; y < src.rows; y++)
		{
   
			for (int x = 0; x < src.cols; x++)
			{
   
				cv::Vec3b bgr = src.at<cv::Vec3b>(y, x);
				bgr[0] = 255 - bgr[0];
				bgr[1] = 255 - bgr[1];
				bgr[2] = 255 - bgr[2];
				src.at<cv::Vec3b>(y, x) = bgr;
			}
		}
	}

	imwrite("test.png",src);
}

原图
在这里插入图片描述
结果图
在这里插入图片描述

3、指针遍历法

利用.ptr和数组下标进行图像像素遍历,这种图像遍历方法相比“数组遍历法”更高效。
示例代码:

#include <opencv2/opencv.hpp>
using namespace cv;
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 这问题比较复杂,需要分几步来实现: 1. 首先,需要使用 OpenCV 和 DCMTK 库来读取 DICOM 图像,并将其显示在窗口。可以使用 OpenCV 的 imread 函数和 DCMTK 的 DicomImage 类来实现。 2. 接下来,需要在窗口实现鼠标拖动功能。可以使用 OpenCV 的 setMouseCallback 函数来实现。 3. 当鼠标拖动时,需要在图像上画出一个圆。可以使用 OpenCV 的 circle 函数来实现。 4. 最后,需要将所有画出的圆都显示在图像上。可以在鼠标回调函数将每个圆的信息保存到一个容器,并在图像显示函数遍历容器,将所有圆都画出来。 下面是一个示例代码,可以作为参考: ```c++ #include <iostream> #include <vector> #include <opencv2/opencv.hpp> #include "dcmtk/dcmdata/dctk.h" #include "dcmtk/dcmimgle/dcmimage.h" using namespace std; using namespace cv; vector<Point2f> centers; // 保存所有圆心点的容器 void onMouse(int event, int x, int y, int flags, void* param) { if (event == EVENT_LBUTTONDOWN) { // 如果鼠标左键按下 centers.push_back(Point2f(x, y)); // 将圆心点保存到容器 } } int main(int argc, char* argv[]) { // 读取 DICOM 图像 DicomImage* image = new DicomImage("path/to/dicom/image"); if (image == NULL) { cerr << "Failed to read DICOM image!" << endl; return -1; } // 将 DICOM 图像转换为 OpenCV Mat 格式 Mat mat(image->getHeight(), image->getWidth(), CV_16UC1, (void*)image->getOutputData(16)); delete image; // 创建窗口并显示图像 namedWindow("DICOM Image", WINDOW_NORMAL); imshow("DICOM Image", mat); // 注册鼠标回调函数 setMouseCallback("DICOM Image", onMouse, NULL); // 循环显示图像,直到用户按下 ESC 键 while (waitKey(30) != 27) { // 在图像上画出所有圆 for (int i = 0; i < centers.size(); i++) { circle(mat, centers[i], 10, Scalar(255, 0, 0), 2); } imshow("DICOM Image", mat); } return 0; } ``` 需要注意的是,这个示例代码只是一个简单的实现,可能会有一些问题,比如圆会重叠在一起等。如果需要更完整、更稳定的实现,需要更多的代码和调试。 ### 回答2: 在Xcode使用OpenCV、C语言和DCMTK库读取DICOM图像并实现使用鼠标拖动在图像上绘制多个圆并显示的方法如下: 1. 首先需要在Xcode创建一个工程,并导入OpenCV、C语言和DCMTK库。 2. 使用DCMTK库读取DICOM图像。可以使用DCMTK库的函数来获取DICOM图像像素数据和相关信息。 3. 使用OpenCV库加载DICOM图像并显示。可以使用OpenCV库的函数将DICOM图像数据转换为Mat对象,并使用imshow函数显示图像。 4. 创建一个鼠标事件的回调函数。可以使用OpenCV库的setMouseCallback函数来注册鼠标事件的回调函数,然后在回调函数实现鼠标拖动绘制圆的功能。 5. 在鼠标事件的回调函数实现拖动绘制圆的功能。可以使用OpenCV库的circle函数来绘制圆,使用imshow函数显示绘制后的图像。 6. 在主函数调用上述函数,将DCMTK读取的DICOM图像传递给OpenCV,然后显示图像并启动事件循环,等待鼠标事件的触发。 以上是一种实现在Xcode使用OpenCV、C语言和DCMTK库读取DICOM图像并实现鼠标拖动绘制多个圆并显示的简单方法。具体的实现过程可能还需要根据具体的需求和图像数据格式进行调整和完善。 ### 回答3: 在Xcode使用OpenCV和DCMTK库读取DICOM图像,并实现通过鼠标拖动绘制多个圆并显示的功能。 首先,我们需要导入OpenCV和DCMTK库,并设置DICOM图像的路径。 ```c++ #include <opencv2/opencv.hpp> #include <dcmtk/dcmimgle/dcmimage.h> using namespace cv; using namespace std; int main() { String dicomPath = "/path/to/dicom"; // 读取DICOM图像 DicomImage dcmImage(dicomPath.c_str()); if (dcmImage.getStatus() != EIS_Normal) { // 读取失败 return -1; } // 创建窗口并显示图像 namedWindow("DICOM Image", WINDOW_NORMAL); imshow("DICOM Image", dcmImage); // 定义圆的半径和颜色 int radius = 10; Scalar color(255, 0, 0); // 蓝色 // 创建画布,并复制DICOM图像 Mat canvas(dcmImage.getHeight(), dcmImage.getWidth(), CV_8UC3); cvtColor(dcmImage, canvas, COLOR_GRAY2BGR); // 定义鼠标事件回调函数 vector<Point> circles; // 存储绘制的圆的心点 auto onMouse = [](int event, int x, int y, int flags, void* userdata) { if (event == EVENT_LBUTTONDOWN) { vector<Point>* circles = static_cast<vector<Point>*>(userdata); circles->push_back(Point(x, y)); } }; // 注册鼠标事件回调函数 setMouseCallback("DICOM Image", onMouse, static_cast<void*>(&circles)); while (true) { // 在画布上绘制已点击的圆 for (const Point& center : circles) { circle(canvas, center, radius, color, -1); // 实心圆 } // 显示更新后的画布 imshow("DICOM Image", canvas); // 等待用户退出 if (waitKey(1) == 27) { break; } } destroyAllWindows(); return 0; } ``` 以上代码通过`DicomImage`类读取DICOM图像,并将其显示在名为"DICOM Image"的窗口。然后,使用`Mat`类创建一个画布,将DICOM图像复制到画布上,并通过鼠标事件回调函数记录用户点击的心点。最后,在画布上绘制已点击的圆,并更新窗口画布,同时等待用户按下"Esc"键退出程序。 请注意,以上代码仅为示例,并未完全测试。实际应用,您可能需要根据具体的需求做进一步的调整和优化,以及添加其他功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shanhedian2013

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值