회전 이미지를 직사각형으로 Labeling하는 방법 - roLabelImg

프로그래밍 팁 2024. 2. 25. 23:13

 

 최신 인공지능 기술이 고도로 발전하여 뚜렷한 성과들이 눈앞에 펼쳐지고 있지만, 현업에서 최신 기술을 적용하는 것은 결코 쉽지 않습니다. 데이터 별로 각각의 특성이 존재하고, 그 데이터에 적합한 인공지능 기술이 반드시 최신 기술이어야만 하는 것은 아닙니다. 즉, SOTA 모델이 특정 데이터에서 가장 좋은 성능을 내지 못할 수도 있다는 것입니다. 다시 말해, 기업에서 수익을 목적으로 데이터에 AI를 적용하기 위해 가장 좋은 AI 모델을 찾는 것이 매우 중요하다는 것입니다.

 

 현업에서 사용하는 데이터를 가공하다 보니, 회전된 이미지가 어느 정도 기울어져 있는지 확인할 수 있는 방법을 찾아야 하는 상황이 생겼습니다. 관련 연구가 있는지 확인해보니 영상에서 기울어진 Object를  Bounding Box를 기울여서 표시하는 연구들이 진행되고 있는 것을 알게 되었습니다. 

 

 AI모델을 찾았으니 이 모델에서 학습할 수 있는 데이터를 만들어야겠지요? 이번 포스팅에서는 기울어진 물체를 Labeling할 수 있는 roLabelImg를 사용하는 방법에 대해 다루어보도록 하겠습니다.

 

https://github.com/cgvict/roLabelImg

 

GitHub - cgvict/roLabelImg: Label Rotated Rect On Images for training

Label Rotated Rect On Images for training. Contribute to cgvict/roLabelImg development by creating an account on GitHub.

github.com

 

 

1. python을 설치합니다. roLabelImg는 python 3.9 이하의 버전에서 구동이 가능합니다.

 

> conda create -n python3.9 -c conda-forge python=3.9

 

2. roLabelImg 프로그램 실행시 필수 패키지인 lxml과 pyqt를 설치합니다.

 

> conda install -c conda-forge lxml pyqt

 

2. git으로 roLabelImg를 다운로드합니다.

 

> git clone https://github.com/cgvict/roLabelImg

 

3. 설치된 pyqt에 포함된 pyrcc로 roLabelImg를 설정합니다. 자신이 설치한 pyqt버전이 5일 경우 pyrcc5를 실행합니다.

 

> pyrcc5 -o resources.py resources.qrc

 

 

4. roLabelImg를 실행합니다.

 

> python roLabelImg.py

 

위 과정대로 진행하셨다면 roLabelImg 프로그램이 실행되는 것을 확인하실 수 있습니다. 자신이 Label을 하고자 하는 이미지룰 불러봅니다.

 

 

 위성으로 찍은 공항 사진에서 비행기가 향하는 방향대로 Label을 진행해보겠습니다. 'Create Rotated RBox'를 클릭하여 아래와 같이 비행기를 전체적으로 Bounding합니다.

 

 

 마우스를 Drag하여 Bounding Box를 만드는 작업이 완료되면 아래와 같이 방금 만든 Label의 속성을 설정할 수 있습니다.

 

 

 방금 만든 Rotated RBox를 회전시켜 원하는 방향으로 Bounding Box를 돌려보겠습니다. 방금 만든 Bounding Box의 모서리 중 하나에 마우스를 이동시킨 다음 마우스 우측 버튼을 클릭후 드래그를 하면 Bounding Box가 회전하는 것을 확인하실 수 있습니다.

 

 

 아래 화면과 같이 Bounding Box가 비행기가 향하는 방향으로 회전된 것을 확인하실 수 있습니다.

 

 

 위와 같은 과정대로 이미지에 있는 3개의 비행기에 Rotated RBox를 모두 적용된 것을 확인하실 수 있습니다.

 

300x250

MMOCR로 OCR 이해하기(2) - OCR 데이터셋 만들기

