Study/C#

C#에서 kd-tree 사용하기 (3차원, 2차원 상의 특정 점에서 가장 가까운 점 찾기)

13.d_dk 2021. 2. 28. 02:23
728x90
반응형

문제의 정의

 3차원의 모델 데이터를 점들로 표현할 수 있다. 그리고 외부의 어떤 한 점이 있을 때, 이 점으로부터 가장 가까운 한 점을 모델 데이터에 속하는 점들에서 찾고자 한다. 

 

방법; kd-tree

 여러 가지 방법이 있을 수 있다. 가장 간단한 방법은 모든 점들을 저장하고 외부의 한 점과 거리를 모두 측정하여 가장 가까운 점을 찾으면 된다. 하지만 이 방법은 매우 오래 걸릴 수 있다. 이를 효율적으로 가장 가까운 점을 찾기 위해서 kd-tree를 사용할 수 있다.

 kd-tree는 수많은 점들을 효율적으로 접근할 수 있는 구조를 만들어 데이터들을 저장해두는 방식이다. 효율적으로 접근할 수 있는 구조로 되어 있기 때문에 원하는 점을 빨리 찾을 수 있다.

 데이터를 효율적으로 접근하기 위한 구조는 이진트리 구조(Binary Search Tree; BST)를 기반으로 한다. BST를 간략하게 설명해보자. BST는 여러 노드로 이루어져 있다. 이 중 하나의 노드는 값을 가지고 2개의 또 다른 노드들을 가지고 있다. 2개의 다른 노드는 똑같이 값을 가지며 또 다른 노드 2개를 가진다. 2개의 또 다른 노드를 자식 노드라고 하고 값을 가지는 노드를 부모 노드라고 명명할 수 있다. 예시를 위해 어떤 숫자의 집합이 있다고 가정하자. 이를 설명한 구조로 정리할 수 있다. 이를 그림으로 표현하면 다음과 같으며 마치 나무의 가지가 뻗어가는 모양이 나타난다. (트리 구조)

숫자의 집합을 이진 트리 구조로 나타난 예시

 

 이 구조를 자세히 보면 가장 가운데의 부모 노드에 여러 숫자들 중 가운데 숫자를 값에 넣었다. 이후 2개의 자식 노드에 각각 하나씩 작은 숫자, 큰 숫자를 넣었다. 이를 반복하여 정리된 구조라는 것을 알 수 있다.

 여기서 어떤 숫자와 가장 가까운 숫자를 찾는다고 생각해보자. 이러한 구조가 없다면 모든 값과 비교하며 가장 가까운 값을 찾으므로 많은 시간이 소요된다. 반면에 이진트리 구조를 사용한다면 하나씩 가지를 따라가며 빠르게 찾을 수 있다. 이를 그림으로 표현하면 아래와 같다.

숫자의 집합에서 어떤 값과 가장 가까운 값 찾는 두 방법의 비교

 

 kd-tree는 이러한 BST를 차원에 적용한 구조이다. 위에서 설명한 숫자의 집합으로 트리를 구성한다면 이는 1d-tree라고 볼 수 있다. x, y의 두 축의 값들을 가지는 점들로 트리를 구성한다면 2d-tree이다. x, y, z의 축을 가지는 3차원의 점들로 트리를 구성하면 3d-tree가 된다. 여기서 2d부터는 트리로 구조화되어 갈 때 각 축을 번갈아가며 노드들을 구성한다. 이를 쉽게 이해하기 위해 아래의 그림을 보자.

