OpenCV 中像素遍历常用的几种方法

OpenCV 中像素遍历常用的几种方法

目录

OpenCV 中像素遍历常用的几种方法

一、IplImage 结构

1.1、IplImage 灰度图像

1)直接访问

2)指针访问

行遍历:

行遍历:

列遍历:

1.2、IplImage 彩色图像(三通道)

1)使用指针的遍历方法如下:

行遍历:

二、Mat 类

Mat类的内存管理

2.1 Mat 灰度图像

2.2 Mat 彩色图像

方法1、

方法2、

方法3、

方法4、

计算算法耗时

【如果你要走,就早点走,就赶紧走;如果不走,也别在最能拼搏的年纪就选择了稳定,更别觉得这世界有什么稳定的工作,你现在享的福都是假象,都会在以后的某一天全部消失。生活是自己的,奋斗也不是为了别人,拼搏是每天必做的事情,只有每天进步才是最稳定的生活。】


           在介绍OpenCV 中像素遍历的几种方法之前,先了解一下OpenCV中存储图像的数据结构IplImage 与 Mat。IplImage 是OpenCV2.1之前旧版本的接口,之后的OpenCV新版本中使用的是Mat。早期的 OpenCV 中,使用 IplImage 和 CvMat 数据结构来表示图像。IplImage和 CvMat 都是 C 语言的结构。使用这两个结构的问题是内存需要手动管理,开发者必须清楚的知道何时需要申请内存,何时需要释放内存。这个开发者带来了一定的负担,开发者应该将更多精力用于算法设计,因此在新版本的 OpenCV 中引入了 Mat 类。新加入的 Mat 类能够自动管理内存。使用 Mat 类,你不再需要花费大量精力在内存管理上。而且你的代码会变得很简洁,代码行数会变少。

一、IplImage 结构

        IplImage是OpenCV中CxCore部分基础的数据结构,用来表示图像,其中Ipl是Intel Image Processing Library的简写。以下是IplImage的结构分析(来自OpenCV中文网站:http://www.opencv.org.cn/index.php/Cxcore%E5%9F%BA%E7%A1%80%E7%BB%93%E6%9E%84#IplImage)

typedef struct _IplImage
    {
        int  nSize;         /* IplImage大小,=sizeof(IplImage)*/
        int  ID;            /* 版本 (=0)*/
        int  nChannels;     /* 大多数OPENCV函数支持1,2,3 或 4 个通道 */
        int  alphaChannel;  /* 被OpenCV忽略 */
        int  depth;         /* 像素的位深度: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16U,
                               IPL_DEPTH_16S, IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F 可支持 */
        char colorModel[4]; /* 被OpenCV忽略 */
        char channelSeq[4]; /* 被OpenCV忽略 */
        int  dataOrder;     /* 0 - 交叉存取颜色通道,对三通道RGB图像,像素存储顺序为BGR BGR BGR ... BGR;
                                     1 - 分开的颜色通道,对三通道RGB图像,像素存储顺序为RRR...R GGG...G BBB...B。
                                  cvCreateImage只能创建交叉存取图像 */
        int  origin;        /* 0 - 顶—左结构,
                               1 - 底—左结构 (Windows bitmaps 风格) */
        int  align;         /* 图像行排列 (4 or 8). OpenCV 忽略它,使用 widthStep 代替 */
        int  width;         /* 图像宽像素数 */
        int  height;        /* 图像高像素数*/
        struct _IplROI *roi;/* 图像感兴趣区域. 当该值非空只对该区域进行处理 */
        struct _IplImage *maskROI; /* 在 OpenCV中必须置NULL */
        void  *imageId;     /* 同上*/
        struct _IplTileInfo *tileInfo; /*同上*/
        int  imageSize;   /* 图像数据大小(在交叉存取格式下imageSize=image->height*image->widthStep),单位字节*/
        char *imageData;  /* 指向排列的图像数据 */
        int  widthStep;   /* 排列的图像行大小,以字节为单位 */
        int  BorderMode[4];  /* 边际结束模式, 被OpenCV忽略 */
        int  BorderConst[4]; /* 同上 */
        char *imageDataOrigin; /* 指针指向一个不同的图像数据结构(不是必须排列的),是为了纠正图像内存分配准备的 */
    }
    IplImage;

IplImage结构来自于 Intel Image Processing Library(是其本身所具有的)。OpenCV 只支持其中的一个子集:

  • alphaChannel 在OpenCV中被忽略。
  • colorModel 和channelSeq 被OpenCV忽略。OpenCV颜色转换的唯一函数 cvCvtColor将原图像的颜色空间和目标图像的颜色空间都作为一个参数。
  • dataOrder 必须是IPL_DATA_ORDER_PIXEL (颜色通道是交叉存取),然而平面图像的被选择通道可以被处理,就像COI(感兴趣的通道)被设置过一样。
  • align 是被OpenCV忽略的,而用 widthStep 去访问后继的图像行。
  • 不支持maskROI 。处理MASK的函数把他当作一个分离的参数。MASK在 OpenCV 里是 8-bit,然而在 IPL他是 1-bit。
  • tileInfo 不支持。
  • BorderMode和BorderConst是不支持的。每个 OpenCV 函数处理像素的邻近的像素,通常使用单一的固定代码边际模式。

除了上述限制,OpenCV处理ROI有不同的要求。要求原图像和目标图像的尺寸或 ROI的尺寸必须(根据不同的操作,例如cvPyrDown 目标图像的宽(高)必须等于原图像的宽(高)除以2 ±1)精确匹配,而IPL处理交叉区域,如图像的大小或ROI大小可能是完全独立的。

访问图像中每个像素过程中涉及到比较重要的两个元素是:char *imageData以及widthStep。imageData是指向存放图像像素数据的指针,而widStep是图像行大小,即以字节为单位的行数据长度。

1.1、IplImage 灰度图像

1、一个m*n的单通道字节型图像(灰度图像),其imageData排列如下:

1)直接访问

