260705_2
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
"""B04 LAS/LAZ 고속 구조화 엔진."""
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
|
||||
import laspy
|
||||
import numpy as np
|
||||
|
||||
from config.config_system import SURFACE_DEFAULT_RGB_VALUE, SURFACE_LAS_CHUNK_SIZE
|
||||
|
||||
|
||||
def structurize_las(
|
||||
las_path: str | Path,
|
||||
output_dir: str | Path,
|
||||
progress_callback: Callable[[int], None] | None = None,
|
||||
) -> Path:
|
||||
"""LAS/LAZ 속성을 청크로 읽어 B04 structured.npz로 원자적 저장한다."""
|
||||
source = Path(las_path)
|
||||
target_dir = Path(output_dir)
|
||||
target_dir.mkdir(parents=True, exist_ok=True)
|
||||
target = target_dir / "structured.npz"
|
||||
|
||||
with laspy.open(source) as las_file:
|
||||
header = las_file.header
|
||||
total_points = int(header.point_count)
|
||||
point_format = header.point_format
|
||||
dimensions = set(point_format.dimension_names)
|
||||
has_rgb = {"red", "green", "blue"}.issubset(dimensions)
|
||||
has_intensity = "intensity" in dimensions
|
||||
has_returns = {"return_number", "number_of_returns"}.issubset(dimensions)
|
||||
has_classification = "classification" in dimensions
|
||||
bounds = np.array(
|
||||
[
|
||||
[float(header.mins[0]), float(header.maxs[0])],
|
||||
[float(header.mins[1]), float(header.maxs[1])],
|
||||
[float(header.mins[2]), float(header.maxs[2])],
|
||||
],
|
||||
dtype=np.float64,
|
||||
)
|
||||
|
||||
xyz = np.empty((total_points, 3), dtype=np.float64)
|
||||
intensity = np.zeros(total_points, dtype=np.uint16)
|
||||
rgb = np.full((total_points, 3), SURFACE_DEFAULT_RGB_VALUE, dtype=np.uint8)
|
||||
return_number = np.ones(total_points, dtype=np.uint8)
|
||||
number_of_returns = np.ones(total_points, dtype=np.uint8)
|
||||
classification = np.zeros(total_points, dtype=np.uint8)
|
||||
|
||||
offset = 0
|
||||
for chunk in las_file.chunk_iterator(SURFACE_LAS_CHUNK_SIZE):
|
||||
chunk_size = len(chunk)
|
||||
section = slice(offset, offset + chunk_size)
|
||||
xyz[section, 0] = np.asarray(chunk.x, dtype=np.float64)
|
||||
xyz[section, 1] = np.asarray(chunk.y, dtype=np.float64)
|
||||
xyz[section, 2] = np.asarray(chunk.z, dtype=np.float64)
|
||||
if has_intensity:
|
||||
intensity[section] = np.asarray(chunk.intensity, dtype=np.uint16)
|
||||
if has_rgb:
|
||||
colors = np.stack(
|
||||
[
|
||||
np.asarray(chunk.red, dtype=np.float64),
|
||||
np.asarray(chunk.green, dtype=np.float64),
|
||||
np.asarray(chunk.blue, dtype=np.float64),
|
||||
],
|
||||
axis=1,
|
||||
)
|
||||
if colors.size and float(colors.max()) > 255.0:
|
||||
colors /= 256.0
|
||||
rgb[section] = colors.clip(0, 255).astype(np.uint8)
|
||||
if has_returns:
|
||||
return_number[section] = np.asarray(chunk.return_number, dtype=np.uint8)
|
||||
number_of_returns[section] = np.asarray(chunk.number_of_returns, dtype=np.uint8)
|
||||
if has_classification:
|
||||
classification[section] = np.asarray(chunk.classification, dtype=np.uint8)
|
||||
offset += chunk_size
|
||||
if progress_callback:
|
||||
progress_callback(int(offset / total_points * 100) if total_points else 100)
|
||||
|
||||
temporary_path: Path | None = None
|
||||
try:
|
||||
with tempfile.NamedTemporaryFile(
|
||||
mode="wb",
|
||||
dir=target_dir,
|
||||
prefix=".structured.",
|
||||
suffix=".npz.tmp",
|
||||
delete=False,
|
||||
) as temporary:
|
||||
temporary_path = Path(temporary.name)
|
||||
np.savez_compressed(
|
||||
temporary,
|
||||
xyz=xyz,
|
||||
intensity=intensity,
|
||||
rgb=rgb,
|
||||
return_number=return_number,
|
||||
number_of_returns=number_of_returns,
|
||||
classification=classification,
|
||||
bounds=bounds,
|
||||
total_points=np.array([total_points], dtype=np.int64),
|
||||
has_rgb=np.array([int(has_rgb)], dtype=np.int8),
|
||||
)
|
||||
temporary.flush()
|
||||
os.fsync(temporary.fileno())
|
||||
os.replace(temporary_path, target)
|
||||
temporary_path = None
|
||||
finally:
|
||||
if temporary_path is not None:
|
||||
temporary_path.unlink(missing_ok=True)
|
||||
|
||||
if progress_callback and total_points == 0:
|
||||
progress_callback(100)
|
||||
return target
|
||||
Reference in New Issue
Block a user