공대생의 팁 2024. 1. 31. 23:53

 

 지난 포스팅에서 OCR(STR)의 개념과 발전 과정에 대해 설명드린 후, MMOCR의 동작 원리에 대해 설명을 드렸습니다. 이번 포스팅에서는 MMOCR에서 사용하고자 하는 데이터셋을 직접 제작하는 방법에 대해 소개드리도록 하겠습니다.

 

 혹시 OCR의 기본 개념 및 원리에 대해 자세히 알고 싶으신 분은 아래 포스팅을 참조해주세요!

https://elecs.tistory.com/434 

 

MMOCR로 OCR 이해하기(1) - OCR(STR)의 개념 이해

2023년은 생성 AI의 극적인 발전으로 실제 사람과 대화를 하는 듯이 답변을 하는 ChatGPT와 같은 생성형 인공지능이 등장하는 시대를 살아가고 있습니다. 심지어 GPT3.5를 뛰어넘은 GPT4가 ChatGPT에 적

elecs.tistory.com

 

 1. OCR 데이터셋의 구조

 

  딥러닝 분야에서 OCR은 이미지 내에서 글자를 찿아내는 Text Detection 과정과 이미지내 글자의 의미를 해석하는 Text Recognition 과정으로 분류됩니다. 쉬운 설명을 위해 사진으로 예시를 보여드리겠습니다.

 

 Text Detection은 이미지를 모델에 입력하였을 때, 이미지에서 글자의 위치를 찾아내는 단계라고 보시면 됩니다 예를 들어 아래와 같은 사진이 모델의 Input으로 들어왔다고 가정합니다.

 

 우리들의 눈으로 보았을 때, 위의 이미지에서 Text 정보로는 전철역 출입구 위에 적혀있는 'CC23', 'one-north'을 확인할 수 있습니다. OCR 모델의 출력은 사람이 글씨를 찾아내는 것과 같이 해당 글자 부분을 Object Detection처럼 표시해줄 것입니다.

 

 

 MMOCR에서 제공하는 Text Detection 데이터셋 구성은 JSON 파일로 Label이 되어 있으며, 다음과 같이 구성됩니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{
    "metainfo":
    {
        "dataset_type": "TextDetDataset",
        "task_name": "textdet"
        "category": [{"id:0, "name": "text"}]
    },
    "data_list":
    [
        {
            "img_path": "img_50.jpg",
            "height": 720,
            "width": 1280,
            "instances":
            [
                {
                    "polygon":[53.0, 137.0, 97.0, 138.0, 97.0, 153.0, 52.0, 151.0]],
                    "bbox": [52.0, 137.0, 97.0, 153.0],
                    "bbox_label": 0,
                    "ignore": false
                },
                {
                    "polygon":[107.0, 138.0, 217.0, 143.0, 216.0, 162.0, 106.0, 157.0]],
                    "bbox": [106.0, 138.0, 217.0, 162.0],
                    "bbox_label": 0,
                    "ignore": false
                }
            ]
        }
    ]
}
cs

 

Text Recognition은 입력된 이미지에서 Text를 추론하는 과정으로서, Text Recognition 모델의 출력은 해당 이미지의 Text입니다.

 Text Recognition 모델에 다음과 같은 이미지가 입력되었을 때, 