///IplImage* src 图像遍历的N种方法.
/// <summary>
/// OK
/// 灰度图像像素遍历.直接访问.
/// </summary>
/// <param name="src"></param>输入:灰度图像.
void f_grayImageRow(IplImage* src)
{
	if (NULL == src->imageData)
	{
		printf("src not exist!!!");
		return;
	}

	int nchannel = src->nChannels;

	//IplImage* img = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
	
	uchar* temp = new uchar;
	if (1 == nchannel)
	{
		for (int i = 0; i < src->height; i++)//行遍历
		{
			for (int j = 0; j < src->width; j++)
			{
				*temp = ((uchar*)(src->imageData + i * src->widthStep))[j];

				if ((i > 10 && i < 50) && (j > 10 && j < 80))
				{
					((uchar*)(src->imageData + i * src->widthStep))[j] = 0;
				}
			}
		}

		//cvSaveImage("dst.jpg", src);
		cvNamedWindow("gray", 0);
		cvShowImage("gray", src);
		cvWaitKey(0);
	}
	delete temp;
}

2)指针访问

行遍历:

void f_grayImageRow1(IplImage* src)
{
	if (NULL==src->imageData)
	{
		printf("src not exist!!!");
		return;
	}

	int i=0;
	int j=0;
	int nchannel = src->nChannels;

	//单通道-灰度图
	uchar* upixel = nullptr;
	if (1 == nchannel)
	{
		//方式1:
		for (i = 0; i < src->height; i++)//行遍历
		{
			upixel = (uchar*)(src->imageData + i * src->widthStep);

			for (j = 0; j < src->width; j++)
			{
				if ((i > 10 && i < 50) && (j > 10 && j < 80))
				{
					upixel[j] = 0;
				}

				//std::cout << "upixel=" << (*upixel) + 0 << std::endl;//+0隐式转换为整型,否则会打印出字符
			}
		}

		//cvSaveImage("dst1.jpg", src);
		cvNamedWindow("gray1",0);
		cvShowImage("gray1", src);
		cvWaitKey(0);
	}
}

