Qt+VTK+PCL图片转灰度图且以灰度为Y轴显示

16 篇文章 1 订阅
7 篇文章 2 订阅

效果

在这里插入图片描述

头文件GrayVirsual.h

#pragma once

#include <QtWidgets/QMainWindow>
#include <QVTKOpenGLNativeWidget.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <vtkImageData.h>
#include <vtkImageDataGeometryFilter.h>
#include <vtkWarpScalar.h>
#include <vtkRenderWindow.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderer.h>

#include <pcl/point_types.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/visualization/cloud_viewer.h>
class GrayVirsual : public QMainWindow
{
	Q_OBJECT

public:
	GrayVirsual(QWidget* parent = nullptr);
	~GrayVirsual();
	
private:
	pcl::visualization::PCLVisualizer::Ptr grayViewer;
	pcl::PointCloud<pcl::PointXYZRGB>::Ptr grayCloud;
	pcl::visualization::PCLVisualizer::Ptr hsvViewer;
	pcl::PointCloud<pcl::PointXYZRGB>::Ptr hsvCloud;
	pcl::visualization::PCLVisualizer::Ptr hViewer;
	pcl::PointCloud<pcl::PointXYZRGB>::Ptr hCloud;
	pcl::visualization::PCLVisualizer::Ptr vViewer;
	pcl::PointCloud<pcl::PointXYZRGB>::Ptr vCloud;
	QWidget* centralWidget;
	QVTKOpenGLNativeWidget* vtkWidget;
	void loadImage(const QString& fileName);
};

实现GrayVirsual.cpp

#include "GrayVirsual.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QMessageBox>
#include <vtkInformation.h>
#include <vtkAxesActor.h>
#include <vtkNamedColors.h>
#include <vtkProperty.h>
#include <vtkRenderer.h>
#include <vtkRendererCollection.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <vtkAxesActor.h>
#include <vtkOBJReader.h>
#include <pcl/io/pcd_io.h>            
#include <pcl/io/ply_io.h>
#include <pcl/io/vtk_io.h>
#include <pcl/filters/statistical_outlier_removal.h>
#include <pcl/filters/passthrough.h>
#include <pcl/segmentation/sac_segmentation.h>
#include <pcl/filters/extract_indices.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/kdtree/kdtree_flann.h>  
#include <pcl/segmentation/extract_clusters.h>
#include <pcl/search/kdtree.h>
#include <pcl/features/normal_3d.h>
#include <pcl/common/transforms.h>
#include <pcl/surface/gp3.h>
#include <pcl/surface/poisson.h>
#include <pcl/surface/mls.h>          
#include <pcl/surface/marching_cubes_hoppe.h>
#include <pcl/surface/marching_cubes_rbf.h>

