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
    {
        // C++클래스 정의 
        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");

        // 자바스크립트 모듈 임포트
        context.eval(R"xxx(
            import * as my from 'MyModule';
            globalThis.my = my;
        )xxx", "<import>", JS_EVAL_TYPE_MODULE);

        // 자바스크립트 코드 호출 및 실행.
        // C++에서 정의된 클래스를 자바스크립트에서 생성, 호출할 수 있다.
        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;
    }
}

이 코드 실행을 위해서는 quickjspp 라이브러리가 필요하다. 아래 링크를 참고해 설치하고, 실행하면 된다. 

레퍼런스

라이다와 카메라 데이터 퓨전 기술을 이용한 3D 객체 인식, 거리, 위치 자동 추출 기술 개발 방법

이 글은 라이다와 카메라 데이터 퓨전 기술을 이용한 3차원 객체 인식, 거리, 위치 자동 추출 기술 개발 방법에 대한 소개이다. 

각자 발전된 라이다와 카메라 데이터 처리 기술은 각각의 장단점을 혼합해 사용된다. 데이터 퓨전 기술을 통해, 카메라 이미지에서는 정밀한 객체 세그먼트 정보를 얻고, 퓨전된 라이다로부터, 정확한 객체 거리 및 위치를 추출할 수 있다. 이를 잘 이용하면, 실내외 객체의 종류, 거리, 위치, 크기를 정확히 얻을 수 있다.

이 글은 특성이 서로 다른 각 데이터의 혼합(fusion) 및 중첩(superposition) 기술을 간략히 정리한다.
Fusion using camera and LiDAR

머리말
센서 퓨전 분야는 라이다와 카메라 센서 같이 이기종 센서의 장점만을 취해, 퓨전된 데이터셋을 얻을려는 시도에서 무인 자율차 등 다양한 분야에서 시도되었다. 

센서 퓨전을 통해, 라이다에서는 정확한 거리나 위치, 카메라에서는 풍부한 객체 세그먼트 정보를 얻을 수 있다. 그러므로, 센서 퓨전은 각 데이터를 하나의 좌표계로 정렬하는 것이 목적이 된다.
센서 퓨전 결과

본문
이 글은 자율차 개발 시 사용되고 있는 센서 퓨전 연구 사례(링크)를 기반으로 설명한다. 이 연구 사례는 센서 퓨전 연구를 위해, 기존 무인자율차 데이터 학습용 KTTI 데이터셋을 사용한다. 

우선, 다음과 같이, 벨로다인 라이다, 카메라를 장착한 차량(로버 등 마운팅 대상은 관계 없다)이 있다고 가정한다. 

모든 센서에서 얻은 값은 특정 센서 기준으로 데이터를 변환해야 한다. 이 경우, 카메라를 기준으로 다른 센서들의 데이터를 변환한다. 그러므로, 이를 변환하기 위한, 각 센서의 캘리브레이션(조정) 파라메터가 필요하다. 이 파라메터는 기준 센서 중심으로 데이터 변환 시 사용된다. 
Calibration for superposition 예시(라이다, 스테레오 카메라 경우)

카메라의 경우, 다음과 같이 2차원 이미지와 3차원 이미지 간에 캘리브레이션 파라메터 관계가 존재한다. 수식을 간단히 설명하면, 이 행렬은 3차원 데이터를 2차원 픽셀 좌표계로 변환한다. 행렬들은 광학적 카메라 렌즈 관련 파라메터(intrinsic properties. 스케일 관련), 외부적 카메라 속성(extrinsic properties. 위치, 회전 행렬) 행렬로 조합되어 있다. 

이는 일반적으로 우리가 3차원 게임을 할때 나타나는 3차원 객체들을 2차원 픽셀 좌표계로 맵핑된 스크린을 보는 방식과 개념상 동일한 좌표변환행렬이다. 이 부분에 대한 자세한 내용은 컴퓨터 그래픽스 좌표변환행렬을 참고하라.