이미지 내의 Text가 Place임을 바로 알아낼 수 있습니다. Text Recognition 데이터셋 JSON Label은 다음과 같이 나타냅니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
    "metainfo":
    {
        "dataset_type": "TextDetDataset",
        "task_name": "textdet"
        "category": [{"id:0, "name": "text"}]
    },
    "data_list":
    [
        {
            "img_path": "word_25.png",
            "instances":
            [
                {
                    "text": "Place
                }
            ]
        }
    ]
}
cs

 

 2. 기존에 공개된 OCR 데이터셋 사용하기

 

 위에서 설명드린 양식대로 데이터셋을 직접 만들 수도 있지만, MMOCR을 처음 접하는 경우 데이셋의 구조가 바로 이해가 되지 않을 것입니다. 데이터셋을 당장 만들기 어려우신 분이라면, 기존에 공개된 OCR 데이터셋을 사용해보시는 것을 추천드립니다. MMOCR은 공개된 데이터셋을 MMOCR에서 활용할 수 있는 양식의 데이터셋으로 변환하여 사용할 수 있게 해줍니다. 변환된 데이터셋의 구조를 확인하고 이해하신다면 Custom Dataset을 만드시는데 큰 도움이 될 것입니다. MMOCR에서는 총 16개의 데이터셋의 다운로드를 제공하고 있으며, 데이터셋의 목록은 아래의 주소에서 확인하실 수 있습니다.

 

https://github.com/open-mmlab/mmocr/tree/main/dataset_zoo

 

 MMOCR에서 제공하는 데이터셋 다운로드 및 변환 기능을 사용해보도록 하겠습니다. 아래의 명령어를 입력하시면 ICDAR 2015 데이터셋을 다운로드한 다음, MMOCR 양식의 데이터셋으로 변환하실 수 있습니다.

 

python tools/dataset_converters/prepare_dataset.py icdar2015 --task textdet

 

 위의 명령어를 실행하면, ICDAR 2015 데이터셋을 Text Detection 양식의 데이터셋으로 변환하실 수 있습니다. 만약 Text Recognition 양식의 데이터셋을 원하시는 분께서는 task 옵션에 'textrecog'를 입력하시면 됩니다. 변환된 ICDAR2015 데이터셋을 data 폴더 내에서 확인하실 수 있습니다.

 

data/icdar2015
├── textdet_imgs
│   ├── test
│   └── train
├── textdet_test.json
└── textdet_train.json

 

3. Tool을 사용하여 Labeling하기(AnyLabeling)

 

 위의 설명을 통해 MMOCR 데이터셋의 구조를 이해하셨다면, 이번에는 우리의 손으로 직접 데이터셋을 만들어보겠습니다. Image 데이터에 Text 레이블링을 할 수 있는 프로그램 중 하나인 AnyLabeling으로 데이터셋을 사용하겠습니다. AnyLabeling은 아래의 github에서 다운로드하실 수 있습니다.

  

https://github.com/vietanhdev/anylabeling/releases

 

Releases · vietanhdev/anylabeling

Effortless AI-assisted data labeling with AI support from YOLO, Segment Anything, MobileSAM!! - vietanhdev/anylabeling

github.com

 

AnyLabeling을 설치하신 다음 실행하여 Labeling을 하고자 하는 이미지를 불러옵니다.

 

 

위의 사진에서 '71'이라는 텍스트가 가장 먼저 보일 것입니다. 이 부분을 Labeling해보도록 하겠습니다. 좌측에서 사각형 Labeling을 선택하신 후 71을 Bounding Box로 표시해줍니다.

 

 

71에 대해 박스 표시가 완료되면, 해당 박스의 Label 정보를 무엇으로 할 지 설정하는 창이 뜰 것입니다. 이 단계에서는 해당 Label의 속성을 설정하는 단계로서, 여기서는 Label의 이름을 'text'로 설정해줍니다. 선택 완료시 labels 목록에 'text'가 추가되었으며, Objects 목록에서도 방금 만든 Box의 객체 정보가 추가된 것을 확인하실 수 있습니다.

 

 

  다음으로, 방금전에 만든 Object에 Text 정보를 추가해보겠습니다. 좌측 하단에 연필 모양의 아이콘을 클릭하시면 수정모드에 진입하게 됩니다. 그와 동시에 우측 상단에 Text를 입력하는 칸이 활성화 됩니다. 여기서 주의할 것은 해당 칸이 활성화 되자마자 텍스트 정보 입력을 하게 되면, 해당 이미지 자체의 text 정보가 저장이 되버립니다. 방금 만든 '71'에 대한 Label 객체에 대해 text 정보를 추가하기 위해서는, 우측 정보란에 Objects 칸에서 '71'에 해당하는 Object를 목록에서 선택해줍니다.

 

 

위의 화면에서 빨간색으로 표시한 부분을 클릭하면 '71'에 대한 Label 객체가 선택된 것을 확인하실 수 있으며, Text 정보 입력 칸 윗부분 표시가 'Object Text'로 변경되면서 Text를 입력할 수 있게 되었음을 확인하실 수 있습니다. 여기서 text 정보인 71을 입력해주시면

 

 

위의 화면과 같이 '71'이라는 text가 해당 Label 객체에 적용된 것을 확인하실 수 있습니다. 위의 과정으로 만든 Label 정보는 아래와 같이 json 양식으로 저장된 것을 확인하실 수 있습니다.

 

 4. OCR 데이터셋으로 변환하기 

 

 지금까지 Anylabeling으로 이미지에 Text Labeling을 수행하는 방법에 대해 설명드렸습니다. 위의 과정으로 만든 json 파일을 MMOCR에서 학습할 수 있는 양식으로 변환하는 과정에 대해 설명드리도록 하겠습니다.

 

 먼저, 이미지에서 Text를 찾아내는 과정인 TextDetection 데이터셋으로 변환해보도록 하겠습니다. 

 

Anylabeling2textdet.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import json 
import random 
import glob 
import os
 
# Anylabeling으로 생성된 Label 데이터
input_path = 'Anylabeling' 
# TextDetection 데이터셋으로 변환된 json 파일 저장경로
output_path = 'TextDet'
 
def extract_instance(shape): 
    instance = dict()
    # 각 Instance별 Bounding Box 좌표값 추출
    x1 = int(shape['points'][0][0])
    y1 = int(shape['points'][0][1])
    x2 = int(shape['points'][1][0])
    y2 = int(shape['points'][1][1])
    instance['polygon'= [x1, y1, x2, y1, x2, y2, x1, y2]
    instance['bbox'= [x1, y1, x2, y2]
    instance['bbox_label'= 0
    instance['text'= shape['text'
    instance['ignore'= False
    return instance
 
def create_json(json_list):
    metainfo = dict()
    # Text Detection 데이터셋
    metainfo['dataset_type'= 'TextDetDataset'
    metainfo['task_name'= 'textdet'
    metainfo['category'= [{'id'0'name''text'}]
    data_list = []
 
    for file in json_list:
        file_path = os.path.join(file)
        file_name = os.path.splitext(os.path.basename(file))[0]
 
        # Anylabeling에서 생성된 JSON파일 Load
        with open(file_path) as f:
            data = json.load(f)
 
        img_info = dict()
        # 이미지 파일 경로. 자신의 환경에 맞게 수정.
        img_info['img_path'= os.path.join(input_path, file_name + '.jpg')
        img_info['height'= int(data['imageHeight'])
        img_info['width'= int(data['imageWidth'])
        img_info['instances'= [] 
        shapes = data['shapes'
 
        for shape in shapes:
            img_info['instances'].append(extract_instance(shape))
        data_list.append(img_info)
 
    json_text = dict()
    json_text["metainfo"= metainfo
    json_text['data_list'= data_list
    return json_text
 
#Input 폴더내 모든 json파일을 불러옴
json_paths = glob.glob(os.path.join(input_path, "*.json"))
#Train-Test셋 데이터 생성(10:1)
random_files = random.sample(json_paths, 11)
remaining_files = [file for file in json_paths if file not in random_files] 
trainset = create_json(remaining_files)
testset = create_json(random_files)
 
with open('textdet_test.json''w'as f:
    json.dump(testset, f, indent=4)
with open('textdet_train.json''w'as f:
    json.dump(trainset, f, indent=4)
cs
 
 

 위 소스코드를 샐행하면 아래와 같이 Text Detection Label 정보가 포함된 json 파일이 생성된 것을 확인하실 수 있습니다.

 

textdet_train.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
    "metainfo":
    {
        "dataset_type": "TextDetDataset",
        "task_name": "textdet",
        "category": [{"id":0, "name": "text"}]
    },
    "data_list":
    [
        {
            "img_path": "img_250.jpg",
            "height": 720,
            "width": 1280,
            "instances":
            [
                "ploygon": [536, 169, 583, 169, 583, 213, 536, 213],
                "bbox": [536, 169, 583, 213],
                "bbox_label": 0,
                "text": "71",
                "ignore": false
            ]
        }
    ]
}
cs

 

 다음으로 Text Detection 데이터셋 정보를 설정해줍니다. 아래의 예제는 Text Detection 데이터셋에 대한 설정 파일입니다.

 

textdet.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
textdet_data_root = 'data/textdet' #Text Detection Label JSON 폴더 위치
 
textdet_train = dict(
    type='OCRDataset',
    data_root=textdet_data_root,
    ann_file='textdet_train.json',
    filter_cfg=dict(filter_empty_gt=True, min_size=32),
    pipeline=None)
 
textdet_test = dict(
    type='OCRDataset'
    data_root=textdet_data_root,
    ann_file='textdet_test.json',
    test_mode=True,
    pipeline=None)
cs

 

 Text Detection 모델 중 하나인 TextSnake를 사용하여 위에서 작성한 Data Detection 데이터셋을 적용해보겠습니다.

 

textsnake.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
_base_ = [
    'configs/text/textsnake/_base_textsnake_resnet50_fpn-unet.py',
    # Text Detection 데이터셋 추가
    'textdet.py',
    'configs/textdet/_base_/default_runtime.py',
    'configs/textdet/_base_/schedules/schedule_sgd_1200e.py',
]
 
# dataset settings
train_list = _base_.textdet_train
test_list = _base_.textdet_test
 
train_dataloader = dict(
    batch_size=4,
    num_workers=4,
    persistent_workers=True,
    sampler=dict(type='DefaultSampler', shuffle=True),
    dataset=train_list)
 
val_dataloader = dict(
    batch_size=1,
    num_workers=1,
    persistent_workers=True,
    sampler=dict(type='DefaultSampler', shuffle=False),
    dataset=test_list)
 
test_dataloader = val_dataloader
 
auto_scale_lr = dict(base_batch_size=4)
cs

 

아래와 같이 한 줄의 명령어를 입력하시면 드디어 Text Detection 데이터셋이 학습되는 것을 확인하실 수 있습니다.

 

$ python tools/train.py textsnake.py
 

축하합니다! 여러분들을 드디어 Text Detection 데이터셋을 모델 학습에 적용하실 수 있게 되었습니다. Text Recognition 과정도 거의 비슷하게 진행되니, 잠시 숨을 고른 다음 Text Recognition 데이터셋 학습도 진행해보도록 합시다.

 

 아래의 소스코드를 실행하여 Text Recognition 학습을 위한 json 파일을 생성합니다. 

 

 

Anylabeling2textrecog.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import os
import copy
from PIL import Image
# Anylabeling이 생성한 json 폴더 경로
folder_path = 'Anylabeling' 
# Label 저장 경로
save_path = 'output' 
# folder_path 내부에 있는 json파일 모두 불러오기
json_files = [file for file in os.listdir(folder_path) if file.endswith('.json')]
# Text Recognition 양식의 JSON 파일 설정
data = {
    "metainfo": {
        "dataset_type""TextRecogDataset",
        "task_name""textrecog"
    },
    "data_list": []
}
# Train Set
train = copy.deepcopy(data) 
# Test Set
test = copy.deepcopy(data) 
# Train-Test Set 분배를 위한 카운터
count = 0
for json_file in json_files:
    # .json 확장자 제거
    file_name = os.path.splitext(json_file)[0
    with open(os.path.join(folder_path, json_file), 'r', encoding='utf-8'as file:
        json_data = json.load(file)
    image = Image.open(os.path.join(folder_path, file_name + '.jpg'))
    # 이미지에 Label이 있는지 확인
    if len(json_data['shapes'> 0
        shapes = json_data['shapes']
        # 이미지 내에 각 Label을 꺼내옴
        for i in range(len(shapes)): 
            # 원본 이미지에서 Text 정보 부분 추출
            cropped_image = image.crop((shapes[i]['points'][0][0],shapes[i]['points'][0][1],shapes[i]['points'][1][0],shapes[i]['points'][1][1]))
            # 추출된 Text 이미지를 별도의 폴더에 저장
            cropped_image.save(os.path.join(save_path, file_name + '_' + str(i) + '.jpg'))
            
            count+=1
            # Text 정보를 TextRecognition Dataset 양식으로 저장
            new_data = {
                "instances": [{"text": shapes[i]['text']}],
                "img_path": os.path.join(os.path.join(os.getcwd(), save_path, file_name + '_' + str(i) + '.jpg'))
                }
            # Train-Test Set 비율을 10:1로 저장
            if count % 11 == 0:
                test['data_list'].append(new_data)
            else:
                train['data_list'].append(new_data)
                
with open("textrecog_train.json""w"as outfile:
    json.dump(train, outfile, indent=4)
with open("textrecog_test.json""w"as outfile:
    json.dump(test, outfile, indent=4)
            
cs
 

 

 위 소스코드를 실행하면 Text Recognition Label 양식의 데이터가 적용된 json 파일이 생성된 것을 확인하실 수 있습니다.

 

textrecog_train.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
    "metainfo":
    {
        "dataset_type": "TextRecogDataset",
        "task_name": "textrecog"
    },
    "data_list":
    [
        {
           "img_path": "output/word_1.png",
            "instances":
            [
                {
                    "text": "71"
                }
            ]
        },
    ]
}
cs

 

 

바로 Text Recognition 데이터셋 설정 파일을 만들어줍니다.

 

textrecog.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
textrecog_data_root = 'data/textrecog' #Text Recognition Label JSON 폴더 위치
 
textrecog_train = dict(
    type='OCRDataset',
    data_root=textrecog_data_root,
    ann_file='textrecog_train.json',
    pipeline=None)
 
textrecog_test = dict(
    type='OCRDataset'
    data_root=textrecog_data_root,
    ann_file='textrecog_test.json',
    test_mode=True,
    pipeline=None)
cs

 

 

  거의 다 오셨습니다! SATRN 데이터셋에 방금 만든 Text Recognition 데이터셋을 적용해줍니다.

 

satrn.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
_base_ = [
    'textrecog.py',
    'configs/textrecog/_base_/default_runtime.py',
    'configs/textrecog/_base_/schedules/schedule_adam_step_5e.py',
    'configs/textrecog/satrn/_base_satrn_shallow.py',
]
 
train_cfg = dict(type='EpochBasedTrainLoop', max_epochs=20, val_interval=1)
 
# dataset settings
train_list = [_base_.textrecog_train]
test_list = [_base_.textrecog_test]
 
train_dataset = dict(
    type='ConcatDataset', datasets=train_list, pipeline=_base_.train_pipeline)
test_dataset = dict(
    type='ConcatDataset', datasets=test_list, pipeline=_base_.test_pipeline)
 
# optimizer
optim_wrapper = dict(type='OptimWrapper', optimizer=dict(type='Adam', lr=3e-4))
 
train_dataloader = dict(
    batch_size=128,
    num_workers=24,
    persistent_workers=True,
    sampler=dict(type='DefaultSampler', shuffle=True),
    dataset=train_dataset)
 
test_dataloader = dict(
    batch_size=1,
    num_workers=4,
    persistent_workers=True,
    drop_last=False,
    sampler=dict(type='DefaultSampler', shuffle=False),
    dataset=test_dataset)
 
val_dataloader = test_dataloader
 
#dataset_prefixes는 임의로 설정
val_evaluator = dict(dataset_prefixes=['IC15'])
test_evaluator = val_evaluator
 
auto_scale_lr = dict(base_batch_size=64 * 8)
cs

 

아래의 명령어를 통하여 SATRN 모델을 학습합니다.

 

$ python tools/train.py satrn.py

 

이제 여러분께서는 MMOCR을 사용하여 Text Detection과 Text Recognition 데이터셋을 만들고 모델을 학습시키는 방법을 터득하였습니다!

 

300x250

P2P 투자 법인 설립 후기(2) - 1년반만에 폐업합니다

흔치않은일상 2023. 12. 30. 17:49

 

 불과 작년에는 P2P 투자를 위해 법인을 설립하고 운영하는 방안에 대해 설명을 드렸습니다만 바로 다음으로 말씀드릴 이야기가 폐업소식이 될 줄을 제 자신도 몰랐습니다. 다행히도 적자 없이 작은 수익을 낼 수 있었으나, 굳이 법인을 유지하면서까지 관리를 해야 하는지에 대한 의문을 갖게 되었고, 가장 결정적으로 현재 자신이 하고 있는 일들에 조금 더 집중하고자 P2P 투자 법인을 폐업하는 것으로 결론을 내렸습니다.

 

 혹여나 P2P 투자를 권하지 않기 위해 이 글을 쓰느냐 하면, 그렇지는 않습니다. 비록 거대한 사업을 꾸리지는 않았지만, 일개 회사 직원으로서는 알수 없었던 실제 회사 경영에 대해 직접 경험할 수 있었고, 법인을 소흘히 관리했다가는 큰 코 다칠 수 있음을 몸소 깨달았기에, 차후 다른 사업으로 법인을 설립하여 대표가 되었을 때, 이 경험을 반면교사로 삼고자 이렇게 글을 남기는 것입니다. 제가 이전에 작성하였던 글도 참고하시어 여러분들의 투자 방향에 참고해 주셨으면 합니다.

 

 

P2P투자법인 운영을 중단한 이유는?

 법인을 설립하였던 2022년 초반, 넘쳐나는 자산 유동성만 보고 저는 단지 P2P 투자 한도를 늘리고자 하는 목적으로 투자 법인을 설립하게 되었습니다. 처음엔 법인 투자가 어려울 것이라 생각했지만, 법인 설립을 도와주겠다는 법률사무소들의 광고만 보았을 때는 누구나 쉽게 법인으로 원하는 사업을 할 수 있을 것 으로 착각하여 법인을 만들었습니다. 그러나, 폐업 및 법인 청산은 설립 과정보다 더 힘들다는 것을  의심하지 못했습니다. 저 또한 P2P 사업회사로 수익 창출만 생각하고 법인을 설립하였지만, 법인 관리 자체만 해도 많은 난관에 부딪치는 상황이 펼쳐졌습니다. 제게 P2P 투자 법인 설립이 매리트가 없었던 3가지 이유를 설명드리고자 합니다.

 

1. 너무나 어려운 법인통장 개설

 

 

 법인통장 개설 전 은행에 전화하여 구비 서류를 다 가지고 갔지만, 사업장 실사 방문, 사업자등록 1년 이상, 대표의 거주지와 법인의 사무실 위치 등 추가적으로 요구하는 사항들이 너무나 많았고, 간신히 모 저축은행에서 계좌를 개설할 수 있었지만, 해당 은행에서 OTP를 제공하지 않아 타 은행 OTP를 등록해야 인터넷 뱅킹을 사용할 수 있는 상황이어서 한동안은 지점에 직접 방문해서 은행 업무를 수행해야 했습니다.

 이토록 은행들이 계좌 취급을 까다롭게 하는 이유는 다름아닌 보이스피싱 예방 차원이라 하지만, 악의적인 목적을 갖고 있지 않은 법인사업자가 통장 없이 어떻게 사업을 운영하라는 건지 참으로 답답한 상황이었습니다. 운 좋게도 1년동안 법인 운영을 열심해 한 덕에 간신히 케이뱅크 법인계좌를 만들 수 있었습니다만, 애초에 이렇게 은행 계좌 하나 만드는게 이토록 어려운 일이었다면 저는 아마도 법인 설립 자체를 시도도 하지 않았을 것입니다.

 

 무엇보다도 보이스피싱이 의심된다고 무턱대고 계좌 개설을 방어하게 만든 문제로 인해 사업을 시작하는 사람들의 꿈이 좌절되는 상황이 한시바삐 해결되었으면 합니다.

 

2. 설립은 쉬우나 해산은 어려운 법인 관리

 

 

 법인 설립은 생각보다 쉽습니다. 업종 선택을 하고 사무실을 구한 다음 자본금과 각종 수수료(법률사무소 대행 수수료, 각종 증빙서류 발급수수료 등)만 지출하면 법인 설립이 완료됩니다.

 그러나 법인의 해산은 절차가 훨씬 어렵습니다. 자본금이 잠식되어 적자 상태인 법인은 채무 청산을 하지 않으면 법인 해산이 불가능하며, 설령 빚이 없는 법인이더라도 법인 혼자서 해산을 진행하기엔 절차가 상당히 복잡하여 법률사무소에 대행을 요청해야 하는데 전체 비용이 2023년 기준 200만원에 육박합니다. 법인 자산 관리를 제대로 하지 못할 경우 금전적인 면에서도 시간적인 면에서도 손해일 수 있습니다. 소액 투자 법인의 경우 법인 등기 신청 후 8년동안 아무것도 하지 않고 기다리면 해산간주하여 자본금을 회수할 수 있지만, 5년동안 그 어떤 영리활동을 할 수 없어 8년간 발생하게 되는 법인 관리 비용이 골치아플 수 있습니다.

 

 물론 저는 이러한 법인 관리의 어려움을 감당할 자신이 있습니다. 그러나 제가 P2P 투자법인을 청산하기로 결정한 계기는 미래의 불확실성이었습니다.

 

3. 예측 불가한 투자 환경의 변화

 

 

 어떤 자산에 투자하든 위험요소를 관리해야 한다는 것은 자명한 이야기입니다. 2023년 현재 대한민국의 경제 상황을 보았을 때, 부동산 투자 상품이 주를 이루는 P2P투자에 위험요소가 커졌다고 저는 중대한 판단을 하게 되었습니다. 2020년 코로나19바이러스 전염으로 전 세계가 경제 침체를 겪고 있는 상황에서 각국은 금리 인하로 경제 부양을 하게 되었고 그 결과 시장에는 엄청난 양의 돈이 쏟아지게 되었습니다. 주식 시장은 V자 반등으로 다시 상승할 수 있었고, 부동산으로도 풀린 자금들이 유입되면서 전국적으로 집값이 2배 이상 상승하는 곳이 대다수였을 정도였습니다.

 그러나 시장에 엄청난 돈이 풀리면서 돈의 가치 하락으로 인해 인플레이션 증가율이 상승시켰고, 이 와중에 러시아가 우크라이나를 침공이 촉발한 원자재 가격 상승으로 인플레이션이 가속화되자 당국은 물가 안정을 위해 기준금리를 올렸고, 자금 경색으로 인해 부동산 기반 프로젝트 파이낸싱(PF)으로 대출을 받던 건설사들이 불어난 이자를 감당하지 못해 최근 T건설이 워크아웃을 신청하는 상황에 이르렀습니다.

 

 지난 2008년 리먼브라더스 사태로 촉발된 경제 위기로 자산 가치가 폭락하였던 비교적 가까운 과거의 상황과 비교하였을 때, 지금 상황이 그렇게 녹록치 않다는 생각을 하였습니다. 차후 P2P 투자상품이 PF로 촉발된 건설업의 침체르 인해 상품의 리스크가 커질것으로 보아 적어도 지금 내가 가지고 있는 자산을 지켜야 하는 방향으로 관리를 해야 겠다 판단을 하였고, P2P 투자 법인을 정리하기로 하였습니다. 투자금이 회수되지 않으면 P2P 투자 법인을 정리하는 것이 더 어려워지기 때문에, 이번달 모든 투자 원금 및 이자를 회수하였고, 며칠전 법인 폐업 신고를 하였습니다. 차후 8년간 P2P 법인 활동을 할 계획이 없으므로 8년 후 자본금과 이자를 회수할 수 있을 것입니다. 하지만, 그 기간 동안 인플레이션으로 인한 돈의 가치를 고려한다면, 안타깝게도 저에게 P2P 법인투자는  큰 이익이 되지 못할 것입니다.

 

 

 

 투자를 함에 있어 가장 중요한 것은 리스크를 어떻게 관리할 것인지를 잊지 말아야 할 것입니다. 설령 시중은행에 예금을 하더라도 5천만원까지만 보호받을 수 있음을 유념하여 자신의 투자 방향에 부합하는지 곰곰히 생각한 후 투자를 결정하셨으면 합니다.

 

 비록 지금은 이렇게 투자 실패기를 작성하지만, 차후에는 좀더 현명한 판단으로 재산도 지키고 자산을 불릴 수 있는 좋은 결과를 소개해드리겠습니다.

 

 

300x250