여러 차원에 따른 트리 구조화 설명

 

 그림에서 보이는 바와 같이 2d-tree의 경우 처음 부모 노드는 x축으로 정했다. 이후 다음 두 노드에 대해서는 y축으로 노드를 설정하였다. 이후 다음 4개의 노드(두 노드가 또 다른 자식 노트를 2개 가지므로)는 x축으로 정해진다. 3d-tree는 처음에는 x축, 그다음 층은 y축, 또 그다음 층은 z 축으로 정해져 있다. 그리고 다음 층마다 이를 반복하게 되어 있다.

 3차원의 모델 데이터를 3차원 점들의 집합으로 표현할 수 있다. 이를 kd-tree(3d-tree)에 구조화를 하여 저장한다. 이후 외부의 한 점에 대하여 가장 가까운 점을 구조화된 tree에서 각 축별로 비교해가며 찾으면 모든 점을 비교하는 것보다 매우 빠르게 찾을 수 있다.

 

C#에서 kd-tree 라이브러리 사용하기

 Visual studio로 C# 개발을 한다면 외부 라이브러리 패키지로 Nuget을 사용할 수 있다. kd-tree를 Nuget을 사용하여 설치하고 이를 사용해보자. 먼저 Nuget으로 kd-tree를 설치해보자. Visual studio 상단 메뉴 중 도구(T)-Nuget 패키지 관리자(N)-패키지 관리자 콘솔(O)를 선택하자.(이 글에서는 visual studio 2012를 사용하였음)

비주얼 스튜디오에서 Neget 패키지 관리자 콘솔을 실행

 

 하단에 나타난 패키지 관리자 콘솔의 PM> 부분에 Install-Package KdTree를 입력하여 실행하자. 

 몇 가지 문구와 함께 KdTree가 설치됨을 확인하자. 또 Solution-Project의 참조에 KdTreeLib가 추가되어 있음을 확인하자.

kd-tree 라이브러리를 Nuget으로 설치를 완료하였을 때 보이는 텍스트
 KdTreeLib가 참조에 추가된 모습

 

 Visual studio에서 콘솔 응용 프로그램을 만들어 테스트해보자. 먼저 kd-tree 라이브러리 사용을 위해 네임스페이스를 정의하자. 이후 kd-tree 라이브러리를 사용하여 트리 구조 변수를 생성하자. 이를 위해 아래와 같이 코드를 입력하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using KdTree;
using KdTree.Math;
 
namespace kdTreeTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // define tree
            var tree = new KdTree<floatstring>(3new FloatMath());
        }
    }
}
 
cs

 

 만들어진 트리 구조에 3차원 점을 임의로 만들어 넣어준다. 예제에서는 a, b, c라는 이름으로 3점을 추가하였다.

1
2
3
4
5
6
7
8
9
10
11
static void Main(string[] args)
{
    // define tree
    var tree = new KdTree<floatstring>(3new FloatMath());
 
    // define each points (3 points)
    // {x, y, z}, point name
    tree.Add(new[] { 50.0f, 80.0f, 80.0f }, "a");
    tree.Add(new[] { 20.0f, 10.0f, 20.0f }, "b");
    tree.Add(new[] { 20.0f, 10.0f, 10.0f }, "c");
}
 
cs

 

 외부의 특정 점을 정하고, 트리에 있는 함수인 GetNearestNeighbours에 파라미터로 입력하여 노드를 찾자. 첫 번째 입력 파라미터는 외부의 특정 점이며 두 번째 입력 파라미터는 nodes에 순서대로 몇개나 가까운 점을 저장할 것인지 결정한다. 예를 들어 1을 입력으로 두면 가장 가까운 점 1개를 저장하며 3인 경우 가장 가까운 점, 두 번째로 가까운 점, 세 번째로 가까운 점 총 3개를 저장한다. 이후 이 노드에 있는 가장 가까운 점의 값이 맞는지 확인하기 위해 콘솔 창에 출력해보자. 코드는 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static void Main(string[] args)
{
    // define tree
    var tree = new KdTree<floatstring>(3new FloatMath());
 
    // define each points (3 points)
    // {x, y, z}, point name
    tree.Add(new[] { 50.0f, 80.0f, 80.0f }, "a");
    tree.Add(new[] { 20.0f, 10.0f, 20.0f }, "b");
    tree.Add(new[] { 20.0f, 10.0f, 10.0f }, "c");
 
    // cal. nearest neighbours
    // {x, y, z}, num of point that nearest neighbours
    var nodes = tree.GetNearestNeighbours(new[] { 30.0f, 20.0f, 10.0f }, 1);
 
    // show point that nearest neighbours
    Console.WriteLine("name of point : " + nodes[0].Value.ToString());
    Console.WriteLine("x : " + nodes[0].Point[0].ToString());
    Console.WriteLine("y : " + nodes[0].Point[1].ToString());
    Console.WriteLine("z : " + nodes[0].Point[2].ToString());
}
 