u,v = P x Coordinate Transformation Matrix [4x4]

좌표변환행렬을 구성하는 파라메터는 다음과 같은 체커보드를 이용한 캘리브레이션 파라메터 추출 알고리즘을 통해 쉽게 얻을 수 있다.

파라메터 획득 후, 아래 공식으로 카메라 이미지 공간으로 다른 센서 값들을 맵핑한다.

Y(2D) = P x R0 x R|t x X (3D)

여기서,  
Y: 2차원 포인트(픽셀)
P: [3x4]행렬. Intrinsic calibration matrix
R0: [3x3]행렬. stereo rectification matrix
R|t = 벨로다인 데이터공간에서 카메라공간 데이터변환 행렬: [3x4]
X: [3x1]행렬. 3차원 포인트

일단, P는 카메라 제조사에서 pre-calibration되므로, 수행할 필요가 없다. 
R0는 스테레오 카메라에만 해당한다. 모노 카메라를 사용하므로 필요가 없다. 
R|t는 실험을 통해, 획득해야 한다. 앞의 라이다와 카메라 위치일 때, 동일 장면을 스캔한다. 스캔된 두 장면의 세개 이상의 포인트를 얻는다. 
LP={L1, L2, L3}
CP={C1, C2, C3} 

LP는 3D, CP는 2D이다. 이를 통해, 이동 및 변환 행렬을 계산할 수 있다. 이제, 동차행렬의 스케일 요소를 이용해, 정확히 라이다 이미지와 카메라 이미지 데이터를 정렬할 때의 파라메터를 계산한다. 이 과정을 통해, R|t 동차행렬을 얻을 수 있다.

이 수식을 이용해, 라이다 데이터를 카메라 이미지 공간으로 변환하였으면, 다양한 객체 검출 모델을 사용해 객체 추출한다. 이 경우, YOLO 딥러닝 모델을 사용하였다(YOLO 참고). 

결과는 다음과 같다. 이로써 객체 종류, 위치, 거리를 자동으로 얻을 수 있다. 
객체 탐지, 위치 및 거리 확인 결과

상세한 소스는 여기 링크를 참고한다. 

마무리
이 글에서는 벨로다인 라이다와 카메라 이미지와 같은 이기종 센서로 부터 퓨전 데이터를 생성한 후, 객체 인식, 위치 및 거리를 인식하는 과정을 살펴보았다. 

레퍼런스

2022년 4월 24일 일요일

오픈소스 기반 ETL 도구 Apache nifi 이야기

이 글은 오픈소스 기반 ETL, Apache Nifi에 대한 개념, 기능 및 사용법에 대한 이야기이다. ETL은 요즘 말하는 데이터 레이크(data lake) 등에 필수적으로 사용되는 기술이다. ETL은 데이터 버스로도 알려져 있으며, 데이터를 추출, 변환, 적재(Extract, Transform and Load)하는 도구를 말한다.

아파치 기반 ETL 도구 nifi (nifi.apache.org)

머리말
Apache nifi 는 오픈 소스 ETL 도구이며 무료로 사용할 수 있다. 상자에서 시각적으로 프로그램을 조립하고(노드 기반 프로그래밍), 코드를 작성하지 않고도 실행할 수 있다. 따라서 코딩에 대한 배경 지식이 없는 사람에게 이상적이다. 카프카, RabbitMQ, JDBC 쿼리, Hadoop, MQTT, UDP 소켓 등을 포함한 다양한 소스와 함께 작동할 수 있다. 이를 사용하여 데이터를 필터링, 조정, 결합, 분할, 향상 할 수 있다.

Apache NiFi를 사용하면 장기 실행 작업을 생성할 수 있으며, 스트리밍 데이터와 주기적 배치를 처리할 수 있다. 수동으로 관리되는 작업도 가능하다. 

