3D visionHow-to

Convert a 3D point cloud from PLY to PCD in your pipeline

Convert a PLY point cloud to PCD with one ready-made tool or one small program, and verify the POINTS line in the header.

LumaSpec Vision11 min read Share on LinkedIn

On this page 10 sections
  1. 01Can you link two compiled executables to convert a PLY to a PCD?
  2. 02What is the difference between a PLY file and a PCD file?
  3. 03Which ready-made command converts a PLY file to PCD?
  4. 04How do you build one program that reads PLY and writes PCD?
  5. 05How do you prove the converted PCD is correct?
  6. 06How large is a converted PCD file per million points?
  7. 07Where does the PLY come from in a vision pipeline?
  8. 08What breaks during PLY to PCD conversion?
  9. 09When should you keep the PLY instead of converting?
  10. 10Questions

The answer up front: you cannot link two compiled executables into one converter — the linker joins object files, not finished programs. Run pcl_ply2pcd capture.ply capture.pcd for files already on disk. Alternatively, write one small program that calls loadPLYFile and savePCDFileBinary in a single read pass and a single write pass. If you have two example programs on your build machine, one writing PLY and one writing PCD, gluing them with the linker looks faster than writing a third program, but it does not work. This guide gives you the two routes that do work, the arithmetic for file size and memory, the header checks that prove a conversion, and the cases where PLY should stay PLY.

No — the linker joins object files and libraries, not finished programs, so the documented route is either one small program that calls the PLY reader and the PCD writer, or the ready-made command-line converter.

An executable image already carries its runtime start-up, initialized globals and exactly one main() entry point. The linker resolves symbols between object files and libraries; it has no mode for splicing one finished image onto another. Public build-tool issue trackers record the attempts: force an executable in as if it were an object and the result crashes at start-up, because two runtimes are being initialized in one process.

The header count that made hand-linking unattractive is real, and it is the build system's job, not yours. You declare the dependency once in CMake, and the compiler and linker sort out the include chain. The project file stays a few lines long. That one-time set-up is the whole cost of the correct route.

So the choice is a binary one: files already sitting on disk go through the converter tool two sections down; conversion inside your pipeline becomes a dozen lines of code in the section after that.

What is the difference between a PLY file and a PCD file?

They carry the same points — x, y and z as 4-byte floats, plus optional normals and colors — but PLY declares elements such as vertices and faces, while a PCD header lists each field with its size and type.

That is why conversion is a copy, not a recomputation: the numbers in every point are identical before and after, and only the container changes. PLY groups data into elements with named properties, so a mesh file can hold vertices and the triangles that reference them. PCD has no element concept; everything is a flat list of points whose fields the header spells out line by line.

PLY PCD
Payload points plus optional faces and edges points only, as typed fields
Header element and property list, encoding line VERSION, FIELDS, SIZE, TYPE, COUNT, WIDTH, HEIGHT, VIEWPOINT, POINTS, DATA
Encodings ascii, binary_little_endian, binary_big_endian ascii, binary, binary_compressed
Home ground interchange, archiving, mesh and graphics tools PCL- and ROS-based processing

The DATA line decides how a PCD reader interprets the payload: ascii for text, binary for raw floats, binary_compressed for an LZF-compressed payload. WIDTH times HEIGHT equals POINTS, so an organized capture keeps its row-and-column shape with a HEIGHT greater than 1, while an unorganized cloud uses HEIGHT 1.

Which ready-made command converts a PLY file to PCD?

One command does it: pcl_ply2pcd ships with the Point Cloud Library's command-line tools, and pcl_ply2pcd input.ply output.pcd reads the PLY once and writes the PCD once, with no code of your own.

On Linux the tool arrives with the library's tools package; on Windows it comes out of the library build you get from the Point Cloud Library project's own releases. The reverse direction exists as a sibling tool, so pcl_pcd2ply capture.pcd capture.ply bridges back the same way. Both are one-read, one-write programs, which is exactly the shape of the job.

pcl_ply2pcd capture.ply capture.pcd

How do you build one program that reads PLY and writes PCD?

Call the two I/O functions in one main(): loadPLYFile fills a pcl::PointCloud, savePCDFileBinary writes it, and the whole converter is about a dozen lines plus a small CMake target.

  1. Declare the dependency once, so the linker sees the I/O module and nothing else needs hand-wiring:
find_package(PCL REQUIRED COMPONENTS common io)
add_executable(ply2pcd_one ply2pcd_one.cpp)
# link ply2pcd_one against the PCL common and io libraries found above
  1. Write the full program — read both paths from the command line, construct the cloud empty, load, guard, save:
// ply2pcd_one.cpp: read a PLY once, write a binary PCD once
#include <pcl/io/ply_io.h>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <string>

