DBMS에서 ST_EXTENT 함수를 제공하지 않을 경우의 대안

PostgreSQL 등과 같은 공간 DB에서는 ST_EXTENT를 지원하지만, 오라클이나 티베로 등과 같은 DBMS에서는 이 함수를 제공하지 않습니다. 다행이 오라클은 SDO_AGGR_MBR라는 함수를 제공함으로써 ST_EXTENT 대신 사용할 수 있습니다. (최근의 오라클은 ST_EXTENT를 지원할지도 모르겠습니다) 그러나 오라클처럼 자체적인 함수 조차도 지원하지 않는 티베로와 같은 경우 사용할 수 있는 대안은 다음과 같습니다.

SELECT * 
FROM 
    (SELECT * FROM (SELECT ST_MINX(the_geom) MinX FROM TSTTABLE ORDER BY ST_MINX(the_geom) ASC) WHERE ROWNUM = 1 ), 
    (SELECT * FROM (SELECT ST_MAXX(the_geom) MaxX FROM TSTTABLE ORDER BY ST_MAXX(the_geom) DESC) WHERE ROWNUM = 1),
    (SELECT * FROM (SELECT ST_MINY(the_geom) MinY FROM TSTTABLE ORDER BY ST_MINY(the_geom) ASC) WHERE ROWNUM = 1),
    (SELECT * FROM (SELECT ST_MAXY(the_geom) MaxY FROM TSTTABLE ORDER BY ST_MAXY(the_geom) DESC) WHERE ROWNUM = 1);

대상 테이블의 이름은 TSTTABLE이고 Geometry 필드는 the_gem입니다. 결과의 예는 다음과 같습니다.

위의 쿼리는 티베로에서 실행한 결과입니다. 위처럼 하나의 Row로 결과를 얻을 수도 있지만 MinX, MaxX, MinY, MaxY 각각을 하나의 Row로 얻을 수도 있습니다. 아래처럼요.

SELECT * FROM (SELECT ST_MINX(the_geom) FROM TSTTABLE  ORDER BY ST_MINX(the_geom) ASC) WHERE ROWNUM = 1 
UNION SELECT * FROM (SELECT ST_MAXX(the_geom) FROM TSTTABLE  ORDER BY ST_MAXX(the_geom) DESC) WHERE ROWNUM = 1
UNION SELECT * FROM (SELECT ST_MINY(the_geom) FROM TSTTABLE  ORDER BY ST_MINY(the_geom) ASC) WHERE ROWNUM = 1
UNION SELECT * FROM (SELECT ST_MAXY(the_geom) FROM TSTTABLE  ORDER BY ST_MAXY(the_geom) DESC) WHERE ROWNUM = 1;

참고로 이 대안들은 속도가 느립니다. 테이블에 데이터가 개수가 많을 수록 그 속도는 더욱 느려집니다. 그러므로 해당 DBMS에서 ST_EXTENT를 제공한다면, ST_EXTENT를 사용해야 하며 ST_EXTENT를 지원하지 않는다면 해당 DBMS에서 자체적인 방법을 제공하는지 확인하여 그 방법을 사용해야 합니다. 자체적인 방법까지도 없다면 위의 대안을 사용할 수 있습니다.

Python의 Thread API

Python의 언어적 한계로 Thread 기능의 지원이 적합하진 않다고 하지만, 문제점을 최대한 개선하여 GIL이라는 장치를 통해 지원한다고 합니다. GIL은 Global Interpreter Lock의 약자입니다. 코드는 다음과 같습니다.

import threading

