2022년 6월 11일 토요일

파이썬 GDAL 설치 시 에러 수정 방법

GDAL은 래스터와 벡터 지리공간 자료 형식을 변환하는 라이브러리이다. 오픈소스이며, OSG재단에서 관리된다. 설치 방법은 다음과 같다. 

GDAL 개념

단, 설치 시 다음과 같이 파이썬 GDAL 라이브러리 단계에서 에러 발생 경우가 있다. 이 글은 GDAL PIP 설치 시 다음과 같이 발생하는 수정 방법을 간략히 정리한 것이다.

pip install GDAL

GDAL 설치 에러 발생 화면

에러가 발생하는 이유에 대한 관련 정보도 많이 나와 있지 않은 데, 마지막 줄 로그가 힌트가 된다. 

error in GDAL setup command: use_2to3 is invalid.

use_2to3 명령은 pip 에서 사용하는 setuptools의명령으로 버전 58에서는 더이상 지원하지 않는다. 그러므로, 최신 pip setuptools에서는 에러가 발생하는 것이다. 

다음과 같이 버전을 약간 낮추어 setuptools를 설치하고 재시도해본다. 

pip install setuptools==57.4.0

그리고, 현재 GDAL 설치 버전을 확인하고, 해당 버전의 PYTHON GDAL 패키지를 설치하면 된다.

ogrinfo --version

pip install GDAL==3.0.4

GDAL 파이썬 버전 설치 성공

이제, 파이썬에서 다음과 같이 gdal을 사용할 수 있다. 

def convert_tiff_jpg():        
    options_list = [
        '-ot Byte',
        '-of JPEG',
        '-b 1',
        '-scale'
    ]           

    options_string = " ".join(options_list)
        
    gdal.Translate(
        'save_image_path.jpg',
        'image_path.tif',
        options=options_string
    )

2022년 5월 3일 화요일

플러그인 개발을 위한 자바 스크립트와 C++ 연동 방법

이 글은 자바스크립트(Javascript)와 C++ 연동 방법에 대한 내용을 간략히 공유한다. 가끔, 플러그인 등을 개발할 때, 외부 스크립트 코드와 연동해야할 경우가 있다. 이와 관련해, 필요한 내용이 무엇인지 알아본다.

자바스크립트 동작 방식은 다음과 같다.
자바스크립트 내부 구조
자바스크립트 실행 상태 예시

C++에서 자바스크립트 호출 구조는 다음과 같다.
#include "quickjspp.hpp"
#include <iostream>

class MyClass
{
public:
    MyClass() {}
    MyClass(std::vector<int>) {}

    double member_variable = 5.5;
    std::string member_function(const std::string& s) { return "Hello, " + s; }
};

void println(qjs::rest<std::string> args) {
    for (auto const & arg : args) std::cout << arg << " ";
    std::cout << "\n";
}

int main()
{
    qjs::Runtime runtime;
    qjs::Context context(runtime);
    try
    {
        // export classes as a module
        auto& module = context.addModule("MyModule");
        module.function<&println>("println");
        module.class_<MyClass>("MyClass")
                .constructor<>()
                .constructor<std::vector<int>>("MyClassA")
                .fun<&MyClass::member_variable>("member_variable")
                .fun<&MyClass::member_function>("member_function");
        // import module
        context.eval(R"xxx(
            import * as my from 'MyModule';
            globalThis.my = my;
        )xxx", "<import>", JS_EVAL_TYPE_MODULE);
        // evaluate js code
        context.eval(R"xxx(
            let v1 = new my.MyClass();
            v1.member_variable = 1;
            let v2 = new my.MyClassA([1,2,3]);
            function my_callback(str) {
              my.println("at callback:", v2.member_function(str));
            }
        )xxx");

        // callback
        auto cb = (std::function<void(const std::string&)>) context.eval("my_callback");
        cb("world");
    }
    catch(qjs::exception)
    {
        auto exc = context.getException();
        std::cerr << (std::string) exc << std::endl;
        if((bool) exc["stack"])
            std::cerr << (std::string) exc["stack"] << std::endl;
        return 1;
    }
}

레퍼런스

라이다와 카메라 데이터 퓨전 기술을 이용한 이미지 중첩 방법 소개

이 글은 라이다와 카메라 데이터 퓨전 기술을 이용한 이미지 중첩 방법에 대한 소개이다. 이제 각자 발전된 라이다와 카메라 데이터 처리 기술은 각각의 장단점을 혼합해 사용되기 시작했다. 이 글은 특성이 서로 다른 각 데이터의 혼합(fusion) 및 중첩(superposition) 기술을 간략히 정리한다.
Fusion using camera and LiDAR

Calibration for superposition

레퍼런스

2022년 3월 10일 목요일

Ubuntu 리눅스 포트 죽이기

이 글은 Ubuntu 리눅스 포트 죽이는 방법을 간단히 정리한 것이다. 가끔 포트가 다른 프로그램에 의해 열려져 있어, 해당 포트를 사용하지  못할 때 사용하면 된다.

netstat -nap | grep :8080

sudo kill $(sudo lsof -t -i:8080)



2022년 3월 2일 수요일