int main(int argc, char* argv[])
{
  if (argc < 3) {
    return 1;  // usage: ply2pcd_one input.ply output.pcd
  }
  const std::string input_ply = argv[1];   // type std::string, set from argv
  const std::string output_pcd = argv[2];  // type std::string, set from argv

  pcl::PointCloud<pcl::PointXYZ> cloud;    // empty until the loader fills it
  if (pcl::io::loadPLYFile(input_ply, cloud) != 0 || cloud.empty()) {
    return 1;  // bad path, unreadable header or zero points
  }
  return pcl::io::savePCDFileBinary(output_pcd, cloud);
}
  1. Build and run it:
cmake -B build .
cmake --build build
./build/ply2pcd_one capture.ply capture.pcd
  1. Match the point type to the file. pcl::PointXYZ carries bare coordinates; pcl::PointXYZRGB fits a PLY with red, green and blue properties; pcl::PointNormal fits one with nx, ny and nz, and the writer then emits FIELDS x y z normal_x normal_y normal_z curvature.

The I/O module also offers an ASCII writer and a compressed writer that produces DATA binary_compressed files. Use ASCII while you debug the header, and compressed only where the gain shows on your own captures.

How do you prove the converted PCD is correct?

Check three things on the written file: POINTS equals the input count, the FIELDS line lists what your downstream code expects, and a few sample coordinates match the PLY to floating-point precision.

Both headers are ASCII even when the payload is binary, so the checks are one-liners. grep -a element capture.ply prints the PLY's element lines — for a mesh file you will see an element vertex line and an element face line, and the face line is your signal to keep the PLY. head -n 11 capture.pcd prints the whole PCD header:

# .PCD v0.7 - Point Cloud Data file format
VERSION 0.7
FIELDS x y z
SIZE 4 4 4
TYPE F F F
COUNT 1 1 1
WIDTH 100000
HEIGHT 1
VIEWPOINT 0 0 0 1 0 0 0
POINTS 100000
DATA binary

POINTS 100000 matching the PLY's element vertex 100000 is the pass condition. Binary to binary copies the 4-byte floats unchanged, so coordinates are bit-identical; ASCII writes rounded text, so treat an ASCII file as a debugging artifact, never the master copy.

If the values are wrong in a bigger way — every coordinate uniformly scaled, for instance — the conversion copied a capture-side error faithfully. That is the same unit-mismatch family as an aligned depth point cloud with the wrong scale, and the fix is in the guide on fixing an aligned depth point cloud with the wrong scale.

Note also that VIEWPOINT 0 0 0 1 0 0 0 is the identity pose written by default. Conversion does not invent a sensor pose, so set it yourself after loading if your consumer transforms points by the viewpoint.

How large is a converted PCD file per million points?

On disk a binary PCD costs SIZE times COUNT bytes per point per field: 3 fields x 4 bytes gives 12 bytes per point, so 1,000,000 points occupy 12,000,000 bytes, about 11.4 MiB.

file bytes = POINTS x sum(SIZE x COUNT per field) 1,000,000 x (3 x 4 x 1) = 12,000,000 B = 11.4 MiB (FIELDS x y z) 1,000,000 x (7 x 4 x 1) = 28,000,000 B = 26.7 MiB (x y z + normals + curvature)

The second line is the pcl::PointNormal layout whose seven fields the writer prints in FIELDS. ASCII costs more room: at the precision the writer prints, x y z text runs roughly 25 to 30 bytes per point, so a million points land around 25 to 30 MB. Compression changes the picture only through DATA binary_compressed, and it pays best on organized captures, where each row of an ordered grid repeats structure. The ratio is shaped by the surface, so check it once on a real file. Memory behaves the same way: the point data alone needs at least 12 MB per million points before any struct padding and extra fields.

Time scales the same way. The converter performs one read pass and one write pass, so doubling the point count doubles the work and nothing else in the job grows.

A line-scan system is the organized case at scale: the MV-HLVS-16K100-L60 pairs a 16K resolution with a 120 kHz sampling rate over a 600 mm emitting area length, and each pass lays points into a grid that keeps its WIDTH and HEIGHT.

If writing the PLY side is what runs slow on an embedded board, the fixes live in the guide on speeding up slow PLY point cloud export on embedded boards.

3D sensorsHikrobot MV-HLVS-16K100-L60 Line-scan 2.5D, 16K resolution, Sampling rate 120 kHz, Emitting area length 600 mmMV-HLVS-16K100-L60
Type
2.5D inspection
Price on requestView and quote

Where does the PLY come from in a vision pipeline?

The file you are converting is usually the saved output of a 3D capture step — a laser profiler sweep, a structured-light snapshot or a ToF frame exported by your own code as PLY.

