Cesium中加载点云图

一、生成的las文件

python代码:

import laspy
import numpy as np
from pymap3d import geodetic2ecef

# ===================== 你的区域坐标 =====================
coordinates = np.array([
    [118.4573848, 44.8212813],
    [118.4783848, 44.8212813],
    [118.4783848, 44.8401613],
    [118.4573848, 44.8401613]
])

min_lon, min_lat = coordinates.min(axis=0)
max_lon, max_lat = coordinates.max(axis=0)

# 点密度
step = 0.00008

# 生成网格点
lons = np.arange(min_lon, max_lon, step)
lats = np.arange(min_lat, max_lat, step)
lon_grid, lat_grid = np.meshgrid(lons, lats)
lon = lon_grid.flatten()
lat = lat_grid.flatten()

# ===================== ✅ 固定高度 = 0(完全贴地)=====================
height = np.zeros_like(lon)  # 所有点高度 = 0

# 坐标转 ECEF
x, y, z = geodetic2ecef(lat, lon, height)

# 彩色
r = ((lon - lon.min())/(lon.max()-lon.min()) * 255).astype(np.uint16)
g = ((lat - lat.min())/(lat.max()-lat.min()) * 255).astype(np.uint16)
b = np.full_like(r, 255, dtype=np.uint16)

# ===================== 生成 LAS =====================
las = laspy.create(file_version="1.2", point_format=2)
las.x = x
las.y = y
las.z = z
las.red = r
las.green = g
las.blue = b

las.header.x_scale = 0.001
las.header.y_scale = 0.001
las.header.z_scale = 0.001

las.write("ground_zero_height.las")
print(f"✅ 贴地点云生成完成(高度=0)!点数:{len(lon)}")

二、转换成3D tiles

三、Cesium中加载