3차원 데이터 처리를 위한 오픈소스 Open3D 라이브러리 소개

Open3D는 3차원 데이터를 다루는 소프트웨어 개발을 지원하는 오픈소스 라이브러리이다. C++, Python을 지원하며, 데이터 구조 및 알고리즘을 제공한다. 빠른 성능을 위해 최적화, 병렬화되었다.

핵심 기능은 다음과 같다.
  • conda, pip를 통한 간단한 설치
  • 3차원 자료 구조 및 알고리즘 제공
  • 장면 렌더링 및 3차원 시각화
  • 표면 정렬
설치는 다음과 같다.
virtualenv venv
pip install open3d

제대로 설치되었으면, Getting started 문서를 참고해 다음 코드를 실행한다.
# Verify installation
python -c "import open3d as o3d; print(o3d.__version__)"

# Python API
python -c "import open3d as o3d; \
           mesh = o3d.geometry.TriangleMesh.create_sphere(); \
           mesh.compute_vertex_normals(); \
           o3d.visualization.draw(mesh, raw_mode=True)"

# Open3D CLI
open3d example visualization/draw



 

2022년 2월 25일 금요일

리눅스 우분투 인터넷 서비스 포트 종료시키기

이 글은 리눅스 우분투 인터넷 서비스 포트 종료시키는 방법을 간략히 보여준다.

예를 들어, 3000번으로 열린 포트를 닫을려면, 터미널에서 다음 명령을 입력한다.

sudo kill -9 $(sudo lsof -t -i:3000)

웹기반 건물 Floor Plan 디자인 무료 도구 및 오픈소스 소개

이 글은 Autodesk Homestyler와 유사한 Floor Plan 디자인 무료 도구 및 오픈소스를 간단히 소개한다. 이를 이용하면, 3차원 가상 투어(Virtual Tour) 웹서비스 등을 손쉽게 개발할 수 있다. 

웹 기반 3차원 건물 공간 모델 디자인 예시

머리말 
평면도의 목표는 공간이 구성되는 방식을 정의한다. 

평면도 소프트웨어는 기본적으로 건물의 경계, 레이아웃, 공간을 나타내는 평면도를 디자인한다. 오픈 소스 평면도 소프트웨어는 무료로 사용할 수 있으며, 소스 코드는 공개된다. 

평면도 소프트웨어를 사용하면 더욱 편리하고 정확하며 효율적으로 평면도를 작성할 수 있다. 바닥 청사진을 만드는 데 필요한 도구를 제공한다. 정확한 평면도 치수는 사용할 가구의 종류와 방의 어디에 배치해야 하는지 결정하는 데 도움이 된다.
 
스위트홈 3D
Sweet Home 3D는 무료 평면도 소프트웨어(github)이다. 집의 인테리어를 계획하고 디자인할 수 있는 모든 기능을 제공한다. 다운로드하거나 웹 사이트에서 바로 사용할 수 있다. 

Sweet Home 3D의 기능은 다음과 같다.
  • 평면도를 2D로 설계하면, 동시에 3D로 시각화할 수 있다. 항공 보기, 가상 방문, 3D 이미지 생성 등과 같은 다양한 3D 보기 선택을 제공한다.
  • 질감, 색상, 크기, 두께, 위치, 가구, 벽, 천장 및 바닥의 정렬을 수정할 수 있다.
  • 마우스나 키보드를 사용하여 정확한 치수로 디자인을 만들 수 있다.
  • 계획에 주석을 달고 햇빛 효과를 조정하고 조명을 변경할 수도 있다.
SmartDraw
SmartDraw는 평면도 디자인 도구이다. 이 도구는 클라우드 기반 소프트웨어이다. 건축 설계 기호, 파일 공유 앱이 포함되어 있으며, 평면도 생성기는 인터넷에 연결된 모든 사람이 접근할 수 있다. 

SmartDraw의 다양한 기능은 다음과 같다.
  • 빠른 시작 템플릿 제공. 스마트하고 자동화된 서식을 사용
  • Development Platform의 내장 확장 기능을 사용하여, 스케치 없이 데이터를 사용하여 즉시 다이어그램을 구성할 수 있음
  • 34,000개 이상의 건축 설계 기호와 CAD와 유사한 그리기 도구가 포함된 SmartDraw의 바닥 디자이너를 사용하여 평면도 개발 가능
  • 다른 소프트웨어와 쉽게 통합할 수 있음. 다이어그램을 OneDrive, Microsoft Office, Dropbox, Jira, Google Drive 및 기타 응용 프로그램에 통합할 수 있음
  • 개략도는 시스템 내부의 부품을 묘사하며 평면도와 같은 부품의 배열 및 측정이 가능

플로어 플래너
FloorPlanner는 세계적으로 2천만 명 이상의 사용자가 있는 평면 설계 소프트웨어 이다(github). FloorPlanner는 가벼운 브라우저 기반 2D, 3D 평면 디자인 도구이다. 