class T(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        pass

    def run(self):
        for i in range(100):
             print(self.name, i)

13번 줄의 코드인 self.name은 Thread-n과 같은 스레드의 내부 이름이며, 여기에서 n은 내부 번호입니다. 앞서 정의한 스레드 T의 사용은 다음과 같습니다.

ts = [T() for i in range(4)]

for t in ts:
    t.start()

총 4개의 스레드를 생성하고 start 매서드를 통해 스레드를 시작합니다.

GeoAI를 이용한 항공사진에서 건물과 비닐하우스 자동 검출

딥러닝을 활용하여 항공사진이나 드론영상에서 건물과 비닐하우스를 자동으로 검출하는 기능에 대한 글입니다. 사각형 영역의 검출(Detection)이 아닌 건물이나 비닐하우스의 형상을 검출(Segmentation)하는 방식입니다. CNN을 활용한 다양한 모델 중 Mask R-CNN 신경망을 커스터마이징하여 이용 했습니다. 신경망 학습을 위해 구축한 건물과 비닐하우스의 개수는 아래와 같습니다.

“사람”에 대한 검출을 위해 구축된 ImageNet 등의 데이터 갯수가 수천만개라는 것과 비교 했을때, 위의 구축 건수는 상대적으로 극히 적습니다.

딥러닝 프레임워크를 활용하여 24 Epoch만큼 학습하고 몇가지 영상 이미지를 학습된 신경망에 입력해 건물과 비닐하우스를 검출하는 테스트 결과는 아래의 동영상과 같습니다.

테스트를 웹에서 바로 수행할 수 있어, 추후 다양한 서비스에 쉽게 접목할 수 있도록 하였습니다. 결과를 보시면 몇개의 건물과 비닐하우스를 검출하지 못하지만, 대체적으로 건물과 비닐하우스를 잘 검출하는 것을 볼 수 있습니다. 보다 정확한 검출 위해서는 더 많은 학습 데이터를 구축하고, 좀더 효율적인 신경망과 다양한 학습방법을 시도함으로써 얻을 수 있습니다.

학습 DB의 구축은 자체적으로 개발한 GeoAI Labeling Tool을 사용였으며 자세한 소개는 아래와 같습니다.

GeoAI Labeling Tool 소개

GeoAI 레이블링 툴은 항공영상이나 드론영상에 대해 Detection과 Segmentation을 위한 데이터를 빠르게 구축하고 신경망 학습을 위한 데이터셋을 바로 제작할 수 있는 툴입니다.

끝으로, GeoAI를 이용해 영상으로부터 건물이나 비닐하우스 등과 같은 객체 검출의 용도를 생각해 보면.. 동일한 위치의 서로 다른 시간대에 촬영된 영상을 통해 새로운 건축물이 생겨 난 것을 빠르게 검출하고, 이러한 시계열적 건축물의 변화 탐지 통해 허가받지 않은 건축물을 파악하는 업무에 활용할 수 있을 것입니다.

Tibero의 JDBC 연결에 대한 예제

티베로에 대한 JDBC 연결 및 쿼리 예제입니다. 티베로의 JDBC 드라이브에 대한 jar는 티베로가 설치된 디렉토리 client/lib/jar에 tibero6-jdbc.jar 파일 하나입니다.

아래의 코드는 예제 코드입니다. 대부분의 DBMS에 대한 JDBC에 대한 예제 코드가 비슷한 패턴이므로 설명은 생략합니다.

package tstTibero;

import java.sql.Connection; 
import java.sql.DriverManager; 
import java.sql.ResultSet; 
import java.sql.ResultSetMetaData; 
import java.sql.SQLException; 
import java.sql.Statement;

public class MainEntry {
    private String ip = "192.168.0.254";
    private int port = 8629;
    private String database = "tibero";
    private String user = "sys";
    private String password = "tibero";
	
    private final String DRIVER_NAME = "com.tmax.tibero.jdbc.TbDriver";
    private final String TIBERO_JDBC_URL = "jdbc:tibero:thin:@" + ip + ":" + port + ":" + database;
	
    private Connection conn = null;

    private void connect() {
        try {
	    Class.forName(DRIVER_NAME);
            conn = DriverManager.getConnection(TIBERO_JDBC_URL, user, password);
        } catch(ClassNotFoundException e) {
            System.err.println(e);
        } catch(SQLException e) {
            System.err.println(e);
        }
    }
	
    private void executeQuery() {
        String sql = "select ST_AsText(GEOM) from tstGIS";
        try {
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(sql);
			
            while(rs.next()) {
                System.out.println(rs.getString(1));
            }
        } catch(SQLException e) {
            System.err.println(e);
        }
    }
	
    private void disconnect() {
        if(conn != null) {
            try {
                conn.close();
            } catch(SQLException e) {
                System.err.println(e);
            }
        }
    }
	
    public static void main(String[] args) {
        MainEntry tibero = new MainEntry();
		
        tibero.connect();
        tibero.executeQuery();
        tibero.disconnect();
    }
}