<!doctype html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Cesium GeoJSON 线要素加载示例</title>
    <!-- 引入 Cesium 库(这里使用官方 CDN,你也可以替换为本地路径) -->
    <script src="https://unpkg.com/cesium@1.138.0/Build/Cesium/Cesium.js"></script>
    <link
      href="https://unpkg.com/cesium@1.138.0/Build/Cesium/Widgets/widgets.css"
      rel="stylesheet"
    />
    <style>
      html,
      body,
      #cesiumContainer {
        width: 100%;
        height: 100%;
        margin: 0;
        padding: 0;
        overflow: hidden;
      }
    </style>
  </head>

  <body>
    <div id="cesiumContainer"></div>

    <script type="module">
      // 1. 初始化 Cesium 地图(替换 MFmap 为原生 Cesium 初始化)
      Cesium.Ion.defaultAccessToken =
        "defaultAccessToken"; // 替换为你的 Token,可从 Cesium 官网获取
      const viewer = new Cesium.Viewer("cesiumContainer", {});

      // // 添加到地图
      // viewer.imageryLayers.addImageryProvider(gaodeImagery);

      // 定义异步加载函数
      async function loadLocalTileset() {
        try {
          // 拼接本地服务器URL
          // 注意:端口号需与你启动的服务器端口一致
          const url = "./3DTILES/point-cloud/tileset.json";

          // 调用最新的 fromUrl 方法
          const tileset = await Cesium.Cesium3DTileset.fromUrl(url, {
            maximumScreenSpaceError: 0.5, // 强制最高精度
            skipLevelOfDetail: true, // 强制加载所有点
            dynamicScreenSpaceError: false, // 关闭自动隐藏
            cullRequestsWhileMoving: false, // 移动时不卸载
            cullWithChildrenBounds: false,
          });

          tileset.style = new Cesium.Cesium3DTileStyle({
            pointSize: 3.0,
            color: {
              conditions: [
                // 正确读取 LAS 转 3D Tiles 后的颜色(RGB 通道)
                [true, "rgb(${R}, ${G}, ${B})"],
              ],
            },
          });

          // 添加到场景
          viewer.scene.primitives.add(tileset);

          // 调试:等待tileset加载完成后查看位置
          console.log("Tileset边界球中心:", tileset.boundingSphere.center);
          console.log("边界球半径:", tileset.boundingSphere.radius);

          // 尝试创建一个红色标记点来辅助定位
          const centerPoint = viewer.entities.add({
            position: tileset.boundingSphere.center,
            point: {
              color: Cesium.Color.RED,
              pixelSize: 10,
            },
          });

          viewer.flyTo(tileset, {
            duration: 1.5,
            offset: new Cesium.HeadingPitchRange(
              Cesium.Math.toRadians(0),
              Cesium.Math.toRadians(-90), // 垂直向下
              30, // 30米高度
            ),
          });

          // 尝试将相机移动到tileset位置
          const workScope = [
            [118.4573848, 44.8212813],
            [118.4783848, 44.8212813],
            [118.4783848, 44.8401613],
            [118.4573848, 44.8401613],
          ];
          const { center, cameraHeight } = await getCenterAndCameraHeight(
            viewer,
            Cesium,
            workScope,
          );
          // flyToWorkScope(center[0], center[1], cameraHeight);
          // limitDisplayLevels(cameraHeight);
        } catch (error) {
          console.error("加载失败:", error);
        }
      }

      loadLocalTileset();

      function getCenterAndCameraHeight(viewer, Cesium, points) {
        // 参数验证
        if (!viewer) {
          throw new Error("[getCenterAndCameraHeight] viewer参数不能为空");
        }

        if (!Cesium) {
          throw new Error("[getCenterAndCameraHeight] Cesium参数不能为空");
        }

        if (!Array.isArray(points) || points.length === 0) {
          throw new Error("[getCenterAndCameraHeight] points必须是非空数组");
        }

        // 验证每个坐标点
        for (let i = 0; i < points.length; i++) {
          const point = points[i];
          if (!Array.isArray(point) || point.length !== 2) {
            throw new Error(
              `[getCenterAndCameraHeight] 第${i}个坐标点格式错误,应为[经度, 纬度]`,
            );
          }

          const [lng, lat] = point;
          if (
            typeof lng !== "number" ||
            isNaN(lng) ||
            lng < -180 ||
            lng > 180
          ) {
            throw new Error(
              `[getCenterAndCameraHeight] 第${i}个坐标点经度无效: ${lng}`,
            );
          }
          if (typeof lat !== "number" || isNaN(lat) || lat < -90 || lat > 90) {
            throw new Error(
              `[getCenterAndCameraHeight] 第${i}个坐标点纬度无效: ${lat}`,
            );
          }
        }

        try {
          // 提取经纬度范围
          const lngs = points.map((p) => p[0]);
          const lats = points.map((p) => p[1]);

          const minLng = Math.min(...lngs);
          const minLat = Math.min(...lats);
          const maxLng = Math.max(...lngs);
          const maxLat = Math.max(...lats);

          // 构建矩形范围
          const rectangle = Cesium.Rectangle.fromDegrees(
            minLng,
            minLat,
            maxLng,
            maxLat,
          );

          // 验证矩形有效性
          if (rectangle.width <= 0 || rectangle.height <= 0) {
            throw new Error(
              `[getCenterAndCameraHeight] 矩形范围无效: 宽度=${rectangle.width}, 高度=${rectangle.height}`,
            );
          }

          // 计算几何中心点
          const centerCartographic = Cesium.Rectangle.center(rectangle);
          const center = [
            Cesium.Math.toDegrees(centerCartographic.longitude),
            Cesium.Math.toDegrees(centerCartographic.latitude),
          ];

          // 计算最佳相机高度
          // 检查方法是否存在(兼容不同Cesium版本)
          if (!viewer.camera.getRectangleCameraCoordinates) {
            // 降级方案:使用默认高度计算
            console.warn(
              "[getCenterAndCameraHeight] getRectangleCameraCoordinates方法不存在,使用降级方案",
            );
            const diagonalDistance = calculateDiagonalDistance(
              minLng,
              minLat,
              maxLng,
              maxLat,
            );
            const cameraHeight =
              diagonalDistance / Math.tan(Cesium.Math.toRadians(30)); // 默认30度视角
            return { center, cameraHeight };
          }

          const cameraPosition =
            viewer.camera.getRectangleCameraCoordinates(rectangle);

          if (!cameraPosition) {
            throw new Error("[getCenterAndCameraHeight] 无法计算相机位置坐标");
          }

          const cartographic =
            Cesium.Cartographic.fromCartesian(cameraPosition);
          const cameraHeight = cartographic.height;

          // 验证计算结果
          if (
            typeof cameraHeight !== "number" ||
            isNaN(cameraHeight) ||
            cameraHeight <= 0
          ) {
            throw new Error(
              `[getCenterAndCameraHeight] 相机高度计算结果无效: ${cameraHeight}`,
            );
          }

          return { center, cameraHeight };
        } catch (error) {
          const errorMsg =
            error instanceof Error ? error.message : String(error);
          throw new Error(`[getCenterAndCameraHeight] 计算失败: ${errorMsg}`);
        }
      }

      function flyToWorkScope(lon, lat, height) {
        if (!viewer || !Cesium) {
          throw new Error(`[flyToWorkScope] Cesium/Viewer 未初始化`);
        }

        if (typeof lon !== "number" || isNaN(lon) || lon < -180 || lon > 180) {
          throw new Error(
            `[flyToWorkScope] 经度无效: ${lon},应为 -180 到 180 之间的数字`,
          );
        }
        if (typeof lat !== "number" || isNaN(lat) || lat < -90 || lat > 90) {
          throw new Error(
            `[flyToWorkScope] 纬度无效: ${lat},应为 -90 到 90 之间的数字`,
          );
        }
        if (typeof height !== "number" || isNaN(height)) {
          throw new Error(`[flyToWorkScope] height无效: ${height},应为数字`);
        }

        viewer.camera.flyTo({
          destination: Cesium.Cartesian3.fromDegrees(lon, lat, height),
          orientation: {
            heading: 0,
            pitch: Cesium.Math.toRadians(-90), // 正俯视
            roll: 0,
          },
          duration: 1,
        });
      }

      function limitDisplayLevels(cameraHeight) {
        if (!viewer || !Cesium) {
          throw new Error(`[limitDisplayLevels] Cesium/Viewer 未初始化`);
        }
        if (typeof cameraHeight !== "number" || isNaN(cameraHeight)) {
          throw new Error(
            `[limitDisplayLevels] cameraHeight无效: ${cameraHeight},应为数字`,
          );
        }

        const TARGET_MAX_LEVEL = 18;
        const levelToDistance = (level) => {
          const earthRadius = 6378137;
          return (earthRadius * 2 * Math.PI) / Math.pow(2, level);
        };
        const maxDistance = levelToDistance(TARGET_MAX_LEVEL);

        const controller = viewer.scene.screenSpaceCameraController;
        controller.inertiaScrollEnabled = false;
        controller.inertiaZoomEnabled = false;
        controller.minimumZoomDistance = maxDistance + 1;
        controller.maximumZoomDistance = cameraHeight + 1000000;
      }
    </script>
  </body>