行遍历:


void f_grayImageRow2(IplImage* src)
{
	if (NULL==src->imageData)
	{
		printf("src not exist!!!");
		return;
	}

	int i=0;
	int j=0;
	int nchannel = src->nChannels;

	//单通道-灰度图
	uchar* upixel = nullptr;
	if (1 == nchannel)
	{
		//方式2:
		for (i = 0; i < src->height; i++)//行遍历
		{
			for (j = 0; j < src->width; j++)
			{
				upixel = (uchar*)(src->imageData + i * src->widthStep + j);

				if ((i > 10 && i < 50) && (j > 10 && j < 80))
				{
					*upixel = 0;
				}

				//std::cout << "upixel=" << (*upixel) + 0 << std::endl;//+0隐式转换为整型,否则会打印出字符
			}
		}

		//cvSaveImage("dst2.jpg", src);
		cvNamedWindow("gray2",0);
		cvShowImage("gray2", src);
		cvWaitKey(0);
	}
}

列遍历:

///IplImage* src 图像遍历的N种方法.
/// <summary>
/// OK
/// 灰度图像像素遍历.用指针来访问.
/// </summary>
/// <param name="src"></param>输入:灰度图像.
void f_grayImageCol(IplImage* src)
{
	if (NULL==src->imageData)
	{
		printf("src not exist!!!");
		return;
	}

	int x=0;
	int y=0;
	int nchannel = src->nChannels;

	//单通道-灰度图
	uchar *upixel = nullptr;
	if (1 == nchannel)
	{
		//方式1:
		for (x = 0; x < src->width; x++)//列遍历
		{
			for (y = 0; y < src->height; y++)
			{
				//if ((i > 10 && i < 50) && (j > 10 && j < 80))
				{
					upixel = (uchar*)(src->imageData + y * src->widthStep + x);
					*upixel = 0;
				}
				cvNamedWindow("gray111",0);
				cvShowImage("gray111", src);
				cvWaitKey(1);
				//std::cout << "upixel=" << (*upixel) + 0 << std::endl;//+0隐式转换为整型,否则会打印出字符
			}
		}

		//cvSaveImage("dst1.jpg", src);
		cvNamedWindow("gray1",0);
		cvShowImage("gray1", src);
		cvWaitKey(0);
	}
}

1.2、IplImage 彩色图像(三通道)

三通道彩色图像在字节图像中,imageData排列如下:

其中(Bi,Bj)(Gi,Gj)(Ri,Rj)表示图像(i,j)处BGR分量的值。

1)使用指针的遍历方法如下:

行遍历:

/// <summary>
/// OK
/// 彩色图像遍历,分离出B/G/R通道的图像.
/// </summary>
/// <param name="src"></param>输入:彩色图像
void f_colorImage(IplImage* src)
{
	if (NULL == src->imageData)
	{
		printf("src not exist!!!");
		return;
	}

	int nchannel = src->nChannels;

	IplImage* b_img;
	IplImage* g_img;
	IplImage* r_img;
	b_img = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
	g_img = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
	r_img = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);

	if (3==nchannel)
	{
		for (int y=0; y<src->height; y++)
		{
			uchar* ptr = (uchar*)(src->imageData + y * src->widthStep);
			uchar* btr = (uchar*)(b_img->imageData + y * b_img->widthStep);
			uchar* gtr = (uchar*)(g_img->imageData + y * g_img->widthStep);
			uchar* rtr = (uchar*)(r_img->imageData + y * r_img->widthStep);

			for (int x=0; x<src->width; x++)
			{
				/*ptr[3 * x + 0] = 0;
				ptr[3 * x + 1] = 255;
				ptr[3 * x + 2] = 255;*/

				btr[x] = ptr[3 * x + 0];
				gtr[x] = ptr[3 * x + 1];
				rtr[x] = ptr[3 * x + 2];
			}
		}
	}

	cvNamedWindow("b");
	cvNamedWindow("g");
	cvNamedWindow("r");
	cvShowImage("b", b_img);
	cvShowImage("g", g_img);
	cvShowImage("r", r_img);

	cvWaitKey(0);
}