FloorPlanner의 다양한 기능은 다음과 같다.
  • FloorPlanner는 Drag&Drop 인터페이스를 웹 브라우저에서 제공함. 바닥 평면도를 처음부터 디자인하거나, 미리 만들어진 디자인을 확장하여 설계할 수 있음
  • 매직 레이아웃 옵션을 사용하여 클릭 한 번으로 전체 공간 레이아웃을 생성할 수도 있음
  • 다양한 카메라 각도에서 평면도의 2D 및 3D 렌더링을 생성함
  • 기호 라이브러리, 업로드 기능, 클라우드 동기화 기능 제공
Roomle
Roomle는 증강 현실 기능이 제공되는 평면도 소프트웨어이다. Roomle는 2D/3D 증강 현실, 홈 인테리어, 제품 설계 시뮬레이션을 지원한다. 

Roomle의 다양한 기능은 다음과 같다.
  • 디자인을 통해 상호작용할 수 있는 대화형 가상 둘러보기 제공
  • 디자인 파일 업로드 기능
  • 공유 기능을 사용하여 평면도 아이디어를 교환하고, 가상 둘러보기 등을 제공
Architectural floor plans
앞서 소개한 도구들은 프리웨어이나, 지금부터 소개하는 도구는 소스코드가 제공되는 것이다. 코드가 있는 대신, 사용을 위해서는 코드를 빌드하거나 설치해야 하는 번거로움이 있다. 
architectural floor plans는 다음 그림과 같은 평면도 설계를 지원한다. 설계된 결과는 SVG로 제공하며, 바닥 면적 등을 CSV 엑셀파일로 저장할 수 있다.

Home Rough Editor
Home Rough Editor는 하우스의 평면 디자인을 지원한다. Javascript로 개발되어 웹 기반으로 동작되며, 매우 가볍게 개발되어 개발이 쉽다. 

Floor Planner 2D
Floor Planner 2D는 React 라이브러리로 개발된 건물 평면 및 3차원 모델 디자인 도구이다. 웹 기반으로 동작하며, 2차원 평면도에서 3차원 모델을 자동 생성한다. 


마무리
이 글에서 오픈소스 및 프리웨어 Floor Plan 라이브러리 및 소프트웨어를 간단히 소개하였다. 이를 이용하면, 3차원 가상 투어(Virtual Tour) 웹서비스 등을 손쉽게 개발할 수 있다. 소개된 도구는 저작도구만 제공할 수도 있고, 소스코드 자체를 제공할 수도 있다. 저작도구만 사용한다면, 디자인만 고려하면 되겠지만, 소스코드를 이용해 무언가를 개발하고자 할 경우에는 해당 도구의 구조를 분석하고, 기능을 추가하는 방법을 익혀야 할 것이다.

레퍼런스

2022년 2월 12일 토요일

PDAL 기반 3차원 포인트 클라우드 데이터 처리 파이프라인

PDAL 소개

PDAL은 Ponit Data Abstraction Library의 약자로, C++로 개발된 3차원 점군 처리 라이브러리(BSD 라이센스)이다. PDAL은 3차원 포인트 클라우드를 다루는 대부분의 어플리케이션에 사용될 수 있으며, 유명한 공간정보 오픈소스 플랫폼의 하위 컴포넌트를 구성한다. 
PDAL 활용 사례(Eric, 2018)

PDAL은 래스터 이미지 데이터 처리에 특화된 GDAL과 유사한 사용법을 가진다. 

PDAL은 명령행 배치처리를 제공해, 점군 처리, 필터링, 변환, 질의 등을 실행할 수 있다. 예를 들어, 아래와 같이 유닉스의 파이프라인 데이터 처리 구조를 지원한다. 
data2 = input data1
data3 = filter data2
result = output data3

이 라이브러리는 전형적인 파이프라인 구조로 설계되었다. 파이프라인을 통해, 각 단계(Stage)는 배치처리 자동화될 수 있다. 

수많은 대용량 입력 데이터 파일들에 대한 배치처리 자동화 처리를 위해, 각 단계는 아래와 같이 추상화된 공통 인터페이스를 설계해 놓았다.
Stage Preparation
   addArgs(ProgramArgs)
   initialize()
   addDimensions(PointLayoutPtr layout)
   prepared(PointTableRef)

Stage Execution
   ready(PointTablePtr)
   run(PointViewPtr)
   done(PointTablePtr)

Streaming Stage Execution
   processOne(PointRef)

여기서, Dimensions는 포인트 치수, PointLayout은 포인트 치수 구조(이름, 유형), PointTable은 포인트 값 테이블, PointView는 포인트의 레퍼런스 값이다.

PDAL은 파이썬에서도 활용가능하다. 상세 설명은 아래 링크를 참고한다.
PDAL을 손쉽게 사용하는 방법 중 하나는 아나콘다를 이용하는 것이다. 아나콘다는 패키지 관리 시스템으로 특히 파이썬 개발 시 필요한 패키지 설치 및 관리를 쉽게 해 준다. 

PDAL 설치
아래 링크를 참고해 아나콘다를 설치한다.
콘다 설치 후 아래 명령으로 PDAL 패키지를 추가한다. 상세 내용은 여기를 참고한다.
conda create --yes --name myenv --channel conda-forge pdal
conda update pdal