GrayVirsual::GrayVirsual(QWidget* parent)
	: QMainWindow(parent)
{
	centralWidget = new QWidget(this);
	vtkWidget = new QVTKOpenGLNativeWidget(this);
	this->resize(600, 400);
	this->setCentralWidget(centralWidget);

	// 垂直布局
	QVBoxLayout* vbox = new QVBoxLayout(this);

	//水平布局
	QHBoxLayout* hBoxLayout = new QHBoxLayout(this);
	//创建标签
	QLabel* label = new QLabel(this);
	label->setText("路径:");
	// 单行编辑框
	QLineEdit* pathEdit = new QLineEdit(this);

	//设置最大高度
	pathEdit->setMaximumHeight(30);

	// 按钮
	QPushButton* pushButton = new QPushButton(this);
	pushButton->setText("加载");

	connect(pushButton, &QPushButton::clicked, [this, pathEdit = pathEdit]() {
		loadImage(pathEdit->text());
		});

	hBoxLayout->addWidget(label);
	hBoxLayout->addWidget(pathEdit);
	hBoxLayout->addWidget(pushButton);
	hBoxLayout->setSpacing(10);
	// 设置布局最大高度
	hBoxLayout->setSizeConstraint(QLayout::SetMaximumSize);
	//添加横向弹簧
	hBoxLayout->addStretch(0);

	vbox->addLayout(hBoxLayout);
	vbox->addWidget(vtkWidget);
	vtkWidget->setMinimumSize({ 400,300 });
	centralWidget->setLayout(vbox);

	grayCloud.reset(new pcl::PointCloud<pcl::PointXYZRGB>);
	hsvCloud.reset(new pcl::PointCloud<pcl::PointXYZRGB>);
	hCloud.reset(new pcl::PointCloud<pcl::PointXYZRGB>);
	vCloud.reset(new pcl::PointCloud<pcl::PointXYZRGB>);

	auto renderWindow = vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New();
	auto grayRenderer = vtkSmartPointer<vtkRenderer>::New();

	grayViewer = pcl::visualization::PCLVisualizer::Ptr(new pcl::visualization::PCLVisualizer(
		grayRenderer
		, renderWindow, "", false));
	grayViewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "grayCloud");
	grayViewer->addPointCloud(grayCloud, "grayCloud");
	grayRenderer->SetViewport(0, 0, .5, .5);

	auto hsvRenderer = vtkSmartPointer<vtkRenderer>::New();
	hsvViewer = pcl::visualization::PCLVisualizer::Ptr(new pcl::visualization::PCLVisualizer(
		hsvRenderer
		, renderWindow, "", false));

	hsvViewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "hsvCloud");
	hsvViewer->addPointCloud(hsvCloud, "hsvCloud");
	hsvRenderer->SetViewport(.5, 0, 1, .5);

	auto hViewerRenderer = vtkSmartPointer<vtkRenderer>::New();
	hViewer = pcl::visualization::PCLVisualizer::Ptr(new pcl::visualization::PCLVisualizer(
		hViewerRenderer
		, renderWindow, "", false));
	hViewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "hViewer");
	hViewer->addPointCloud(hCloud, "hCloud");
	hViewerRenderer->SetViewport(0, .5, .5, 1);

	auto vViewerRenderer = vtkSmartPointer<vtkRenderer>::New();
	vViewer = pcl::visualization::PCLVisualizer::Ptr(new pcl::visualization::PCLVisualizer(
		vViewerRenderer
		, renderWindow, "", false));
	vViewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "vViewer");
	vViewer->addPointCloud(vCloud, "vCloud");
	vViewerRenderer->SetViewport(.5, .5, 1, 1);


	vtkWidget->setRenderWindow(renderWindow);
	grayViewer->setupInteractor(vtkWidget->interactor(), vtkWidget->renderWindow());
	hsvViewer->setupInteractor(vtkWidget->interactor(), vtkWidget->renderWindow());
	hViewer->setupInteractor(vtkWidget->interactor(), vtkWidget->renderWindow());
	vViewer->setupInteractor(vtkWidget->interactor(), vtkWidget->renderWindow());

	vtkNew<vtkAxesActor> grayAxes, hsvAxes, hAxes, vAxes;
	grayAxes->SetTotalLength(1, 1, 1);
	hsvAxes->SetTotalLength(1, 1, 1);
	hAxes->SetTotalLength(1, 1, 1);
	vAxes->SetTotalLength(1, 1, 1);
	grayRenderer->AddActor(grayAxes);
	hsvRenderer->AddActor(hsvAxes);
	hViewerRenderer->AddActor(hAxes);
	vViewerRenderer->AddActor(vAxes);

	renderWindow->AddRenderer(grayRenderer);
	renderWindow->AddRenderer(hsvRenderer);
	renderWindow->AddRenderer(hViewerRenderer);
	renderWindow->AddRenderer(vViewerRenderer);
}

GrayVirsual::~GrayVirsual()
{}

