GeoAI Labeling Tool 소개

GIS 기반의 AI 기술 중, 항공영상이나 위성영상 지도로부터 특정 대상을 추출해 내는 기능이 있습니다. 특정 대상이라함은 영상 지도에서 ‘건물’이나 ‘차량’, ‘비닐하우스’ 등과 같은 것을 말합니다. AI에서는 이처럼 특정 대상을 분류하고 검출하는 모델을 딥러닝(Deep Learning)을 통해 신경망 차원에서 학습 및 개발할 수 있는데요. 이처럼 이미지를 통해 특정 대상을 검출하는 방식으로는 Detection과 Segmentation 방식이 있습니다. 이 두가지에 대한 보다 자세한 내용은 아래의 글을 참고 하시기 바랍니다.

사람에 대한 Detection, Segmentation @A.I-TestBed

위의 글은 Detection과 Segmentation에 대한 개념적 소개와 그 차이점, 그리고 실제로 웹에서 이미지를 입력하면 해당 이미지에서 ‘사람’을 추출해 내는 실제 개발된 시스템에 대한 소개입니다.

이미지에 대한 Detection과 Segmentation에 대한 신경망 모델은 매우 다양합니다. 모델에 따라 분류 정확도 및 정밀도에 대한 지표에 차이가 있습니다. 이런 점에서 신경망 모델의 선택도 중요하지만, 이보다 훨씬 더 중요한 것은 신경망 학습에 사용되는 데이터, 즉 학습 데이터가 얼마나 정확하고 얼마나 더 많은가가 더욱 중요합니다.

이 글에서 소개하는 GeoAI 레이블링 툴은 항공영상이나 위성영상에 대해 Detection과 Segmentation을 위한 데이터를 빠르게 구축할 수 있는 툴로써 다음과 같은 장점을 갖습니다.

아래의 동영상은 GeoAI Labeling Tool에서 Detection 데이터를 구축하는 내용을 담고 있습니다.

추가로 아래의 동영상은 GeoAI Labeling Tool에서 Segmentation 데이터를 구축하는 내용을 담고 있습니다.

DuraMap-Xr에서 고정값으로 라벨 회전

라벨을 일정한 값으로 회전하는 API에 대한 내용을 정리합니다. 아래는 해당 코드입니다.

if(axXr1.Layers.AddShapeMapLayer("lyr", "d:/_/ecl_cadastral.shp"))
{
    axXr1.WaitForAllConnections();

    axXr1.Labels.AddLabel("lyr", "{label}");
    
    var label = axXr1.Labels.GetLabel("lyr", "{label}");

    label.Rotate.Enable = true; // <회전을 위해 반드시 지정해야 함>
    label.Rotate.Angle = 90; // <회전값, 단위: Degree>
    label.Effect.Enable = true; // <회전을 위해 반드시 지정해야 함>
    label.Effect.OutlineEnable = true;
    label.Effect.OutlineColor = RGB(255, 255, 255);
    label.Effect.OutlineWidth = 3;
    label.Font.Bold = true;
    label.Font.Color = RGB(0, 0, 0);
    label.Font.Size = 16;

    axXr1.ZoomFullExtent();
    axXr1.MapScale = 2500;
    axXr1.MouseMode = XrMapLib.XrMapViewModeEnum.XrPanMode;
    axXr1.Update();
}

위의 코드는 모든 라벨의 텍스트를 일정하게 90도로 회전하는 코드인데요. 아래는 위의 코드에 대한 실행 결과입니다.

위의 코드가 지원되는 DuraMap-Xr의 버전은 3.9.8.4입니다. 듀라맵의 최선 버전은 http://www.gisdeveloper.co.kr/?p=4760 에서 다운로드 받을 수 있는 설치관리자 프로그램을 이용해 간단히 설치할 수 있습니다.

DuraMap-Xr에서 Image Symbol 크기 조정

듀라맵에서 ShapeMapLayer에 대한 이미지 심벌을 지정할 경우에, 그 크기는 원본 이미지 크기 그대로 표시됩니다. 이에 대해 개발자가 직접 이미지의 크기를 지정할 수 있는 방식도 제공되는데요. 아래의 코드는 이미지의 크기를 가로 84px로 지정하는 경우입니다. 세로의 크기는 가로와 세로에 대한 비율을 유지하도록 자동으로 계산됩니다.

axXr1.Layers.AddShapeMapLayer("lyr", "D:/__Data__/points.shp");
axXr1.WaitForAllConnections();

if (axXr1.Resources.AddImageResource("img", "d:/__data__/temperature.png"))
{
    var sym = axXr1.Layers.GetLayerAsShapeMap("lyr").PointSymbol;

    sym.SetImage("img", axXr1.Resources);
    sym.Size = 84; // 이미지 심벌을 가로로 84px로 지정 !!
}