CSV 형식의 데이터에 국한되지 않다. 사진, 비디오, 오디오 및 바이너리 데이터를 쉽게 처리할 수 있다. 다양한 대기열 정책(FIFO, LIFO 등)을 사용할 수 있다. Data Provenance는 데이터 흐름 모든 것을 기록하는 서비스이다. 데이터가 어떻게 저장되거나 수행되었는지 확인할 수 있어 편리하다. 

기능 소개
Apache nifi에는 다음 기능이 포함되어 있다.
  • 브라우저 기반 사용자 인터페이스
  • 설계, 제어, 피드백 및 모니터링을 위한 원활한 경험
  • 데이터 출처 추적
  • 손실 허용 및 보장된 데이터 배송
  • 낮은 대기 시간 및 높은 처리량
  • 동적 우선순위 지정
  • 흐름 구성의 런타임 수정
  • 확장 가능한 디자인
  • 맞춤형 프로세서 및 서비스를 위한 구성 요소 아키텍처
  • 신속한 개발 및 반복 테스트
  • 보안 통신
  • 구성 가능한 인증 전략이 있는 HTTPS
  • TLS 및 SSH를 포함한 암호화된 통신을 위한 표준 프로토콜
사용법
다음은 nifi를 이용해, 많은 엑셀 파일에서 데이터를 추출해, MySQL 로 저장하는 과정을 보여준다. 코딩이 필요 없다.
 

nifi는 다음 링크에서 설치 가능하며, 도커 이미지도 지원하므로, 쉽게 설치할 수 있다.
nifi 도커 이미지

좀 더 상세한 내용은 레퍼런스를 살펴본다.

레퍼런스

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을 지원하며, 데이터 구조 및 알고리즘을 제공한다. 빠른 성능을 위해 최적화, 병렬화되었다. Open3D는 로보틱스, 무인자율차, 3차원 비전 기술 개발 등에 사용된다.  


핵심 기능은 다음과 같다.
  • conda, pip를 통한 간단한 설치
  • 3차원 자료 구조 및 알고리즘 제공
  • 장면 렌더링 및 3차원 시각화
  • 표면 정렬
  • 좌표 변환 행렬 지원
  • 메쉬 간 교차, 라플라시안 연산 등 제공
  • 공간 인덱싱 제공
  • 레이캐스팅, 거리 질의 등 연산 제공
  • 다양한 파일 포맷 지원
  • 파이썬 개발 지원. NumPy와 연동
  • CUDA 지원
  • 리눅스, 윈도우 등 다중 플랫폼 지원
  • Google CoLab 지원


사용 시작 
설치는 virtualenv 환경을 만든 후 open3d를 설치한다.
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

소스 코드 분석
우선 소스 코드를 분석을 위해, 소스 파일을 다운로드 받는다. 
git clone https://github.com/isl-org/Open3D

Open3D 파일구조는 다음과 같다. 

우분투에서 설치할 경우, util 폴더 아래 셀 명령을 실행해 디펜던시를 설치한다.
util/install_deps_ubuntu.sh

다운로드된 소스 파일 중에는 3차원 점군 가시화를 위한 예제들이 있는 데, 파이썬으로 실행해 볼 수 있다.

다음 코드는 open3d의 일반적인 동작 방식을 보여준다. 
import open3d as o3d
ply_point_cloud = o3d.data.PLYPointCloud()             # 포인트 클라우드 객체 생성
pcd = o3d.io.read_point_cloud(ply_point_cloud.path)  # PLY 파일 로딩
print(pcd)
print(np.asarray(pcd.points))
o3d.visualization.draw_geometries([pcd],
                                  zoom=0.3412,
                                  front=[0.4257, -0.2125, -0.8795],
                                  lookat=[2.6172, 2.0475, 1.532],
                                  up=[-0.0694, -0.9768, 0.2024])  # 점군 시각화


코드는 보다시피 매우 간략하고, 이해하기 쉽다. 

