论文复现-一种基于室内点云数据的建筑墙线提取

研究意义

针对室内点云数据无结构化属性、数据间无连接、不承载语义信息且数据点密度高的特点,结合建筑物点云几何特征室内导航需求,通过数据降维简化建筑几何特征提取的复杂性,提出一种基于室内点云数据提取建筑物墙线的方法

算法思路

  • 首先通过向特定方向投影,利用点云密度直方图完成天花板面、地板面和房间墙面的初步分割;
  • 然后将房间墙面点云数据向地面投影,生成点云分布矩阵并将其转化为二值图,利用Hough变换算法提取直线,并利用直线方程求取交点得到备选墙线;
  • 最后将备选墙线和墙线点云二值图进行叠加从而获取最终建筑墙线。

实验步骤

编程语言?不要纠结,认清一点,编程语言只是工具,重要的的算法的逻辑思路,什么语言方便用什么语言。matlab虽然算法编程简单,矩阵运算方便,但是再人工编写的循环中不如C++Eigen。

本人编程语言:

  • matlab:主要负责显示图像和简单运算
  • C++:主要负责算法实现Eigen。

分割出墙

1、使用matlab尝试向坐标轴投影生成点云密度直方图。

  • 代码
clc,clear
%%%将点云向坐标轴z投影生成点云密度直方图。
home=importdata('hallway_2.txt');%读取txt点云数据
home_z=home(:,3);%获取点云z轴数据,即向z轴投影
h=histogram(home_z);%显示密度直方图
h.BinWidth=0.02;%设置尺度后再画图,也可以默认

% 总结:
% hallway_2.txt 提取墙面范围[0.02,2.56]
% 
  • 实验结果
    在这里插入图片描述

2、使用C++对点云直通滤波,提取墙的那部分:

  • 关键代码
	pcl::PassThrough<pcl::PointXYZ> pass;// 创建滤波器对象
	pass.setInputCloud(cloud);
	pass.setFilterFieldName("z"); //滤波字段名被设置为Z轴方向
	pass.setFilterLimits(0.02, 2.56); //设置在过滤方向上的过滤范围
	pass.setNegative(false); //设置保留范围内的还是过滤掉范围内的,标志为false时保留范围内的点
	pass.filter(*cloud_filtered);
  • 实验结果
    在这里插入图片描述

霍夫变换找线

1、墙点云向地面投影最终生成二值图
//获取点云中的最值数据
最小值:x_min=-13.084 y_min=4.321 z_min=0.02
最大值:x_max=-6.018 y_max=5.956 z_max=2.56
//设置像素为0.02m计算矩阵的行列数
矩阵大小:91x363
//开始投影,计算每个像素内的点云数,生成点云分布矩阵

  • 代码
	cout << "/*---------------------------------------------------------------*/" << endl;
	cout << "/*开始向地面投影生成点云分布矩阵*/" << endl;
	//获取点云中的最值数据
	PointT minPt, maxPt;//把最大/最小用点来存
	pcl::getMinMax3D(*cloud, minPt, maxPt);
	cout << "最小值:x_min=" << minPt.x << " y_min=" << minPt.y << " z_min=" << minPt.z << endl;
	cout << "最大值:x_max=" << maxPt.x << " y_max=" << maxPt.y << " z_max=" << maxPt.z << endl;
	//设置像素为0.02m计算矩阵的行列数
	float pix_size = 0.02;
	int row = (maxPt.y+0.1 - (minPt.y-0.1)) / pix_size;//获得行数 宽松1dm
	int col = (maxPt.x + 0.1 - (minPt.x - 0.1)) / pix_size;//获得列数 宽松1dm
	cout << "矩阵大小:" << row << "x" << col << endl;
	//开始投影,计算每个像素内的点云数,生成点云分布矩阵
	Eigen::MatrixXf ground_matrix=Eigen::MatrixXf::Zero(row, col);//创建矩阵
	int row_n;//行下标
	int col_n;//列下标
	for (int i = 0; i < cloud->size(); i++)//遍历所有点,统计每个像素的点云数
	{
		//仅仅统计当前点x,y在哪个像素
		row_n = (cloud->points[i].y - (minPt.y - 0.1)) / pix_size;
		col_n = (cloud->points[i].x - (minPt.x - 0.1)) / pix_size;
		//对应像素+1
		ground_matrix(row_n, col_n) += 1;
	}
	//点云分布矩阵灰度化
	ground_matrix = 255 * ground_matrix / ground_matrix.maxCoeff();
	//灰度矩阵二值化
	float bin_threshold = 10;
	for (int i = 0; i < ground_matrix.rows(); i++)
	{
		for (int j = 0; j < ground_matrix.cols(); j++)
		{
			if (ground_matrix(i, j) > bin_threshold)	ground_matrix(i, j) = 1;
			else										ground_matrix(i, j) = 0;
		}
	}
	//将矩阵写入ground_matrix.txt
	ofstream outfile;
	outfile.open("ground_matrix.txt",ios::app);
	outfile << ground_matrix << endl;
	outfile.close();
	cout << "成功将矩阵写入文件" << endl;
  • 实验结果
    转为灰度图:
    在这里插入图片描述