Capture and conversion stay separate programs on purpose: the capture application writes what it saw, and downstream code reads a stable file. If you already export a structured point cloud as XYZ and PNG from one capture, adding a PCD writer to the same export function is one more call, and the PLY stays as the interchange copy. The hardware side of that capture step is the 3D sensors range, browsable through the camera finder.

Three catalog sensors that sit at the front of exactly this kind of pipeline:

3D sensorsHikrobot MIS-DP3850-01P 3D laser profile sensorMIS-DP3850-01P
Type
Laser profiler
$12,379.00 · quotedView and quote
3D sensorsHikrobot MV-DPS200P-02 Projected structured-light 3D cameraMV-DPS200P-02
Type
Stereo / structured light
$8,829.00 · quotedView and quote
3D sensorsHikrobot MV-DT01WMU ToF sensing cameraMV-DT01WMU
Type
Depth / ToF camera
Price on requestView and quote
Cameras3D sensors

Laser profilers and stereo 3D cameras

170 models · from $699.00

What breaks during PLY to PCD conversion?

Most failed conversions are field mismatches: a PointXYZ-only writer silently drops normals and colors, ASCII and binary headers disagree with the reader's expectation, and NaN points ride along into the PCD.

  • Silent field loss. Writing pcl::PointXYZ when the PLY holds nx, ny, nz or RGB leaves the extra data behind with no error message. The FIELDS line in the output header is the tell: it lists only what the point type carries. Fix: use pcl::PointNormal or pcl::PointXYZRGB and re-run.
  • Encoding surprise. DATA says binary but a hand-rolled or third-party reader expects ASCII, or the reverse. Keep binary for program-to-program traffic and ASCII for eyeballing; the header states the encoding on its last line.
  • NaN passengers. Shadowed or out-of-range samples export as NaN coordinates. Downstream steps that fit planes, estimate normals or store the cloud in a voxel grid for 3D inspection choke on them or spread the damage. Decide deliberately: keep NaNs only where you need the organized grid — converting a 3D point cloud to a depth image for 2D processing needs it — otherwise filter before writing.
  • Viewpoint left at identity. VIEWPOINT stays 0 0 0 1 0 0 0 because a PLY carries no sensor pose. If the consumer transforms points by the viewpoint, set the pose after loading instead of trusting the default.

When should you keep the PLY instead of converting?

Keep the PLY when the file must still carry mesh faces, when a graphics or metrology viewer is the consumer, or when you archive captures; PCD wins only when a PCL- or ROS-based consumer needs fast binary loads.

  • PCL or ROS code downstream: convert to DATA binary PCD — typed header, fastest load.
  • Faces in the file: stay PLY. A PCD has nowhere to put triangles, so converting would silently destroy them.
  • Archive copy: stay PLY, the format general 3D tools open. For capture-side recording rather than file conversion, the H5 route keeps raw frames for offline processing — see recording 3D camera data to H5 files for offline processing.
  • Data already in memory as ROS messages: skip files entirely. The PointCloud2 to point cloud conversion for calibration happens in code, with no PLY intermediate to write or read.

The one-sentence decision: run pcl_ply2pcd for files on disk, link the reader and writer functions in one program for pipeline use, and keep PLY wherever faces, archives or non-PCL consumers are involved. If you are choosing the sensor that produces these clouds rather than converting old files, configure a full vision system or contact the applications team — free expert support comes with every order.

Questions

Can a PCD file store mesh faces like a PLY can?

No. A PCD header declares point fields only — x, y, z, normals, colors, custom scalars — so faces, edges and other PLY elements have nowhere to go. Converting a mesh PLY to PCD keeps the vertices and drops the faces. If the triangles matter, keep the PLY as the master file and write the PCD as a points-only working copy.

How do I convert a PCD back to PLY?

Use the companion tool that ships with the same command-line set: pcl_pcd2ply input.ply output.ply. The same one-read, one-write logic applies in reverse, so a two-way bridge between the formats costs two tool calls and no custom code. Note that the PLY will contain points only, since the PCD never had faces to give back.

Does converting PLY to PCD change the coordinate values?

No. Conversion copies each point's numbers unchanged; there is no rescaling, rotation or unit conversion in a format conversion. If your cloud is in millimetres it stays in millimetres, and scale errors baked in upstream come along too. Check units before converting — a 1000x scale error is a capture-calibration problem, not a file-format one.

Why does my PCD load slower than I expected?

The DATA line decides. DATA ascii makes the reader parse text for every coordinate, which is orders of magnitude slower than DATA binary. ASCII is for debugging and diffing; write binary for anything a program reads. Also check that you wrote only the fields the consumer uses — extra fields widen every point and every read.

Do I need the full Point Cloud Library installed just to convert files?

For the command-line tool, yes: pcl_ply2pcd comes with the library's tools build, so install the library from the project's own releases. For a compiled pipeline you already link the library if you process clouds; the conversion adds no new dependency, because the reader and writer live in the I/O module you already link.