PDAL 예제
pdal은 명령행 배치 프로세스를 지원한다. shell 명령을 이용하면, 3차원 점군 데이터 처리를 자동화할 수 있는 스크립트를 개발할 수 있다.

다음은 LAS 포맷 요약 정보를 출력한 후, 파일을 변환하고,  명령 예이다. 
pdal info myfile.las
pdal translate input.las output.las
pdal pipeline --stdin < pipeline.json

for f in *.pcd; do pcl_convert_pcd_ascii_binary $f ./ascii/$f 0; done
for f in *.pcd; do pdal translate ./$f $f.las; done

여기서 pipeline은 shell 명령과 같이, 다중 입력 파일에 대한 pdal 명령 처리를 지원한다. 
파이프라인 예시

예를 들어, 다음은 file1, file2를 laz파일로 변환하는 파이프라인 예시이다.
{
    "pipeline" : [
        {
            "tag" : "las1",
            "type" : "readers.las"
        },
        {
            "tag" : "las2",
            "type" : "readers.las"
        },
        "placeholder.laz"
    ]
}

pdal pipeline translate.json --writers.las.filename=output.laz --stage.las1.filename=file1.las --stage.las2.filename=file2.las

다음은 las 파일을 DTM tiff 파일로 변환하는 예시이다. las를 입력받고, 좌표변환을 한 후, tiff 파일로 변환한다. 
pdal pipeline las_to_tiff.json

las_to_tiff.json 파일 정의는 다음과 같다. 
{
  "pipeline": [
    "/home/ktw/Projects/pcd_pl/dbscan/1.pcd.las",
    {
      "type":"filters.transformation",
      "matrix":"0  0  -1  0  1  0  0  0  0  0  1  0  0  0  0  1"
    },
    {
      "type":"writers.las",
      "filename":"/home/ktw/Projects/pcd_pl/dbscan/transformed.las"
    },      
    {
      "filename":"/home/ktw/Projects/pcd_pl/dbscan/dtm.tif",
      "gdaldriver":"GTiff",
      "output_type":"all",
      "resolution":"0.2",
      "type": "writers.gdal"
    }        
  ]
}

결과는 다음과 같다.

PDAL 기능
PDAL은 다음같은 기능을 지원한다.
  • chamfer: 두 점군 간에 Chamfer 거리 계산
  • delta: 점군에서 가장 가까운 점 리턴
  • density: 점군에서 삼각화된 레이어 생성
  • eval: 두 점군 간 classification 치수 계산
  • ground: 점군에서 지반 세그먼트 계산 및 출력
  • hausdorff: 두 점군간 Hausdorff 거리를 계산함
  • info: 점군 통계 정보 등 기본정보 출력
  • merge: 여러 점군들 병합
  • pipeline: 배치 처리 프로세스 지원
  • random: 랜덤 점군 생성
  • sort: 점군 정렬
  • split: 단일 점군에서 그리드 기반 다중 점군 생성
  • tile: 단일 점군에서 정규 격자 기반 점군 생성
  • tindex: 점군의 공간 인덱스 파일 생성
  • translate: 필터, 파이프라인 등을 이용한 점군 데이터 변환. 필터는 Create, Order, Move, Cull, New, Join, Metadata, Mesh 등을 지원.
PDAL의 모든 점 데이터는 치수(Dimensions) 집합으로 저장된다. 이를 이용해, 다양한 필터링을 수행할 수 있다.
포인트에 대한 PDAL 치수 집합

PDAL 데이터는 8비트부터 64비트 실수형 포인트까지 다룰 수 있다.
파이썬과 연결해 다음과 같이 기능을 확장 및 사용할 수있다.
json = """
[
    "1.2-with-color.las",
    {
        "type": "filters.sort",
        "dimension": "X"
    }
]
"""

import pdal
pipeline = pdal.Pipeline(json)
count = pipeline.execute()
arrays = pipeline.arrays
metadata = pipeline.metadata
log = pipeline.log

PDAL은 확장 가능하고 재활용이 쉬운 구조로 되어 있어, 많은 프로젝트에서 활용되고 있다.
PDAL 활용 사례 중 일부(DBSCAN based feature extraction, Batran, 2021. 항공뷰 래스터화 후 특징 추출)

레퍼런스
좀 더 자세한 내용은 아래 링크를 참고한다.

2022년 2월 7일 월요일

사진측량 및 라이다 기반 3차원 포인트 클라우드 처리 라이브러리 소개

이 글은 최근 사용 사례가 크게 많아지고 있는 무인자율로봇의 비전, 사진측량이나 라이다 스캔을 통해 얻은 3차원 포인트 클라우드 데이터를 처리하는 방법을 간단히 설명한다.

3D 점군 응용 분야 예(Geospatial World, 2018.11, Surveying and Mapping - From Site to Structure, Realtime 3D Drone Localization with LiDAR)

이 분야는 3차원 계산 기하학, 수치해석 등 다양한 기술이 사용된다. 이와 관련된 수많은 라이브러리가 있으나, 이 중에 가장 많이 사용되고 있는 PCL(Point Cloud Library)을 중심으로 내용을 살펴본다. PCL은 3차원 포인트 클라우드 데이터를 다루고, 처리하는 데 필요한 일반적인 자료구조, 알고리즘을 제공한다. 당연히, 특수한 데이터 유형이나 알고리즘은 PCL을 확장해 별도 개발한다. 이를 위해서 관련 데이터 및 클래스 구조, 모듈 아키텍처 분석 내용, 개발방법을 공유한다.