转为二值图:
在这里插入图片描述
2、二值图进行霍夫变换找到备选墙线

  • matlab代码
BW  = importdata('ground_matrix_bin.txt');
[H,T,R] = hough(BW);%霍夫变换 H:霍夫矩阵,计票箱 T:theta R:rho
imshow(H,[],'XData',T,'YData',R,...
            'InitialMagnification','fit');%显示霍夫矩阵
xlabel('\theta'), ylabel('\rho');
axis on, axis normal, hold on;
P  = houghpeaks(H,10,'threshold',ceil(0.1*max(H(:))));%统计霍夫矩阵峰值 最大峰值数 阈值
x = T(P(:,2)); y = R(P(:,1));
plot(x,y,'s','color','white');%显示找到的峰值
% Find lines and plot them
lines = houghlines(BW,T,R,P,'FillGap',10,'MinLength',5);%遍历所有点的(tho,theta)找到所有的线段 FillGap:合并间距 MinLength:最短长度
figure, imshow(BW), hold on
max_len = 0;
for k = 1:length(lines)
   xy = [lines(k).point1; lines(k).point2];
   plot(xy(:,1),xy(:,2),'LineWidth',2,'Color','green');

   % Plot beginnings and ends of lines
   plot(xy(1,1),xy(1,2),'x','LineWidth',2,'Color','yellow');
   plot(xy(2,1),xy(2,2),'x','LineWidth',2,'Color','red');

   % Determine the endpoints of the longest line segment
   len = norm(lines(k).point1 - lines(k).point2);
   if ( len > max_len)%获得最大长度 对应端点
      max_len = len;
      xy_long = xy;
   end
end

% highlight the longest line segment
plot(xy_long(:,1),xy_long(:,2),'LineWidth',2,'Color','blue');
  • 实验结果
    标记的峰值
    在这里插入图片描述
    粗略地墙线,懒得写程序画直线了,画直线更简单,直接根据(rho,theta)的峰值,就确定了每条直线。
    在这里插入图片描述

叠加最终墙线

就是把二值图和备选墙线叠加,比较出来最终墙线。

总结与展望