void GrayVirsual::loadImage(const QString& fileName)
{
	cv::Mat image = cv::imread(fileName.toStdString());
	if (image.empty())
	{
		QMessageBox::information(this, "提示", "加载图片失败");
		return;
	}
	cv::Mat grayImage;
	cv::cvtColor(image, grayImage, cv::COLOR_BGR2GRAY);
	grayCloud.reset(new pcl::PointCloud<pcl::PointXYZRGB>);
	// 遍历像素
	for (int i = 0; i < grayImage.rows; i++)
	{
		for (int j = 0; j < grayImage.cols; j++)
		{
			grayCloud->push_back({ static_cast<float>(i),
				static_cast<float>(j),
				static_cast<float>(grayImage.at<uchar>(i, j)),
				255,255,255 });
		}
	}

	// 点坐标同除255
	for (int i = 0; i < grayCloud->size(); i++)
	{
		grayCloud->points[i].x /= 255;
		grayCloud->points[i].y /= 255;
		grayCloud->points[i].z /= 255;
	}
	grayViewer->updatePointCloud(grayCloud, "grayCloud");


	// 垂直卷积
	cv::Mat vImg;
	cv::Mat vKernel = (cv::Mat_<float>(3, 3) << -1, 0, 1, -1, 0, 1, -1, 0, 1);
	cv::filter2D(grayImage, vImg, image.depth(), vKernel);

	vCloud.reset(new pcl::PointCloud<pcl::PointXYZRGB>);
	// 遍历像素
	for (int i = 0; i < vImg.rows; i++)
	{
		for (int j = 0; j < vImg.cols; j++)
		{
			vCloud->push_back({ static_cast<float>(i),
				static_cast<float>(j),
				static_cast<float>(vImg.at<uchar>(i, j)),
				255,255,255 });
		}
	}
	// 点坐标同除255
	for (int i = 0; i < vCloud->size(); i++)
	{
		vCloud->points[i].x /= 255;
		vCloud->points[i].y /= 255;
		vCloud->points[i].z /= 255;
	}
	vViewer->updatePointCloud(vCloud, "vCloud");


	// 水平卷积
	cv::Mat hImg;
	cv::Mat hKernel = (cv::Mat_<float>(3, 3) << -1, -1, -1, 0, 0, 0, 1, 1, 1);
	cv::filter2D(grayImage, hImg, image.depth(), hKernel);
	hCloud.reset(new pcl::PointCloud<pcl::PointXYZRGB>);
	// 遍历像素
	for (int i = 0; i < hImg.rows; i++)
	{
		for (int j = 0; j < hImg.cols; j++)
		{
			hCloud->push_back({ static_cast<float>(i),
				static_cast<float>(j),
				static_cast<float>(hImg.at<uchar>(i, j)),
				255,255,255 });
		}
	}
	// 点坐标同除255
	for (int i = 0; i < hCloud->size(); i++)
	{
		hCloud->points[i].x /= 255;
		hCloud->points[i].y /= 255;
		hCloud->points[i].z /= 255;
	}
	hViewer->updatePointCloud(hCloud, "hCloud");

	cv::Mat hsv;

	// 转为 HSV
	cv::cvtColor(image, hsv, cv::COLOR_BGR2HSV);

	cv::Mat mask;

	// HSV 筛选掩膜
	cv::inRange(hsv, cv::Scalar{ 40, 70, 46 }, cv::Scalar{ 77, 255, 255 }, mask);

	// 腐蚀
	cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
	cv::erode(mask, mask, element);

	// 与
	cv::Mat mask_and;
	cv::bitwise_and(image, image, mask_and, mask);

	// 中值模糊
	cv::medianBlur(mask_and, image, 3);

	// 转为灰度图
	cv::cvtColor(image, grayImage, cv::COLOR_BGR2GRAY);
	hsvCloud.reset(new pcl::PointCloud<pcl::PointXYZRGB>);
	// 遍历像素
	for (int i = 0; i < grayImage.rows; i++)
	{
		for (int j = 0; j < grayImage.cols; j++)
		{
			hsvCloud->push_back({ static_cast<float>(i),
				static_cast<float>(j),
				static_cast<float>(grayImage.at<uchar>(i, j)),
				255,255,255 });
		}
	}
	// 点坐标同除255
	for (int i = 0; i < hsvCloud->size(); i++)
	{
		hsvCloud->points[i].x /= 255;
		hsvCloud->points[i].y /= 255;
		hsvCloud->points[i].z /= 255;
	}
	hsvViewer->updatePointCloud(hsvCloud, "hsvCloud");
}

跪求优雅的图片转点云的方式!!!

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1.该程序用QT开发,实现图片导入、显示、缩放、拖动及处理(冷暖色、灰度、亮度、饱和、模糊、锐化)。 经实测,我写的这个软件在导入10000*7096像素的超大图片的时候,缩放的速度比2345看图软件还快,2345缩放超大图会卡顿,但本软件不会^_^ 关于程序中缩放拖动部分的说面参见我的博客https://blog.csdn.net/weixin_43935474/article/details/89327314; 2.载入图片后,鼠标移动的时候可以显示鼠所在点的图像的坐标以及灰度; 3.缩放的时候,图片右上角可以显示当前图片的缩放比例; 4.用户可导入16位深的tiff灰度图文件(一般来说是由相机拍摄的灰度图数据),导入16位深的tiff的时候,用户需要先点击界面左上角的checkbox,然后再导入tiff图片,否则图片解析不出来。 注:Qt自带的QImage只能导入8位深的tiff灰度图,如果用qt的QImage导入16位深的灰度图图像数据会被强制换成argb格式的图像,数据就被更改了,所以我自己编写一个解析tiff文件的功能,我翻阅了很多博客,其中如下链接给我的帮助最大: https://blog.csdn.net/chenlu5201314/article/details/56276903 上述博客作为详细解析tiff文件结构的说明文档,写的非常详细,我也是根据上面的内容,自己编写了一个解析tiff文件的类(当然功能很少,只能解析符合特定条件的tiff文件) //************************************************************ //by Bruce Xu //注:解析tiff的类只解析特定的tiff文件! //1.解析的tiff文件中只存在一幅图,如果文件中存在多幅图,本类不支持解析! //2.图像数据为8位或16位深度的灰度图,如果是其他类型的图片,本类不支持解析! //3.图片没有被压缩过! //************************************************************
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值