liblas : 書き込み

lasファイルを書き込むのはなかなか大変です。
ヘッダにデータ点群の情報をいろいろと書き込む必要があるためですが、
その情報の中に点数、範囲などがあるため、
例えばXYZのテキストファイルをlasファイルにしようとしたりする場合、
情報収集のために1回スキャンし、
ヘッダを書き込んだ後で2回目のスキャンで点群をlasファイルに書き込まなければいけないようです。
実際、付属のtxt2lasのソースコードを見るとそうしているようです。
というのも、LASWriterクラスのコンストラクタがLASHeaderオブジェクトを要求するためです。

何とかヘッダを楽チンに作りたいところです。
入力元がlasファイルなら、入力lasファイルのヘッダをコピーし、
必要なところだけを修正して書き出したりすればいいはずです。

スキャン2回というのも大変です。
それでなくてもLASファイルは大容量になりやすいので、2回もスキャンしていては処理時間が膨大になってしまいます。
ところがスキャン1回にする方法もどうやらあるようです(少々インチキくさい)。
仮ヘッダをとりあえず書き込んでおいて、点群書き込みと情報収集を同時に行い、
最後に再びヘッダを書き込むという方法です。

なお、可変ヘッダはまた別のようです。

本当にできるでしょうか

以下の例では、入力LASファイルから何らかの条件で点を抽出して出力LASファイルに書き込む例です。
変数readerに入力LASファイルのLASReaderオブジェクトが、
変数header_inには入力LASファイルのLASHeaderオブジェクトがそれぞれ入っているとします。

  std::ofstream ofs;
  int nPoints = 0;
  double dMinX, dMinY, dMinZ;
  double dMaxX, dMaxY, dMaxZ;
  dMinX = dMinY = dMinZ = DBL_MAX;
  dMaxX = dMaxY = dMaxZ = -1*DBL_MAX;

  int aRecCount[5] = { 0, 0, 0, 0, 0 };
  if ( !liblas::Create( ofs, argv[2] ) )
  {
    exit( 1 );
  }
  
  // 仮ヘッダの作成
  liblas::LASHeader header_out;
  
  // この時点でわかる情報を設定する
  header_out.SetDataFormatId( header_in.GetDataFormatId() );
  header_out.SetScale( 
    header_in.GetScaleX(), header_in.GetScaleY(), header_in.GetScaleZ() );
  header_out.SetOffset( 
    header_in.GetOffsetX(), header_in.GetOffsetY(), header_in.GetOffsetZ() );
  
  // 仮ヘッダを使ってLASWriterオブジェクトを作成
  liblas::LASWriter writer( ofs, header_out );

  // スキャン開始
  while ( reader.ReadNextPoint() )
  {
    liblas::LASPoint const &p = reader.GetPoint();
    if ( /* 何か条件*/ )
    {
      writer.WritePoint( p );
      nPoints++;
      if ( p.GetX() < dMinX ) dMinX = p.GetX();
      if ( p.GetY() < dMinY ) dMinY = p.GetY();
      if ( p.GetZ() < dMinZ ) dMinZ = p.GetZ();
      if ( p.GetX() > dMaxX ) dMaxX = p.GetX();
      if ( p.GetY() > dMaxY ) dMaxY = p.GetY();
      if ( p.GetZ() > dMaxZ ) dMaxZ = p.GetZ();
      aRecCount[p.GetReturnNumber()]++;
    }
  }
  
  // 残りの情報をヘッダに設定
  header_out.SetMin( dMinX, dMinY, dMinZ );
  header_out.SetMax( dMaxX, dMaxY, dMaxZ );
  header_out.SetPointRecordsCount( nPoints );
  header_out.SetPointRecordsByReturnCount( 0, aRecCount[0] );
  header_out.SetPointRecordsByReturnCount( 1, aRecCount[1] );
  header_out.SetPointRecordsByReturnCount( 2, aRecCount[2] );
  header_out.SetPointRecordsByReturnCount( 3, aRecCount[3] );
  header_out.SetPointRecordsByReturnCount( 4, aRecCount[4] );
  
  // 完成ヘッダを再度書き込み
  writer.WriteHeader( header_out );

何か条件のところに、例えば1st returnだったらなどの条件を入れたりするといいでしょう。

ためしに実行してみるとどうやらちゃんとできているようです。

アーカイブ