<think>好的,我现在需要帮助用户实现线线流动效果的Shader,用户提到了Unity或OpenGL。首先,我得理解用户的具体需求。线线流动效果,通常指的是在墙面或管道上模拟液体或能量流动的视觉效果,可能包括线条的渐变、颜色变化和动态移动。接下来,我需要考虑用户可能使用的平台,Unity还是OpenGL,不过用户给了两者选项,可能需要分别说明或选择一个更常见的。考虑到Unity在游戏开发中更普及,可能先以Unity为例,但也要兼顾OpenGL的实现思路。 首先,我应该回忆一下Shader编程的基本结构,特别是在Unity中的表面着色器(Surface Shader)和片段着色器(Fragment Shader)。流动效果通常需要处理UV坐标的偏移,可能使用时间变量来驱动动画。例如,通过修改UV的某个分量,加上时间变量,可以实现纹理的移动效果。另外,颜色渐变可能需要使用渐变纹理或数学函数来混合颜色。 然后,考虑管线效果可能需要沿着特定路径流动,比如沿着管道的走向。这时候可能需要使用遮罩纹理(Mask Texture)来限制流动的区域,确保效果只在线或管道的部分显示。用户提到的Unsharp Mask Texture引用可能与此相关,可以用来增强边缘或细节,但需要确认其具体应用方式。 接下来,我应该规划实现步骤。在Unity中,可以创建一个Unlit Shader,使用顶点和片段着色器。首先定义属性,比如主纹理、流动颜色、速度参数等。然后在顶点着色器中计算UV坐标,并将其传递给片段着色器。在片段着色器中,根据时间偏移UV,结合遮罩纹理来限制流动区域,最后混合颜色输出。 对于OpenGL的实现,流程类似,但需要手动编写顶点和片段着色器代码,并在应用程序中传递时间等uniform变量。需要注意OpenGL的版本差异,比如GLSL的语法可能有所不同。 可能遇到的问题包括UV流动的自然程度,颜色过渡的平滑性,以及性能优化。需要确保时间参数正确传递,避免动画卡顿。另外,遮罩纹理的创建也需要用户根据具体模型来绘制,可能需要美术资源的配合。 用户可能还希望有更复杂的效果,比如流动的扭曲效果、多层次的流动叠加,或者交互式的流动响应。这些可以作为进阶优化的方向,但当前需要先实现基础版本。 最后,需要验证代码的正确性,可能通过Unity中的材质球预览,或在OpenGL中设置测试场景。如果用户遇到问题,可能需要调试UV计算、遮罩应用或颜色混合的部分。</think>以下是关于线线流动效果的Shader实现方案,以Unity ShaderLab为例说明实现步骤: ### 一、Unity Shader基础实现 ```glsl Shader "Custom/PipelineFlow" { Properties { _MainTex ("Base Texture", 2D) = "white" {} _FlowMap ("Flow Map (R=Mask)", 2D) = "white" {} _FlowColor ("Flow Color", Color) = (1,0.5,0,1) _Speed ("Flow Speed", Range(0,5)) = 1 _Width ("Flow Width", Range(0,1)) = 0.2 } SubShader { Tags {"Queue"="Transparent" "RenderType"="Transparent"} CGPROGRAM #pragma surface surf Lambert alpha:fade struct Input { float2 uv_MainTex; float2 uv_FlowMap; }; sampler2D _MainTex; sampler2D _FlowMap; fixed4 _FlowColor; float _Speed; float _Width; void surf (Input IN, inout SurfaceOutput o) { // 基础纹理采样 fixed4 base = tex2D(_MainTex, IN.uv_MainTex); // 流动遮罩采样 float mask = tex2D(_FlowMap, IN.uv_FlowMap).r; // 动态偏移计算 float flow = sin(_Time.y * _Speed + IN.uv_FlowMap.y * 10) * 0.5 + 0.5; float flowMask = smoothstep(0.5-_Width, 0.5+_Width, flow) * mask; // 颜色混合 o.Albedo = base.rgb; o.Emission = _FlowColor.rgb * flowMask; o.Alpha = base.a + flowMask; } ENDCG } FallBack "Diffuse" } ``` ### 二、核心实现原理 1. **流动动画**:通过`_Time.y`变量驱动三角函数波动,产生周期性运动效果。偏移计算使用公式: $$flow = \frac{\sin(\omega t + \phi)}{2} + \frac{1}{2}$$ 其中$\omega$对应_Speed参数,$\phi$由UV坐标决定[^1] 2. **遮罩控制**:使用FlowMap的红色通道限定流动区域,smoothstep函数控制流动线宽度: $$mask_{final} = \text{smoothstep}(0.5-w, 0.5+w, flow) \times mask$$ 3. **颜色叠加**:通过Emission通道实现自发光效果,使流动线在暗处保持可见 ### 三、OpenGL实现要点 ```glsl // 顶点着色器 uniform float u_time; varying vec2 v_texcoord; void main() { v_texcoord = uv; gl_Position = projection * view * model * vec4(position, 1.0); } // 片段着色器 uniform sampler2D u_flowMap; uniform vec3 u_flowColor; uniform float u_speed; void main() { float mask = texture2D(u_flowMap, v_texcoord).r; float flow = sin(u_time * u_speed + v_texcoord.y * 10.0) * 0.5 + 0.5; float alpha = smoothstep(0.4, 0.6, flow) * mask; gl_FragColor = vec4(u_flowColor * alpha, alpha); } ``` ### 四、效果优化建议 1. **多重流动层**:叠加多个不同速度的流动层 ```glsl float flow1 = sin(_Time.y * _Speed)... float flow2 = sin(_Time.y * _Speed*1.2 + 3.14)... ``` 2. **扭曲效果**:添加法线贴图扰动 ```glsl float2 distort = tex2D(_NormalMap, uv).rg * 0.1; float flow = sin(_Time.y * _Speed + (uv.y + distort.y) * 10); ``` 3. **渐变色控制**:使用梯度纹理代替单色 ```glsl float gradientPos = flow * mask; vec3 color = texture2D(_RampTex, vec2(gradientPos, 0.5)).rgb; ``` ### 五、参数调试建议 1. 流动速度_Speed建议范围:0.5-3.0 2. 线宽_Width建议值:0.1-0.3 3. FlowMap制作要点: - 白色区域允许流动 - 黑色区域禁止流动 - 可使用Photoshop手绘或程序生成
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值