二、Mat 类

Mat类定义如下所示,关键的属性如下方代码所示:

class CV_EXPORTS Mat
{
public:
//一系列函数
...

/* flag 参数中包含许多关于矩阵的信息,如:
      -Mat 的标识
      -数据是否连续
      -深度
      -通道数目
*/
int flags;

//矩阵的维数,取值应该大于或等于 2
int dims;

//矩阵的行数和列数,如果矩阵超过 2 维,这两个变量的值都为-1
int rows, cols;

//指向数据的指针
uchar* data;

//指向引用计数的指针
//如果数据是由用户分配的,则为 NULL
int* refcount;

//其他成员变量和成员函数
...

};

Mat类的内存管理

         Mat 是一个类,由两个数据部分组成:矩阵头(包含矩阵尺寸,存储方法,存储地址等信息)和一个指向存储所有像素值的矩阵的指针。矩阵头的尺寸是常数值,但矩阵本身的尺寸会依图像的不同而不同,通常比矩阵头的尺寸大数个数量级。复制矩阵数据往往花费较多时间,因此除非有必要,不要复制大的矩阵。
        为了解决矩阵数据的传递,OpenCV 使用了引用计数机制。其思路是让每个Mat 对象有自己的矩阵头信息,但多个 Mat 对象可以共享同一个矩阵数据。让矩阵指针指向同一地址而实现这一目的。很多函数以及很多操作(如函数参数传值)只复制矩阵头信息,而不复制矩阵数据。 如果 Mat 类自己申请数据空间,那么该类会多申请 4 个字节,多出的 4 个字节存储数据被引用的次数。引用次数存储于数据空间的后面,refcount 指向这个位置,如图所示。当计数等于 0 时,则释放该空间。

Mat的存储形式和Matlab里的数组格式有点像,但一般是二维向量,如果是灰度图,一般存放 <uchar>类型;如果是RGB彩色图,存放 <Vec3b>类型
单通道灰度图数据存放格式:
 
多通道的彩色图像中,每列并列存放通道数量的子列,如RGB三通道彩色图:
 
注意通道的顺序反转了:BGR。通常情况内存足够大的话图像的每一行是连续存放的,也就是在内存上图像的所有数据存放成一行,这中情况在访问时可以提供很大方便。可以用  isContinuous()函数来判断图像数组是否为连续的。

    cv::Mat src = cv::imread("..\\testPicture\\happyfish.jpg", cv::IMREAD_COLOR);
    cv::namedWindow("0", 0);
    cv::imshow("0", src);
    cv::waitKey(0);

	cv::Mat grayImg;
	cv::cvtColor(src, grayImg, CV_RGB2GRAY);


	int xwCol = src.cols;
	int yhRow = src.rows;
	int nchannels = src.channels();


/*
Mat类提供了一个at的方法用于取得图像上的点,它是一个模板函数,可以取到任何类型的图像上的点。
这种方法不推荐用来遍历图像,它主要用来获取或更改图像的中随机元素的。
基本用途是用来访问特定的矩阵元素(知道行数和列数)。
 */

// image.at<uchar>(i,j):取出灰度图像中i行j列点的像素

2.1 Mat 灰度图像

方法1、


	if (1==nchannels)
	{
		for (int y = 0; y < yhRow; y++)//行遍历
		{
			uchar* ptr = src.ptr<uchar>(y);
			for (int x = 0; x < xwCol; x++)
			{
				ptr[x] = 255 - ptr[x];
			}
		}
	}