만약, 복셀을 다운 샘플링하고 싶다면, 아래 함수를 실행하면 된다.
downpcd = pcd.voxel_down_sample(voxel_size=0.05)
o3d.visualization.draw_geometries([downpcd],
                                  zoom=0.3412,
                                  front=[0.4257, -0.2125, -0.8795],
                                  lookat=[2.6172, 2.0475, 1.532],
                                  up=[-0.0694, -0.9768, 0.2024])

DBScan 알고리즘을 이용해 평면을 세그먼테이션해 줄 수도 있다(물론 알고리즘 특성상 완벽하지는 않음).
ply_point_cloud = o3d.data.PLYPointCloud()
pcd = o3d.io.read_point_cloud(ply_point_cloud.path)

with o3d.utility.VerbosityContextManager(
        o3d.utility.VerbosityLevel.Debug) as cm:
    labels = np.array(
        pcd.cluster_dbscan(eps=0.02, min_points=10, print_progress=True))

max_label = labels.max()
print(f"point cloud has {max_label + 1} clusters")
colors = plt.get_cmap("tab20")(labels / (max_label if max_label > 0 else 1))
colors[labels < 0] = 0
pcd.colors = o3d.utility.Vector3dVector(colors[:, :3])
o3d.visualization.draw_geometries([pcd],
                                  zoom=0.455,
                                  front=[-0.4999, -0.1659, -0.8499],
                                  lookat=[2.1813, 2.0619, 2.0999],
                                  up=[0.1204, -0.9852, 0.1215])


이외, 다양한 기능은 아래 튜토리얼을 참고한다. 
라벨링 점군 데이터 가시화 프로그램 개발
이 장에서는 다음과 같이 각 점군에 대한 라벨링 데이터가 포함된 파일을 읽어 가시화해주는 프로그램을 간단히 개발해 보겠다. 

다음은 코드 구현 내용이다. 
import copy
import numpy as np
import open3d as o3d
import argparse, readline

cmap = np.array([[1.00, 1.00, 1.00], [0.00, 0.00, 0.00], \
                    [1.00, 0.00, 0.00], [1.00, 0.00, 0.40], \
                    [1.00, 0.00, 0.80], [1.00, 0.20, 1.00], \
                    [1.00, 0.60, 1.00], [0.60, 0.80, 1.00], \
                    [0.20, 0.80, 1.00], [0.20, 0.80, 0.60], \
                    [0.20, 0.80, 0.00], [0.20, 0.40, 0.00], \
                    [0.20, 0.45, 0.40], [0.20, 0.40, 0.80], \
                    [0.60, 0.40, 0.80], [0.60, 0.80, 0.80], \
                    [0.60, 0.80, 0.40], [1.00, 0.60, 0.80]],'f')    # 컬러맵 정의

if __name__ == "__main__":
    parser = argparse.ArgumentParser()   # 명령행 인자 파싱
    parser.add_argument('--input_pcd', type=str, required=True)    # pcd format is txt. xyzrgb
    parser.add_argument('--input_label', type=str, required=True)
    args = parser.parse_args() 
    
    label = np.loadtxt(args.input_label)    # 라벨링 파일 로딩
    label = label.astype(np.int32)           

    pcd_origin = np.loadtxt(args.input_pcd, delimiter=' ')    # 텍스트 점군 파일 로딩. xyzrgb 포맷
    pcd = o3d.geometry.PointCloud()
    pcd.points = o3d.utility.Vector3dVector(pcd_origin[:,0:3])  # XYZ points
    pcd.colors = o3d.utility.Vector3dVector(cmap[label])       # 라벨을 컬러로 변환해 각 점군마다 설정

    o3d.visualization.draw_geometries([pcd])  # 가시화
    print('end')

결과는 다음과 같다.


마무리
지금까지 Open3D 라이브러리 개념, 기능 및 사용법을 설명하고, 간단한 프로그램을 개발해 보았다. 대중적인 알고리즘이 포함되어 있어, 컴퓨터 그래픽스 관련 기술 개발 시 유용하다. 


2022년 2월 25일 금요일

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

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

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

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