이 글은 3차원 계산 기하학, 수치해석, 선형대수, 컴퓨터 그래픽스 등 내용은 자세히 설명하지 않는다. 이 라이브러리를 다루기 위해서는 별도로 이와 관련된 내용을 공부할 필요가 있다. 관련 용어 및 내용 등은 아래 링크를 참고한다.
이 글에서 개발환경은 아래의 리눅스 우분투 환경을 기본으로 한다. 
윈도우와 같은 다른 운영체제에서도 개발 절차는 비슷하며, 관련 메뉴얼을 제공하므로 참고한다.

PCL 소개
PCL은 대규모 오픈소스 프로젝트로, 2차원 및 3차원 이미지와 점군 처리를 지원하는 라이브러리이다. 이 라이브러리의 프레임웍은 필터링, 특징 예측, 표면 구성, 정합, 모델 피팅 및 세그먼테이션(segmentation)을 위한 최신 기술을 포함한다. 이러한 알고리즘은 다양한 곳에 사용될 수 있는 데, 예를 들면, 노이즈 데이터에서 아웃라이어(outliers) 필터링, 3차원 점군 스티치(stitch), 세그먼테이션, 키포인트 추출, 특징 기술자(descriptors) 계산, 표면 재구성 등에 사용된다. 

PCL은 BSD 라이센스로 개발되었고, 상업 및 연구적 목적에서 무료이다. 크로스 플랫폼 개발을 지원하여, 리눅스, 맥, 윈도우, 안드로이드 등에 배포될 수 있다. PCL은 잘 모듈화되어 있어 배포에 용이하다. 
PCL 모듈 구조

PCL은 유명한 오픈소스 프로젝트를 다수 지원하고 있다. 예를 들어, ROS(Robot Operating System), PDAL 등은 이 라이브러리를 직접 사용한다.

PCL은 X, Y, Z, R, G, B 등으로 구성된 3차원 점을 처리하는 데 특화되어 있다. 2011년 부터 개발된 PCL은 현재 1.12 버전까지 릴리즈되어있다.

다양한 3차원 점군 데이터 응용 기술

3차원 점군 처리에 가장 많이 활용되는 필터링을 확인해보자. 3차원 점군에서 필터 처리는 매우 중요하다. 필터는 노이즈 제거, LoD(Level of Detail) 처리, 법선 및 곡률 계산 등을 포함하며, 점군에서 어떤 처리를 통해 얻은 결과물의 품질을 직접적으로 좌우한다. 
Point Index 처리

스캔 데이터는 일반적으로 많은 노이즈를 가진다. 이런 데이터는 3차원 특징점을 계산하기 어렵게 한다. 이러한 몇몇 아웃라이어는 필터링 기법으로 삭제가 가능하다. 점군에 속한 각 점들은 이웃점을 가지며, 이와 관련된 평균 거리 등을 계산할 수 있다. 이에 대한 분포는 평균, 분산과 함께 가우시안 분포를 가진다. 이런 특징을 이용해 통계적으로 필터링을 할 수 있다.

필터링된 점군은 각 점마다 특징(피처, Feature)을 계산할 수 있다. 피쳐 모듈은 3차원 특징 예측을 위한 자료 구조와 알고리즘을 포함한다. 공간을 스캔한 점군은 점 주변에 기하학적인 패턴을 가진다. 자료 공간을 검색할 때 사용하는 k-이웃검색(k-neighborhood)를 통해, 특정 주변 점군을 선택할 수 있을 것이다. 이 점군들은 특정 표면의 측정 결과일 것이며, 표면에 대한 곡률과 법선 정보가 내포된 것이다. 

질의점 p에서 획득한 이웃점 k개를 획득할 때는 대용량 점군을 그대로 사용할 수 없다. 계산 성능을 개선하기 위해 공간인덱싱 기법 중 하나인 옥트리(octree), kD-tree를 사용한다. 그러므로, 이웃점을 질의하는 연산은 그리 간단한 문제가 아니다. 보통 이웃점 검색에 사용되는 파라메터는 반경 r혹은 이웃점 갯수 k이다. 

획득된 이웃점들을 이용해, 법선 벡터를 구하기 위해서, 고유벡터(eigen vector)를 계산해야 한다. 이를 위해, 고유값분해(digen decomposition)을 수행한다. 법선벡터는 이렇게 계산된 고유벡터 중 가장 작은 고유치를 가진 벡터가 된다. 
법선 벡터

각 3차원 점에 대한 법선벡터 n을 획득하였다면, 각 점에 대한 곡률벡터는 이웃점의 법선벡터로 계산할 수 있다. 여기서 획득한 값들은 곡면이나 객체를 분류할 때 유용하게 사용될 수 있다.
3차원 점군 곡률(곡률 높은 값=녹색, 낮은 값=적색. 모서리=청색)