方法2、

	if (1==nchannels)
	{
		for (int i = 0 ; i < src.rows ; i ++)
		{
			for(int j = 0 ; j < src.cols ; j ++)
			{
				src.at<uchar>(i,j) = 255-src.at<uchar>(i,j);
			}
		}
	}

2.2 Mat 彩色图像

方法1、

	//方法1:at<typename>(i,j)
	//基于Mat对象的随机像素访问API实现,通过行列索引方式遍历每个像素值。
	for (int y = 0; y < yhRow; y++)
	{
		for (int x = 0; x < xwCol; 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;
		}
	}

方法2、

	//方法2
	//基于Mat对象的行随机访问指针方式实现对每个像素的遍历.
	for (int y = 0; y < yhRow; y++)
	{
		cv::Vec3b* curR = src.ptr<cv::Vec3b>(y);
		for (int x = 0; x < xwCol; x++)
		{
			cv::Vec3b bgr = curR[x];
			bgr[0] = 255 - bgr[0];
			bgr[1] = 255 - bgr[1];
			bgr[2] = 255 - bgr[2];
	
			//src.at<cv::Vec3b>(y, x) = bgr;//OK
			curR[x] = bgr;//OK
		}
	}

方法3、

	//方法三
	//直接获取Mat对象的像素块的数据指针,基于指针操作,实现快速像素方法.
	for (int y = 0; y < yhRow; y++)
	{
		uchar* uc_pixel = src.data + y * src.step;
		for (int x = 0; x < xwCol; x++)
		{
			uc_pixel[0] = 255 - uc_pixel[0];
			uc_pixel[1] = 255 - uc_pixel[1];
			uc_pixel[2] = 255 - uc_pixel[2];

			uc_pixel += 3;
		}
	}


        //methed_1  数组访问    Spendtime:11.1927ms.
        //methed_2  Vec3b指针  Spendtime:6.3479ms.
        //methed_3  字节指针    Spendtime:0.1558ms.

方法4、

 //二、高效一点:用指针来遍历图像
 void colorReducePtr(const Mat& image, Mat& outImage,int div)
 {
    //创建与原图像等尺寸的图像
    outImage.create(image.size(),image.type());
	//行数
    int nr=image.rows;  
    //将3通道转换为1通道
    int nl=image.cols*image.channels();//列数*通道数=每一行元素的个数
    
	//双重循环,遍历所有的像素值
	for(int k=0;k<nr;k++) //行循环
    {
         //每一行图像的指针
        const uchar* inData=image.ptr<uchar>(k);//获取第K行的首地址
        uchar* outData=outImage.ptr<uchar>(k);
        for(int i=0;i<nl;i++) //列循环
        {
			outData[i] = inData[i]/div*div+div/2;//对每一个像素值进行处理
			//*outData++ = *inData++/div*div+div/2;
        }
    }
}

    //更高效的方法    
    //一般来说图像行与行之间往往存储是不连续的,但是有些图像可以是连续的,
    //Mat提供了一个检测图像是否连续的函数isContinuous。当图像连通时,我们就可以把图像完全展开,看成是一行。

    //方法四
	//更高效的方法	
	//一般来说图像行与行之间往往存储是不连续的,但是有些图像可以是连续的,
	//Mat提供了一个检测图像是否连续的函数isContinuous。当图像连通时,我们就可以把图像完全展开,看成是一行。
	int div = 64;
	cv::Mat outImage;
	outImage.create(src.size(), src.type());
	
	//int yhRow = src.rows;
	//int xwCol = src.cols;
	if (src.isContinuous() && outImage.isContinuous())
	{
		yhRow = 1;
		xwCol = xwCol * src.rows * src.channels();
	}
	for (int i = 0; i < yhRow; i++)
	{
		const uchar* inData = src.ptr<uchar>(i);
		uchar* outData = outImage.ptr<uchar>(i);
		for (int j = 0; j < xwCol; j++)
		{
			//*outData++ = *inData++ / div * div + div / 2;
			*outData++ = 255 - (*inData++);
		}
	}
	cv::namedWindow("case 4", 0);
	cv::imshow("case 4", outImage);
	cv::waitKey(0);