cs

 

 임의의 점 [30, 20, 10] 에서 가장 가까운 점은 c이고 x, y, z 각 값은 [20, 10, 10] 임을 출력을 통해 확인할 수 있다.

콘솔에 출력되는 가장 가까운 점의 이름과 각 축의 값

 

 만약 두번째로 가까운 점을 확인하고 싶다면 GetNearestNeighbours에 3번째 입력 파라미터를 2로 바꾸고, 노드에 접근하는 인덱스를 1로 바꾸면 확인할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static void Main(string[] args)
{
    // define tree
    var tree = new KdTree<floatstring>(3new FloatMath());
 
    // define each points (3 points)
    // {x, y, z}, point name
    tree.Add(new[] { 50.0f, 80.0f, 80.0f }, "a");
    tree.Add(new[] { 20.0f, 10.0f, 20.0f }, "b");
    tree.Add(new[] { 20.0f, 10.0f, 10.0f }, "c");
 
    // cal. nearest neighbours
    // {x, y, z}, num of point that nearest neighbours
    var nodes = tree.GetNearestNeighbours(new[] { 30.0f, 20.0f, 10.0f }, 2);
 
    // show point that nearest neighbours
    Console.WriteLine("name of point : " + nodes[1].Value.ToString());
    Console.WriteLine("x : " + nodes[1].Point[0].ToString());
    Console.WriteLine("y : " + nodes[1].Point[1].ToString());
    Console.WriteLine("z : " + nodes[1].Point[2].ToString());
}
 
cs
콘솔에 출력되는 두번째로 가까운 점의 이름과 각 축의 값

 

Reference

 

[자료구조] 9-3. 이진 탐색 트리의 연산 (2)

이진 탐색 트리의 추가 연산은 저장할 노드를 생성하는 과정을 통해 수행된다. 새로 추가되는 노드는 반드시 leaf node이다. 먼저 다른 자료구조에서 시간 복잡도가 어떻게 나오는지 복습해보자. -

makemethink.tistory.com

 

Octree-kdTree · 3D_People_Detection

 

adioshun.gitbooks.io

 

26. BST (Binary Search Tree, 이진탐색트리) 알고리즘

1. 개요  BST 알고리즘은 쉬움.  부모 노드를 기준으로 좌측엔 부모 노드보다 작은 데이터, 우측엔 부모 노드보다 큰 데이터 삽입. (반드시 만족해야 함)  가장 오른쪽 노드의 데이터가 최대 크기

makefortune2.tistory.com

 

균형 이진탐색트리(Balanced Binary Search Tree)

트리에서는 기본적인 트리 자료구조에 대해서 알아 보았고 이어서 자식이 2개 이하로만 구성되어 있는  이진트리(Binary Tree) 그리고 이진트리 중 검색을 매우 빠르게 할 수 있는 자료구조인 

jackpot53.tistory.com

 

KDTree(T) Class

K-dimensional tree. Namespace:  Accord.Collections Assembly:  Accord.MachineLearning (in Accord.MachineLearning.dll) Version: 3.8.0 [SerializableAttribute] public class KDTree : KDTreeBase > Public Class KDTree(Of T) Inherits KDTreeBase(Of KDTreeNode(Of

accord-framework.net

 

codeandcats/KdTree

A fast, generic, multi-dimensional Binary Search Tree written in C# - codeandcats/KdTree

github.com

 

KdTree

Generic multi-dimensional binary search tree.

nugetmusthaves.com

 

반응형