PCL 설치 및 기본 코드 분석
PCL 최신버전을 설치하고, 기본 코드 중 하나인 특징점을 계산하는 PCL코드를 살펴보자.
우선 다음 링크를 참고해, PCL 코드를 다운로드 한 후 빌드한다. 빌드 중 다양한 이유로 에러가 발생할 수 있다. 하나씩 해결해 나가며 빌드해 본다.
빌드를 성공했다면, PCL루트 폴더에서 다음 명령을 입력해 법선계산 프로그램 코드를 찾아본다.
find -name '*normal*.cpp'


해당 코드를 열어보면 다음과 같다. 

이 중에 compute 함수를 찾아본다. 함수의 핵심 루틴은 다음과 같다.
 
    NormalEstimation<PointXYZ, Normal> ne;
    ne.setInputCloud (xyz);
    ne.setSearchMethod (search::KdTree<PointXYZ>::Ptr (new search::KdTree<PointXYZ>));
    ne.setKSearch (k);
    ne.setRadiusSearch (radius);
    ne.compute (normals);

각 코드의 의미는 다음과 같다.
1. 법선벡터 계산 템플릿 클래스 생성
2. 3차원 점군 설정
3. 이웃점 탐색 공간인덱싱 기법 설정
4. 이웃점 탐색 개수 설정
5. 이웃점 탐색 반경 설정
6. 법선벡터 계산

빌드된 법선벡터 계산 프로그램을 실행해 보자. 

참고로, 사용된 점군은 라이다에서 얻은 점군을 SLAM처리하여 얻은 대학교 캠퍼스(UNF) 데이터이다. 
UNF 캠퍼스 건물 스캔 데이터(Google Earth)

pcl_viewer를 실행해 법선벡터 값을 아래와 같이 확인해 본다. 곡률이 작으면 적색, 많으면 녹색이다. 이 값은 앞서 언급한 파라메터에 따라 달라질 수 있다.
곡률 계산 결과(PCL)

다음 명령을 이용해 las 데이터로 변환한 후 CloudCompare에서 확인해 본다.
for f in *.pcd; do pcl_convert_pcd_ascii_binary $f ./ascii/$f 0; done
for f in *.pcd; do pdal translate ./$f $f.las; done

곡률 계산 결과(CloudCompare)

PCL 패키지 모듈 분석
PCL모듈을 좀 더 명확히 이해하기 위해, Make파일을 통해 구조를 분석해 본다.

PCL은 매우 모듈화된 구조를 제공한다. 예를 들어, 가시화 모듈인 pcl_visualization을 이용해, 다양한 목적의 3차원 점군 데이터 뷰어를 개발할 수 있다. 다음 그림과 같이, 해당 모듈 폴더에서 소스파일을 갱신한 후 메이크해본다.
touch pcl_visualizer.cpp 
cd ..
cd build
make
Make 결과

이 결과, lib 폴더 아래에 libpcl_visualization.so 라이브러리가 빌드되고, pcl_outofcore_viewer, pcl_oni_viewer, pcl_image_grabber_saver, pcl_vlp_viewer, pcl_pcd_image_viewer, pcl_viewer, pcl_octree_viewer 도구 등이 이 라이브러리를 이용하는 것을 확인할 수 있다.

소스파일 의존관계를 좀 더 깊게 확인해 보기 위해, make 파일을 분석해 본다. PCL은 CMake를 사용한다. CMake 파일은 아래와 같이 구성된다(참고. graphviz). 행렬수치해석 Eigen 등 사용하는 패키지를 포함하고, 루트 폴더 내 모듈 폴더들을 획득해, add_subdirectory 명령으로 하위 CMakeFile을 추가하고 있음을 확인할 수 있다.
...
# Eigen (required)
find_package(Eigen 3.1 REQUIRED)
include_directories(SYSTEM ${EIGEN_INCLUDE_DIRS})

# Boost (required)
include("${PCL_SOURCE_DIR}/cmake/pcl_find_boost.cmake")
...
collect_subproject_directory_names("${PCL_SOURCE_DIR}" "CMakeLists.txt" PCL_MODULES_NAMES PCL_MODULES_DIRS doc)

foreach(subdir ${PCL_MODULES_DIRS})
  add_subdirectory("${PCL_SOURCE_DIR}/${subdir}")
endforeach()

하위 폴더는 모듈을 빌드하는 CMake 파일이 존재한다. Common 모듈의 경우, 아래와 같다.
set(SUBSYS_NAME common)
set(SUBSYS_DESC "Point cloud common library")
set(SUBSYS_DEPS)

set(build TRUE)
PCL_SUBSYS_OPTION(build "${SUBSYS_NAME}" "${SUBSYS_DESC}" ON)
PCL_SUBSYS_DEPEND(build "${SUBSYS_NAME}" DEPS ${SUBSYS_DEPS} EXT_DEPS eigen boost)

set(range_image_incs
  include/pcl/range_image/bearing_angle_image.h
  include/pcl/range_image/range_image.h
  include/pcl/range_image/range_image_planar.h
  include/pcl/range_image/range_image_spherical.h
)

set(range_image_incs_impl
  include/pcl/range_image/impl/range_image.hpp
  include/pcl/range_image/impl/range_image_planar.hpp
  include/pcl/range_image/impl/range_image_spherical.hpp
)

