<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>happy_quokka.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 10 Dec 2024 05:33:46 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>happy_quokka.log</title>
            <url>https://velog.velcdn.com/images/happy_quokka/profile/ed690b7f-2ab6-4ac4-8f62-9441982e4704/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. happy_quokka.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/happy_quokka" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Ubuntu 18.04] Anaconda 설치]]></title>
            <link>https://velog.io/@happy_quokka/Ubuntu-18.04-Anaconda-%EC%84%A4%EC%B9%98</link>
            <guid>https://velog.io/@happy_quokka/Ubuntu-18.04-Anaconda-%EC%84%A4%EC%B9%98</guid>
            <pubDate>Tue, 10 Dec 2024 05:33:46 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/happy_quokka/post/30d82663-8978-4f9a-bc18-f3838d2fd936/image.png" alt=""></p>
<h3 id="1-anaconda-다운로드">1. Anaconda 다운로드</h3>
<ul>
<li><a href="https://www.anaconda.com/download#Downloads">Anaconda 홈페이지</a>에서 installer 다운로드
: Linux -&gt; 64-bit installer의 링크 주소 복사</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/e68e7629-b59b-4176-a440-0de4b7b481dc/image.png" alt=""></p>
<ul>
<li>터미널에서 wget 파일로 다운로드<pre><code>$ wget https://repo.anaconda.com/archive/Anaconda3-2024.10-1-Linux-x86_64.sh</code></pre></li>
</ul>
<h3 id="2-sh-파일-권한-설정">2. sh 파일 권한 설정</h3>
<ul>
<li><p><code>ll</code>을 입력하여 권한을 확인하였을 때 x 즉, 실행 권한이 없으면 권한을 실행 권한을 추가해줘야 한다
<img src="https://velog.velcdn.com/images/happy_quokka/post/144ea3e0-9f89-4397-a0ba-03ef940ecf10/image.png" alt=""></p>
</li>
<li><p><code>sudo chmod +x Anaconda3-2024.10-1-Linux-x86_64.sh</code> 로 실행 권한 추가
<img src="https://velog.velcdn.com/images/happy_quokka/post/ff11226e-8879-45cb-bfb5-a128d51112aa/image.png" alt=""></p>
</li>
</ul>
<h3 id="3-sh-파일-실행">3. sh 파일 실행</h3>
<pre><code>$ ./Anaconda3-2024.10-1-Linux-x86_64.sh</code></pre><ul>
<li><p>please review the license라는 문구가 나오고 ENTER를 입력하라고 하면 ENER를 치면 된다
<img src="https://velog.velcdn.com/images/happy_quokka/post/a961148b-4426-49b3-ba0d-b38e6e59503f/image.png" alt=""></p>
</li>
<li><p>license accept를 물어보면 <code>yes</code> 입력하면 된다
<img src="https://velog.velcdn.com/images/happy_quokka/post/2fb81a01-0ee7-4389-aed6-1a495fcdcf64/image.png" alt=""></p>
</li>
<li><p>shell과 관련된 문구가 나오면 <code>yes</code> 또는 <code>no</code>를 입력하면 된다</p>
<ul>
<li><code>yes</code>를 선택하면 conda 명령어가 셸에서 제대로 작동하도록 셸 설정 파일(.bashrc, .zshrc 등)을 수정해준다</li>
<li>보통 <code>yes</code>를 선택하는 것이 좋고 나도 <code>yes</code>를 선택했다
<img src="https://velog.velcdn.com/images/happy_quokka/post/03092b9c-6d78-4409-aa11-1b8e289f3849/image.png" alt=""></li>
</ul>
</li>
<li><p><code>yes</code>를 선택했을 경우 <code>vi ~/.bashrc</code>로 해당 파일을 확인해보면 아래의 내용이 추가된 것을 확인할 수 있다
<img src="https://velog.velcdn.com/images/happy_quokka/post/fc6ae3d3-a771-48d8-be25-ac98181163e8/image.png" alt=""></p>
</li>
</ul>
<h3 id="4-conda-명령어-설정">4. conda 명령어 설정</h3>
<ul>
<li><p>하지만 아직 conda 명령어가 실행이 안된다</p>
</li>
<li><p>위에서 수정된 .bashrc 를 적용해야 conda 명령어를 인식한다</p>
<pre><code>$ source ~/.bashrc</code></pre></li>
<li><p>conda 버전을 확인했을 때 버전이 뜨면 성공!</p>
<pre><code>conda -V</code></pre><p><img src="https://velog.velcdn.com/images/happy_quokka/post/80484b5f-0a52-463f-99b5-10ce33cc4533/image.png" alt=""></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[리눅스(ubuntu) 윈도우 파일 전송 방법 2가지 (MobaXterm 또는 scp)]]></title>
            <link>https://velog.io/@happy_quokka/%EB%A6%AC%EB%88%85%EC%8A%A4ubuntu-%EC%9C%88%EB%8F%84%EC%9A%B0-%ED%8C%8C%EC%9D%BC-%EC%A0%84%EC%86%A1-%EB%B0%A9%EB%B2%95-2%EA%B0%80%EC%A7%80-MobaXterm-%EB%98%90%EB%8A%94-scp</link>
            <guid>https://velog.io/@happy_quokka/%EB%A6%AC%EB%88%85%EC%8A%A4ubuntu-%EC%9C%88%EB%8F%84%EC%9A%B0-%ED%8C%8C%EC%9D%BC-%EC%A0%84%EC%86%A1-%EB%B0%A9%EB%B2%95-2%EA%B0%80%EC%A7%80-MobaXterm-%EB%98%90%EB%8A%94-scp</guid>
            <pubDate>Tue, 30 Jul 2024 08:06:06 GMT</pubDate>
            <description><![CDATA[<p>ssh로 리눅스 pc에서 윈도우 pc로 파일을 전송하기 위한 방법!!</p>
<h2 id="리눅스-ssh-설치-및-실행">리눅스 ssh 설치 및 실행</h2>
<h3 id="1-ssh-설치">1. ssh 설치</h3>
<ul>
<li>먼저 ssh가 설치되어 있는지 확인<pre><code>$ dpkg -l | grep openssh-server</code></pre></li>
<li>설치가 안되어 있다면 ssh 설치<pre><code>$ sudo apt update
$ sudo apt install openssh-server</code></pre></li>
<li>다시 설치 확인<pre><code>$ dpkg -l | grep openssh-server</code></pre><img src="https://velog.velcdn.com/images/happy_quokka/post/c4d013ea-0ac6-4954-8ff4-45093525258e/image.png" alt=""></li>
</ul>
<h3 id="2-ssh-실행">2. ssh 실행</h3>
<ul>
<li>ssh가 실행되고 있는지 확인<pre><code>$ sudo systemctl status ssh</code></pre><img src="https://velog.velcdn.com/images/happy_quokka/post/d5cfb3f6-5d16-44a1-a2f4-0ebc433e92ee/image.png" alt=""></li>
</ul>
<ul>
<li>만약 ssh 확인 결과 <code>Active: inactive (dead)</code>가 표시된다면 ssh가 실행되고 있지 않다는 것</li>
<li>따라서 ssh 서비스를 시작해준다<pre><code>$ sudo systemctl start ssh</code></pre></li>
</ul>
<hr>
<h2 id="방법-1-mobaxterm-사용">방법 1. MobaXterm 사용</h2>
<h3 id="1-1-윈도우에-mobaxterm-설치">1-1. 윈도우에 MobaXterm 설치</h3>
<ul>
<li><a href="https://mobaxterm.mobatek.net/download-home-edition.html">MobaXterm 다운로드 홈페이지</a></li>
<li>Installer 버전으로 설치 후 압축 해제</li>
<li>MobaXterm_installer_24.2 실행하여 설치</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/9ecc71a6-8f0e-4b7c-ac04-7fb016f71fc0/image.png" alt=""></p>
<h3 id="1-2-mobaxterm-한글-설정">1-2. MobaXterm 한글 설정</h3>
<ul>
<li>한글 파일명이 깨지지 않도록 한글 설정을 해준다</li>
<li>Settings -&gt; Terminal -&gt; Default terminal font settings -&gt; eucKR 설정</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/1c320ea5-aed2-4d10-a015-38e7d137933d/image.png" alt=""></p>
<h3 id="1-3-mobaxterm으로-리눅스ubuntu-접속">1-3. MobaXterm으로 리눅스(ubuntu) 접속</h3>
<ul>
<li>ssh를 사용하여 리눅스에 연결</li>
<li>Session -&gt; SSH 클릭 후 접속할 pc의 정보 입력<ul>
<li>Remote host : 리눅스 주소 (ip)<ul>
<li>리눅스에서 <code>$ ifconfig</code> 명령어를 통해 ip 주소를 확인할 수 있다
<img src="https://velog.velcdn.com/images/happy_quokka/post/74793545-baf7-4751-9781-dfce0981ee20/image.png" alt=""></li>
</ul>
</li>
<li>Specify username : 접속할 계정 (username)<ul>
<li>리눅스에서 <code>$ whoami</code> 명령어를 통해 username을 확인할 수 있다</li>
</ul>
</li>
<li>Port : ssh 접속 포트<ul>
<li>기본 22</li>
<li>만약 다른 포트로 설정하였다면 포트 번호 수정</li>
</ul>
</li>
<li>만약 보안키가 있다면 Advanced SSH settings -&gt; Use private key에 보안키를 넣어준다
<img src="https://velog.velcdn.com/images/happy_quokka/post/abfb5216-e60c-4d68-b80c-242b1993d812/image.png" alt=""></li>
</ul>
</li>
</ul>
<ul>
<li>처음 접속하였다면 아래와 같은 창이 뜬다</li>
<li>Accept 선택</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/4773f699-e3cc-4733-80f2-c3af37edeb2d/image.png" alt=""></p>
<ul>
<li>그 후 리눅스 계정 비밀번호를 입력하면 된다</li>
<li>이 때 최초로 로그인하는 상황이라면 패스워드 저장 여부를 물어보는 창이 뜬다<ul>
<li>Yes를 누르면 차후 같은 session에 접근할 때 패스워트 입력없이 자동 로그인이 된다</li>
<li>원하지 않는다면 No를 선택하면 된다
<img src="https://velog.velcdn.com/images/happy_quokka/post/c063f1e7-b327-45ac-b4fa-2ebca56e387b/image.png" alt=""></li>
</ul>
</li>
</ul>
<ul>
<li>만약 위에서 Yes를 선택했다면 보안을 위해 마스터 패스워크를 등록하라는 창이 뜬다<ul>
<li>원하는 패스워드를 입력하고 OK를 누르면 된다
<img src="https://velog.velcdn.com/images/happy_quokka/post/d2453b26-c12b-4ac7-8fef-d70456161474/image.png" alt=""></li>
</ul>
</li>
</ul>
<ul>
<li>접속 성공!
<img src="https://velog.velcdn.com/images/happy_quokka/post/68331b2e-2cf4-45fd-a73e-3556954c6c2c/image.png" alt=""></li>
</ul>
<h3 id="1-4-파일-복사">1-4. 파일 복사</h3>
<ul>
<li>리눅스에 접속하게 되면 왼쪽 사이드바에서 리눅스의 파일들을 확인할 수 있다</li>
<li>원하는 곳으로 드래그한 후 드롭하면 윈도우로 파일을 복사할 수 있다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/2e2c3de6-420d-45f0-9973-014fb014f524/image.png" alt=""></p>
<hr>
<h2 id="방법-2-scp-사용">방법 2. scp 사용</h2>
<ul>
<li>scp는 Secure Copy Protocol의 약자</li>
<li>네트워크를 통해 파일과 디렉터리를 안전하게 복사하기 위한 프로토콜</li>
<li>scp는 ssh 프로토콜을 사용하여 데이터를 암호화하고 안전하게 전송</li>
</ul>
<ul>
<li>윈도우에서 아래의 명령어를 통해 리눅스의 파일을 가지고 올 수 있다 (윈도우의 cmd 창에서 실행)<ul>
<li>리눅스 username : 리눅스 pc의 username</li>
<li>리눅스 ip주소 : 리눅스 pc의 ip 주소</li>
<li>리눅스 파일 경로 : 복사하고 싶은 파일의 경로<ul>
<li>예시) /home/quokka/test.c</li>
</ul>
</li>
<li>윈도우 파일 경로 : 복사할 파일을 저장할 윈도우 pc의 경로</li>
</ul>
</li>
</ul>
<pre><code>scp [리눅스 username]@[리눅스 ip주소]:[리눅스 파일 경로] [윈도우 파일 경로]

예시) scp quokka@192.168.0.1:/home/quokka/test.c ./</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Ubuntu 18.04] GPU 개발 환경 (Nvidia Cuda toolkit 설치)]]></title>
            <link>https://velog.io/@happy_quokka/Cuda-toolkit-%EC%84%A4%EC%B9%98</link>
            <guid>https://velog.io/@happy_quokka/Cuda-toolkit-%EC%84%A4%EC%B9%98</guid>
            <pubDate>Fri, 14 Jun 2024 00:18:52 GMT</pubDate>
            <description><![CDATA[<h2 id="1-환경-확인">1. 환경 확인</h2>
<h3 id="1-1-driver-version-확인">1-1. driver version 확인</h3>
<ul>
<li><code>$ nvidia-smi</code>를 통해 자신의 driver version 확인
<img src="https://velog.velcdn.com/images/happy_quokka/post/8b2cbec1-a2d1-4481-aded-dfcee299dd25/image.png" alt=""></li>
</ul>
<h3 id="1-2-cuda-version-확인">1-2. CUDA Version 확인</h3>
<ul>
<li>driver version이 460.91.03이다. 그 옆에 CUDA Version을 추천해주는데 이게 뜬다고 해서 CUDA가 설치되어 있는 것은 아니다 (왜냐면... 난 <code>nvcc -V</code>이 안되기 때문..)</li>
<li>이와 맞는 CUDA version을 정확히 확인하기 위해서 구글에 driver version 460.91 cuda version이라고 검색한다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/45c4296e-5c03-4f16-9d24-5bbdc38adde0/image.png" alt=""></p>
<ul>
<li>검색해서 나온 사이트에 들어가면 CUDA version을 확인할 수 있다</li>
<li>나의 경우에는 11.2.2 라고 뜬다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/9e8fb325-7f28-4346-992a-76c79a0605fa/image.png" alt=""></p>
<hr>
<h2 id="2-cuda-toolkit-설치">2. CUDA toolkit 설치</h2>
<ul>
<li><a href="https://developer.nvidia.com/cuda-toolkit-archive">https://developer.nvidia.com/cuda-toolkit-archive</a> 여기 사이트에 들어가서 자신에게 맞는 버전을 설치하면 된다</li>
<li>이때 본인의 운영체제에 맞게 선택해야하기 때문에 운영체제를 확인을 먼저 해야한다</li>
</ul>
<h3 id="2-1-운영체제-확인-방법">2-1. 운영체제 확인 방법</h3>
<ul>
<li>커널 아키텍처 확인 방법<ul>
<li><code>$ uname -m</code>
<img src="https://velog.velcdn.com/images/happy_quokka/post/022cd625-9c18-479f-80de-e792cd594817/image.png" alt=""></li>
</ul>
</li>
<li>리눅스 os 종류, 버전 확인<ul>
<li><code>$ cat /etc/*-release</code>
<img src="https://velog.velcdn.com/images/happy_quokka/post/d20a5dac-5f3b-4f1b-8e65-d3f7e864d80c/image.png" alt=""></li>
</ul>
</li>
</ul>
<h3 id="2-2-cuda-toolkit-다운로드">2-2. CUDA toolkit 다운로드</h3>
<ul>
<li><p>확인한 운영체제에 맞게 선택해주면 된다
<img src="https://velog.velcdn.com/images/happy_quokka/post/86b7ea34-8ae0-4b53-864e-edd6db34d19e/image.png" alt=""></p>
</li>
<li><p>그러면 아래와 같이 설치 명령어를 볼 수 있다
<img src="https://velog.velcdn.com/images/happy_quokka/post/16f04fc5-b86c-4051-bc79-d5efa333b441/image.png" alt=""></p>
</li>
<li><p>CUDA Toolkit 설치용 파일 다운로드
<code>$ wget https://developer.download.nvidia.com/compute/cuda/11.2.2/local_installers/cuda_11.2.2_460.32.03_linux.run</code>
<img src="https://velog.velcdn.com/images/happy_quokka/post/c4ac35a0-b5f5-4a9d-adf9-2934c8f97d87/image.png" alt=""></p>
</li>
</ul>
<h3 id="2-3-설치-파일-실행">2-3. 설치 파일 실행</h3>
<p><code>$ sudo sh cuda_11.2.2_460.32.03_linux.run</code></p>
<ul>
<li><p>accept 입력
<img src="https://velog.velcdn.com/images/happy_quokka/post/147d2a73-3f7e-453b-a0a1-84a9199169ee/image.png" alt=""></p>
</li>
<li><p>install 선택</p>
<ul>
<li>이때 이미 Driver가 설치되어 있는 상황이라면 X 선택을 해제해야한다</li>
<li>나의 경우 이미 NVIDIA driver를 설치한 상황이기 때문에 선택을 해제하였다
<img src="https://velog.velcdn.com/images/happy_quokka/post/9ec86131-eb75-4dce-96cd-6d74692acc94/image.png" alt=""></li>
</ul>
</li>
<li><p>설치 완료
<img src="https://velog.velcdn.com/images/happy_quokka/post/423bdfbd-39dd-4bd9-b782-358bbea7654e/image.png" alt=""></p>
</li>
</ul>
<h3 id="2-4-환경-변수-설정">2-4. 환경 변수 설정</h3>
<ul>
<li><p>하지만 아직 환경 변수 설정을 안했기 때문에 <code>nvcc -V</code>은 되지 않는다.</p>
<pre><code>$ export PATH=/usr/local/cuda-11.2/bin:$PATH
$ export LD_LIBRARY_PATH=/usr/local/cuda-11.2/lib64:$LD_LIBRARY_PATH</code></pre></li>
<li><p>위의 환경 변수 설정을 하고 유지하기 위해서는 위의 내용을 <code>~/.bashrc</code>에 추가한 후 적용한다</p>
<pre><code>$ sudo vim ~/.bashrc
환경 변수 추가
$ source ~/.bashrc</code></pre><p><img src="https://velog.velcdn.com/images/happy_quokka/post/aeb26a02-cb4f-4edc-9e64-b405bf863f05/image.png" alt=""></p>
</li>
</ul>
<h3 id="2-5-최종-확인">2-5. 최종 확인</h3>
<ul>
<li>nvcc 버전을 확인했을 때 버전이 나오면 잘 설치된 것이다!!!!!!!<pre><code>$ nvcc -V</code></pre><img src="https://velog.velcdn.com/images/happy_quokka/post/6b2ab392-a067-41c3-8817-449f0719e3ae/image.png" alt=""></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[CeyMo : See more on road - A novel benchmark dataset for road marking detection 논문 리뷰]]></title>
            <link>https://velog.io/@happy_quokka/CeyMo-See-more-on-road-A-novel-benchmark-dataset-for-road-marking-detection-%EB%85%BC%EB%AC%B8-%EB%A6%AC%EB%B7%B0</link>
            <guid>https://velog.io/@happy_quokka/CeyMo-See-more-on-road-A-novel-benchmark-dataset-for-road-marking-detection-%EB%85%BC%EB%AC%B8-%EB%A6%AC%EB%B7%B0</guid>
            <pubDate>Tue, 21 May 2024 00:34:53 GMT</pubDate>
            <description><![CDATA[<p>road marking detection을 위한 dataset과 road marking을 탐지하는 내용에 관한 논문이다.</p>
<h2 id="0-abstract">0. Abstract</h2>
<ul>
<li>ceymo dataset<ul>
<li>11 class</li>
<li>4706개의 road marking이 포함된 2887 image</li>
<li>1920 x 1080 해상도</li>
<li>polygon, bounding box, segmentation 3가지의 annotation</li>
</ul>
</li>
</ul>
<hr>
<h2 id="1-introduction">1. introduction</h2>
<h4 id="기존-연구">기존 연구</h4>
<p>후보 영역 생성 → 머신러닝 기반 알고리즘 사용</p>
<h4 id="최근-연구">최근 연구</h4>
<ul>
<li>end-to-end 딥러닝 기반 instance segmentation, semantic segmentation 네트워크 사용</li>
<li>빠르고 효율적 but! 연구 부족<h4 id="본-연구">본 연구</h4>
instance segmentation 및 객체 감지 기반 접근 방법 사용</li>
<li>instance segmentation → mask RCNN</li>
<li>object detection → SSD</li>
<li>inverse perspective transform (IPT) 함께 사용</li>
</ul>
<hr>
<h2 id="2-related-work">2. related work</h2>
<p>기존 공개 road marking detection dataset + detection 알고리즘 분석
<img src="https://velog.velcdn.com/images/happy_quokka/post/2ac91ad2-c651-4c73-b9e8-6d9ce461d6ea/image.png" alt=""></p>
<h3 id="21-datasets">2.1 Datasets</h3>
<ul>
<li>A Practical System for Road Marking Detection and Recognition의 dataset<ul>
<li>11 class</li>
<li>1443개의 annotation</li>
<li>800 x 600 해상도</li>
<li>bounding box에 대한 정보</li>
<li>이미지 처리 기반 접근 방식, 별도의 train, test set 제공x, 평가 기준 명확x</li>
</ul>
</li>
</ul>
<ul>
<li>TRoM (Tsinghua Road Marking Dataset)<ul>
<li>19 class (차선 + road marking)</li>
<li>712개의 이미지</li>
<li>픽셀 수준의 sementic segmentation annotation이 PNG로 제공</li>
<li>데이터셋의 수 적음, annotation과 평가가 sementic segmentation 기반이라 다양한 mark 감지에 제한적</li>
</ul>
</li>
</ul>
<ul>
<li>VPGNet<ul>
<li>2만개의 이미지</li>
<li>17 class (but! road marking 관련은 8개 class, 5만개의 instance가 other road marking으로 라벨링)</li>
<li>차선의 소실점을 포함한 차선, road marking의 픽셀 수준 및 grid 수준 annotation이 MAT 파일 형식으로 제공</li>
<li>평가 : IoU</li>
<li>차선 위주</li>
</ul>
</li>
</ul>
<h3 id="22-알고리즘">2.2 알고리즘</h3>
<h4 id="고전적인-방법">고전적인 방법</h4>
<ul>
<li>대부분 고전적인 <strong>이미지 처리 기술 + 간단한 머신러닝</strong></li>
<li>일반적인 detection pipeline : 이미지 전처리 → ROI 생성 → 특징 추출 및 머신러닝 알고리즘</li>
<li>원본 이미지를 IPT하여 보정하는 것은 일반적으로 사용되는 전처리 기술</li>
<li>IPT 대신 차선 정보를 사용하여 검색 영역을 축소할 수 있다고 제안 (Fast symbolic road marking and stop-line detection for vehicle localization 논문) → but! 차선 탐지 정확도에 직접적으로 의존하기 때문에 성능 제한될 수 있음</li>
</ul>
<ul>
<li>A practical system for road marking detection and recognition 논문<ul>
<li>Maximally stable extremal regions (MSER)은 가능한 후보 영역으로 사용됨</li>
<li>histogram of oriented gradients (HOG) feature descriptor가 각 클래스에 대한 template pool을 구축하는데 사용됨</li>
<li>추론 시에 각 이미지는 모든 템플릿 이미지와 비교되어 클래스가 할당됨</li>
<li>그러나 복잡한 시나리오에서는 일반적으로 supervised 학습 방법이 템플릿 매칭 방법보다 더 잘 수행됨</li>
<li><strong>MSER 영역과 HOG 특징은 SVM 분류기와 함께 사용되어 symbol 기반 road marking 인지에 사용</strong>됨</li>
<li>별도의 <strong>OCR 알고리즘</strong>은 텍스트 기반 도로 표지판을 인식하는데 사용됨</li>
<li>그러나 서로 다른 road marking에 대해 다른 접근 방식을 가지로 있는 것은 계산 중복성을 초래할 수 있음</li>
<li>이러한 두가지 방법 모두 HOG 특징 추출을 포함하며 이는 시간이 많이 소요됨</li>
</ul>
</li>
</ul>
<ul>
<li>Road marking detection and classification using machine learning algorithms 논문<ul>
<li>Binarized normed gradients (BING) objectiveness 추정 알고리즘은 가능한 road marking 영역 제안을 생성하는데 사용</li>
<li><strong>PCANet 및 SVM integrated classifier는 road marking을 인지</strong>하는데 사용</li>
<li>이 방법의 단점은 BING이 일반적으로 더 큰 영역을 제안하기 때문에 위치 정확도가 낮음</li>
<li>로지스틱 회귀를 PCANet과 함께 사용되어 분류 정확도 향상</li>
<li><strong>얕은 CNN</strong>도 road marking인식을 위한 분류기로 소개됨</li>
<li>MSER 영역을 식별한 후에는 밀도 기반 클러스터링 알고리즘을 사용하여 제안된 road marking 영역을 병합하여 분류기를 위한 road marking 영역을 얻음</li>
<li>영역 제안을 얻기 위해 많은 전처리 기술 사용하고 PCANet 또는 얕은 CNN 분류기는 인식 부분에만 사용됨</li>
</ul>
</li>
</ul>
<h4 id="딥러닝-기반-방법">딥러닝 기반 방법</h4>
<ul>
<li>road marking detection 분야에서 딥러닝 기반 네트워크가 널리 사용되지는 않았다</li>
</ul>
<ul>
<li>Benchmark for road marking detection: Dataset specification and performance baseline 논문<ul>
<li>ResNet-101와 피라미드 pooling 앙상블을 결합한 합성곱 신경망 모델을 사용하여 차선, road marking을 semantic segmentation으로 얻음</li>
<li>이는 TRoM dataset에서 평균 결과 달성</li>
</ul>
</li>
</ul>
<ul>
<li>VPGNet<ul>
<li>차선, road marking을 동시에 감지하기 위한 CNN 기반 아키텍처</li>
<li>road marking 탐지를 grid 최귀 작업으로 다루고 grid sampling 및 box clustering을 후처리 기술로 사용하여 grid cell 병합</li>
<li>그러나 이는 주로 차선 탐지 및 소실점 예측 작업에 더 많이 집중</li>
</ul>
</li>
</ul>
<hr>
<h2 id="3-benchmark-dataset">3. benchmark dataset</h2>
<h3 id="31-data-annotation">3.1 data annotation</h3>
<ul>
<li>11 class</li>
<li>polygon 좌표 JSON</li>
<li>bounding box XML</li>
<li>픽셀 수준의 segmentation mask PNG</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/c6f46d33-5577-4cbb-b0a7-8e0f42bfdc46/image.png" alt=""></p>
<h3 id="32-dataset-통계">3.2 dataset 통계</h3>
<ul>
<li><strong>2887장</strong>의 이미지 (2099개, 788개 → train, test)</li>
<li>클래스 불균형이 있음</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/96fdb727-2f80-4935-ad02-044e03bd5608/image.png" alt=""></p>
<ul>
<li>test set 이미지는 6가지 시나리오로 구성</li>
<li>정상, 혼잡, 눈부심, 야간, 비, 그림자 (normal, crowded, dazzle light, night, rain and shadow)</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/ea2f2abe-7568-46e8-addc-a7ac4ef4f8f3/image.png" alt=""></p>
<h3 id="33-평가-martix">3.3 평가 martix</h3>
<ul>
<li><strong>F1 score</strong>와 Macro F1 score 사용</li>
<li>예측과 실제 사이의 교차 영역 값이 계산되며 IoU가 0.3보다 크면 해당 예측이 true로 간주됨</li>
<li>F1 score<ul>
<li>$precision = \frac{TP}{TP + FP}$</li>
<li>$recall = \frac{TP}{TP + FN}$</li>
<li>$F1 = \frac{2 \times precision \times recall}{precision + recall}$</li>
</ul>
</li>
<li><strong>macro F1 score</strong>는 11 class의 F1 score의 평균<ul>
<li>$macro F1 = \frac{1}{C}\sum_{i=1}^CF1_i$</li>
<li>macro F1 score는 dataset에 나타나는 빈도에 관계없이 모든 class에 동일한 중요도 부여</li>
</ul>
</li>
</ul>
<hr>
<h2 id="4-방법">4. 방법</h2>
<ul>
<li>2가지 접근 방법<ul>
<li>object detection 방법</li>
<li>instance segmentation 방법</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/cb6548c8-4c8b-419b-bf35-96dc8abe143d/image.png" alt=""></p>
<h3 id="41-object-detection-approach">4.1 Object detection approach</h3>
<ol>
<li><strong>BEV 변환</strong><ul>
<li>먼저 <strong>inverse perspective transform(IPT)</strong>를 사용하여 bird eye view로 변환</li>
<li>IPT는 캡쳐된 이미지의 투시 왜곡을 줄이고 더 넓은 영역의 배경을 제거하여 road marking이 더 두드려지게 만듦</li>
<li>IPT는 관련 homography matirx인 M이 주어진 homography transformation<ul>
<li>$Destination[\hat{x}, \hat{y}, :] = Source[x, y, :]$</li>
<li>$\hat{x} = \frac{M_{11}x + M_{12}y + M_{13}}{M_{31}x+M_{32}y + M_{33}}$</li>
<li>$\hat{y} = \frac{M_{21}x + M_{22}y + M_{23}}{M_{31}x+M_{32}y + M_{33}}$</li>
<li>즉, $\begin{bmatrix} x&#39; \newline y&#39; \newline z&#39; \end{bmatrix} = M \begin{bmatrix} x \newline y \newline 1 \end{bmatrix}$ 한 후 $\hat {x} = \frac{x&#39;}{z&#39;} , \hat{y} = \frac{y&#39;}{z&#39;}$ 하는 계산 과정</li>
</ul>
</li>
</ul>
</li>
</ol>
<ol start="2">
<li>모델 <ul>
<li>BEV 이미지에서 road marking을 감지하기 위해 end-to-end object detector 모델이 사용됨</li>
<li><strong>MobileNet-v1 backbone을 사용하는 SSD</strong>와 <strong>Inception-v2 backbone을 사용하는 SSD</strong>의 성능 평가</li>
<li><strong>500 x 500 해상도</strong>의 이미지가 입력됨</li>
<li><strong>bounding box</strong>로 road marking 감지 결과 출력</li>
</ul>
</li>
</ol>
<ol start="3">
<li>최종 단계에서 bounding box는 M의 역행렬을 사용하여 원래 이미지 도메인으로 변환됨</li>
</ol>
<h3 id="42-instance-segmentation-approach">4.2 Instance Segmentation Approach</h3>
<ul>
<li>instance segmentation의 목표는 객체 instance와 그 객체의 픽셀 단위 segmentation mask를 예측하는 것</li>
<li><strong>Mask R-CNN</strong> 네트워크 아키텍처를 <strong>inception-V2와 Resnet-50</strong> 두 가지 backbone과 함께 사용<ul>
<li>Mask R-CNN은 Faster R-CNN 아키텍처를 확장하여 각 ROI마다 bounding box와 segment mask 예측함</li>
<li>instance segmentation 네트워크는 일반적으로 추론 속도가 낮기 때문에 추가 전처리 단계 없이 입력 이미지를 낮은 해상도인 <strong>500 x 500</strong>으로 resize한 후 모델에 직접 입력함</li>
<li>네트워크는 road marking에 대한 bounding box와 <strong>segmentation mask</strong>를 출력함</li>
<li>본 논문은 convex hull을 얻을 수 있는 segmentation mask만 평가</li>
</ul>
</li>
</ul>
<hr>
<h2 id="5-experiment">5. experiment</h2>
<h3 id="51-data-augmentation">5.1 data augmentation</h3>
<ul>
<li><strong>클래스 불균형 문제</strong>의 영향을 줄이기 위한 단계</li>
<li>훈련 중에 부족한 instance 수를 증가시키기 위해 간단한 data augmentation 방법을 사용</li>
<li>왼쪽 화살표, 직진-우회전 화살표 클래스가 오른쪽 화살표, 직진-좌회전 화살표 클래스에 비해 상대적으로 적은 instance를 가지고 있음</li>
<li>따라서 화살표가 포함된 이미지를 수평으로 뒤집어 (<strong>horizontally flip</strong>) 대칭된 표지판을 얻음</li>
<li>그러나 <strong>cycle lane, bus lane, slow road marking 클래스</strong>의 instance를 뒤집으면 의미를 잃기 때문에 이러한 instance를 포함한 이미지는 피함</li>
<li>또한 탐지 모델을 훈련하는 동안 이미지의 <strong>밝기, 채도, 대비, 색조를 무작위로 변경</strong>함<h3 id="52-implementation-details">5.2 implementation details</h3>
</li>
<li>Intel Core i9-9900K CPU &amp; Nvidia RTX-2080 Ti GPU</li>
<li>TensorFlow로 모델 학습</li>
<li>SSD-MoblieNet-v1 &amp; SSD-Inception 모델<ul>
<li>RMSProp optimization : 초기 learning rate 0.004, momentum 0.9, batch size 24</li>
<li>Mask-RCNN-Inception 모델</li>
</ul>
</li>
<li>momentum 포함한 SGD : 초기 lr 0.0001, momentum 0.9, batch size 4<ul>
<li>Mask-RCNN-ResNet50 모델</li>
</ul>
</li>
<li>momentum 포함한 SGD : 초기 lr 0.0003, momentum 0.9, batch size 2</li>
</ul>
<hr>
<h2 id="6-result">6. Result</h2>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/07808596-553d-4079-b0e7-af8b58849e17/image.png" alt=""></p>
<ul>
<li>표 4a<ul>
<li>각 모델의 F1 score, 추론 속도(FPS)</li>
<li>instance segmentation 방식이 object detection 보다 더 나음</li>
<li>Mask-RCNN-ResNet50 모델이 전체적으로 가장 높은 F1 score를 기록했지만 FPS가 가장 낮음</li>
<li>Mask-RCNN-Inception-v2 모델을 정확도와 속도 사이의 균형 제공</li>
<li>SSD 모델은 IPT와 함께 사용될 때 중간 정도의 정확도와 더 높은 추론 속도 보임</li>
<li>모든 모델이 normal에서 더 나은 성능을 보이고, 도전 시나리오에서는 F1 score가 비교적 낮음</li>
</ul>
</li>
</ul>
<ul>
<li>표 4b<ul>
<li>macro F1 score</li>
<li>모든 모델의 macro F1 score가 전체 F1 score 보다 약 2%정도 낮다는 것을 알 수 있음 → 모델이 특정 클래스에서 더 잘 작동하는 경향이 있음을 의미</li>
<li>dataset에서 자주 발견되고 도로의 큰 영역을 차지하는 횡단보도는 모든 모델에서 잘 감지됨</li>
<li>slow, bus lane, cycle lane 클래스는 dataset에서 비교적 수가 적음 → 하지만 독특한 형태와 특징이 있기 때문에 잘 감지함</li>
<li>화살표의 경우 data augmentation을 통해 수를 증가시켰음에도 다른 sign에 비해 정확도가 낮음 → 화살표 클래스들 간의 유사성뿐만 아니라 도로 표면의 차선과도 유사성이 있기 때문일 수도 있음</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/b8532015-eda8-4738-bb3f-37725183a2a3/image.png" alt=""></p>
<ul>
<li>Mask R-CNN 모델이 특히 도전적인 시나리오에서 나은 성능을 보임</li>
<li>segmentation mask는 객체 탐지 방법보다 road marking의 위치를 더 정밀하게 지정함</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Deep Learning] 클래스 불균형 (class imbalance) 다루는 방법]]></title>
            <link>https://velog.io/@happy_quokka/Deep-Learning-%ED%81%B4%EB%9E%98%EC%8A%A4-%EB%B6%88%EA%B7%A0%ED%98%95-class-imbalance-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@happy_quokka/Deep-Learning-%ED%81%B4%EB%9E%98%EC%8A%A4-%EB%B6%88%EA%B7%A0%ED%98%95-class-imbalance-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Mon, 01 Apr 2024 12:11:53 GMT</pubDate>
            <description><![CDATA[<h2 id="class-imbalance란">class imbalance란?</h2>
<ul>
<li>특정 class의 데이터 수가 다른 class의 데이터 수에 비해 적을 때</li>
<li>데이터의 수가 많은 class를 majority class라고 하고 적은 class를 minority class라고 한다</li>
<li>이런 경우 평가지표로 단순히 accuracy를 사용하는 것은 좋지 않다
: 만약 개와 고양이를 분류하는 문제에서 개의 데이터가 95%, 고양이의 데이터가 5%인 경우, 전체 데이터를 개로 분류하여도 95%의 accuracy가 나오기 때문이다</li>
<li>이런 경우에는 precision, recall의 조화평균인 F1 score 지표를 보는 것이 좋다<ul>
<li>일반적으로 precision은 높고 recall은 매우 낮은 경항이 있다</li>
</ul>
</li>
</ul>
<h3 id="class-imbalance의-문제">class imbalance의 문제</h3>
<ul>
<li>학습을 할 때 불균형이 크다면 majority class로 편향되어 학습된다</li>
<li>즉, minority class에 대한 정확도와 예측력이 낮아질 수 있다</li>
</ul>
<hr>
<h2 id="class-imbalance-해결-방법">class imbalance 해결 방법</h2>
<h3 id="1-데이터-수집">1. 데이터 수집</h3>
<ul>
<li>테이터의 수가 minority class의 데이터를 더 수집한다</li>
<li>하지만 이 방법은 현실적으로 어려운 경우가 많아서 주로 아래의 방법들을 사용한다</li>
</ul>
<h3 id="2-weight-balancing">2. weight balancing</h3>
<p>train 과정에서 각 class의 loss를 계산할 때 특정 class(주로 minority class)의 loss에 가중치를 부여하여 해당 class를 더 잘 학습할 수 있도록 유도하는 방법</p>
<h4 id="2-1-balanced-cross-entropy-loss">2-1. Balanced Cross-Entropy Loss</h4>
<ul>
<li>모든 클래스의 손실에 가중치 $\alpha$를 부여하여 클래스 간의 불균형을 조정하는 방법</li>
<li>주로 minority class에 높은 가중치를 부여하여 minority class에 대한 손실을 증가시키고, majority class에 대한 손실을 감소<ul>
<li>예를 들어, class frequency의 역수를 사용할 수 있다</li>
<li>class 불균형이 1:10이라면 minority에는 $1$을 곱하고 majority에는 $\frac{1}{10}$을 곱해준다</li>
</ul>
</li>
<li>아래와 같은 식으로 표현할 수 있다<ul>
<li>$\alpha$ : 가중치</li>
<li>$p_t$ : 모델이 예측한 class의 확률</li>
</ul>
</li>
</ul>
<p>$$BCE(p_t) = -\alpha_t log(p_t)
$$</p>
<ul>
<li>단점 : 단순히 class 샘플의 수를 고려한 것이기 때문에 easy sample과 herd sample을 구분하기 어렵다</li>
</ul>
<h4 id="2-2-focal-loss">2-2. Focal Loss</h4>
<ul>
<li>잘 분류되는 샘플(easy sample)에는 작은 가중치를 부여하고, 분류가 어려운 샘플(hard sample)에는 큰 가중치를 부여하는 방법</li>
</ul>
<p>$$FL(p_t) = -(1-p_t)^\gamma log(p_t)
$$</p>
<ul>
<li>easy sample의 $p_t$는 1에 가까운 값이고, hard sample의 $p_t$는 0에 가까운 값이다</li>
<li>즉, loss로 보면 easy sample의 loss를 작게 하고 hard sample의 loss를 강조하여 모델이 잘 학습할 수 있도록 도와준다</li>
<li>이때 $\gamma$의 값이 클수록 잘못 분류되는 경우의 값들이 강조되어 해당 값을 잘 학습할 수 있도록 유도된다</li>
</ul>
<h4 id="2-3-balanced-focal-loss">2-3. Balanced Focal Loss</h4>
<ul>
<li>Balanced Cross-Entropy Loss와 Focal Loss를 결합한 방법이다
$$ FL(p_t) = -\alpha_t(1-p_t)^\gamma log(p_t)
$$</li>
</ul>
<ul>
<li>Balanced Cross-Entropy Loss에서 사용되는 $\alpha$ 값을 통해 class 간의 가중치를 조절한다</li>
<li>Focal Loss에서 사용되는 $\gamma$ 값을 통해 hard sample을 잘 학습할 수 있도록 한다</li>
</ul>
<h3 id="3-resampling">3. resampling</h3>
<p>데이터 샘플을 수를 조절하는 방법으로 under-sampling과 over-sampling 방법이 있다</p>
<h4 id="3-1-under-sampling">3-1. under-sampling</h4>
<ul>
<li>majority class 샘플의 일부를 삭제하여 개수를 맞추는 방법</li>
<li>데이터가 많은 경우에 사용할 수 있다</li>
<li>단점 : 데이터의 수가 줄어들기 때문에 정보 손실이 발생한다</li>
</ul>
<h4 id="3-2-over-sampling">3-2. over-sampling</h4>
<ul>
<li>minority class 샘플을 복사하여 majority class 수 만큼 늘려주는 방법</li>
<li>단점 : 중복된 샘플이 많아 훈련 속도를 느리게하고 모델 과적합 문제가 발생할 수 있다</li>
</ul>
<hr>
<h4 id="reference">reference</h4>
<ul>
<li><a href="https://medium.com/data-science-ecom-express/focal-loss-for-handling-the-issue-of-class-imbalance-be7addebd856">https://medium.com/data-science-ecom-express/focal-loss-for-handling-the-issue-of-class-imbalance-be7addebd856</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[3D Perception] 2. LiDAR data의 protocol, format, library]]></title>
            <link>https://velog.io/@happy_quokka/3D-Perception-2.-LiDAR-data%EC%9D%98-protocol-format-library</link>
            <guid>https://velog.io/@happy_quokka/3D-Perception-2.-LiDAR-data%EC%9D%98-protocol-format-library</guid>
            <pubDate>Fri, 22 Mar 2024 04:22:07 GMT</pubDate>
            <description><![CDATA[<h2 id="1-lidar-data">1. LiDAR Data</h2>
<ul>
<li>point cloud data로 부르며 3차원 공간에 존재하는 특정한 점이다</li>
<li>일반적인 경우 LiDAR 센서는 이더넷 TCP/IP (대부부은 UDP)를 통해 데이터를 수신하고 데이터의 포맷은 packet 형태를 가진다</li>
<li>LiDAR가 ethernet protocol을 통해 local로 data를 보내준다 </li>
</ul>
<ul>
<li>ethernet 통신의 데이터를 packet이라고 한다</li>
<li>ethernet 통신을 하는 이유?<ul>
<li>LiDAR 데이터가 크기 때문이다</li>
<li>만약 충분이 적은 데이터를 가진 센서라면 다른 인터페이스를 사용하는 경우도 있다</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/58042914-f539-4fc4-ab1f-aa35fc3c0ddd/image.png" alt=""></p>
<h3 id="1-1-packet-구조">1-1. Packet 구조</h3>
<ul>
<li>많은 LiDAR 센서는 Ethernet Packer 구조를 사용하기 때문에 라이브러리 또는 SDK를 제공하지 않는다면 직접 packet을 un-pack(parsing)하는 과정이 필요하다</li>
<li>이때는 LiDAR 회사의 공식 문서를 참고해야한다 </li>
<li>시중의 대부분의 센서는 라이브러리나 SDK를 제공한다</li>
</ul>
<ul>
<li>Ethernet packet의 파일 확장자는 <code>*.pcap</code> 을 사용하고 packet stream Ehsms PCAP file을 point cloud format으로 변환하여 사용한다</li>
</ul>
<h4 id="velodyne-vlp-16-lidar-데이터-구조">Velodyne VLP-16 LiDAR 데이터 구조</h4>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/c164bb32-5e23-4f07-b297-ff21421d815a/image.png" alt=""></p>
<ul>
<li>한 packet의 크기는 1248bytes</li>
<li>packet의 시작과 끝을 찾고 그 안에 있는 데이터를 가지고 온다<ul>
<li>header : 42bytes</li>
<li>tail : 6bytes -&gt; 시간, factory(끝을 의미)하는 내용이 담겨있다</li>
</ul>
</li>
<li>header와 tail을 제외한 데이터 : 1200 bytes<ul>
<li>12개의 data block (0 ~ 11 block)</li>
<li>Flag : data block의 시작을 확인할 수 있다, 16진수이기 때문에 2 bytes를 차지한다</li>
<li>Azimuth : Lidar의 각도</li>
<li>sequence : VLP-16은 16채널이기 때문에 채널에 대한 데이터가 각각 16개씩 들어있다</li>
</ul>
</li>
</ul>
<h3 id="1-2-data-format">1-2. data format</h3>
<ul>
<li>일반적으로 point cloud data는 다음과 같은 데이터를 의미한다</li>
<li>X, Y, Z : point의 3차원 위치를 표현</li>
<li>(optional) intensity : 반사된 laser의 세기</li>
<li>point cloud data는 *.pcd, *.las, *.xyz 등을 사용한다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/9ef1ed3e-8d77-485a-8d18-a07a3ca443b0/image.png" alt=""></p>
<hr>
<h2 id="2-pcl-pointcloudlibrary">2. PCL (PointCloudLibrary)</h2>
<ul>
<li>point cloud를 다루는 라이브러리 </li>
<li>github : <a href="https://github.com/PointCloudLibrary/pcl">https://github.com/PointCloudLibrary/pcl</a></li>
<li>site : <a href="https://pointclouds.org/">https://pointclouds.org/</a></li>
</ul>
<ul>
<li>open3D와 같은 유용한 라이브러리도 존재한다</li>
<li>또한 OUSTER처럼 자체 SDK를 제공하는 경우도 있다</li>
</ul>
<ul>
<li>ROS를 사용한다면 PCL 라이브러리는 자동으로 설치된다</li>
<li>ROS 환경이 아니라면 <code>sudo apt install libpcl-dev</code> 명령어로 설치한다</li>
<li>PCL은 의존 라이브러리가 많이 때문에 직접 컴파일한다면, 반드시 공식 홈페이지를 참고하여 설치한다</li>
<li>Mandatory, Optional 모두 설치해야한다</li>
</ul>
<h3 id="2-1-container-data-type">2-1. container, data type</h3>
<h4 id="container">container</h4>
<ul>
<li>데이터를 담아두는 그릇</li>
<li><code>pcl::PointCloud&lt;T&gt;</code></li>
<li><code>pcl::PCLPointCloud2</code> : PCL 1.x 버전 이상</li>
</ul>
<h4 id="data-type">data type</h4>
<ul>
<li>데이터를 표현하는 이름 </li>
<li><code>pcl::PointXYZ</code> : 3D scanner를 사용하는 경우</li>
<li><code>pcl::PointXYZI</code> : 3D scanner에서 intensity를 같이 사용하는 경우</li>
<li><code>pcl::PointXYZRGB</code> : RGB-D camera 또는 LiDAR &amp; Camera calibration을 통해 point cloud에 RGB값을 부여한 경우</li>
<li><code>pcl::PointXYZRGBA</code> : <code>pcl::PointXYZRGB</code>에 투명도가 추가된 것 </li>
<li><code>pcl::PointXY</code> : 2D scanner를 사용하는 경우</li>
</ul>
<h3 id="2-2-cmakeliststxt-작성-방법">2-2. CMakeLists.txt 작성 방법</h3>
<ul>
<li>c++로 PCL을 사용할 경우 CMakeLists를 사용해야한다</li>
</ul>
<pre><code>cmake_minimum_required(VERSION 3.10)

project(project_name)
add_executable(project_name main.cpp)

find_package(PCL 1.12 REQUIRED)

if(PCL_FOUND)
  include_directories(${PCL_INCLUDE_DIRS})
  add_definitions(${PCL_DEFINITIONS})
  link_directoried(${PCL_LIBRAry_DIRS})
  target_link_libraries(project_name ${PCL_LIBRARIES})
endif()</code></pre><p><img src="https://velog.velcdn.com/images/happy_quokka/post/a5931014-2e94-41bb-9e87-4301442a5701/image.png" alt=""></p>
<h3 id="2-3-pointcloud-container-작성-방법">2-3. PointCloud Container 작성 방법</h3>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/28dced2d-f710-49ce-ac3e-cd42812f007b/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kalman Filter] (1) 1차원 kalman filter]]></title>
            <link>https://velog.io/@happy_quokka/Kalman-Filter-1-1%EC%B0%A8%EC%9B%90-kalman-filter</link>
            <guid>https://velog.io/@happy_quokka/Kalman-Filter-1-1%EC%B0%A8%EC%9B%90-kalman-filter</guid>
            <pubDate>Fri, 08 Mar 2024 18:31:10 GMT</pubDate>
            <description><![CDATA[<h2 id="kalman-filter란">Kalman Filter란?</h2>
<ul>
<li>1960년대 루돌프 칼만이 개발한 알고리즘</li>
<li>칼만 필터는 시스템의 상태에 대한 확률적인 정보를 가지고 노이즈가 섞인 측정값과 예측값을 효과적으로 결합하여 상태를 추정</li>
<li>칼만 필터는 센서를 통해 측정한 값에 노이즈가 포함되어도 이 노이즈를 제거</li>
<li>연산 과정이 빠르기 때문에 실시간, 임베디드 시스템에 적합</li>
</ul>
<h4 id="kalman-filter-사용-조건">Kalman Filter 사용 조건</h4>
<ul>
<li>motion model과 observation model이 linear한 경우</li>
<li>motion model과 observation model이 가우시안 분포를 따를 경우</li>
</ul>
<ul>
<li>motion model<ul>
<li>로봇이 현재 위치에서 control input을 받아서 이동했을 때의 확률 모델</li>
</ul>
</li>
<li>observation model<ul>
<li>로봇이 현재 위치에서 센서를 통해 자신의 위치를 측정했을 때의 확률 모델</li>
</ul>
</li>
</ul>
<hr>
<h2 id="kalman-filter의-전체-과정">Kalman Filter의 전체 과정</h2>
<ul>
<li>칼만 필터는 <strong>상태 예측 (state prediction)</strong>과 <strong>측정 업데이트 (measurement update)</strong>를 반복적으로 수행하며 현재 상태를 계산<ul>
<li>상태 예측 (state prediction): 이전 상태의 추정값과 모델을 이용하여 다음 상태 예측</li>
<li>측정 업데이트 (measurement update): 예측값과 실제 측정값을 비교하여 상태를 업데이트</li>
</ul>
</li>
</ul>
<ul>
<li>로봇의 경우 로봇이 이동하기 위해서 어디에 있는지 위치를 정확하게 알아야하는데 이때 칼만 필터를 사용<ul>
<li>상태 예측: 이전 로봇의 파라미터(위치) + 로봇 모션 입력 -&gt; 현재 로봇 파라미터 예측</li>
<li>측정 업데이트: 예측한 로봇의 파라미터 + 센서(GPS 등)를 통해 얻은 로봇의 위치 -&gt; 로봇 파라미터 값 업데이트</li>
</ul>
</li>
</ul>
<h4 id="간단-과정">간단 과정</h4>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/8b500455-f76c-4ea3-b248-4278b2e55732/image.png" alt=""></p>
<h4 id="정규-분포로-본-과정">정규 분포로 본 과정</h4>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/9ada9f41-a4ad-4e0d-b4ce-250eeeef7326/image.png" alt=""></p>
<ol>
<li><strong>state predict</strong><ul>
<li>이전 위치에서 motion input을 받아 이동한 현재 위치를 예측하는 과정</li>
<li>위치에 대한 정규분포와 이동에 대한 정규분포의 합성곱(convolution) 계산을 통해 이루어진다</li>
</ul>
</li>
<li><strong>measure</strong><ul>
<li>센서를 사용하여 현재 위치를 측정하는 과정</li>
<li>측정한 값도 정규분포로 나타난다</li>
</ul>
</li>
<li><strong>update</strong><ul>
<li>predict를 통해 예측한 값과 센서를 통해 측정한 값을 통해 최적의 값을 계산한다 (예측된 값 갱신)</li>
<li>예측값의 정규분포와 측정값의 정규분포의 곱(product) 계산을 통해 이루어진다</li>
</ul>
</li>
<li><strong>time + 1</strong><ul>
<li>최적의 값을 사용하여 다시 1번부터 수행한다</li>
</ul>
</li>
</ol>
<hr>
<h2 id="kalman-filter-세부-과정">Kalman Filter 세부 과정</h2>
<p>kalman filter를 사용하여 이동하는 <strong>로봇의 위치</strong>를 추정하는 과정에 대해 자세히 설명한다</p>
<h3 id="0-로봇의-위치-이동량-정규분포">0. 로봇의 위치, 이동량 정규분포</h3>
<ul>
<li>칼만 필터에서는 현재 위치와 이동량을 정규분포로 표현할 수 있다</li>
<li>정규분포로 표현하는 이유는 불확실도(uncertainty)를 포함하여 위치, 이동량을 표현하기 위해서이다</li>
</ul>
<h4 id="로봇의-위치">로봇의 위치</h4>
<ul>
<li>로봇이 $x=0$ 위치에 있을 가능성이 가장 높다고 가정한다</li>
<li>로봇이 정확하게 $x=0$ 위치에 있을지에 대해 불확실도가 있기 때문에 아래와 같은 정규분포로 표현할 수 있다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/d045adcf-5795-44f5-9140-dff11e1fdd63/image.png" alt=""></p>
<h4 id="로봇의-이동량">로봇의 이동량</h4>
<ul>
<li>로봇이 $x=3$ 만큼 오른쪽으로 이동한다고 가정한다</li>
<li>이동량 역시 정확하게 $x=3$만큼 이동하는지에 대해 불확실도가 있기 때문에 정규분포로 표현할 수 있다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/b4e6008d-5bd6-4960-b081-26f3c135a239/image.png" alt=""></p>
<h3 id="1-state-predict">1. State Predict</h3>
<ul>
<li>로봇이 $x=0$인 상태에서 $x=3$만큼 이동하였을 때 상태를 예측한다<ul>
<li>로봇이 $x=0$인 상태 = 이전 로봇의 파라미터</li>
<li>$x=3$만큼 이동 = motion control input</li>
</ul>
</li>
<li><strong>위치에 대한 정규분포</strong>와 <strong>이동에 관한 정규분포</strong>의 <strong>합성곱(convolution)</strong> 계산을 통해 예측값을 구한다<ul>
<li>합성곱을 하는 이유: 물체가 실제로 −∞ 부터 ∞까지 존재할 가능성이 있기 때문에 이 모든 경우에 대해서 이동량의 분포를 곱하고 더해주어야한다 (곱하고 더하는것 = 합성곱)</li>
</ul>
</li>
</ul>
<ul>
<li>로봇이 $x=0$에서 $x=3$만큼 이동했다면 아래와 같은 분포로 표현할 수 있다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/375fde23-d284-4334-9657-0cac6130399a/image.png" alt=""></p>
<h4 id="정규분포의-합성곱convolution-계산">정규분포의 합성곱(convolution) 계산</h4>
<ul>
<li>합성곱은 임의의 두 함수 $f(t)$와 $g(t)$에 대해 다음과 같이 정의되는 연산이다</li>
<li><code>(1)</code> $f(t) * g(t) = \int_{-\infty}^\infty f(\tau)g(t-\tau)d\tau$</li>
</ul>
<ul>
<li>$f(t), g(t)$를 정규분포로 표현하면 다음과 같다 (여기에서 $\mathcal{N}$은 정규분포, $\mu$는 평균, $\sigma$는 분산을 의미한다)<ul>
<li>$f(t)$는 $\mathcal{N}_1(x; \mu_1, \sigma_1^2)$</li>
<li>$g(t)$는 $\mathcal{N}_2(x; \mu_2, \sigma_2^2)$</li>
</ul>
</li>
</ul>
<ul>
<li>두 함수의 합성곱 결과는 다음과 같다<ul>
<li><code>(2)</code> $f(t) * g(t) = \mathcal{N}(x; \mu_1 + \mu_2, \sigma_1^2 + \sigma_2^2)$</li>
</ul>
</li>
</ul>
<ul>
<li>위의 예시로 보면 $f(t)$가 로봇의 위치에 관한 함수, $g(t)$가 로봇의 이동량에 관한 함수이고 합성곱 결과는 예측값에 관한 함수이다</li>
<li><strong>예측값 평균</strong>은 <code>(2)</code>에 이전 위치 평균인 $\mu_1$에 0을 대입하고, 이동량 평균인 $\mu_2$에 3을 대입하면 구할 수 있다<ul>
<li>$\mu_1 + \mu_2=0+3=3$</li>
</ul>
</li>
<li><strong>예측값 분산</strong>은 <code>(2)</code>에 이전 위치 분산을 $\sigma_1$에 대입하고, 이동량 분산을 $\sigma_2$에 대입하면 구할 수 있다</li>
</ul>
<ul>
<li>여기에서 합성곱 결과 함수도 정규분포를 따르고, 합성곱 결과 함수의 분산은 두 입력 정규분포의 분산보다 항상 크다는 것을 알 수 있다</li>
<li>즉, 합성곱을 수행할 때마다 분산이 증가한다 (=불확실도가 증가한다)</li>
</ul>
<h3 id="2-measure">2. Measure</h3>
<ul>
<li>센서를 사용하여 현재 위치를 측정하는 단계이다</li>
<li>센서를 사용하여 측정한 측정값에도 noise, 즉 불확실도가 있기 때문에 정규분포로 표현할 수 있다</li>
</ul>
<ul>
<li>센서에서 측정한 로봇의 현재 위치가 $x=4$라고 가정하면 아래와 같은 정규분포로 표현할 수 있다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/741819ab-e068-4d30-ad2b-934d38056de8/image.png" alt=""></p>
<h3 id="3-update">3. Update</h3>
<ul>
<li>예측값과 측정값을 사용하여 최적값을 구하는 단계이다. 즉, 이전값을 갱신(update)하는 단계이다</li>
<li>이때 <strong>베이즈 정리(Bayes&#39; theorem)</strong>를 사용하여 예측값 정규분포와 측정값 <strong>정규분포의 곱(product)</strong> 계산을 통해 최적값을 구한다</li>
</ul>
<ul>
<li>최적값 역시 <strong>정규분포</strong>로 표현할 수 있다<ul>
<li>두 정규분포(측정값, 예측값)의 곱은 정규분포 모양이지만 정규분포는 아니다</li>
<li>하지만 아래의 베이즈 수식에서 볼 수 있듯이 $P(E)$를 나누어 정규화를 해주기 때문에 최적값도 정규분포가 된다</li>
</ul>
</li>
</ul>
<ul>
<li>그 후 이 최적값이 1번의 predict 과정에서 로봇의 상태(위치)로 사용되고 1번부터 3번이 반복된다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/8da0468f-974c-43c8-8070-4690f9fda40c/image.png" alt=""></p>
<h4 id="베이즈-정리bayes-theorem">베이즈 정리(Bayes&#39; theorem)</h4>
<ul>
<li>베이즈 정리는 사전 정보(prior)와 관측(likelihood)를 사용하여 사후 확률(posterior)를 계산하는 것으로 이전 정보를 갱신하고 새로운 정보를 통합할 수 있다</li>
<li>베이즈 정리는 불확실성이 있는 상황에서 신뢰도 있는 값을 추정하기 위해 사용된다</li>
</ul>
<ul>
<li>칼만 필터는 상태를 확률 분포로 표현하고, 센서로 측정한 측정값도 오차가 있을 수 있다</li>
<li>이런 상황에서 확률 기반으로 값을 추정하는 베이즈 정리를 사용하면 현재 상태 추정에 대한 신뢰도를 표현할 수 있고 기존의 상태 추정과 통합하여 값을 갱신할 수 있다</li>
</ul>
<ul>
<li>베이즈 정리 수식</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/9e974b98-c017-4371-aaf5-2923586b45fe/image.png" alt=""></p>
<ul>
<li>위의 예시(로봇의 위치 추정)에서의 의미<ul>
<li>$P(H|E)$ : 최적값</li>
<li>$P(E|H)$ : 센서를 통해 측정한 측정값</li>
<li>$P(H)$ : 예측값 (state predict 과정에서 예측한 예측값)</li>
</ul>
</li>
</ul>
<ul>
<li>즉, 최적값을 구하려면 측정값과 예측값의 곱을 계산해야한다</li>
</ul>
<h4 id="정규분포의-곱product-계산">정규분포의 곱(product) 계산</h4>
<ul>
<li>$f(t), g(t)$를 정규분포로 표현하면 다음과 같다 (여기에서 $\mathcal{N}$은 정규분포, $\mu$는 평균, $\sigma$는 분산을 의미한다)<ul>
<li>$f(t)$는 $\mathcal{N}_1(x; \mu_1, \sigma_1^2)$</li>
<li>$g(t)$는 $\mathcal{N}_2(x; \mu_2, \sigma_2^2)$</li>
</ul>
</li>
</ul>
<ul>
<li>정규분포의 곱 결과 분포를 $\mathcal{N}<em>{new}(x; \mu</em>{new}, \sigma_{new}^2)$라고 하면 그 계산 과정은 다음과 같다<ul>
<li>$\mu_{new} = \frac{\mu_1 \sigma_2^2 + \mu_2 \sigma_2^2}{\sigma_1^2+\sigma_2^2}$</li>
<li>$\sigma_{new}^2 = \frac{1}{1/\sigma_1^2 + 1/\sigma_2^2} = \frac{\sigma_1^2\sigma_2^2}{\sigma_1^2 + \sigma_2^2}$</li>
</ul>
</li>
</ul>
<ul>
<li>위의 예시로 보면 $f(t)$가 예측값에 관한 함수, $g(t)$가 측정값에 관한 함수이고 곱 결과는 최적값에 관한 함수이다</li>
</ul>
<hr>
<h4 id="reference">reference</h4>
<ul>
<li><a href="https://angeloyeo.github.io/2021/04/07/Kalman_filter.html">https://angeloyeo.github.io/2021/04/07/Kalman_filter.html</a></li>
<li><a href="https://medium.com/@celinachild/kalman-filter-%EC%86%8C%EA%B0%9C-395c2016b4d6">https://medium.com/@celinachild/kalman-filter-%EC%86%8C%EA%B0%9C-395c2016b4d6</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[데브코스 자율주행 TIL] DAY 78 (01/15) 🧬]]></title>
            <link>https://velog.io/@happy_quokka/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-%EC%9E%90%EC%9C%A8%EC%A3%BC%ED%96%89-TIL-DAY-78-0115</link>
            <guid>https://velog.io/@happy_quokka/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-%EC%9E%90%EC%9C%A8%EC%A3%BC%ED%96%89-TIL-DAY-78-0115</guid>
            <pubDate>Tue, 05 Mar 2024 08:36:23 GMT</pubDate>
            <description><![CDATA[<h2 id="20240115-day78">2024.01.15 DAY78</h2>
<p>딥러닝 프로젝트 발표일!!</p>
<p>주말 동안 centerpoint 모델 학습을 진행하였다. 학습 epoch을 조절하고, pretrained weight를 사용해보는 등 여러 방법으로 학습을 진행하였다. 코드를 직접 구현한 것이 아니라 코드를 파악하기 어려웠다. epoch은 어디서 조절해야하는지, class를 바꾸려면 어디를 수정해야하는지 찾는데 시간이 오래걸렸다. 그리고 환경 변수 에러, gpu, docker 관련 에러 등 평소에 자주 보지 못했던 에러들이 많이 발생해서 이를 해결하는 과정도 오래걸렸다.</p>
<p>또한 AWS를 다같이 사용해서 모델을 학습시키다보니 용량이 부족한 문제가 자주 발생했다. 결국 docker container를 하나만 사용하는 방법으로 용량 문제를 해결했다. 사실 완전히 해결한 것은 아니지만 일단 다른 container를 제거해서 용량을 확보했다.</p>
<h2 id="🧬-딥러닝-프로젝트-결과">🧬 딥러닝 프로젝트 결과</h2>
<p>같은 dataset으로 epoch를 조절하여 학습을 진행해보았다. 또한 mini dataset은 데이터 수가 부족하여 학습이 잘 되지 않는 것 같다고 판단하여 train dataset을 사용하여 학습을 진행해보았다.</p>
<p>그 결과 v1.0-trainval-1의 경우 데이터의 수가 훨씬 많기 때문에 mini의 경우보다 성능이 좋았다. </p>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/e44469ae-1386-4130-8d17-805ebca6cbb6/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[딥러닝 프로젝트] 4. Center Point 모델 사용하여 object detection 수행하기 - 결과]]></title>
            <link>https://velog.io/@happy_quokka/%EB%94%A5%EB%9F%AC%EB%8B%9D-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-4.-Center-Point-%EB%AA%A8%EB%8D%B8-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-object-detection-%EC%88%98%ED%96%89%ED%95%98%EA%B8%B0-%EA%B2%B0%EA%B3%BC</link>
            <guid>https://velog.io/@happy_quokka/%EB%94%A5%EB%9F%AC%EB%8B%9D-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-4.-Center-Point-%EB%AA%A8%EB%8D%B8-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-object-detection-%EC%88%98%ED%96%89%ED%95%98%EA%B8%B0-%EA%B2%B0%EA%B3%BC</guid>
            <pubDate>Tue, 05 Mar 2024 08:17:24 GMT</pubDate>
            <description><![CDATA[<h2 id="object-detection">Object Detection</h2>
<h4 id="dataset-nuscenes-dataset">Dataset (nuScenes Dataset)</h4>
<ul>
<li>v1.0 mini 와 v1.0 part 1 dataset 사용</li>
<li>object detection은 10개의 class로 구분되어 있다</li>
</ul>
<h3 id="object-detection-성능-평가-지표">Object Detection 성능 평가 지표</h3>
<ul>
<li><strong>AP (Mean Average Precision)</strong> : Precision-Recall 곡선의 면적, 높은 mAP 값은 모델이 다양한 클래스에 대해 높은 precision과 recall을 가진다</li>
<li><strong>ATE (Mean Absolute Trajectory Error)</strong> : 2D 상에서의 Euclidean center distance (meters) 즉, 모델이 추정한 위치와 실제 위치 간의 차이를 의미한다</li>
<li><strong>ASE (Mean Absolute Scale Error)</strong> : orientation과 translation을 align 해 준 뒤의 3D IOU (1 - IOU)</li>
<li><strong>AOE (Average Orientation Error)</strong><ul>
<li>예측 값과 GT 사이의 가장 작은 yaw angle 차이 값 (radians)</li>
<li>yaw angle은 차량의 주향 방향에 대한 각도를 의미한다</li>
</ul>
</li>
<li><strong>AVE (Average Velocity Error)</strong> : velocity 차이의 L2 norm으로 velocity error의 절댓값 (m/s)</li>
<li><strong>AAE (Average Attribute Error) :</strong> 1에서 attribute classification accuracy를 빼준 값 (1 - acc)</li>
<li><strong>NDS (nuScenes Detection Score)</strong><ul>
<li>mAP, mATE, mASE, mAOE, mAVE, mAAE에 가중치를 부여하여 합산한 값</li>
<li>TP 오류를 TP 점수로 변환한다</li>
<li>$TP_score=max⁡(1−TP_error,0.0)$</li>
<li>mAP에는 가중치 5를 할당하고 나머지에는 가중치 1을 할당하여 계산한다</li>
</ul>
</li>
</ul>
<h3 id="결과-비교">결과 비교</h3>
<table>
<thead>
<tr>
<th>dataset</th>
<th>epoch</th>
<th>mAP⇧</th>
<th>mATE⇩</th>
<th>mASE⇩</th>
<th>mAOE⇩</th>
<th>mAVE⇩</th>
<th>mAAE⇩</th>
<th>NDS⇧</th>
<th>Eval time (s)</th>
</tr>
</thead>
<tbody><tr>
<td>v1.0 mini</td>
<td>20</td>
<td>0.1685</td>
<td>0.5884</td>
<td>0.4970</td>
<td>0.9463</td>
<td>1.4115</td>
<td>0.4628</td>
<td>0.2348</td>
<td>4.5</td>
</tr>
<tr>
<td>v1.0 mini</td>
<td>80</td>
<td>0.1962</td>
<td>0.5061</td>
<td>0.5055</td>
<td>0.7282</td>
<td>1.0762</td>
<td>0.3330</td>
<td>0.2908</td>
<td>3.7</td>
</tr>
<tr>
<td>v1.0-trainval-1</td>
<td>20</td>
<td>0.4181</td>
<td>0.4012</td>
<td>0.3433</td>
<td>0.4573</td>
<td>0.5910</td>
<td>0.3490</td>
<td>0.4949</td>
<td>1.3 / Local</td>
</tr>
</tbody></table>
<ul>
<li>같은 dataset에서 epoch를 늘린 경우가 그렇지 않은 경우보다 성능이 좋다</li>
<li>하지만 mini dataset은 데이터의 수가 적기 때문에 성능이 좋은 편은 아니다</li>
<li>v1.0-trainval-1의 경우 데이터의 수가 훨씬 많기 때문에 mini의 경우보다 성능이 좋다</li>
<li>딥러닝을 학습할 때는 epoch도 중요하지만 데이터의 절대적인 수가 매우 중요하다</li>
</ul>
<h4 id="dataset--v10-mini--epoch--20">dataset : v1.0 mini / epoch : 20</h4>
<table>
<thead>
<tr>
<th>Object Class</th>
<th>AP⇧</th>
<th>ATE⇩</th>
<th>ASE⇩</th>
<th>AOE⇩</th>
<th>AVE⇩</th>
<th>AAE⇩</th>
<th>Dist <a href="mailto:AP@0.5">AP@0.5</a></th>
<th>Dist <a href="mailto:AP@1.0">AP@1.0</a></th>
<th>Dist <a href="mailto:AP@2.0">AP@2.0</a></th>
<th>Dist <a href="mailto:AP@4.0">AP@4.0</a></th>
<th>mean AP</th>
</tr>
</thead>
<tbody><tr>
<td>car</td>
<td>0.630</td>
<td>0.277</td>
<td>0193</td>
<td>0.923</td>
<td>0.493</td>
<td>0.232</td>
<td>46.76</td>
<td>30.96</td>
<td>69.90</td>
<td>74.30</td>
<td>0.6297970530209718</td>
</tr>
<tr>
<td>truck</td>
<td>0.158</td>
<td>0.318</td>
<td>0.287</td>
<td>0.439</td>
<td>0.886</td>
<td>0.270</td>
<td>10.90</td>
<td>13.24</td>
<td>15.25</td>
<td>24.01</td>
<td>0.15849554291426088</td>
</tr>
<tr>
<td>bus</td>
<td>0.078</td>
<td>0.880</td>
<td>0232</td>
<td>0.798</td>
<td>3.742</td>
<td>0.829</td>
<td>0.00</td>
<td>3.26</td>
<td>7.98</td>
<td>19.86</td>
<td>0.07774084063257547</td>
</tr>
<tr>
<td>trailer</td>
<td>0.000</td>
<td>1.000</td>
<td>1.000</td>
<td>1.000</td>
<td>1.000</td>
<td>1.000</td>
<td>0.00</td>
<td>0.00</td>
<td>0.00</td>
<td>0.00</td>
<td>0.00</td>
</tr>
<tr>
<td>construction_vehicle</td>
<td>0.000</td>
<td>1.000</td>
<td>1.000</td>
<td>1.000</td>
<td>1.000</td>
<td>1.000</td>
<td>0.00</td>
<td>0.00</td>
<td>0.00</td>
<td>0.00</td>
<td>0.00</td>
</tr>
<tr>
<td>pedestrian</td>
<td>0.740</td>
<td>0.290</td>
<td>0.269</td>
<td>1.362</td>
<td>1.027</td>
<td>0.211</td>
<td>65.40</td>
<td>74.24</td>
<td>76.35</td>
<td>79.81</td>
<td>0.7395133329115092</td>
</tr>
<tr>
<td>motorcycle</td>
<td>0.069</td>
<td>0.256</td>
<td>0.310</td>
<td>1.615</td>
<td>1.041</td>
<td>0.155</td>
<td>5.74</td>
<td>6.98</td>
<td>7.02</td>
<td>7.87</td>
<td>0.06904350200740915</td>
</tr>
<tr>
<td>bicycle</td>
<td>0.003</td>
<td>0.407</td>
<td>0.521</td>
<td>1.070</td>
<td>2.103</td>
<td>0.006</td>
<td>0.00</td>
<td>0.29</td>
<td>0.51</td>
<td>0.51</td>
<td>0.0032737595888300826</td>
</tr>
<tr>
<td>traffic_cone</td>
<td>0.000</td>
<td>0.140</td>
<td>0.584</td>
<td>nan</td>
<td>nan</td>
<td>nan</td>
<td>0.00</td>
<td>0.00</td>
<td>0.00</td>
<td>0.00</td>
<td>0.00</td>
</tr>
<tr>
<td>barrier</td>
<td>0.007</td>
<td>1.316</td>
<td>0.575</td>
<td>0.309</td>
<td>nan</td>
<td>nan</td>
<td>0.00</td>
<td>0.00</td>
<td>1.13</td>
<td>1.68</td>
<td>0.00700824102726923</td>
</tr>
</tbody></table>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/70ff3ef7-811a-4826-aec6-ff315a6ebbf9/image.png" alt=""></p>
<h4 id="epoch-50">epoch 50</h4>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/9094e71a-6232-4f8c-8392-a8d6c0e6219f/image.png" alt=""></p>
<h4 id="epoch-80">epoch 80</h4>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/8b6742e2-f1fa-4670-96fe-aa6e0dbed982/image.png" alt=""></p>
<h2 id="tracking-결과">Tracking 결과</h2>
<p>tracking 결과</p>
<p>AMOTA   AMOTP   RECALL  MOTAR   GT      MOTA    MOTP    MT      ML      FAF     TP      FP      FN      IDS     FRAG    TID     LGD
bicycle         0.000   1.603   0.345   0.000   58      0.000   0.262   0       5       530.6   17      833     38      3       1       0.62    1.88
bus             0.117   1.515   0.453   0.444   86      0.186   0.655   1       2       30.8    36      20      47      3       5       3.25    3.62
car             0.609   0.856   0.647   0.856   2729    0.541   0.356   60      48      153.1   1725    248     963     41      25      1.71    2.30
motorcy         0.047   1.445   0.261   0.328   238     0.084   0.357   1       5       59.4    61      41      176     1       3       2.00    4.21
pedestr         0.668   0.540   0.786   0.791   1470    0.591   0.362   53      17      173.5   1098    229     314     58      18      0.76    1.17
trailer         0.000   2.000   0.000   0.000   41      0.000   2.000   0       1       500.0   0       nan     41      nan     nan     20.00   20.00
truck           0.176   1.344   0.226   0.725   177     0.164   0.284   0       6       9.1     40      11      137     0       0       1.00    3.83</p>
<p>Aggregated results:
AMOTA   0.231
AMOTP   1.329
RECALL  0.388
MOTAR   0.449
GT      685
MOTA    0.224
MOTP    0.611
MT      115
ML      84
FAF     208.1
TP      2977
FP      1382
FN      1716
IDS     106
FRAG    52
TID     4.19
LGD     5.29
Eval time: 46.8s</p>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/c02c3cea-9552-48f2-b2db-9224e188ddb2/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ImportError: cannot import name 'url_quote' from 'werkzeug.urls' (/opt/conda/lib/python3.8/site-packages/werkzeug/urls.py)]]></title>
            <link>https://velog.io/@happy_quokka/ImportError-cannot-import-name-urlquote-from-werkzeug.urls-optcondalibpython3.8site-packageswerkzeugurls.py</link>
            <guid>https://velog.io/@happy_quokka/ImportError-cannot-import-name-urlquote-from-werkzeug.urls-optcondalibpython3.8site-packageswerkzeugurls.py</guid>
            <pubDate>Mon, 05 Feb 2024 04:56:52 GMT</pubDate>
            <description><![CDATA[<h3 id="에러">에러</h3>
<ul>
<li>flask가 사용될 때 발생할 수 있는 오류라고 한다</li>
<li>werkzeug 라이브러리 버전이 맞지 않아서 발생한다</li>
</ul>
<pre><code>ImportError: cannot import name &#39;url_quote&#39; from &#39;werkzeug.urls&#39; (/opt/conda/lib/python3.8/site-packages/werkzeug/urls.py)</code></pre><h3 id="해결">해결</h3>
<pre><code>pip uninstall -y Werkzeug
pip install Werkzeug==2.3.6</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Sensor Fusion 프로젝트] 2. LiDAR Projection 과정 및 구현 (Open3D, Mayavi)]]></title>
            <link>https://velog.io/@happy_quokka/Sensor-Fusion-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-3.-LiDAR-Projection-%EA%B3%BC%EC%A0%95-%EB%B0%8F-%EA%B5%AC%ED%98%84-Open3D-Mayavi</link>
            <guid>https://velog.io/@happy_quokka/Sensor-Fusion-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-3.-LiDAR-Projection-%EA%B3%BC%EC%A0%95-%EB%B0%8F-%EA%B5%AC%ED%98%84-Open3D-Mayavi</guid>
            <pubDate>Sun, 04 Feb 2024 13:02:22 GMT</pubDate>
            <description><![CDATA[<p>KITTI 데이터셋의 LiDAR와 image 데이터를 사용하여 LiDAR data를 이미지에 projection 해보았다</p>
<h2 id="1-data-형식">1. data 형식</h2>
<h3 id="1-1-lidar-data">1-1. LiDAR data</h3>
<ul>
<li>velodyne HDL-64E</li>
<li>LiDAR data 파일 형식 : <code>*.bin</code></li>
<li>data format : x, y, z, intensity로 구성되어있다</li>
<li>수평 시야각 : 360도, 수직 시야각 26.8도</li>
<li>맨 위쪽부터 반시계 방향 순서로 데이터가 기록되어 있다</li>
</ul>
<ul>
<li>LiDAR 좌표계<ul>
<li>LiDAR 센서 중심 (0,0,0)</li>
<li>x축 : 전방</li>
<li>y축 : 왼쪽</li>
<li>z축 : 높이, 하늘 방향</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/91f4e14c-1465-4489-990b-249f01474083/image.png" alt=""></p>
<h3 id="1-2-calibration-data">1-2. calibration data</h3>
<ul>
<li>KITTI에서는 camera calibration과 관련된 데이터을 제공해준다 (camera calibration matrices of object data set)</li>
</ul>
<h4 id="calib-파일-txt-내용">calib 파일 (.txt) 내용</h4>
<ul>
<li><code>P0</code>, <code>P1</code>, <code>P2</code>, <code>P3</code> : projection 행렬<ul>
<li>world 좌표를 image 좌표로 projection 해주는 행렬</li>
<li>12개의 값 -&gt; 3x4 행렬</li>
<li>아래와 같이 KITTI 데이터는 4대의 카메라(cam 0, cam1, cam2, cam3)을 사용한다</li>
<li>다운받은 데이터셋이 cam2이기 때문에 <code>P2</code>만 사용하면 된다</li>
<li><img src="https://velog.velcdn.com/images/happy_quokka/post/d0297f72-7df4-4680-b89b-7680b92c182f/image.png" alt=""></li>
</ul>
</li>
<li><code>R0_rect</code> : rectification<ul>
<li>world 평면으로 회전시켜주는 회전 변환 행렬</li>
<li>9개의 값 -&gt; 3x3 행렬</li>
<li>KITTI는 사용하는 카메라가 4대이기 때문에 이들 간의 world 좌표를 맞춰주기 위해서 이 행렬을 사용한다</li>
</ul>
</li>
<li><code>Tr_velo_to_cam</code> : LiDAR 좌표 -&gt; camera 좌표<ul>
<li>LiDAR 좌표를 camera 좌표 (정확히는 cam0)으로 변환해주는 변환 행렬</li>
<li>9개의 값 -&gt; 3x3 행렬</li>
</ul>
</li>
</ul>
<hr>
<h2 id="2-lidar와-camera-mapping">2. LiDAR와 camera mapping</h2>
<p>$s(x, y, 1) = P2 * R0_rect * Tr_velo_to_cam * (X, Y, Z, 1)$</p>
<ol>
<li>LiDAR 좌표 $(X, Y, Z, 1)$을 camera 기준 좌표계로 변환<ul>
<li>$Tr_velo_to_cam * (X, Y, Z, 1)$</li>
</ul>
</li>
<li>world 좌표로 변환<ul>
<li>$R0_rect * Tr_velo_to_cam * (X, Y, Z, 1)$</li>
<li>cam0의 좌표를 최종적인 world 평면으로 회전시켜주는 과정</li>
<li>KITTI의 카메라 4대가 스테레오 카메라처럼 일렬로 배치되어 있는데 스테레오 연산을 위해서는 이 카메라들이 동일한 world 평면에서 정렬되어 있어야하기 때문이다</li>
</ul>
</li>
<li>카메라의 이미지 좌표로 변환<ul>
<li>$P2 * R0_rect * Tr_velo_to_cam * (X, Y, Z, 1)$</li>
</ul>
</li>
<li>결과 $(x, y, 1)$<ul>
<li>결과 이미지 좌표로 (x, y, z)가 나오는데 이때 z는 1이기 때문에 $x = x/z, y = y/z$ 계산을 수행한다</li>
</ul>
</li>
</ol>
<ul>
<li>계산을 수행할 때 행렬의 크기를 고려해야한다. 3x4 행렬의 경우 (0,0,0,1)을 추가하여 4x4 행렬로 변환한 후 사용하여도 되고 아니면 3x1 좌표를 4x1로 변환하여 계산해도 된다</li>
</ul>
<hr>
<h2 id="3-lidar-data-시각화하기">3. LiDAR data 시각화하기</h2>
<h3 id="3-1-open3d-라이브러리">3-1. Open3D 라이브러리</h3>
<pre><code class="language-python">def visualization_open3d(data):
    pcd = open3d.geometry.PointCloud()
    pcd.points = open3d.utility.Vector3dVector(data[:, :3])
    open3d.visualization.draw_geometries([pcd])

file_name = &#39;000000&#39;
velo_file = f&#39;./data_object_velodyne/training/velodyne/{file_name}.bin&#39;

with open(velo_file, &#39;rb&#39;) as f:
    data = np.fromfile(f, dtype=np.float32).reshape(-1,4)

visualization_open3d(data)</code></pre>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/c616913c-5ef6-46aa-8480-9b4dd9f7fb15/image.png" alt=""></p>
<h3 id="3-2-mayavi-라이브러리">3-2. Mayavi 라이브러리</h3>
<pre><code class="language-python">def visualization_mayavi(data):
    x = data[:, 0]
    y = data[:, 1]
    z = data[:, 2]

    mlab.figure(bgcolor=(0, 0, 0))
    mlab.points3d(x, y, z, color=(0, 1, 0), mode=&#39;point&#39;) 
    # mlab.axes() 
    mlab.show()
file_name = &#39;000000&#39;
velo_file = f&#39;./data_object_velodyne/training/velodyne/{file_name}.bin&#39;

with open(velo_file, &#39;rb&#39;) as f:
    data = np.fromfile(f, dtype=np.float32).reshape(-1,4)

visualization_mayavi(data)</code></pre>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/25a5f08a-18c1-4fb4-87f3-c7347f26c897/image.png" alt=""></p>
<hr>
<h2 id="4-projection-구현">4. projection 구현</h2>
<h3 id="4-1-calib-파일-불러오기">4-1. calib 파일 불러오기</h3>
<ul>
<li>projection 행렬들 중에서는 P2만 필요하기 때문에 4개의 projection 행렬 중에 P2만 가져온다</li>
</ul>
<pre><code class="language-python">def read_calib_file(file_path):
    with open(file_path, &#39;r&#39;) as f:
        lines = f.readlines()

    P2 = np.array([float(i) for i in lines[2].split(&#39; &#39;)[1:]]).reshape(3,4)
    R0_rect = np.array([float(i) for i in lines[4].split(&#39; &#39;)[1:]]).reshape(3,3)
    Tr_velo_to_cam = np.array([float(i) for i in lines[5].split(&#39; &#39;)[1:]]).reshape(3,4)

    return P2, R0_rect, Tr_velo_to_cam</code></pre>
<h3 id="4-2-projection한-좌표-구하기">4-2. projection한 좌표 구하기</h3>
<ul>
<li><code>[z&gt;0]</code> 을 하는 이유는 depth가 양수인 것만 투영하도록 하기 위해서이다. depth가 음수인 경우는 뒷쪽 lidar 데이터이기 때문이다</li>
</ul>
<pre><code class="language-python">file_name = &#39;000000&#39;
calib_file = f&#39;./calib/training/calib/{file_name}.txt&#39;
image_file = f&#39;./data_object_image_2/training/image_2/{file_name}.png&#39;
velo_file = f&#39;./data_object_velodyne/training/velodyne/{file_name}.bin&#39; #lidar 파일

P2, R0_rect, Tr_velo_to_cam = read_calib_file(calib_file)

R0 = np.eye(4)
R0[:3, :3] = R0_rect #3x3 행렬인 R0_rect을 4x4 행렬로 변환
Tr = np.vstack([Tr_velo_to_cam, [0,0,0,1]]) #3x4 행렬인 Tr_velo_to_cam를 4x4 행렬로 변환

#lidar 데이터 불러오기
with open(velo_file, &#39;rb&#39;) as f:
    data = np.fromfile(f, dtype=np.float32).reshape(-1,4)

XYZ1 = np.vstack([data[:,:3].T, np.ones((1, data.shape[0]))])

xyz = np.dot(P2,np.dot(R0,np.dot(Tr, XYZ1))) 
z = xyz[2, :]
x = (xyz[0, :] / z).astype(np.int32)[z&gt;0] 
y = (xyz[1, :] / z).astype(np.int32)[z&gt;0]</code></pre>
<h3 id="4-3-시각화하기">4-3. 시각화하기</h3>
<h4 id="image-lidar">image, LiDAR</h4>
<pre><code class="language-python">def visualization_plt(image_file, data, x, y):
    img = cv2.imread(image_file)
    img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    aspect_ratio = float(img.shape[1]) / img.shape[0]

    fig, axs = plt.subplots(1, 2, figsize=(20, 25 ))

    axs[0].imshow(img_rgb)
    axs[0].axis(&#39;off&#39;) 

    x_values = data[:, 0]

    x_min, x_max = np.percentile(x_values, 1), np.percentile(x_values, 99) 

    scatter = axs[1].scatter(x, img.shape[0] - y, c=x_values, cmap=&#39;jet&#39;, marker=&#39;.&#39;, s=15, vmin=x_min, vmax=x_max)
    axs[1].set_xlim([0, img.shape[1]])
    axs[1].set_ylim([0, img.shape[0]])
    axs[1].axis(&#39;off&#39;)

    for ax in axs:
        ax.set_aspect(aspect_ratio)

    plt.tight_layout()
    plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/fbfb3873-0904-4a75-93cc-b28b9c584110/image.png" alt=""></p>
<h4 id="lidar-데이터를-이미지-위로-시각화하였다">LiDAR 데이터를 이미지 위로 시각화하였다</h4>
<pre><code class="language-python">def visualization_projection(image_file, data, x, y):
    img = cv2.imread(image_file)
    img_mapped = img.copy()
    img_h, img_w = img.shape[:2]

    # 거리에 따라 color값을 다르게 주기 위한 부분
    x_normalized = (data[:, 0] - np.min(data[:, 0])) / (np.max(data[:, 0]) - np.min(data[:, 0]))
    colors = plt.cm.magma(x_normalized)

    for i, (ix, iy) in enumerate(zip(x, y)):
        if 0 &lt;= ix &lt; img_w and 0 &lt;= iy &lt; img_h:
            color = (colors[i] * 255).astype(np.uint8)[:3]
            color = (int(color[2]), int(color[1]), int(color[0]))
            cv2.circle(img_mapped, (ix, iy), radius=1, color=color, thickness=2)

    img_mapped_rgb = cv2.cvtColor(img_mapped, cv2.COLOR_BGR2RGB)

    plt.imshow(img_mapped_rgb)
    plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/b18ad67d-4ee2-4018-b884-c554abbb6780/image.png" alt=""></p>
<hr>
<h4 id="reference">reference</h4>
<p><a href="https://www.cvlibs.net/datasets/kitti/setup.php">https://www.cvlibs.net/datasets/kitti/setup.php</a>
<a href="https://darkpgmr.tistory.com/190">https://darkpgmr.tistory.com/190</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[docker 환경에서 open3d 시각화 에러 (GLFWError: X11)]]></title>
            <link>https://velog.io/@happy_quokka/docker-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-open3d-%EC%8B%9C%EA%B0%81%ED%99%94-%EC%97%90%EB%9F%AC-GLFWError-X11</link>
            <guid>https://velog.io/@happy_quokka/docker-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-open3d-%EC%8B%9C%EA%B0%81%ED%99%94-%EC%97%90%EB%9F%AC-GLFWError-X11</guid>
            <pubDate>Thu, 01 Feb 2024 23:20:35 GMT</pubDate>
            <description><![CDATA[<h3 id="에러-메세지--발생-환경">에러 메세지 &amp; 발생 환경</h3>
<p>docker 환경에서 open3d를 시각화하려고 하면 아래와 같은 에러가 발생한다</p>
<pre><code>GLFWError: X11: Failed to open display :0
Fail to initialize GLFW
Failed creating OpenGL window.</code></pre><p><img src="https://velog.velcdn.com/images/happy_quokka/post/ff0c1ed0-88b4-456a-9c48-587c0c9c66c6/image.png" alt=""></p>
<h3 id="해결">해결</h3>
<ul>
<li>docker container를 만들 때 <code>-e DISPLAY=$DISPLAY</code> 와 <code>-v /tmp/.X11-unix:/tmp/.X11-unix</code> 옵션을 추가해야한다<ul>
<li><code>e DISPLAY=$DISPLAY</code> : display 환경 설정</li>
<li><code>v /tmp/.X11-unix:/tmp/.X11-unix</code> : host의 x window 연결</li>
</ul>
</li>
<li>그리고 container를 시작하기 전에 <code>xhost +</code> 를 해주어야한다</li>
<li>만약 위의 옵션을 다 추가해서 container를 생성하였는데도 에러가 발생하였다면 <code>xhost +</code> 를 다시 해보면 된다</li>
</ul>
<pre><code>$ xhost +
$ docker run -it --net=host --ipc=host -v /home/nahye/dev_practice/:/workspace -v /tmp/.X11-unix:/tmp/.X11-unix --device=/dev/video0:/dev/video0 -e DISPLAY=$DISPLAY --env QT_X11_NO_MITSHM=1 --name &quot;torch_gui&quot; pytorch/pytorch /bin/bash
</code></pre><ul>
<li>해결되면 아래와 같이 시각화를 할 수 있다!!</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/de5107ed-2f10-456f-a27d-df72d2c4100d/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS EC2 인스턴스 용량 늘리기 (볼륨 증설)]]></title>
            <link>https://velog.io/@happy_quokka/AWS-EC2-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EC%9A%A9%EB%9F%89-%EB%8A%98%EB%A6%AC%EA%B8%B0-%EB%B3%BC%EB%A5%A8-%EC%A6%9D%EC%84%A4</link>
            <guid>https://velog.io/@happy_quokka/AWS-EC2-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EC%9A%A9%EB%9F%89-%EB%8A%98%EB%A6%AC%EA%B8%B0-%EB%B3%BC%EB%A5%A8-%EC%A6%9D%EC%84%A4</guid>
            <pubDate>Wed, 31 Jan 2024 16:41:01 GMT</pubDate>
            <description><![CDATA[<p>AWS 인스턴스를 사용하다보면 <code>No space left on device</code> 오류가 자주 발생한다. 이는 AWS EC2에서 제공하는 용량을 모두 사용하여 발생하는 오류이다. 이를 해결하기 위해서는 내부의 필요없는 데이터를 지워야하지만 용량을 늘릴 수도 있다.</p>
<h3 id="용량-증설-전-상태">용량 증설 전 상태</h3>
<ul>
<li><code>$ df -h</code>로 용량을 확인해보면 <code>dev/nvme0n1p1</code>의 avail 용량이 얼마남지 않은 것을 확인할 수 있다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/f73bafe3-b869-4679-999a-e7d2145075b5/image.png" alt=""></p>
<h3 id="용량-증설-방법">용량 증설 방법</h3>
<h4 id="1-aws-콘솔에서-볼륨-증설">1. AWS 콘솔에서 볼륨 증설</h4>
<ul>
<li>EC2 &gt; 인스턴스 &gt; 스토리지 에서 볼륨 크기를 확인 후 볼륨 ID 클릭</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/108c84c3-d602-4d8f-9800-8925fdb8e756/image.png" alt=""></p>
<ul>
<li>원하는 볼륨 선택 후 오른쪽 위의 <code>작업</code>에서 <code>볼륨 수정</code> 클릭</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/36645b12-806a-4110-a780-3dd71afabdb8/image.png" alt=""></p>
<ul>
<li>원하는 크기를 입력 후 수정 클릭</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/4f72225e-29b0-4260-bd15-d2488f0c5cd6/image.png" alt=""></p>
<h4 id="2-인스턴스-접속-후-증설-적용">2. 인스턴스 접속 후 증설 적용</h4>
<ul>
<li>AWS 콘솔에서 볼륨을 증설하여도 바로 적용되지 않는다</li>
<li>아래와 같이 증설이 되었지만 <code>dev/nvme0n1p1</code>는 여전히 45G로 늘지 않은 것을 확인할 수 있다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/949803e1-fe6d-4c79-90fc-38b1fb8439fd/image.png" alt=""></p>
<ul>
<li>아래의 두 명령어를 통해 원하는 파티션에 연결하면 된다<pre><code>$ sudo growpart 용량_늘어난_파티션 1
$ sudo resize2fs 적용할_파티션</code></pre></li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/7f492b80-0f6a-4132-95f6-269ca59de157/image.png" alt=""></p>
<h3 id="용량-증설-후">용량 증설 후</h3>
<p><code>dev/nvme0n1p1</code>가 200G로 증가된 것을 확인할 수 있다</p>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/71269a35-6f07-4ba6-ae02-8a10a0f04fd2/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Sensor Fusion 프로젝트] 1. Object Detection (2D & 3D)]]></title>
            <link>https://velog.io/@happy_quokka/Sensor-Fusion-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-1.-Object-Detection-2D-3D</link>
            <guid>https://velog.io/@happy_quokka/Sensor-Fusion-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-1.-Object-Detection-2D-3D</guid>
            <pubDate>Wed, 31 Jan 2024 16:22:33 GMT</pubDate>
            <description><![CDATA[<h2 id="1-dataset-kitti">1. Dataset (KITTI)</h2>
<ul>
<li>KITTI dataset을 사용한다 (<a href="https://www.cvlibs.net/datasets/kitti/eval_object.php?obj_benchmark=3d">KITTI homepage</a>)</li>
<li>KITTI &gt; object &gt; 3d object 에서 아래 3개를 다운받는다 <ul>
<li>left color images of object data set (12 GB) : 2d 이미지 데이터</li>
<li>Velodyne point clouds (29 GB) : 3d LiDAR 데이터</li>
<li>training labels of object data set (5 MB) : 데이터 라벨링 값</li>
<li>camera calibration matrices of object data set (16 MB) : 카메라 calibraion 정보</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/b9b2c117-d908-44b6-894e-3af2d547e64c/image.png" alt=""></p>
<hr>
<h2 id="2-object-detection-모델">2. Object Detection 모델</h2>
<h3 id="2-1-2d-object-detection-yolo-v7">2-1. 2D Object Detection (YOLO v7)</h3>
<ul>
<li><a href="https://github.com/WongKinYiu/yolov7">yolov7 github</a></li>
<li>2D 이미지에서 객체를 인식하기 위해 yolo v7 모델을 사용한다</li>
<li>yolo v7 모델도 다양한 버전이 있는데 기존 버전을 사용하기로 결정했다</li>
<li>실시간 FPS와 성능을 고려하여 선택하였다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/49f1f5da-ac04-41f0-9dc4-70585940514f/image.png" alt=""></p>
<h3 id="2-2-3d-object-detection-centerpoint">2-2. 3D Object Detection (CenterPoint)</h3>
<ul>
<li><a href="https://github.com/tianweiy/CenterPoint-KITTI">centerpoint-kitti github</a></li>
<li>LiDAR의 3D 데이터 즉, point cloud 에서 객체를 인식하기 위해 centerpoint 모델을 사용한다</li>
</ul>
<ul>
<li>class : [&#39;Car&#39;, &#39;Pedestrian&#39;, &#39;Cyclist&#39;] 3종류</li>
<li><code>centerpoint.yaml</code> 모델, <code>pv_rcnn.yaml</code> 모델 2종류로 각각 학습을 진행하였다<ul>
<li>결과를 확인할 때 해당 파일의 POST_PROCESSING &gt; SCORE_THRESH 값을 바꿔주며 결과 이미지를 확인했다 (해당 부분이 </li>
</ul>
</li>
</ul>
<hr>
<h2 id="3-kitti-format을-yolo-format으로-변환하기">3. KITTI format을 YOLO format으로 변환하기</h2>
<h3 id="kitti-format">KITTI format</h3>
<ul>
<li>(class_type, truncated, occluded, alpha, bbox_xmin, bbox_ymin, bbox_xmax, bbox_ymax, dimension_height, dimension_width, dimension_length, location_x, location_y, location_z, rotation) 형식으로 되어 있다</li>
</ul>
<ul>
<li>예시<pre><code>Car 0.00 0 -1.59 553.89 184.63 598.47 220.55 1.26 1.60 3.56 -1.39 1.73 27.81 -1.64
Car 0.00 0 -1.49 502.27 183.99 537.52 210.78 1.37 1.63 3.57 -4.95 2.00 39.72 -1.61
Car 0.00 0 2.52 837.66 190.04 921.14 223.55 1.38 1.35 3.30 11.95 2.18 32.11 2.87
Car 0.00 0 2.24 955.63 189.82 1046.94 226.08 1.59 1.73 4.02 18.79 2.44 34.70 2.73
Van 0.00 2 -1.61 560.17 165.84 585.44 198.47 2.26 1.64 5.20 -2.79 1.81 52.88 -1.66
DontCare -1 -1 -10 486.65 171.90 546.07 186.53 -1 -1 -1 -1000 -1000 -1000 -10</code></pre></li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/d240e341-66a6-4ec8-b849-e42db372d7d8/image.png" alt=""></p>
<h3 id="yolo-format">YOLO format</h3>
<ul>
<li>(class_id, center_x, center_y, width, height) 형식으로 되어있다</li>
<li>bounding box를 의미하는 center_x, center_y, width, height는 0 ~ 1 사이의 값으로 normalized 되어있다</li>
<li>normalized는 이미지의 width, height로 나누어 수행한다</li>
</ul>
<h3 id="변환하기">변환하기</h3>
<ul>
<li>KITTI의 class_type, bbox 정보 4개만 가지고 와서 YOLO 형식으로 변환한다</li>
<li>string으로 되어 있는 class_type을 int 형식으로 변환하고 bbox도 YOLO에 맞게 변환한다</li>
</ul>
<pre><code class="language-python">def xyxy2xywh_np(bbox, img_width, img_height):
    min_x, min_y, max_x, max_y = np.array(bbox, dtype=np.float32)

    center_x = round((max_x + min_x) / 2,2)
    center_y = round((max_y + min_y) / 2,2)
    bbox_width = round(max_x - min_x,2)
    bbox_height = round(max_y - min_y,2)

    yolo_x = center_x / img_width
    yolo_y = center_y / img_height
    yolo_width = bbox_width / img_width
    yolo_height = bbox_height / img_height

    bbox = (yolo_x, yolo_y, yolo_width, yolo_height)

    return bbox

class convert2yolo():
    def __init__(self):
        self.label_dir = &quot;./kitti_labels/val/&quot;
        self.img_dir = &quot;./images/&quot;
        self.img_train_dir = self.img_dir + &quot;val/&quot;
        # self.img_valid_dir = self.img_dir + &quot;valid/&quot;
        self.output_dir = &quot;./labels/val/&quot;

        self.class_names = {
                        &#39;Car&#39; : 0, 
                        &#39;Van&#39; : 1, 
                        &#39;Truck&#39; : 2,
                        &#39;Pedestrian&#39; : 3, 
                        &#39;Person_sitting&#39; : 4, 
                        &#39;Cyclist&#39; : 5, 
                        &#39;Tram&#39; : 6,
                        &#39;Misc&#39; : 7,
                        &#39;DontCare&#39; : 8
                    }

        self.label_dir_list = glob(self.label_dir + &quot;/*&quot;)
        os.makedirs(self.output_dir, exist_ok=True)

    def save(self):
        for file in self.label_dir_list:
            img_path = file.split(&#39;/&#39;)[-1].split(&#39;.&#39;)[0]
            img_name = self.img_train_dir + img_path + &quot;.png&quot;
            img = cv2.imread(img_name, cv2.IMREAD_ANYCOLOR)
            img_width = img.shape[1]
            img_height = img.shape[0]

            yolo_file = open(self.output_dir + file.split(&quot;/&quot;)[-1],&quot;w+&quot;)
            with open(file, &#39;r&#39;, encoding=&#39;UTF-8&#39;) as f:
                lines = f.readlines()
                for line in lines:
                    line = line.split(&#39; &#39;)
                    class_id = self.class_names[line[0]]
                    cx, cy, w, h = xyxy2xywh_np(line[4:8], img_width, img_height)
                    yolo_file.write(f&quot;{class_id} {cx} {cy} {w} {h}\n&quot;)
            f.close()
            yolo_file.close()


if __name__ == &quot;__main__&quot;:
    convert = convert2yolo()
    convert.save()</code></pre>
<hr>
<h2 id="4-yolo-v7-학습하기">4. YOLO v7 학습하기</h2>
<h3 id="4-0-프로젝트-환경">4-0. 프로젝트 환경</h3>
<ul>
<li>AWS EC2 G5<ul>
<li>GPU 1, 24GB memory</li>
<li>용량 : 45GB</li>
</ul>
</li>
<li>ubuntu 18.04</li>
<li>pytorch</li>
</ul>
<h3 id="4-1-yolo-v7-다운-받기">4-1. yolo v7 다운 받기</h3>
<pre><code>$ https://github.com/WongKinYiu/yolov7.git</code></pre><h3 id="4-2-docker-container-생성">4-2. docker container 생성</h3>
<pre><code>$ docker run -it --ipc=host --gpus all -v /home/ubuntu/yolov7/:/workspace/yolov7 -v /home/ubuntu/KITTI/:/workspace/KITTI -p 8888:8888 --name yolov7 nvcr.io/nvidia/pytorch:21.12-py3
</code></pre><h3 id="4-3-dataset-구조-설정">4-3. dataset 구조 설정</h3>
<p>📦KITTI
┣ 📂images
┃ ┣ 📂train
┃ ┣ 📂val
┃ ┗ 📂test
┣ 📂labels
┃ ┣ 📂train
┃ ┗ 📂val
┣ 📜train.txt : 전체 경로를 포함한 이미지 경로 리스트
┣ 📜val.txt
┗ 📜test.txt</p>
<ul>
<li>이미지 경로 리스트 만드는 방법<pre><code># ls | while read file; do echo &quot;$(pwd)/$file&quot;; done &gt; ../train.txt</code></pre></li>
</ul>
<h3 id="4-4-환경-설정">4-4. 환경 설정</h3>
<ul>
<li>container 내부에 yolo v7을 위한 환경을 설정한다<pre><code># apt update
# apt install -y zip htop screen libgl1-mesa-glx
</code></pre></li>
</ul>
<h1 id="pip-install-seaborn-thop">pip install seaborn thop</h1>
<h1 id="cd-yolov7">cd /yolov7</h1>
<h1 id="pip-install--r-requirementstxt">pip install -r requirements.txt</h1>
<pre><code>
### 4-5. 코드 수정 
- coco.yaml 파일에서 경로, class 관련 내용 변경
```yaml
train : /workspace/KITTI/train.txt
val : /workspace/KITTI/val.txt
test : /workspace/KITTI/test.txt

# number of classes
nc: 9

# class names
names: [&#39;Car&#39;, &#39;Van&#39;, &#39;Truck&#39;, &#39;Pedestrian&#39;, &#39;Person_sitting&#39;, &#39;Cyclist&#39;, &#39;Tram&#39;, &#39;Misc&#39;, &#39;DontCare&#39;]</code></pre><ul>
<li>yolov7.yaml 파일에도 class 수 변경</li>
<li>train.py 파일에서도 class 수와 관련된 내용 변경</li>
</ul>
<h3 id="4-6-train">4-6. train</h3>
<ul>
<li>아래의 명령어로 학습 시작!<pre><code>python train.py --workers 8 --device 0 --batch-size 16 --data data/coco.yaml --img 640 640 --cfg cfg/training/yolov7.yaml --weights &#39;&#39; --name yolov7 --hyp data/hyp.scratch.p5.yaml</code></pre></li>
</ul>
<h4 id="max_split_size_mb-에러">max_split_size_mb 에러</h4>
<ul>
<li>gpu out of memory 에러가 발생했다</li>
<li>원래 batch-size가 32였는데 이를 16으로 줄이니까 해결되었다</li>
</ul>
<h3 id="4-7-inference">4-7. inference</h3>
<pre><code># python detect.py --weight ./runs/train/yolov7/weights/best.pt --conf 0.25 --img-size 640 --source /workspace/KITTI/images/test/000002.png</code></pre><hr>
<h2 id="5-object-detection-결과">5. Object Detection 결과</h2>
<h3 id="5-1-camera-image-2d-object-detection">5-1. camera image 2D Object Detection</h3>
<h3 id="5-2-lidar-cloud-point-3d-object-detection">5-2. LiDAR cloud point 3D Object Detection</h3>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/happy_quokka/post/9758302a-c958-476a-a0f1-ff11297732ba/image.png" alt=""></th>
</tr>
</thead>
<tbody><tr>
<td><img src="https://velog.velcdn.com/images/happy_quokka/post/a882a40b-3e69-4b62-904b-0a4ec70e3d98/image.png" alt=""></td>
</tr>
</tbody></table>
<ul>
<li>threshold를 0.4로 설정</li>
<li>아래의 이미지는 2D 이미지와 매칭을 해보았다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/7555a6f0-a957-4ee2-a447-c4118859e234/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[docker 실행 시 cannot connect to the docker daemon 에러]]></title>
            <link>https://velog.io/@happy_quokka/docker-%EC%8B%A4%ED%96%89-%EC%8B%9C-cannot-connect-to-the-docker-daemon-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@happy_quokka/docker-%EC%8B%A4%ED%96%89-%EC%8B%9C-cannot-connect-to-the-docker-daemon-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Wed, 31 Jan 2024 16:18:51 GMT</pubDate>
            <description><![CDATA[<p>docker 에러</p>
<h3 id="오류">오류</h3>
<p>cannot connect to the docker daemon at unix:///var/run/docker.sock. is the docker daemon running?</p>
<ul>
<li><code>docker ps -a</code> 또는 <code>docker image ls</code> 등 docker를 확인하려고 하자 docker를 실행할 수 없다는 에러가 발생했다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/c17c810f-0416-43e2-871d-e14dc8d1c44b/image.png" alt=""></p>
<h3 id="원인">원인</h3>
<ul>
<li>docker service가 꺼져있기 때문이다</li>
<li><code>$ sudo systemctl status docker</code>를 실행하면 fail 상태인 것을 확인할 수 있다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/9c0d5b82-baac-46aa-a49a-63d51dc5beef/image.png" alt=""></p>
<h3 id="해결">해결</h3>
<ul>
<li>아래의 명령어로 다시 실행시켜주면 해결된다<pre><code>$ sudo systemctl start docker
$ sudo systemctl enable docker</code></pre></li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/8d7e20ae-fe21-4ef6-8a09-b30d017e493a/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[데브코스 자율주행 TIL] DAY 77 (01/12) 🧬]]></title>
            <link>https://velog.io/@happy_quokka/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-%EC%9E%90%EC%9C%A8%EC%A3%BC%ED%96%89-TIL-DAY-77-0112</link>
            <guid>https://velog.io/@happy_quokka/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-%EC%9E%90%EC%9C%A8%EC%A3%BC%ED%96%89-TIL-DAY-77-0112</guid>
            <pubDate>Tue, 30 Jan 2024 17:15:27 GMT</pubDate>
            <description><![CDATA[<h2 id="20240112-day77">2024.01.12 DAY77</h2>
<p>AWS에서 center point 환경설정을 드디어 성공했다!! 환경 설정이 이렇게 어렵고 오래 걸릴줄 몰랐다... 지나고보면 간단한 거였지만 그 당시에는 에러를 찾아보고 원인이 무엇인지 찾는 과정에 시간이 오래 걸린 것 같다. 환경 설정을 마무리 했으니 train 학습을 돌릴때도 용량 에러가 발생해서 해결하는에 오래걸렸다. 알고보니 container를 생성할 때 옵션을 주었어야했다. </p>
<p>인스턴스를 팀원들과 공유해서 같이 사용하는데 인스턴스의 용량이 부족해서 설치가 안된다는 에러가 계속 발생했다... 사실 이런 문제들은 local에서 각자 진행할 때는 발생하지 않는 문제인데 AWS로 동시에 사용하니 발생하는 것 같다. 이런 문제들도 해결해가는 과정에서 많은 경험이 쌓이는 것 같긴하다. 결국 용량 문제는 제대로 해결하지 못했다.. 내일 다시 찾아서 해결해봐야할 것 같다. </p>
<p>일단 train을 실행해놓고 자러가야겠다. 밤새 학습이 잘 되기를!!</p>
<hr>
<h2 id="🧬딥러닝-프로젝트-과정-및-어려웠던-점">🧬딥러닝 프로젝트 과정 및 어려웠던 점</h2>
<h3 id="환경-변수-설정">환경 변수 설정</h3>
<ul>
<li>center point와 nuscenes-devkit과 관련한 환경변수를 설정해주지 않았었다</li>
<li>그랬더니 에러가 발생하여 환경변수를 설정해주었다</li>
<li>bashrc에 내용들을 저장하여 다시 접속했을 때도 적용이 되도록 설정했다</li>
</ul>
<h3 id="spconv-에러">spconv 에러</h3>
<ul>
<li>center point의 github에서 알려준 spconv 설치 방법대로 spconv 설치를 수행했었다</li>
<li>이때 cmake 에러, torch::jit::RegisterOperators 에러, cout 에러 등 많은 에러가 발생했다</li>
<li>이 에러들은 코드를 수정하고 git clone을 받아오는 등의 해결 방법으로 해결하였다</li>
<li>하지만 !!more than one operator &quot;==&quot;,&quot;&gt;&quot; matches these operands 에러가 발생했고... 여러 방법들을 다 적용해보았지만 해결이 안되었다</li>
</ul>
<ul>
<li>결국 spconv에 관해 찾아보다가 단순히 <code># pip install spconv-cu114</code> 명령어로 spconv를 설치할 수 있다는 것을 알게 되었고 이 방법으로 간단히 spconv를 설치할 수 있었다</li>
</ul>
<ul>
<li>앞으로 어떤 라이브러리를 설치할 때에는 단순히 다른 사람이 알려주는 설치 방법을 따르는 것보다 그 라이브러리를 먼저 찾아보는 습관을 들여야겠다</li>
</ul>
<h3 id="용량-부족-에러">용량 부족 에러</h3>
<h4 id="shared-memory-shm-에러">shared memory (shm) 에러</h4>
<ul>
<li>모델 학습을 시작할 때 <code>shared memory (shm)</code> 에러가 발생하였다</li>
<li>이는 host와 IPC 네임스페이스를 공유하도록 설정하면 된다</li>
<li>하지만 이 방법은 container를 생성할 때 옵션으로 지정해줘야 하기 때문에 container를 다시 생성하고 환경 설정도 다시 해주어야 했다</li>
</ul>
<h4 id="no-space-left-on-device">no space left on device</h4>
<ul>
<li><code>no space left on device</code>라는 에러가 발생했다</li>
<li>인스턴스의 용량이 부족하다는 에러였다</li>
<li>팀원들이 같이 사용하고 있고 데이터셋도 용량이 커서 발생하는 문제 같았다</li>
</ul>
<ul>
<li>용량을 확인해 보았고 overlay가 가장 많은 용량을 차지하고 있었다</li>
<li>overlay를 찾아보니 이걸 삭제하면 쓸데없는 container를 지울 수 있다고 해서 삭제를 진행하였다</li>
<li>그런데! up 상태인 container는 사라지지 않았지만 종료된 container는 다 사라졌다..!</li>
<li>결국 다시 container를 만드니 용량이 부족하다는 에러가 다시 발생하였다</li>
</ul>
<ul>
<li>opt/conda 폴더를 제거하였다</li>
<li>이 폴더에는 conda 등의 여러 파일들이 있는데 현재 인스턴스에서는 필요하지 않기 때문에 삭제해도 아무런 문제가 없었다</li>
</ul>
<ul>
<li>하지만... 그 이후에도 용량 에러가 계속 발생했다</li>
<li>환경상 인스턴스의 용량을 늘릴 수 없어서 문제를 해결할 수 없었다...</li>
<li>결국 팀원과 같은 container를 사용하여 하나의 container만 남기고 다 삭제한 후 프로젝트를 진행해야했다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[데브코스 자율주행 TIL] DAY 76 (01/11) 🧬]]></title>
            <link>https://velog.io/@happy_quokka/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-%EC%9E%90%EC%9C%A8%EC%A3%BC%ED%96%89-TIL-DAY-76-0111</link>
            <guid>https://velog.io/@happy_quokka/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-%EC%9E%90%EC%9C%A8%EC%A3%BC%ED%96%89-TIL-DAY-76-0111</guid>
            <pubDate>Tue, 30 Jan 2024 16:52:34 GMT</pubDate>
            <description><![CDATA[<h2 id="20240111-day76">2024.01.11 DAY76</h2>
<p>딥러닝 프로젝트를 시작했다!! 이번주는 온라인으로 진행하고 다음주부터 오프라인으로 진행한다. 딥러닝보다 AWS를 사용해보는게 기대가 되었다. 객체 인식 딥러닝을 진행하는데 nuScene 데이터로 center point 모델을 사용하여 학습하는 프로젝트이다. </p>
<p>처음에는 local에서 모델을 사용해보려고 했다. 하지만 환경 설정하는 중에 에러가 났다. 알고 보니 CUDA가 필수인데 local 환경에는 GPU가 없어서 발생하는 에러였다. 그래서 팀원들과 AWS 인스턴스를 만들고 docker로 환경설정을 진행했다. 오늘 환경설정을 다 하지는 못했다ㅠㅠ 에러가 너무 많이 나고 그 이유를 찾기 힘들어서 오래걸렸다... AWS를 처음 사용해봐서 인스턴스 만들고 사용하는 것부터 쉽지 않았다. docker 환경설정을 오늘 다 끝내기 못했다ㅠㅠ 내일 이어서 해야겠다!!</p>
<hr>
<h2 id="🧬-딥러닝-프로젝트-과정-및-어려웠던-점">🧬 딥러닝 프로젝트 과정 및 어려웠던 점</h2>
<h3 id="center-point-환경-설정하기">Center Point 환경 설정하기</h3>
<ul>
<li>결국... local은 GPU가 없어서 환경을 설정하지 못하였다. AWS에서 docker로 환경 설정을 해야한다</li>
<li>발생하는 에러들을 하나씩 해결하며 환경 설정을 하려고 시도하였다</li>
<li>하지만 CUDA가 없어서 발생하는 에러였고 AWS로 진행해야한다는 것을 알게 되었다</li>
</ul>
<h3 id="aws-인스턴스-만들기">AWS 인스턴스 만들기</h3>
<ul>
<li>AWS를 처음 사용해봐서 인스턴스를 만드는 것부터 생소했다</li>
</ul>
<ol>
<li>IAM에서 MFA 인증하기</li>
<li>원하는 Image 선택하기 : GPU + pytorch + ubuntu 18.04를 선택했다</li>
<li>인스턴스 유형 선택 : g5.xlarge</li>
<li>key pair 설정</li>
<li>인스턴스 시작</li>
</ol>
<ul>
<li>그 후에는 ssh로 연결하면 된다</li>
</ul>
<h3 id="docker-container-생성--환경-설정">docker container 생성 &amp; 환경 설정</h3>
<pre><code>docker run -it --gpus all -v [host 경로]:[container 경로] -p 8888:8888 --name [container명] [image명] /bin/bash</code></pre><ul>
<li>위의 명령어로 docker container를 생성했다</li>
<li><code>--gpus all</code> 이 옵션을 넣어야 gpu를 사용할 수 있다</li>
<li>center point를 설치하고 필요한 라이브러리도 설치했다</li>
<li>nuscenes-devkit 설치를 진행했다</li>
<li>APEX 설치를 진행하면서 에러가 많이 발생했는데 알고보니 docker image에 포함되어 있어서 설치할 필요가 없었다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[3D Perception] 1. LiDAR의 종류와 측정 원리]]></title>
            <link>https://velog.io/@happy_quokka/3D-Perception-LiDAR</link>
            <guid>https://velog.io/@happy_quokka/3D-Perception-LiDAR</guid>
            <pubDate>Tue, 30 Jan 2024 07:50:27 GMT</pubDate>
            <description><![CDATA[<h2 id="1-lidar">1. LiDAR</h2>
<ul>
<li>Light Detection And Ranging</li>
<li>빛을 탐지하고 범위를 측정하는 센서</li>
<li>RADAR(RAdio Detection And Ranging)에서 사용하는 전파 (radio)를 빛(light)로 변경한 센서</li>
<li>point cloud : LiDAR data</li>
</ul>
<h3 id="1-1-lidar-종류">1-1. LiDAR 종류</h3>
<ol>
<li>single point distance sensor -&gt; 레이저 포인터와 같은 점</li>
<li>2D Scanners -&gt; 강의장에서 사용하는 LiDAR (x, y)</li>
<li>3D Scanners </li>
<li>Non-Repeating Pattern Scanners -&gt; 원이 아닌 복잡하고 비반복적인 패턴을 사용한다</li>
</ol>
<h4 id="2d-lidar와-3d-lidar의-차이">2D LiDAR와 3D LiDAR의 차이</h4>
<ul>
<li>센서의 채널 수가 다르다</li>
<li>2D LiDAR : 1개의 채널, (N, 2)</li>
<li>3D LiDAR : N개의 채널, (N, 3), 채널 수에 따라 객체의 입체적인 데이터를 가진다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/b1cf8365-3f40-431a-9899-be266527caa7/image.png" alt=""></p>
<h3 id="1-2-lidar-센서의-측정-원리">1-2. LiDAR 센서의 측정 원리</h3>
<ul>
<li>빛이 반사된 거리를 측정한다</li>
<li>빛의 속도와 반사된 시간을 곱하여 거리를 측정할 수 있다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/cc3c25bf-ae99-4fa3-b368-0f9d2730f478/image.png" alt=""></p>
<h3 id="1-3-lidar-parsing">1-3. LiDAR Parsing</h3>
<ul>
<li>LiDAR는 반사된 객체와의 직선 거리를 반환하기 때문에 반사된 포인트의 위치를 파악하기 위해서는 별도의 계산이 필요하다 </li>
<li>거리($d$)와 빛의 각도($\alpha, \beta, \gamma$)를 알 수 있기 때문에 이를 활용하여 위치를 계산한다</li>
</ul>
<ul>
<li>LiDAR 센서마다 방법이 다르다</li>
<li>센서마다 제공하는 데이터가 다르기 때문에 반드시 공식 문서를 참고한다</li>
<li>센서의 SDK를 제공하는 경우도 있다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/530a8e96-6f0a-4edd-a8e6-031597b6088b/image.png" alt=""></p>
<h4 id="velodyne-vlp-16-lidar">Velodyne VLP-16 LiDAR</h4>
<ul>
<li>16 채널(layer)를 가지고 있다</li>
<li>Laser ID를 layer라고 한다</li>
<li>R값(거리)값들을 받을 후 $\omega$와 $\alpha$를 계산한다 </li>
<li>그 후 공식을 사용하여 위치를 추정한다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/e3435c02-281e-41b1-b0c0-cbeef32e026a/image.png" alt=""></p>
<hr>
<h2 id="2-lidar-종류">2. LiDAR 종류</h2>
<h3 id="2-1-3d-lidar">2-1. 3D LiDAR</h3>
<ul>
<li>N개의 채널을 만드는 방법에 따라 다양한 구조를 가지고 있다</li>
</ul>
<h4 id="mechanical-3d-spinning-lidar">Mechanical 3D Spinning LiDAR</h4>
<ul>
<li>발광부와 수신부가 직접 회전하는 방식</li>
</ul>
<ul>
<li>장점 : 360도 회전이 가능하다</li>
<li>단점<ul>
<li>발광부와 수신부의 개수에 따라 채널이 제한되고 크기가 커진다</li>
<li>진동에 의한 내구성이 약하다</li>
</ul>
</li>
</ul>
<ul>
<li>오른쪽 이미지는 128 채널의 LiDAR</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/4aa093fe-b392-4262-8664-3b24ddc10d3b/image.png" alt=""></p>
<h4 id="solid-state-lidar">Solid-State LiDAR</h4>
<ul>
<li>특정한 반사물질을 이용한다</li>
<li>주로 거울이 사용된다</li>
<li>두번째 반사물질이 여러 각도로 빛을 퍼지게 만들어주어 여러 채널을 갖는 효과를 만들어준다</li>
</ul>
<ul>
<li>장점<ul>
<li>single-Laser로도 N개의 채널을 표현할 수 있다</li>
<li>따라서 가격이 비교적 저렴</li>
</ul>
</li>
<li>단점<ul>
<li>복잡한 동작 방식</li>
<li>매우 높은 Hz (~160Hz)</li>
<li>그에 따른 내구성 문제가 존재한다</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/2e671d3b-6249-4aca-ab70-78fce27e38f4/image.png" alt=""></p>
<h4 id="mems-lidar">MEMS LiDAR</h4>
<ul>
<li>반사물질이 상/하, 좌/우 반복운동을 하고 N개의 Laser를 사용할 수 있어 데이터 취득에 더욱 용이하다</li>
<li>Solid-state LiDAR와 비슷하지만 두번째 반사물질을 제거한 형태이다</li>
<li>따라서 반사물질을 개수를 줄일 수 있었다</li>
</ul>
<ul>
<li>장점 : single-laser로도 N개의 채널을 표현할 수 있다</li>
<li>단점<ul>
<li>높은 제작 난이도</li>
<li>매우 높은 Hz(~800Hz) : 하나의 laser로 여러 채널을 표현해야하기 때문에 그만큼 빨리 움직여야한다</li>
<li>그에 따른 내구성 문제가 존재한다</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/5ab62822-82f0-481b-a70d-75fdfe6d73c8/image.png" alt=""></p>
<h4 id="그-외의-lidar">그 외의 LiDAR</h4>
<ul>
<li>복합적인 요소를 사용하는 LiDAR도 존재한다</li>
<li>왼쪽 LiDAR (L)<ul>
<li>고정형 발광 &amp; 수신부 : 이전까지는 한줄로 이루어져있었지만 이 방법은 2D array에 해당하는 형식으로 이루어져있다</li>
<li>mirror 회전 방식</li>
<li>낮은 Hz (빛이 면으로 발광하기 때문에)로도 넓은 FoV 확보 가능</li>
</ul>
</li>
<li>오른쪽 LiDAR (R)<ul>
<li>non-repeating pattern scanners 방식</li>
<li>비반복적인 패턴을 사용해서 낮은 laser로도 매우 높은 채널을 만들어내는 방법이다</li>
<li>최근에는 이 방법이 많이 사용되는 것 같다</li>
<li>반복적 방법의 문제점 : layer들이 고정되어 있기 때문에 먼 거리에서 레이저 사이에 있는 물체는 검출하지 못하는 경우가 발생할 수 있었다</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/5260138f-aff0-4b2b-bee9-84ad8d75f63c/image.png" alt=""></p>
<hr>
<h2 id="3-lidar와-radar의-차이">3. LiDAR와 RADAR의 차이</h2>
<table>
<thead>
<tr>
<th></th>
<th>LiDAR</th>
<th>RADAR</th>
</tr>
</thead>
<tbody><tr>
<td></td>
<td><img src="https://velog.velcdn.com/images/happy_quokka/post/f96628f8-18ce-4fb2-bc07-cd1e349b8d83/image.png" alt=""></td>
<td><img src="https://velog.velcdn.com/images/happy_quokka/post/018c6dac-3027-4d4e-a30b-1393bb29bdba/image.png" alt=""></td>
</tr>
<tr>
<td>활용 소재</td>
<td>레이저 (light)</td>
<td>전자파 (radio)</td>
</tr>
<tr>
<td>형체 인식 정도</td>
<td>가능</td>
<td>정확한 인식 불가능</td>
</tr>
<tr>
<td>외부 환경에 대한 영향 (눈, 비)</td>
<td>있음</td>
<td>없음</td>
</tr>
<tr>
<td>가격</td>
<td>고가</td>
<td>저가</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[객체인식 & Depth Estimation 프로젝트] 7. 전체 알고리즘]]></title>
            <link>https://velog.io/@happy_quokka/%EA%B0%9D%EC%B2%B4%EC%9D%B8%EC%8B%9D-Depth-Estimation-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-7.-%EC%A0%84%EC%B2%B4-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</link>
            <guid>https://velog.io/@happy_quokka/%EA%B0%9D%EC%B2%B4%EC%9D%B8%EC%8B%9D-Depth-Estimation-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-7.-%EC%A0%84%EC%B2%B4-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</guid>
            <pubDate>Mon, 29 Jan 2024 10:12:23 GMT</pubDate>
            <description><![CDATA[<h2 id="객체인식--depth-estimation-경진대회-규정">객체인식 &amp; Depth Estimation 경진대회 규정</h2>
<h3 id="주행-코스">주행 코스</h3>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/12aae23d-ee2c-429b-a862-8690447309ab/image.png" alt=""></p>
<h3 id="목표">목표</h3>
<ul>
<li>목표1 : 객체를 인식하여 자율주행<ul>
<li>모든 차량은 출발지점의 정지선에서 신호 대기 -&gt; 정지선 인식</li>
<li>신호등의 출발 신호에 맞춰 출발 -&gt; 신호등 인식</li>
<li>제시되는 표지판의 내용대로 정지, 좌, 우 조향 제어 -&gt; 표지판 인식</li>
<li>동적 장애물은 갑작스레 등장할 예정 -&gt; LiDAR를 사용하여 장애물 인식</li>
</ul>
</li>
</ul>
<ul>
<li>목표2 : 차량주변의 객체 위치정보를 Bird’s Eye View로 표현<ul>
<li>트랙을 주행하며 주변 객체의 정보를 인식 -&gt; object detection </li>
<li>BEV에 객체 정보 표현 </li>
<li>객체는 2D로 X,Y 좌표만 표현 (Point로 표현) -&gt; depth 추정 </li>
</ul>
</li>
</ul>
<h3 id="점수-산정-기준">점수 산정 기준</h3>
<ul>
<li>합산한 과태료가 가장 적은 팀이 우승이다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/f08e21c0-b2ca-481d-aa0b-96a52beaac72/image.png" alt=""></p>
<hr>
<h2 id="전체-알고리즘">전체 알고리즘</h2>
<h3 id="1-lidar로-장애물-인식">1. LiDAR로 장애물 인식</h3>
<ul>
<li>갑자기 튀어나오는 장애물이나 차선 안에 주차되어 있는 차량 장애물을 인식하는 것은 다른 것들에 비해 가장 우선으로 인식되어야 한다</li>
<li>2가지 상황에 따른 대처<ul>
<li>갑자기 튀어나오는 장애물 -&gt; 정지</li>
<li>차선 안에 주차되어 있는 차량 -&gt; 회피 주행</li>
</ul>
</li>
</ul>
<h4 id="lidar-알고리즘">LiDAR 알고리즘</h4>
<ul>
<li>LiDAR의 앞쪽 60도를 인식하여 차량 장애물과 돌발 장애물 인식</li>
<li>차량 장애물의 경우<ul>
<li>오른쪽 30도의 LiDAR 값이 0.4보다 작은 값의 개수가 10개 이상이면 물체가 있다고 판단하여 회피 주행</li>
<li>왼쪽 30도의 LiDAR 값이 0.4보다 작은 값의 개수가 10개 이상이면 물체가 있다고 판단하여 회피 주행</li>
</ul>
</li>
<li>돌발 장애물의 경우<ul>
<li>왼쪽, 오른쪽의 총 60도의 l값이 0.4보다 작은 값의 개수가 40개 이상이면 앞쪽에 물체가 있다고 판단하여 정지</li>
</ul>
</li>
</ul>
<h3 id="2-정지선-인식">2. 정지선 인식</h3>
<ul>
<li>정지선을 인식하면 3초 정지하였다가 출발</li>
<li>강제로 0.5초 동안 주행하도록 설정하였다. 이렇게 하지 않으면 그 다음 프레임에서도 정지선이 인식되어 계속 멈춰있기 때문이다</li>
</ul>
<ul>
<li>표지판이 감지되지 않을 경우에만 정지선 인식으로 판단한다</li>
<li>표지판이 있을 경우, 해당 표지판에 따라 행동해야하기 때문이다</li>
</ul>
<h4 id="정지선-인식-방법">정지선 인식 방법</h4>
<ul>
<li>차선을 인식하는 ROI 부분에서 검출된 직선이 평행에 가까울 때 정지선이라고 판단</li>
<li>기울기가 0.02보다 작고 그 선의 길이가 160보다 클 때 정지선이라고 판단</li>
</ul>
<h3 id="3-object표지판-신호등-차-인식">3. Object(표지판, 신호등, 차) 인식</h3>
<ul>
<li>YOLO v7 모델로 예측한 object의 id와 bounding box 정보를 메세지로 받는다</li>
<li>추가로 계산한 depth도 메세지로 같이 받는다</li>
<li>이 메세지를 활용하여 어떤 object인지 자이카와의 거리가 어떤지를 파악하여 object에 맞는 알고리즘을 지정해주었다</li>
</ul>
<ul>
<li>신호등 (red light, green light, yellow light)<ul>
<li>초록불 : 전진</li>
<li>빨간불 : 정지 (초록불이 들어올 때까지 정지)</li>
</ul>
</li>
<li>정지 표지판 (stop)<ul>
<li>3초 정지 후 전진</li>
</ul>
</li>
<li>횡단보도 표지판 (crosswalk)<ul>
<li>3초 정지 후 전진</li>
</ul>
</li>
<li>오른쪽 표지판 (right)<ul>
<li>2초 동안 오른쪽으로 회전 후 전진</li>
</ul>
</li>
<li>왼쪽 표지판 (left)<ul>
<li>2초 동안 왼쪽으로 회전 후 전진</li>
</ul>
</li>
</ul>
<h3 id="4-default">4. default</h3>
<ul>
<li>기본적으로 위와 같은 상황이 아니라면 차선을 인식하여 주행</li>
</ul>
<hr>
<h2 id="결과-성적">결과 성적</h2>
<ul>
<li>최종 2등을 하였다!!</li>
<li>차선을 한번 이탈하고, 돌발 장애물과 충돌하여 최종 과태료가 6만원이 나왔다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/fd9167b6-9c81-48e1-9907-6a0e6b5c8c93/image.png" alt=""></p>
<hr>
<h2 id="어려웠던-점">어려웠던 점</h2>
<h3 id="교차로-구간">교차로 구간</h3>
<h4 id="차선이-안보이는-문제">차선이 안보이는 문제</h4>
<ul>
<li>교차로 구간에는 차선이 없는 부분이 많고 회전시 차선이 안보이는 문제도 같이 발생하기 때문에 어려웠다</li>
<li>2초 또는 3초 동안 회전하도록 지정해주었다</li>
<li>추후에 칼만 필터를 적용하여 현재 자신의 위치를 파악하고 그에 맞도록 주행하도록 개선하고 싶다</li>
<li>또한 시간 문제를 해결하면서 지속적으로 프레임을 받아 올 수 있기 때문에 차선이 보일 때까지 강제로 회전하고 차선이 보인다면 차선 주행을 하는 방법으로도 수정하고 싶다</li>
</ul>
<h4 id="표지판의-위치">표지판의 위치</h4>
<ul>
<li>표지판의 위치가 차선의 오른쪽 또는 왼쪽에 위치하는 경우도 있고 차선 앞쪽에 위치하는 경우도 있었다</li>
<li>이 경우에는 depth가 다르게 추정되기 때문에 depth에 조건을 주는 것이 어려웠다</li>
<li>따라서 표지판이 앞에 있는 경우로 판단되면 depth을 조금 늘려주는 방법을 통해 해결하였다</li>
</ul>
<h3 id="시간-관련-문제">시간 관련 문제</h3>
<ul>
<li>몇 초 동안 회전하고 주행하도록 설정하였는데 이때 시간을 계산하는 함수를 처음에 <code>ctime의 time()</code>을 사용하였다</li>
<li>하지만 <code>time()</code> 함수는 1초 단위로밖에 조절할 수가 없고 그 사이에 <code>sleep(1)</code>을 사용하였더니 sleep되는 동안 다른 알고리즘은 멈춰버리는 문제가 있었다</li>
<li>이를 <code>ctime의 clock()</code>으로 바꾸니 문제가 해결되었다</li>
</ul>
<h3 id="하드웨어-문제">하드웨어 문제</h3>
<h4 id="자이카-바퀴">자이카 바퀴</h4>
<ul>
<li>오른쪽으로 회전할 때 바퀴가 최대로 꺾이지 않는 문제가 있어서 우회전을 할 때 제대로 회전하지 못하였다</li>
<li>또한 좌회전과 우회전의 정도가 달랐기 때문에 이를 맞추는 과정이 어려웠다</li>
<li>하드웨어 문제는 바로 해결할 수 없었기 때문에 좌회전의 각도를 최대 50이 아닌 조금 더 낮은 값으로 설정하여 양쪽의 비율을 맞춰주었다</li>
</ul>
<h4 id="라이다">라이다</h4>
<ul>
<li>라이다에서 앞의 오른쪽 값이 제대로 들어오지 않았다</li>
<li>이는 더 확인을 해봐야하는 문제지만 반대인 왼쪽은 값이 잘 들어오는데 오른쪽은 잘 들어오지 않아서 돌발 장애물과 차량 장애물을 회피하는 부분이 아쉬웠다</li>
</ul>
<hr>
<h2 id="향후-하고-싶은-작업">향후 하고 싶은 작업</h2>
<ul>
<li>칼만 필터를 적용하여 차선이 보이지 않을 때 발생하는 문제를 해결하고 싶다</li>
<li>표지판의 위치에 따라 depth를 다르게 계산하여 더 정확한 depth를 구하고 싶다</li>
<li>전체적인 코드를 깔끔하게 작성하고 싶다</li>
<li>라이다의 정확도를 높이고 싶다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[객체인식 & Depth Estimation 프로젝트] 6. Depth 추정 (Homography) + Bird-Eye view 표현]]></title>
            <link>https://velog.io/@happy_quokka/%EA%B0%9D%EC%B2%B4%EC%9D%B8%EC%8B%9D-Depth-Estimation-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-6.-Depth-%EC%B6%94%EC%A0%95-Homography</link>
            <guid>https://velog.io/@happy_quokka/%EA%B0%9D%EC%B2%B4%EC%9D%B8%EC%8B%9D-Depth-Estimation-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-6.-Depth-%EC%B6%94%EC%A0%95-Homography</guid>
            <pubDate>Mon, 29 Jan 2024 09:19:43 GMT</pubDate>
            <description><![CDATA[<h2 id="depth-추정">Depth 추정</h2>
<ul>
<li>object와 자이카와의 depth를 추정하여 즉, 거리를 추정하여 추후에 자이카를 제어한다</li>
<li>depth를 추정하는 방법에는 크게 3가지가 있는데 여기에서는 homography를 사용하는 방법을 선택했다</li>
<li>그 이유은 homography 방법은 간단하지만 지면이 평평한 경우에만 사용할 수 있는데 프로젝트 환경은 평평한 바닥이기 때문에 이 방법을 선택하였다</li>
</ul>
<h3 id="homography">homography</h3>
<ul>
<li>간단하게 정리를 하자면 homography는 <strong>두 평면 간의 변환 관계</strong>를 의미한다</li>
<li>최소 4짱의 좌표값을 알면 homography matrix를 구할 수 있다</li>
</ul>
<ul>
<li>이 경우에는 바닥 평면과 카메라 이미지 평면 간의 관계를 구하는 것이다</li>
</ul>
<hr>
<h2 id="depth-추정-과정">Depth 추정 과정</h2>
<h3 id="1-world-좌표와-image-좌표쌍-구하기">1. world 좌표와 image 좌표쌍 구하기</h3>
<ul>
<li>최소 4쌍 이상의 좌표쌍이 필요한데 많을수록 정확한 homography matrix를 구할 수 있기 때문에 많은 좌표쌍을 구했다</li>
<li>최대한 멀리, 넓게 좌표쌍을 정해야 정확한 값을 얻을 수 있다</li>
</ul>
<ul>
<li>바닥 평면의 원하는 지점에 블록을 놓고 이미지를 촬영한다</li>
<li>이렇게 하는 이유는 바닥 평면의 grid 선이 이미지 상에서 뚜렷하게 보이지 않기 때문에 원하는 좌표쌍을 얻기 위해 잘 보이는 블록을 놓아서 이미지를 획득하였다</li>
<li>이때 자이카를 원하는 지점에 놓고 그 지점부터 블록까지의 거리를 알고 있어야한다</li>
<li>이 경우에서는 자이카의 가장 앞 부분을 (270, 540) 좌표에 맞춰서 놓고 이미지를 획득하였다</li>
</ul>
<ul>
<li>바닥 평면의 <strong>한 grid의 실제 크기는 45cm</strong>이다</li>
<li>하지만 조금 더 넓은 평면으로 변환을 하고 싶었기 때문에 90으로 조정하였다</li>
<li>아래 오른쪽 이미지를 보면 검은색 grid는 바닥 평면을 의미하고 빨간색 좌표는 카메라에서 획득한 이미지 상에서의 좌표를 의미한다</li>
</ul>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/happy_quokka/post/93a4f47a-090b-4816-a27b-7372f30804ad/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/happy_quokka/post/1203662c-ae4b-4b15-903b-8e41a9bdd596/image.png" alt=""></th>
</tr>
</thead>
</table>
<ul>
<li><strong>world 좌표는 (x, y, 1)</strong> 과 같이 homography 좌표로 표현해야하고, <strong>image 좌표는 (x, y)</strong> 로 표현해야한다<pre><code class="language-python"># x, y, 1
homo_3d_points = numpy.array([
  [270, 0, 1],
  [180, 90, 1],
  [270, 90, 1],
  [360, 90, 1],
  [180, 180, 1],
  [270, 180, 1],
  [360, 180, 1],
  [180, 270, 1],
  [270, 270, 1],
  [360, 270, 1],
  [180, 360, 1],
  [270, 360, 1],
  [360, 360, 1],
  [180, 450, 1],
  [270, 450, 1],
  [360, 450, 1],
  [90,180, 1],
  [450,180, 1]
], dtype=numpy.float32)
</code></pre>
</li>
</ul>
<h1 id="x-y">x, y</h1>
<p>homo_points = numpy.array([
    [319, 256],
    [255, 260],
    [319,260],
    [386,260],
    [241, 265],
    [321, 265],
    [401,265],
    [217, 274],
    [321, 274],
    [423, 273],
    [174, 289],
    [320,289],
    [469,289],
    [70,327],
    [322,325],
    [571,325],
    [160,265],
    [479,265]
], dtype=numpy.float32)</p>
<pre><code>
### 2. homography matrix 구하기
- opencv의 `findHomography` 함수를 사용하여 계산한다
- 이때 method 옵션으로 **RANSAC**을 사용하면 outlier로 인한 오차를 줄여줄 수 있다고 한다
</code></pre><p>homography, _ = cv2.findHomography(homo_points, homo_3d_points, method=cv2.RANSAC)</p>
<h2 id="homography-matrix">homography matrix</h2>
<p>[[-1.36650485e-01, -1.13733437e+00, 3.12113770e+02],
 [ 6.53381187e-04, -2.45035619e+00, 6.27102327e+02],
 [ 3.93808370e-06, -4.24034141e-03, 1.00000000e+00]]</p>
<pre><code>
### 3. depth 구하기
#### 3-1. 원하는 object의 좌표 구하기
- 예측한 object의 **bounding box에서의 밑변의 중심 좌표**를 구한다
- 밑변으로 해야하는 이유는 object가 **바닥과 붙어있는 지점**으로 해야지 거리를 구할 수 있기 때문이다
- 여기에서 x0, x1은 bounding box의 min_x, max_x를 의미한다
- y0, y1은 bounding box의 min_y, max_y를 의미한다

```python 
x0 = int(box[0])
y0 = int(box[1])
x1 = int(box[2])
y1 = int(box[3])

center_x = (x0 + x1)/2</code></pre><h4 id="3-2-image-좌표를-world-좌표로-변환하기">3-2. image 좌표를 world 좌표로 변환하기</h4>
<ul>
<li>앞에서 구한 homography matrix를 사용하면 image 좌표값을 world 좌표값으로 변환할 수 있다</li>
<li>image 좌표값을 homography 좌표로 변환하고 내적을 수행하면 x, y, z값이 나온다</li>
<li>이때 z값은 1이여야하기 때문에 x, y, z를 z로 나누면 원하는 world 좌표상의 x, y 값을 구할 수 있다</li>
</ul>
<pre><code class="language-python">bbox_point = np.array([center_x, box[3], 1])
estimate = np.dot(self.homo_mat, bbox_point)
x, y, z = estimate[0], estimate[1], estimate[2]

depth_x = x/z
depth_y = y/z</code></pre>
<h4 id="3-2-depth-계산하기">3-2. depth 계산하기</h4>
<ul>
<li>depth는 자이카와 원하는 object와의 거리를 의미한다</li>
<li>자이카가 (270,540) 좌표에 위치해 있기 때문에 이 좌표와의 거리를 계산하면 된다</li>
<li>방법 1은 두 점 간의 거리를 계산한 것으로 x, y 좌표를 모두 고려한 것이다. 프로젝트 때는 이 방법을 사용하였다</li>
<li>방법 2은 y 좌표만 고려한 것이다</li>
</ul>
<pre><code class="language-python"># 방법 1
distance = int(np.sqrt(((270 - depth_x)/2)**2 + ((540-depth_y)/2)**2))

# 방법 2
distance = int((540 - depth_y)/2)</code></pre>
<hr>
<h2 id="bird-eye-view-bev-표현">Bird-Eye View (BEV) 표현</h2>
<ul>
<li>Bird-Eye view는 위에서 바라본 것을 의미한다</li>
<li>자이카와 그 주변의 object들이 어떻에 위치해있는지 보기 위해 Bird-Eye view로 그 좌표값들을 변환하였다</li>
</ul>
<ul>
<li>방법 1의 경우 카메라 이미지를 BEV로 변환한 것이다</li>
<li>하지만 이 결과는 깔끔해보이지 않기 때문에 방법 2를 선택하였다</li>
<li>방법 2의 경우 그냥 검은 이미지를 의미하고 추후에 world 좌표로 표현한 object의 위치를 여기에 나타내 주었다</li>
</ul>
<pre><code class="language-python"># 방법 1
bev_img = cv2.warpPerspective(bev_img, self.homo_mat, (540,540))

# 방법 2
bev_img = np.zeros((540, 540, 3), dtype=np.uint8)

# 시각화 
depth_text = &#39;{}:{}&#39;.format(class_names[cls_id], distance)
cv2.circle(bev_img, (int(depth_x), int(depth_y)), 10, color, -1)
cv2.putText(bev_img, depth_text, (int(depth_x), int(depth_y) - 20), font, 1, color, thickness=1)</code></pre>
<hr>
<h2 id="결과">결과</h2>
<ul>
<li>아래쪽 가운데에 자이카가 위치해있다</li>
<li>object의 위치와 자이카로부터 object 간의 거리를 나타내 주었다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/happy_quokka/post/d31f040e-31da-47b5-a114-8e7d8f509836/image.png" alt=""></p>
]]></description>
        </item>
    </channel>
</rss>