其他:

//用迭代器来遍历   迭代遍历图像 Safe Method

//用迭代器来遍历   迭代遍历图像 Safe Method
/*下面的方法可以让我们来为图像中的像素声明一个迭代器:

MatIterator_<Vec3b> it;
Mat_<Vec3b>::iterator it;

如果迭代器指向一个const图像,则可以用下面的声明:
MatConstIterator<Vec3b> it; 或者
Mat_<Vec3b>::const_iterator it;
 
*/
void colorReduce4(const Mat& image,Mat& outImage,int div)
{
    outImage.create(image.size(),image.type());
	
    MatConstIterator_<Vec3b> it_in=image.begin<Vec3b>(); //初始位置的迭代器
	MatConstIterator_<Vec3b> itend_in=image.end<Vec3b>(); //终止位置的迭代器
   
    MatIterator_<Vec3b> it_out=outImage.begin<Vec3b>();
    MatIterator_<Vec3b> itend_out=outImage.end<Vec3b>();
	
	//获取彩色图像的像素值
    while(it_in!=itend_in)
    {
        (*it_out)[0]=(*it_in)[0]/div*div+div/2; //B
        (*it_out)[1]=(*it_in)[1]/div*div+div/2; //G
        (*it_out)[2]=(*it_in)[2]/div*div+div/2; //R
        it_in++;
        it_out++;
   }
}
//如果想从第二行开始,则可以从image.begin<Vec3b>()+image.rows开始。

//颜色缩减
void colorReduce(Mat &image, int div)
{
	int n1 = image.rows; //行数
	//每行的元素个数
	int nc = image.cols*image.channels();
	for(int j=0;j<n1;j++)
	{
		//得到第j行的首地址
		uchar* data = image.ptr<uchar>(j);
		for(int i=0;i<nc;i++)
		{
			//处理每一个元素
			data[i] = data[i]/div*div+div/2;
			//像素处理完成
		}//行处理完成
	}
}

计算算法耗时

为了验证几种方法的效率,可以用一个简单的计时和输出:


	double t1 = (double)(cv::getTickCount());
    …………………………………………………………………………………………………………
      ……………………………一系列算法操作………………………………
    …………………………………………………………………………………………………………
	double t2 = (double)(cv::getTickCount());
	double t = (double)((t2 - t1) / (cv::getTickFrequency())) * 1000;
	std::cout << "Spendtime:" << t << "ms." << std::endl;



	ostringstream ss;
	ss << "Spendtime : " << std::fixed << std::setprecision(2) << t << " ms ";
	putText(src, ss.str(), cv::Point(10, 30), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255), 1, 8);

