public byte[] getPntsBytes(List<PointModel> pointList, List<PointAttributeTransferModel> attributeTransferList) { XYZModel offsetModel = calculateQuantizedVolumeOffset(pointList); XYZModel maxOffset = calculateQuantizedVolumeOffsetMax(pointList); XYZModel scaleModel = calculateQuantizedVolumeScale(offsetModel, maxOffset); //组装头文件 String magic = "pnts"; int version = 1; int byteLength = 0; int featureTableJSONByteLength = 0; int featureTableBinaryByteLength = 0; int batchTableJSONByteLength = 0; int batchTableBinaryByteLength = 0; //体文件 //量化后坐标 List<PointModel> quantizedList = calculatePositionQuantized(pointList, offsetModel, scaleModel); //坐标 byte[] positionBinary = new byte[0]; for (PointModel xyzModel : quantizedList) { positionBinary = byteMergerByList(positionBinary, short2byte_Little((short) xyzModel.x), short2byte_Little((short) xyzModel.y), short2byte_Little((short) xyzModel.z)); } positionBinary = fillBytesWithZero(positionBinary, 8); //颜色 byte[] colorByteRGB = {(byte) 0, (byte) 0, (byte) 0}; byte[] colorBinary = new byte[0]; for (PointModel xyzModel : pointList) { colorBinary = byteMergerByList(colorBinary, colorByteRGB); } //要素表 FeatureTable featureTable = new FeatureTable(); featureTable.POINTS_LENGTH = pointList.size(); featureTable.POSITION_QUANTIZED = new OffsetModel(); featureTable.POSITION_QUANTIZED.byteOffset = 0; //偏移 if (!ObjectUtils.isEmpty(offsetModel)) { featureTable.QUANTIZED_VOLUME_OFFSET = offsetModel.toMatrixArray(); } //缩放 if (!ObjectUtils.isEmpty(scaleModel)) { featureTable.QUANTIZED_VOLUME_SCALE = scaleModel.toMatrixArray(); } //RGB featureTable.RGB = new OffsetModel(); featureTable.RGB.byteOffset = positionBinary.length; String featureTableJson = JSON.toJSONString(featureTable); byte[] featureTableJsonBytes = featureTableJson.getBytes(); featureTableJsonBytes = fillBytesWithEmpty(featureTableJsonBytes); featureTableJSONByteLength = featureTableJsonBytes.length; //批量表 BatchTable batchTable = transferBatchTableItem(pointList, attributeTransferList); byte[] batchTableBinary = new byte[0]; int byteOffsetIndex = 0; //分布式处理+ForkJoin并行处理生成pnts List<PNTSProcessModel> processList = new PNTSProcess().process(batchTable.items); for (BatchTableItem batchItem : batchTable.items) { PNTSProcessModel pntsProcessModel = processList.stream().filter(it -> it.batchTableItem.name.equals(batchItem.name)).findFirst().orElse(null); if (!ObjectUtils.isEmpty(pntsProcessModel)) { batchTableBinary = byteMergerByList(batchTableBinary, pntsProcessModel.batchTableBinary); batchItem.batchTableBaseItem.byteOffset = byteOffsetIndex; byteOffsetIndex += pntsProcessModel.byteOffsetIndex; } } String batchTableJSONString = batchTable.toBatchTableJSONString(); byte[] batchTableJSONBytes = batchTableJSONString.getBytes(); batchTableJSONBytes = fillBytesWithEmpty(batchTableJSONBytes); batchTableJSONByteLength = batchTableJSONBytes.length; //batchTableBinary batchTableBinary = fillBytesWithZero(batchTableBinary, 8); batchTableBinaryByteLength = batchTableBinary.length; //计算填充量 int fillNum = 0; int i = 28 + positionBinary.length + colorBinary.length; if (i % 8 != 0) { fillNum = 8 - i % 8; } if (fillNum != 0) { colorBinary = fillInputBytesWithZero(colorBinary, fillNum); } //合并 byte[] featureTableBinary = byteMergerByList(positionBinary, colorBinary); featureTableBinaryByteLength = featureTableBinary.length; byteLength = 28 + featureTableJSONByteLength + featureTableBinaryByteLength + batchTableJSONByteLength + batchTableBinaryByteLength; //组装pnts的二级制文件 byte[] bytes1 = magic.getBytes(); byte[] bytes2 = int32ToLHByte(version); byte[] bytes3 = int32ToLHByte(byteLength); byte[] bytes4 = int32ToLHByte(featureTableJSONByteLength); byte[] bytes5 = int32ToLHByte(featureTableBinaryByteLength); byte[] bytes6 = int32ToLHByte(batchTableJSONByteLength); byte[] bytes7 = int32ToLHByte(batchTableBinaryByteLength); byte[] bytes8 = featureTableJsonBytes; byte[] bytes9 = featureTableBinary; byte[] bytes10 = batchTableJSONBytes; byte[] bytes11 = batchTableBinary; //合并bytes byte[] pntsBinaryBytes = byteMergerByList(bytes1, bytes2, bytes3, bytes4, bytes5, bytes6, bytes7, bytes8, bytes9, bytes10, bytes11); return pntsBinaryBytes; } public class PointModel { public double x; public double y; public double z; public double value; } public class PointAttributeTransferModel { /** * input文件对应输入字段名称 */ public String inputKey; /** * PointModel对应字段 */ public Field field; /** * Pnts输出字段名称 */ public String outputKey; public PointAttributeTransferModel(String outputKey, Field field, String inputKey){ this.outputKey = outputKey; this.field = field; this.inputKey = inputKey; } } public static XYZModel calculateQuantizedVolumeOffset(List<PointModel> points) { double minX = Double.MAX_VALUE; double minY = Double.MAX_VALUE; double minZ = Double.MAX_VALUE; // Find minimum values for each dimension for (PointModel point : points) { minX = Math.min(minX, point.x); minY = Math.min(minY, point.y); minZ = Math.min(minZ, point.z); } // Calculate quantized volume offset XYZModel offsetModel = new XYZModel(minX, minY, minZ); return offsetModel; } public static XYZModel calculateQuantizedVolumeOffsetMax(List<PointModel> points) { double maxX = -Double.MAX_VALUE; double maxY = -Double.MAX_VALUE; double maxZ = -Double.MAX_VALUE; // Find minimum values for each dimension for (PointModel point : points) { maxX = Math.max(maxX, point.x); maxY = Math.max(maxY, point.y); maxZ = Math.max(maxZ, point.z); } // Calculate quantized volume offset XYZModel offsetModel = new XYZModel(maxX, maxY, maxZ); return offsetModel; } public static XYZModel calculateQuantizedVolumeScale(XYZModel quantizedVolumeOffset, XYZModel maxOffset) { double[] range = {maxOffset.x - quantizedVolumeOffset.x, maxOffset.y - quantizedVolumeOffset.y, maxOffset.z - quantizedVolumeOffset.z}; double offsetQuantizedX = range[0] > 1 ? range[0] / 1 : 1; double offsetQuantizedY = range[1] > 1 ? range[1] / 1 : 1; double offsetQuantizedZ = range[2] > 1 ? range[2] / 1 : 1; // Calculate quantized volume scale XYZModel scaleModel = new XYZModel(offsetQuantizedX, offsetQuantizedY, offsetQuantizedZ); return scaleModel; } public List<PointModel> calculatePositionQuantized(List<PointModel> points, XYZModel quantizedVolumeOffset, XYZModel quantizedScaleModel) { List<PointModel> positionQuantizedList = new ArrayList<>(); for (PointModel point : points) { int xQuantized = (int) Math.floor(((point.x - quantizedVolumeOffset.x) / quantizedScaleModel.x * 65535)); int yQuantized = (int) Math.floor(((point.y - quantizedVolumeOffset.y) / quantizedScaleModel.y * 65535)); int zQuantized = (int) Math.floor(((point.z - quantizedVolumeOffset.z) / quantizedScaleModel.z * 65535)); //short xQuantized = (short) ((point.x - quantizedVolumeOffset.x) / quantizedScaleModel.x * 65535); //short yQuantized = (short) ((point.y - quantizedVolumeOffset.y) / quantizedScaleModel.y * 65535); //short zQuantized = (short) ((point.z - quantizedVolumeOffset.z) / quantizedScaleModel.z * 65535); PointModel positionQuantized = new PointModel(); positionQuantized.x = xQuantized; positionQuantized.y = yQuantized; positionQuantized.z = zQuantized; positionQuantizedList.add(positionQuantized); } return positionQuantizedList; } public static byte[] byteMergerByList(byte[]... bytes) { int totalLength = 0; for (byte[] array : bytes) { totalLength += array.length; } ByteBuffer buffer = ByteBuffer.allocate(totalLength); for (byte[] array : bytes) { buffer.put(array); } return buffer.array(); } public static byte[] fillBytesWithZero(byte[] featureTableBinary, int fillBoundary) { int length = featureTableBinary.length; int remain = length % fillBoundary; Short s = 0; if (remain != 0) { int fillNum = fillBoundary - remain; for (int i = 0; i < fillNum; i++) { featureTableBinary = byteMergerByList(featureTableBinary, int8ToLHByte(s)); } } return featureTableBinary; } public static byte[] fillInputBytesWithZero(byte[] featureTableBinary, int fillNum) { Short s = 0; for (int i = 0; i < fillNum; i++) { featureTableBinary = byteMergerByList(featureTableBinary, int8ToLHByte(s)); } return featureTableBinary; } public static byte[] int8ToLHByte(int n) { byte[] b = new byte[1]; b[0] = (byte) (n & 0xff); return b; } public class FeatureTable { public int POINTS_LENGTH; public OffsetModel POSITION_QUANTIZED; public List<Double> QUANTIZED_VOLUME_OFFSET; public List<Double> QUANTIZED_VOLUME_SCALE; public OffsetModel RGB; } public static byte[] fillBytesWithEmpty(byte[] featureTableJsonBytes) { int length = featureTableJsonBytes.length; int remain = length % 8; String emptyStr = " "; if (remain != 0) { int fillNum = 8 - remain; for (int i = 0; i < fillNum; i++) { featureTableJsonBytes = byteMergerByList(featureTableJsonBytes, emptyStr.getBytes()); } } return featureTableJsonBytes; } public BatchTable transferBatchTableItem(List<PointModel> pointList, List<PointAttributeTransferModel> attributeTransferList) { BatchTable batchTable = new BatchTable(); batchTable.items = new ArrayList<>(); List<FiledNameAndType> pointModelFiledNames = getFiledName(new PointModel()); for (PointAttributeTransferModel pointAttributeTransferModel : attributeTransferList) { String enumKey = pointAttributeTransferModel.field.getName().toLowerCase(); List<FiledNameAndType> collect = pointModelFiledNames.stream().filter(it -> it.filedName.equals(enumKey)).collect(Collectors.toList()); BatchTableItem batchTableItem = new BatchTableItem(); batchTableItem.batchTableBaseItem = new BatchTableBaseItem(); batchTableItem.batchTableBaseItem.byteOffset = 0; batchTableItem.batchTableBaseItem.componentType = ObjectUtils.isEmpty(collect) ? "DOUBLE" : collect.get(0).filedType.toUpperCase(); batchTableItem.batchTableBaseItem.type = "SCALAR"; batchTableItem.name = pointAttributeTransferModel.outputKey; List<Object> valueList = extractColumn(pointList, "enumKey"); batchTableItem.dataList = valueList; batchTable.items.add(batchTableItem); } return batchTable; } public static List<FiledNameAndType> getFiledName(Object o) { List<FiledNameAndType> retList = new ArrayList<>(); Field[] fields = o.getClass().getDeclaredFields(); for (int i = 0; i < fields.length; i++) { FiledNameAndType filedNameAndType = new FiledNameAndType(); String filedName = fields[i].getName(); String filedType = fields[i].getType().toString(); filedNameAndType.filedName = filedName; filedNameAndType.filedType = filedType; retList.add(filedNameAndType); } return retList; } public static <T> List<T> extractColumn(List<PointModel> pointList, String fieldName) { return pointList.stream() .map(point -> { try { return (T) PointModel.class.getField(fieldName).get(point); } catch (Exception e) { e.printStackTrace(); return null; } }) .collect(Collectors.toList()); }
点云数据转pnts二进制数据
于 2023-10-19 11:49:13 首次发布