</html>

内容概要:本文介绍了基于改进Retinex算法的视频图像增强技术研究,并提供了相应的Matlab代码实现。Retinex理论源于人类视觉系统对光照变化的适应性,通过分离图像的照度与反射分量,有效提升图像的亮度、对比度和色彩保真度。文中所提出的改进算法旨在克服传统Retinex方法中存在的光晕伪影、噪声放大和计算复杂等问题,可能引入了如多尺度分解、颜色校正或自适应滤波等优化策略,从而实现更自然、清晰的图像增强效果。该研究特别适用于低光照、雾霾、水下拍摄等恶劣成像条件下的视频与图像处理,提升后续视觉分析的准确性。; 适合人群:具备一定图像处理基础和Matlab编程经验的科研人员、研究生及工程技术人员,尤其是从事计算机视觉、视频监控、遥感影像、医学影像或无人机视觉导航等领域研究的专业人士。; 使用场景及目标:① 解决实际应用中因光照不足或环境干扰导致的图像质量下降问题;② 学习和掌握Retinex算法的核心思想及其改进方法;③ 获取可直接运行和调试的Matlab代码,作为相关课题研究或项目开发的技术参考。; 阅读建议:此资源以Matlab代码实现为核心,建议读者在阅读时结合代码逐行分析,理解算法的每一步实现细节。同时,应尝试使用不同的测试图像进行实验,调整算法参数,观察增强效果的变化,从而深入理解算法的性能特点和优化方向。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值