【如果你要走,就早点走,就赶紧走;如果不走,也别在最能拼搏的年纪就选择了稳定,更别觉得这世界有什么稳定的工作,你现在享的福都是假象,都会在以后的某一天全部消失。生活是自己的,奋斗也不是为了别人,拼搏是每天必做的事情,只有每天进步才是最稳定的生活。】

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: OpenCV可视化点云数据集需要通过PCL(Point Cloud Library)库来实现。下面是一个简单的操作步骤: 1. 安装 PCL 库并链接到 OpenCV 项目。 2. 加载点云数据集,例如通过 PCL 库的 `pcl::PointCloud` 类型读取点云数据。 3. 将点云转换为 OpenCV 的图像,例如通过 `cv::Mat` 类型将点云数据转换为灰度图像或彩色图像。 4. 显示转换后的图像,例如通过 OpenCV 的 `cv::imshow` 函数进行显示。 下面是一个简单的示例代码,可以将点云数据渲染成深度图像并显示出来: ```cpp #include <opencv2/opencv.hpp> #include <pcl/point_cloud.h> #include <pcl/point_types.h> #include <pcl/io/pcd_io.h> int main(int argc, char** argv) { // 读取点云数据 pcl::PointCloud<pcl::PointXYZ> cloud; pcl::io::loadPCDFile<pcl::PointXYZ>("data.pcd", cloud); // 转换点云为深度图像 cv::Mat depth_image(cloud.height, cloud.width, CV_32FC1); for (int i = 0; i < cloud.height; i++) { for (int j = 0; j < cloud.width; j++) { depth_image.at<float>(i, j) = cloud.at(j, i).z; } } // 显示深度图像 cv::normalize(depth_image, depth_image, 0, 255, cv::NORM_MINMAX); cv::Mat depth_image_8bit; depth_image.convertTo(depth_image_8bit, CV_8UC1); cv::imshow("Depth Image", depth_image_8bit); cv::waitKey(); return 0; } ``` 这个示例代码,我们首先使用 PCL 库的 `pcl::PointCloud` 类型读取点云数据,然后将点云数据转换为 OpenCV 的图像。在转换点云数据为深度图像时,我们遍历每一个点并将其 `z` 坐标值赋值给深度图像对应的像素。最后,我们通过 OpenCV 的 `cv::normalize` 函数将深度图像归一化到 [0, 255] 范围内,并将深度图像转换为 8 位无符号整型格式,最后显示出来。 ### 回答2: 在OpenCV,可视化点云数据集可以通过以下步骤进行操作: 1. 导入必要的库和模块:首先,需要导入OpenCV库以及其他必要的库和模块,例如numpy、matplotlib等。 2. 加载点云数据集:使用OpenCV的函数加载点云数据集,例如cv2.imread()函数加载图像数据集,或使用其他相关函数加载其他类型的点云数据集。 3. 数据预处理:根据具体需求,对加载的点云数据集进行预处理。例如,可以对数据进行滤波、降噪等操作,以提高点云数据集的质量。 4. 可视化点云数据集:使用OpenCV的函数或方法可视化点云数据集。一种常用方法是使用matplotlib库的scatter()函数将点云数据集显示在二维平面上。可以设置点的颜色、标记、大小等属性以及坐标轴的范围。 5. 显示图像:最后,使用OpenCV的imshow()函数或其他相关函数显示可视化后的点云数据集图像。可以将图像显示在窗口,也可以保存为图像文件以后使用。 需要注意的是,具体的点云数据集的可视化操作可能会因使用其他库或模块而有所不同。因此,在实际操作,除了OpenCV外,可能还需要使用其他工具或库来处理和可视化点云数据集。 ### 回答3: 使用OpenCV进行点云数据集的可视化有以下几个具体操作步骤: 1.导入所需的库和模块:首先需要确保已经安装了OpenCV库,并导入所需的相关模块,如cv2和numpy。 2.读取点云数据集:使用OpenCV提供的函数,如cv2.imread()或cv2.VideoCapture()来读取点云数据集。在读取之前,确保点云数据集以适当的格式保存,如PLY、XYZ、PCD等。 3.处理点云数据:对读取的数据进行必要的处理,例如转换坐标系、调整亮度对比度等。可以使用numpy数组对点云数据进行操作。 4.显示点云:使用OpenCV的绘图功能,如cv2.imshow()或cv2.imshow3D()来显示点云数据集。可以使用不同的颜色和大小来表示不同的点或属性。 5.添加交互功能:通过OpenCV提供的函数,如cv2.waitKey()等,可以为点云数据集的可视化添加一些交互功能,如旋转、移动、缩放等。 6.保存可视化结果:如果需要将可视化结果保存为图像或视频文件,可以使用OpenCV的函数,如cv2.imwrite()或cv2.videoWriter()来保存。 总结来说,使用OpenCV进行点云数据集的可视化需要读取数据、处理数据、显示数据,并可以添加交互功能。通过以上操作,可以将点云数据集以直观的方式展示出来。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值