axXr1.Update();

위의 코드와 함께 듀라맵에 소소한 개선으로 두가지가 이루어졌는데요. 첫째는 휠마우스에 대한 Delta 값을 보다 정확히 음수와 양수로 얻을 수 있다는 것과 두번째는 GetFIDFromMousePoint 매서드를 통해 도형을 선택할 때, 포인트 심벌의 크기에 맞춰 선택할 수 있도록 개선되었는데요. 기존에는 포인트 심벌의 크기를 고려하지 않아 포인트의 심벌 중앙을 클릭해야 선택되는 사용상의 불편함이 해소되었습니다.

DuraMap-Xr에서 마우스 Wheel로 지도 확대, 축소하기

DuraMap-Xr의 Mouse Wheel 이벤트를 이용해 지도를 확대 축소하는 이벤트에 대한 글을 정리합니다. 배경지도는 TMS 형식으로 활용할 수 있는 VWorld를 이용합니다. 먼저 VWorld의 배경지도에 대한 Layer는 아래처럼 추가할 수 있는데요. 저는 아래의 코드를 폼(Form)이 Shown 이벤트에 추가하였습니다.

XrMapLib.ValueList URLs = new XrMapLib.ValueList();
URLs.AddValueByString(VWORLD_TMS_URL);

XrMapLib.Extent MaxMBR = new XrMapLib.Extent();
MaxMBR.MinX = -20037508.342789244;
MaxMBR.MinY = -20037508.342789244;
MaxMBR.MaxX = 20037508.342789244;
MaxMBR.MaxY = 20037508.342789244;

double[] upps = {
    78000, 39000, 19600, 9800, 4900, 2400, //Dummy
    1222.9924523925781, 611.4962261962891, 305.74811309814453,
    152.87405654907226, 76.43702827453613, 38.218514137268066,
    19.109257068634033, 9.554628534317017, 4.777314267158508,
    2.388657133579254, 1.194328566789627, 0.5971642833948135, 0.5971642833948135/2 };

XrMapLib.ValueList UPPs = new XrMapLib.ValueList();

Scales.Clear();

for (int iUPP = 0; iUPP < upps.Length; ++iUPP)
{
    double UPP = upps[iUPP];
    double MapScale = axXr1.GetMapScaleFromUPP(UPP);

    UPPs.AddValueByFloat(UPP, 14);

    if (iUPP > 5) // upps 배열에서 0~4번째는 한국에서는 사용되지 않음
    {
        Scales.Add(MapScale);
    }
}

axXr1.Layers.AddTMSLayer("basemap", URLs, MaxMBR, UPPs, true);
axXr1.WaitForAllConnections();

XrMapLib.Coord MapPt = new XrMapLib.Coord();
MapPt.X = 14145315;
MapPt.Y = 4514689;
            
axXr1.MapCenter = MapPt;
axXr1.MapScale = Scales[0];
axXr1.Update();

axXr1.MouseMode = XrMapLib.XrMapViewModeEnum.XrPanMode;

참고로 듀라맵 지도 뷰어 컨트롤의 변수는 axXr1입니다. 위의 코드를 보면 Scales라는 전역 변수가 보입니다. 이 Scales는 다음처럼 정의되어 있습니다. Scales 컨터이너 변수는 각 지도 줌 단계에 대한 축척값을 담고 있습니다.

private List<double> Scales = new List<double>();

이제 지도 컨트롤의 마우스 휠 이벤트를 등록합니다. 아래의 코드는 듀라맵 지도 뷰 컨트롤의 휠 이벤트에 할당된 이벤트 함수입니다.

private void axXr1_OnMouseWheel(object sender, AxXrMapLib._IXrMapControlEvents_OnMouseWheelEvent e)
{
    double NewMapScale = axXr1.MapScale;

    // 현재 지도 축척값 중 가장 가까운 줌 레벨 얻기
    int cntLevels = Scales.Count;
    double Gap = Double.MaxValue;
    int CurrentLevel = -1;
    for (int i = 0; i < cntLevels; i++)
    {
        double ThatGap = Math.Abs(NewMapScale - Scales[i]);
        if (Gap > ThatGap)
        {
            Gap = ThatGap;
            CurrentLevel = i;
        }
    }

    if(e.delta == 120) // Wheel Up
    {
        int idx = CurrentLevel + 1;
        if (idx >= Scales.Count) idx = Scales.Count - 1;
        axXr1.MapScale = Scales[idx];
    }
    else  // Wheel Down
    {
        int idx = CurrentLevel - 1;
        if (idx < 0) idx = 0;
        axXr1.MapScale = Scales[idx];
    }

    axXr1.Update();
}