set(range_image_srcs
  src/bearing_angle_image.cpp
  src/range_image.cpp
  src/range_image_planar.cpp
)

set(srcs
  src/point_types.cpp
  src/pcl_base.cpp
  src/PCLPointCloud2.cpp
  src/io.cpp
...
set(LIB_NAME "pcl_${SUBSYS_NAME}")
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include")
PCL_ADD_LIBRARY(${LIB_NAME} COMPONENT ${SUBSYS_NAME} SOURCES ${srcs} ${kissfft_srcs} ${incs} ${common_incs} ${impl_incs} ${tools_incs} ${kissfft_incs} ${common_incs_impl} ${range_image_incs} ${range_image_incs_impl})

PCL_MAKE_PKGCONFIG(${LIB_NAME} COMPONENT ${SUBSYS_NAME} DESC ${SUBSYS_DESC})
...

생성된 make 파일의 주요 부분은 다음과 같다. 적색 표시 부분은 각 폴더의 Make파일을 실행한 결과 생성되는 라이브러리 패키지이다.
...
# The top-level source directory on which CMake was run.
CMAKE_SOURCE_DIR = /home/ktw/Projects/pcl-1.12

# The top-level build directory on which CMake was run.
CMAKE_BINARY_DIR = /home/ktw/Projects/pcl-1.12/build
...
# Special rule for the target list_install_components
list_install_components:
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Available install components are: \"pcl_2d\" \"pcl_common\" \"pcl_features\" \"pcl_filters\" \"pcl_geometry\" \"pcl_io\" \"pcl_kdtree\" \"pcl_keypoints\" \"pcl_ml\" \"pcl_octree\" \"pcl_outofcore\" \"pcl_people\" \"pcl_recognition\" \"pcl_registration\" \"pcl_sample_consensus\" \"pcl_search\" \"pcl_segmentation\" \"pcl_stereo\" \"pcl_surface\" \"pcl_tools\" \"pcl_tracking\" \"pcl_visualization\" \"pclconfig\""
.PHONY : list_install_components
# The main clean target
clean/fast: clean
# Build rule for target.
uninstall: cmake_check_build_system
$(MAKE) -f CMakeFiles/Makefile2 uninstall
.PHONY : uninstall
...
# Build rule for target.
pcl_common: cmake_check_build_system
$(MAKE) -f CMakeFiles/Makefile2 pcl_common
.PHONY : pcl_common
...
# Build rule for target.
pcl_sample_consensus: cmake_check_build_system
$(MAKE) -f CMakeFiles/Makefile2 pcl_sample_consensus
.PHONY : pcl_sample_consensus

# fast build rule for target.
pcl_sample_consensus/fast:
$(MAKE) -f sample_consensus/CMakeFiles/pcl_sample_consensus.dir/build.make sample_consensus/CMakeFiles/pcl_sample_consensus.dir/build
.PHONY : pcl_sample_consensus/fast

PCL ROOT 폴더 구조를 보면, 각 모듈이 위치한 폴더들을 확인할 수 있다. 각 모듈은 include, src 소스파일 폴더로 구성된다. 

유지관리하기 편리하게, 모듈 패키지 별로 폴더를 구성했음을 확인할 수 있다.
패키지 다이어그램(UML)

모듈 패키지에서 제공하는 함수 역할을 다음과 같다.
  • cuda: CUDA 가속 계산 함수
  • features: 특징 계산
  • filters: 점군 필터링
  • geometry: 기하 형상 자료 구조 제공. polygon, mesh 등. 
  • gpu: GPU 가속 계산 함수
  • io: 센서 및 파일 데이터 입출력 함수
  • kdtree: kD-tree 공간 인덱싱 함수 
  • keypoints: 키포인트 계산 함수
  • ml: SVM(Support Vector Machine)등 기계학습 지원 함수
  • octree: Octree 공간 인덱싱 함수
  • ontoforce: 디스크 기반 공간 인덱싱 함수
  • recognition: 객체 인식 함수
  • registration: 3차원 정합 함수
  • sample_consensus: RANSAC(Random sample consensus) 처리 함수
  • search: 점군 검색 함수
  • segmentation: 점군 세그먼테이션 함수
  • simulation: 3차원 점군 시뮬레이션
  • stereo: 스테레오 이미지 생성 함수
  • surface: 표면 재구성 함수
  • test: 테스트 실행 함수
  • tools: PCL 도구
  • tracking: 3차원 점군 추적 함수
  • visualization: 3차원 점군 시각화 함수

이 중에 주요 패키지인 common, feature를 중심으로 코드를 역설계해본다. 핵심적인 코드는 common 패키지의 pcl_base, PCLHeader.h, point_cloud_h, feature의 feature.h이다. 
주요 코드

PCL 아키텍처는 처음부터 재활용성, 확장성과 유지보수성을 크게 염두해두고, 객체지향 소프트웨어 공학적으로 개발되었다. 이런 이유로, 새로운 모듈을 계속 확장하면서도, 복잡도를 줄일 수 있었다. 예를 들어, 3차원 점군은 다음과 같이 매우 다양한 유형을 가질 수 있다. 
  • 정수형 X, Y, Z
  • 실수형 X, Y, Z
  • RGBD
  • 반사강도
  • 사용자 데이터 등
이를 모두 표현하면서, 개발된 다양한 입출력 포맷, 알고리즘은 코드 수정없이 재활용할 수 있도록, 객체지향의 템프릿 클래스를 사용한다. 
  template <typename PointT>
  class PCL_EXPORTS PointCloud

핵심적인 클래스인 점군 자료구조, 입출력, 특징 계산과 관련된 아키텍처는 다음과 같다.

역설계된 PCL 주요 아키텍처(UML)

PCL은 이렇게 신뢰성, 확장성이 매우 뛰어나, ROS등 다양한 프레임웍에 기본으로 사용된다.
PCL 사용 회사

소프트웨어 개발 
PCL을 이용한 간단한 개발 방법을 살펴보자. 우선, 프로젝트 파일을 담을 폴더를 생성하고, PCD파일을 읽을 코드를 작성한다.
mkdir pcd_io
cd pcd_io

pcd_io.cpp 파일 코드는 다음과 같다. 
#include <stdio.h>
#include <iostream>
#include <pcl/PCLPointCloud2.h>
#include <pcl/io/pcd_io.h>
#include <pcl/features/normal_3d.h>

using namespace pcl;
using namespace pcl::io;
using namespace pcl::console;

int main(int argc, char** argv)
{
  // Load in the point cloud
  PointCloud<PointXYZ>::Ptr cloud_in (new PointCloud<PointXYZ> ());
  if (loadPCDFile (argv[1], *cloud_in) != 0)
  {
    print_error ("Could not load input file %s\n", argv[1]);
    return (-1);
  }
  std::cout << "Loaded " << cloud_in->width * cloud_in->height << " fields: " << std::endl;

  for (const auto& point: *cloud_in)
    std::cout << "    " << point.x << " "    << point.y << " "    << point.z << std::endl;  
return 0;
}

아래와 같이 CMakeFile.txt를 작성한다.
# set default env varibles
cmake_minimum_required(VERSION 3.16.1)

set(SUBSYS_NAME pcd_io)
set(SUBSYS_DESC "Useful PCD io command")
set(DEFAULT ON)
set(REASON "")
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
set(PCL_ROOT "/home/ktw/Projects/pcl-1.12")

message("BIN PATH = " ${CMAKE_BINARY_DIR})
message("PCL ROOT = " ${PCL_ROOT}/common/include)

# set project
project(pcd_io)

# set include and library
include_directories(PUBLIC ${PCL_ROOT}/common/include ${PCL_ROOT}/features/include ${PCL_ROOT}/filters/include ${PCL_ROOT}/geometry/include ${PCL_ROOT}/io/include ${PCL_ROOT}/kdtree/include ${PCL_ROOT}/keypoints/include ${PCL_ROOT}/octree/include ${PCL_ROOT}/recognition/include ${PCL_ROOT}/registration/include ${PCL_ROOT}/sample_consensus/include ${PCL_ROOT}/search/include ${PCL_ROOT}/segmentation/include ${PCL_ROOT}/visualization/include ${PCL_ROOT}/build/include /usr/include/eigen3)

link_directories(${PCL_ROOT}/build/lib/)
link_libraries(libpcl_common.so libpcl_features.so libpcl_io.so libpcl_kdtree.so libpcl_common.so libpcl_ml.so libpcl_keypoints.so libpcl_octree.so libpcl_outofcore.so libpcl_recognition.so libpcl_registration.so libpcl_sample_consensus.so libpcl_segmentation.so libpcl_search.so libpcl_surface.so libpcl_visualization.so)

# program
add_executable(pcd_io pcd_io.cpp)

다음과 같이 빌드한다.
mkdir build
cd build
cmake ..
make

그럼, 다음 같이 bin  폴더 아래 실행파일이 생성된다. 스캔된 PCD 파일을 테스트해 본다.

LDD명령을 사용해, 실행파일의 라이브러리 의존성을 체크해 본다. 제대로 링크되었음을 확인할 수 있다.
ldd ./pcd_density 

참고로, 아래와 같이 cmake --graphviz 옵션을 사용하면, 사용된 라이브러리 의존성을 그래픽으로 확인할 수 있다.
cmake .. --graphviz=dep.dot
dot -Tpng dep.dot -o dep.png

이런 방식으로 필요한 패키지를 사용해, 원하는 소프트웨어를 개발할 수 있다.

레퍼런스
좀 더 자세한 내용은 아래 링크를 참고한다.
PCL의 상세한 내용은 아래 문서를 살펴본다.
PCL 튜터리얼은 아래 문서를 살펴본다.
참고: pwndbg
pwndbg는 disassemble 기반 역설계 도구이다. 실행 파일을 어셈블리 기반으로 분석할 수 있다.

참고: QGIS 활용 SHP 파일 정보 확인
가끔 SHP 파일을 확인할 필요가 있다. 이 경우, SHP파일 렌더링을 위해, QGIS를 설치한다.
SHP파일을 QGIS로 드래그&드롭 한다.