- 리눅스 초기에 사용되던 시스템 - 호환성 없음 - EXT2의 원형 - 2GB의 데이터와 파일명 255자까지 지정 가능
EXT2
- 고용량 디스크 사용을 염두하고 설계된 파일 시스템 - 호환과 업그레이드가 쉬움 - 4TB 파일 크기까지 지원 - 설정 방법) mke2fs - t ext2
EXT3
- 리눅스의 대표적인 저널링을 지원하도록 확장된 파일 시스템 - ACL (Access Control List)를 통한 접근 제어 지원 - 16TB의 파일 크기까지 지원 - 설정 방법 1)mke2fs -j - 설정 방법 2)mke2fs -t ext3
EXT4
- 파일에 디스크 할당 시 물리적으로 연속적인 블록을 할당 - 64비트 기억 공간 제한을 없앰 - 16TB 파일 크기까지 지원 - 설정 방법 1)mke2fs -t ext4
3. 저널링 파일 시스템 (JFS, XFS, ReiserFS, EXT3)
파일 시스템
설명
JFS
- Journaling File System의 약자 - IBM사의 독자적인 저널링 파일 시스템 - GPL로 공개하여 현재 리눅스용으로 개발
XFS
- eXetended File System의 약자 - 고성능 저널링 시스템 (SGI 제작) - 64bit 주소 지원 및 확장성 있는 자료 구조와 알고리즘 사용 - 데이터 읽기/쓰기 트랜잭션으로 성능 저하를 최소화 - 64bit 파일 시스템으로 8EB까지의 대용량 파일도 다룰 수 있음
ReiserFS
- 독일의 한스 라이저가 개발한 파일 시스템 - 모든 파일 객체들을 B트리에 저장, 간결한 색인화된 디렉터리 지원
4. 네트워크 파일 시스템
파일 시스템
설명
SMB
- Server Message Block - 옵션) smbfs - 삼바 파일 시스템을 마운트 지정 - 윈도우 계열 OS 환경에서 사용되는 파일/프린터 공유 프로토콜 - 리눅스, 유닉스 계절 OS와 윈도우 OS와의 자료 및 하드웨어 공유 (다른 OS와 자료 및 HW 공유 용이)
CIFS
- Common Internet File System - SMB를 확장한 파일 시스템 - SMB를 기초로 응용하여 라우터를 뛰어넘어 연결할 수 있는 프로토콜
NFS
- Network File System - 옵션) nfs - 동일 OS간 RPC를 기반으로 파일 공유시 사용 권장 - 파일 공유 및 파일 서버로 사용하고, 공유된 영역을 마운트할 때 지정 - HW, OS 또는 네트워크 구조가 달라도 공유 가능 - NFS 서버의 특정 디렉터리를 마운트하여 사용 가능
EXT4
- 파일에 디스크 할당 시 물리적으로 연속적인 블록을 할당 - 64비트 기억 공간 제한을 없앰 - 16TB 파일 크기까지 지원 - 설정 방법 1) mke2fs-t ext4
5. 기타 파일 시스템
파일 시스템
설명
FAT
- Windows NT가 지원하는 파일 시스템 중 가장 간단한 시스텥ㅁ - 옵션) vfat - FAT로 포맷된 디스크는 클러스터 단위로 할당하며, 클러스터 크기는 볼륨 크기에 따라 결정 - 읽기 전용, 숨김, 시스템 및 보관 파일 특성만 지원 - 삼바 파일 시스템을 마운트 지정
VFAT
- Virtual FAT - FAT 파일 시스템이 확장된 것으로 FAT보다 제한이 적음 - 파일 이름도 최고 255자까지 만들 수 있음 (= EXT) - 공백이나 여러 개의 구두점도 포함
FAT32
- SMB를 확장한 파일 시스템 - 다중 부팅 가능 - 파일 크기 최대 4GB, 파티션 크기 최대 32GB
NTFS
- 윈도우에서 사용하는 파일 시스템 - 옵션) ntfs - 안정성이 뛰어나고 대용량 파일도 저장 - 파일 크기 및 볼륨은 이론상 최대 16EB이지만, 실질적으로 2TB가 한계
ISO 9660
- CD-ROM의 표준 파일 시스템 - 옵션) loop - 1988년에 재정된 표준
UDF
- Universal Disk Format의 약자로 최신 파일 시스템 형식 - 광학 매체용 파일 시스템 표준 - ISO 9660 파일 시스템을 대체하기 위한 것으로 대부분 DVD에서 사용
HPFS
OS/2 운영체제를 위해 만들어진 파일 시스템
6. 관련 명령어
mount & umount
:mount [option] [device] [directory]
: 마운트는 특정 디바이스를 특정 디렉터리처럼 사용하기 위해 장치와 디렉터리를 연결
: 리눅스는 PnP (Plug and Play)를 지원하지만, 지원하는 HW가 많지 않으므로 시스템 부팅 후 수동으로 마운트한 뒤 사용을 끝낸 뒤에는 직접 언마운트 시켜야 함
: /etc/mtab에 현재 마운트된 블록 시스템 정보 표시
: 옵션 정보
-a: /etc/fstab에 명시된 파일 시스템 마운트 시 사용
-t 타입명: 파일 시스템 유형 지정 시 사용
-o 항목명: 추가 설정 및 다수 옵션 지정시 사용 항목명 ro: 읽기 전용 항목명 rw: 읽기 및 쓰기, 기본 지정 항목명 remount: 재마운트 항목명 loop: iso 9660 마운트 시 사용 항목명 noatime: atime 변경 제어로 작업 성능 향상
eject
: 이동식 보조기억장치등과 같은 미디어를 해제하고 장치를 제거하는 명령어
: 이전에 umount (자동) 필수 수행!
fdisk
: 새로운 파티션의 생성, 기존 파티션의 삭제, 파티션의 타입 결정 등의 작업 수행
: 한 번에 한 디스크에 대해서만 작업 수행 : 명령어 저장 위치 /sbin/fdisk: 디스크 파티션에 부여된 UUID 값 확인시 'blkid' 명령어 사용
: 옵션 정보
-l: 현재 디스크 및 파티션 조회
-n: 신규 파티션 추가
-t: 파티션 종류 변경 SWAP - 82, Linux - 83, LVM - 8e, RAID - fd, FAT32 - b
-p: 파티션 테이블로 설정 보기
-m: 메뉴 보기
-a: 부트 가능 플래그로 변경
-d: 뒷번호부터 차례대로 파티션 삭제
-w: 파티션 설정 저장
mkfs
: 리눅스 파일 시스템 생성
: fdisk로 하드디스크 파티션을 나눈 후 해당 파티션에 맞는 파일 시스템 생성
mke2fs
: ext2, ext3, ext4 타입의 리눅스 파일 시스템을 생성하는 명령어
fsck
: 파일 시스템의 무결성 점검 및 대화식으로 복구
: 결함이 있는 파일에 대한 정보가 저장되는 디렉토리 /etc/lost+found를 사용하여 점검 및 복구
(tar 옵션: z) gzip ↔ gunzip: 가장 많이 사용. LZ77 알고리즘. 허프만 부호화 (-l 정보, -v 과정, -d 압축해제) -
(tar 옵션: j) bzip2 ↔ bunzip2: 가장 많이 사용. 블록정렬 알고리즘. 허프만 부호화
( tar 옵션: J)xz ↔ unxz: 가장 압축률이 높음
명령어 및 옵션
tar명령어를 사용하여 다양한 압축명으로 압축 및 압축 해제 가능
-c: tar로 파일 묶기(압축)
-x: tar 압축 해제
-p: 파일 권한 저장
-v: 과정 출력
-f: 파일 이름 지정
-r: 마지막 추가
-t: 목록 나열
-C: 경로 지정
-z: gzip 압축/해제
-j: bzip2 압축/해제
-J: xz 압축/해제
cvf 기본적으로 압축한다고 생각하면 됨
xvf 기본적으로 압축 풀기
tvf 기본적으로 아카이브 내용 확인이라고 생각하면 됨
z : gz 파일관련
j : bz2 파일관련
* 은 항상 뒤에 붙는다 생각!! "*" : 모든을 뜻함
2. 소스 코드 설치
[방법 1] configure → make (makefile) → make install
환경설정(configure): /configure 프로그램 설치 과정에서 필요로 하는 환경파일makefile생성 ☞ 환경설정 파일의 역할: 어떤 프로그램을 컴파일하고 링크해야하는가를 make에게 설명해주는 역할 ☞ make clean: configure로 생성된 다양한 파일 제거
컴파일(make makefile): make makefile을 기반으로 소스파일을 컴파일하여,설치 파일(set up)생성 ☞ make dep: 컴파일 전 의존성 검사
파일 설치(make install):make install로 컴파일 된 실행파일을 지정된 속성으로 지정된 디렉터리에 설치
: 아카이브는 다수 개의 파일이나 디렉터리를 하나의 파일로 묶는 것
: 아카이브 파일은 다른 시스템으로 다수 개의 파일을 한 번에 전송하거나 파일 백업용으로 사용
[방법 2]cmake→ make install
: cmake는 별도의 make 과정 없이 OS에 알맞는 makefile을 생성해줌으로써 훨씬 간편!
# 개념과 특징 - 리눅스 환경의 각종 애플리케이션과 유틸리티에 대해그래픽 사용자 인터페이스(GUI)를 제공한다.
참고] 원래 리눅스 환경은 CUI(Command User Interface)
- 플랫폼과 독립적으로 작동하는 그래픽 시스템이다. - x-윈도우는 x11, x, x-windows system이라 부르기도 한다.
- 오픈 데스크톱 환경으로는 KDE, GNOME, XFCE 등이 있다.
- x-윈도우의 특징 1) 네트워크 기반의 그래픽 환경을 제공한다. 2) 이기종 시스템 사이에서도 사용 가능하다.
참고] windows에서도 삼바와 같은 프로토콜을 이용하여 x-윈도우 화면을 이용할 수 있다.
3) 스크롤바, 아이콘, 색상 등 그래픽 환경 자원들이 특정 형태로 정의되어 있지 않다.(표준화된 x-windows는 없음) 4) 디스플레이 장치에 의존적이지 않으며 원하는 인터페이스를 만들 수 있다.
- x-윈도우는 네트워크 프로토콜(x프로토콜) 기반의클라이언트/서버시스템이다. > 서버 프로그램과 클라이언트 프로그램으로 나누어 작동한다.
(두 개의 개별 소프트웨어 부분에 의해 제어)
>서버는 클라이언트의 디스플레이어 관련 접근 허용, 클라이언트 간의 자원 공유,
네트워크 메시지 전달, 클라이언트와 입출력 기기의 중계를 담당한다. >클라이언트는 애플리케이션으로 x 서버가 제공하는 기능들을 이용한다.
# 구성요소와 종류 1.XProtocol:
- 서버와 클라이언트 사이에서 통신되는 Request, Reply, Event, Error의 기본 메시지이다.
- 서버와 클라이언트 사이에서 정보를 주고받게 해주는 프로토콜
- 메시지 교환방법 규정, 메시지 타입 규정 등 메시지 제어
2.Xlib:
- 클라이언트에서 사용하는 라이브러리이다.
- C나 리스프(LISP) 언어로 만들어짐
- 윈도우 생성, 이벤트 처리, 창 조회, 키보드 처리 등의 라이브러리를 제공한다. 3.XCB:
- 클라이언트에서 사용하는 라이브러리이다.
- Xlib에서 XCB로 대체되어 현재는 XCB를 사용한다.
- Xlib에서 지원하는 것을 모두XCB에서 지원한다.
-Xlib에 비해향상된 쓰레드 기능을 가지며, 확장성이 좋다. 4.Xtookit:
- 클라이언트에서 사용하는 라이브러리이다.
- 고급레벨의 GUI 이용 가능하다. (위젯, GUI 구성하는 객체를 만들 수 있음) 5.XFree86:
- X 윈도우 시스템의 구현체
- 인텔 x86운영체제에서 사용하는 서버 프로그램
- 무료로 사용 가능
- 다양한 환경 설정이 필요함(->xf86config에서 설정) 6.XF86Config:
- XFree86의 환경설정을 담당(설정 파일)
- 폰트, 키보드, 마우스, 모니터, 비디오 카드, 색상 등을 설정
-'/etc/X11' or '/usr/X11R6/lib/X11'에서 볼 수 있다.
-XF86Config는 세부적으로 세 개의 환경설정 파일로 나누어짐 1) Xconfigurator 2) XF86Config 3) XF86setup
참고]
xlib, xcb(저수준 라이브러리) -> GTK+, QT, FLTK, XFome 등의 라이브러리가 있다. Xtoolkit(고수준 라이브러리) -> XView, Xaw, Motif, XtIntrinsics 등의 라이브러리가 있다.
(2) X-윈도우 설정과 실행
# 파일 /etc/inittab
- init 프로세스가 읽는 파일로, init 프로세스가 무엇을 해야할 것인가를 설정한다. - 리눅스 사용 환경을 초기화한다. - 형식)이름 : 런레벨 : 옵션 : process -옵션
ex> id:3:initdefault: ->디폴트 런레벨을 3으로 지정 (initdefault 키워드 뒤에는 process 필드가 무시된다.)
런레벨
설명
0
halt (Do NOT set initdefault to this)
1
Single user mode
2
Multiuser, without NFS (The same as 3, if you do not have networking)
3
Full multiuser mode (CUI로 부팅)
4
unused
5
X11( x 윈도우로 부팅)
6
reboot (Do NOT set initdefault to this)
# X-윈도우 실행
- 그래픽 환경이 아닌 터미널 윈도우로 로그인한 경우에는 몇 개의 프로그램을 실행해야 한다. - 터미널 윈도우의 명령어 프롬프트상에서 다음의 명령어를 실행시켜야 한다. - 형식)startx -- [인자값]
(일반적으로 화면이 하나가 나오는데 인자값을 입력하면 여러 개의 X-윈도우 프로그램을 구동할 수 있다.)
- 명령어 startx는 X윈도우를 실행하는 스크립트로 시스템 환경을 초기화하고 xint(프로세스)를 호출한다. - 명령어 startx 실행 시 인자값(argument)을 xinit에 전달하는 옵션은 '--'이다
-> 터미널 모드로 사용하다가 GUI로 변경하려면 startx -- 명령어를 사용
# 환경변수 DISPLAY (x윈도우 관련 환경변수)
- 환경 변수는 프로세스가 컴퓨터에서 동작하는 방식에 영향을 주는 동적인 값이다. - 셀에서 정의되고 실행하는 동안 프로그램에 필요한 변수이다. - 환경변수 DISPLAY는 현재 x윈도우 display 위치를 저장할 수 있다. - 형식)export DISPLAY=IP주소:디스플레이번호.스크린번호(x윈도우 디스플레이 위치 변경 가능)
(3) 윈도우 매니저와 데스크톱 환경
가장 핵심은 데스크톱 매니저이다. * 데스크톱 매니저 기반으로 윈도우 매니저, 디스플레이 매니저 활성화되어 사용
# 윈도우 매니저
- 윈도우 매니저는 X-windows상에서 창(window)외 배치와 표현을 담당하는 시스템 프로그램이다. - 창 열기와 닫기, 창의 생성 위치, 창의 크기 조정, 창의 외향과 테두리를 변화시킬 수 있다. - 라이브러리는 Xlib와 XCB를 사용한다. - 리눅스에서 사용 가능한 윈도우 매니저들은 다양하다. - 윈도우 매니저의 대표적인 종류로는 fvwm, twm, windowMaker, AfterStep 등이 있다.
# 데스크톱 환경(Desktop Environment 또는 Desktop Manager)
- GUI 사용자에게 제공하는 인터페이스 스타일로 데스크톱 관리자라고도 한다. - 윈도우 매니저, 파일 관리자, 도움말, 제어판 등 다양한 도구를 제공하는 패키지 형태의 프로그램이다. - 아이콘, 창, 도구 모음, 폴더, 배경화면, 데스크톱 위젯도 제공한다. - 드래그 앤 드롭(drag & drop)과 프로세스 간의 통보 가능을 지원한다. - 대표적인 데스크톱 환경에는 KDE, GNOME, LXDE, xfce 등이 있다.
(가장 일반적인 것은 KDE, GNOME)
- 데스크톱 환경에 따라 아이콘, 창, 도구모음, 폴더 등이 다르게 지원
QT라이브러리는 KDE 환경에서 사용. GTK+는 GNOME 환경에서 사용.
# 디스플레이 매니저
- X window system상에서 작동하는 프로그램이다.
(데스크톱 환경을 설치하면 데스크톱 환경이 사용하는 디스플레이 매니저가 있다)
- 1988년 XTIR3에서 xdm 디스플레이 매니저가 도입되었다. - 1989년 XTIR4에서 원격제어가 가능하도록 xdmcp(X Display Manager Control Protocol)가 도입되었다. - 디스플레이 매니저 종류들로는 XDM, GDM, KDM, dtlogin 등이 있다. - 로컬 또는 리모트 컴퓨터의 X server의 접속과 세션 시작을 담당한다. - 사용자에게 그래픽 로그인 화면을 띄워주고 아이디와 패스워드를 입력받아 인증을 진행한다.
- 인증이 정상적으로 완료되면 세션을 시작한다.
참고]
XDM : 초창기 사용했으며 현재는 거의 사용하지 않는다. KDM : KDE 데스크톱 환경에서 사용하는 디스플레이 매니저이다.
GDM : GNOME 데스크톱 환경에서 사용하는 디스플레이 매니저이다. /
GNOME 그래픽 로그인 프로그램을 담당한다. /
gtk 라이브러리를 이용해서 구현되고 있다. /
gnu, gpl기반의 라이센스를 따르고 있다. /
(1) 원격지에서 X 클라이언트 이용
- (원격지에 있는 서버를 접속하기 위해서 클라이언트들이 이용하는 명령어 : xhost, xauth가 있다.) - (접속을 위해서 인증 필요)
xhost : ip주소기반 xhost + : 모든 호스트 허용 xhost - : 모든 호스트 거부 xhost : 현재 상태 확인 xauth : 인증키 기반 서버에서 인증키 생성 -> 클라이언트 보냄 -> 홈디렉터리에 저장
# xhost:
-IP나 도메인명을 이용해서 서버 접속 요청 - 명령어 xhost는 X 서버에 접속할 수 있는 클라이언트를 지정하거나 해제한다. - x 서버에게 디스플레이를 요청 시 해당 요청에 대한 허용 거부를 호스트 단위로 제어한다. - 형식)xhost [+][-] [ip|도메인명]
(특정 IP를 기재하지 않으면 모든 호스트 접속 허용/금지)
- 환경변수 DISPLAY로 x 서버 프로그램이 실행될 때 표시되는 클라이언트 주소를 지정한다.
# xauth :
- xauth는 .Xauthority 파일의 쿠키 내용을 추가, 삭제, 리스트를 출력하는 유틸리티이다.
(.Xauthority 파일에는mit magic cookie1값을 가지고 있고 이 값을 기반으로 사용자 인증을 하고, 인증된 사용자만 x 서버의 서비스를 클라이언트가 받을 수 있다.)
-쿠키 값(키) 기반의 인증으로 인증 절차가 xhost보다 강화된 방법 - xhost가 호스트 기반 인증 방식을 사용하기 위해 필요한 유틸릴티라면, xauth는 MMC방식(사용자 인증 기반)의 인증 방식을 사용하기 위한 필수 유틸리티이다. - 원격지에서 접속하는 x 클라이언트를 허가할 때 IP 주소나 호스트명이 아닌 x-윈도우 실행 시에 생성되는 키 값으로 인증할 때 사용한다. - 사용자 인증 기반을 지원하기 위해 각 사용자에게 네트워크화된 홈 디렉터리에 파일 $HOME/.Xauthority에 대해 읽기 및 쓰기 권한이 있어햐 한다. - 형식)xauth [옵션]
(2) X윈도우 응용프로그램
응용프로그램
설명
오피스
LibreOffice
오피스 프로그램 패키지
Gedit
텍스트 편집 프로그램
Kwrite
KDE기반의 텍스트 편집기
그래픽
GIMP
이미지 편집 프로그램
ImageMegick
이미지를 생성 및 편집을 지원하는 프로그램
Eog
GNOME의 이미지 뷰어 프로그램
Kolourpaint
Ubuntu이미지 편집 프로그램
Gthumb
GNOME데스트콥 이미지 뷰어 프로그램
Gwenview
KDE의 기본 이미지 뷰어
멀티미디어
Totem
GNOME기반의 사운드 및 비디오 재생 프로그램
RHYTHMBOX
통합형 음악 관리 프로그램
CHEESE
GNOME기반의 카메라 동영상 프로그램
개발
ECLIPSE
통합 개발 환경으로 자바를 비롯한 다양한 언어를 지원
기타
Colphin
KDE용 파일 관리자
KSnapshot
스크린샷 프로그램
인터넷 활용 (1) WWW(World Wide Web) 서비스 (웹 서비스)
- 프로토콜 HTTP(Hyper Text Transfer Protocol)를 기반으로 한 멀티미디어와 하이퍼텍스트를 통합한 정보 검색 시스템
- 인터넷에 연결된 전 세계 컴퓨터의 모든 문서들을 언제 어디서든 다양한 컴퓨터 환경에서 검색이 가능하게 해줌
- 다양한 그래픽 유저 인터페이스들을 사용하는 것이 가능
- 분산 클라이언트-서버 모델을 기반으로 함(1대의 서버에 여러 대의 클라이언트가 접속하여 서비스를 받음)
- 1989년 CERN(유럽입자 물리학 연구소)에서 하이퍼텍스트가 시작되었으며,
1990년 WWW라는 넥스트 플랫폼용 브라우저가 공개됨
- 표준 웹 프로토콜(HTTP, XML, SOAP, WSDL, UDDI)을 기본으로 하여
서로 다른 개발 환경과 운영체제에서도 상호 통신이 가능
- 다양한 웹 브라우저들이 있음
(2) 메일 서비스
- 전자 메일 시스템은 컴퓨터 사용자끼리 편지를 주고받는 서비스
- MTA(메일 서버에 메일을 전달), MUA(메일을 작성하는 사용자 인터페이스),
MDA(수신자 메일 박스에 메일 전달)로 구성
- 메일 클라이언트에서 송신은 SMTP(메일발송/서버간 메일교환),
수신은 POP3 or IMAP4(메일 서버에 도착한 메일을 사용자 컴퓨터에서 확인) 를 이용
- MIME(Multipurpose Internet Mail Extension)은 멀티미디어 전자우편을 위한 표준,
멀티미디어 데이터를 ASCll 형식으로 변환할 필요 없이 인터넷 전자 우편으로 송신하기 위한 SMTP의 확장 규격
(메일 송신 프로토콜)
(3) FTP 서비스(파일전송 프로토콜)
- FTP(File Transfer Protocol) TCP/IP에 의해 제공되는 호스트 간의 파일 복사를 위한 프로토콜
- 통신 모드 : 패시브 모드(passive mode: FTP 서버가 지정한 포트로 클라이언트가 트래픽 송수신) /
import torch
import json
import cv2
import numpy as np
import os
from torch.utils.data import Dataset
from torchvision.transforms import functional as F
class KeypointDataset(Dataset):
def __init__(self, root, transform=None, demo=False):
self.demo = demo
# Visualize를 통해 시각화로 확인하고자 할때 비교를 위한 변수
self.root = root
# 현재 데이터셋은 root 인자에 dataset 하위 폴더의 train, test 폴더를 택일하도록 되어 있음
self.imgs_files = sorted(os.listdir(os.path.join(self.root, "images")))
# 이미지 파일 리스트. train 또는 test 폴더 하위에 있는 images 폴더를 지정하고, 해당 폴더의 내용물을 받아옴
# 이미지를 이름 정렬순으로 불러오도록 sorted를 붙임
self.annotations_files = sorted(os.listdir(os.path.join(self.root, "annotations")))
# 라벨링 JSON 파일 리스트. 상기 images 폴더를 받아온 것과 동일
self.transform = transform
def __getitem__(self, idx):
img_path = os.path.join(self.root, "images", self.imgs_files[idx])
# 이번에 호출되는 idx번째 이미지 파일의 절대경로
annotations_path = os.path.join(self.root, "annotations", self.annotations_files[idx])
# 이번에 호출되는 idx번째 이미지 파일의 라벨 JSON 파일 경로
img_original = cv2.imread(img_path)
img_original = cv2.cvtColor(img_original, cv2.COLOR_BGR2RGB)
# 이미지를 읽은 후, BGR 순서를 RGB 형태로 바꿈
with open(annotations_path, "r", encoding="utf-8") as f:
data = json.load(f)
# 라벨 JSON 파일을 JSON 모듈로 받아옴
bboxes_original = data["bboxes"]
# JSON 하위 "bboxes" 키로 bbox 정보들이 담겨있음
keypoints_original = data["keypoints"]
# "keypoints" 키로 키포인트 정보가 담겨있음
bboxes_labels_original = ['Glue tube' for _ in bboxes_original]
# 현재 데이터셋은 모든 객체가 접착제 튜브이므로,
# 모든 bbox에 대해 일관적으로 'Glue tube'라는 라벨을 붙여줌
if self.transform: # if self.transform is not None:
# "keypoints": [
# [[1019, 487, 1], [1432, 404, 1]], [[861, 534, 1], [392, 666, 1]]
# ]
keypoints_original_flattened = [el[0:2] for kp in keypoints_original for el in kp]
# kp : [[1019, 487, 1]]
# el : [1019, 487, 1]
# el[0:2] : [1019, 487] (평면 이미지에서 3번째 축 요소는 필요없기 때문에 제거)
# keypoints_original_flattened = [[1019, 487], [1432, 404], [861, 534], [392, 666]]
# albumentation transform은 평면 이미지에 적용되므로 2차원 좌표만이 필요함
# albumentation 적용
transformed = self.transform(image=img_original, bboxes=bboxes_original,
bboxes_labels=bboxes_labels_original,
keypoints=keypoints_original_flattened)
img = transformed["image"] # albumentation transform이 적용된 image
bboxes = transformed["bboxes"]
keypoints_transformed_unflattened = np.reshape(np.array(transformed["keypoints"]), (-1, 2, 2)).tolist()
# transformed["keypoints"] : [1019, 487, 1432, 404, 861, 534, 392, 666]
# keypoints_transformed_unflattened : [[[1019, 487], [1432, 404]], [[861, 534], [392, 666]]]
keypoints = []
for o_idx, obj in enumerate(keypoints_transformed_unflattened):
obj_keypoints = []
# o_idx : 현재 순회중인 요소의 순번 (index)
# obj : 현재 순회중인 요소, ex) [[1019, 487], [1432, 404]]
for k_idx, kp in enumerate(obj):
# k_idx : 현재 순회중인 하위 요소의 순번 (index)
# kp : 현재 순회중인 요소, ex) [1019, 487]
obj_keypoints.append(kp + [keypoints_original[o_idx][k_idx][2]])
# torch.Tensor에서 벡터곱을 하는 과정에서 필요할 3번째 축 요소를 덧붙임 ex) [1019, 487, 1]
keypoints.append(obj_keypoints)
# Tensor 형태로 사용할 keypoints 리스트에 3번째 축 요소를 덧붙인 키포인트 좌표를 담음
else:
img, bboxes, keypoints = img_original, bboxes_original, keypoints_original
# transform이 없는 경우에는 변수 이름만 바꿔줌
# transform을 통과한 값들을 모두 tensor로 변경
bboxes = torch.as_tensor(bboxes, dtype=torch.float32)
# as_tensor 메서드가 list를 tensor로 변환할 때 속도 이점이 있음
target = {}
# keypoint 모델에 사용하기 위한 label이 dictionary 형태로 필요하므로, dict 형태로 꾸림
target["boxes"] = bboxes
target["labels"] = torch.as_tensor([1 for _ in bboxes], dtype=torch.int64)
# 모든 객체는 동일하게 접착제 튜브이므로, 동일한 라벨 번호 삽입
target["image_id"] = torch.tensor([idx])
# image_id는 고유번호를 지칭하는 경우도 있는데, 그러한 경우에는 JSON 파일에 기입이 되어있어야 함
# 이번 데이터셋은 JSON상에 기입되어있지 않으므로, 현재 파일의 순번을 넣어줌
target["area"] = (bboxes[:, 3] - bboxes[:, 1]) * (bboxes[:, 2] - bboxes[:, 0])
# 해당하는 bbox의 넓이.
# bboxes[:, 3] - bboxes[:, 1] : bboxes 내부 요소들의 (y2 - y1), 즉 세로 길이
# bboxes[:, 2] - bboxes[:, 0] : bboxes 내부 요소들의 (x2 - x1), 즉 가로 길이
target["iscrowd"] = torch.zeros(len(bboxes), dtype=torch.int64)
# 이미지상에 키포인트 또는 bbox가 가려져있는지를 묻는 요소
target["keypoints"] = torch.as_tensor(keypoints, dtype=torch.float32)
img = F.to_tensor(img)
# image의 텐서 변환
bboxes_original = torch.as_tensor(bboxes_original, dtype=torch.float32)
target_original = {}
target_original["boxes"] = bboxes_original
target_original["labels"] = torch.as_tensor([1 for _ in bboxes_original],
dtype=torch.int64) # all objects are glue tubes
target_original["image_id"] = torch.tensor([idx])
target_original["area"] = (bboxes_original[:, 3] - bboxes_original[:, 1]) * (
bboxes_original[:, 2] - bboxes_original[:, 0])
target_original["iscrowd"] = torch.zeros(len(bboxes_original), dtype=torch.int64)
target_original["keypoints"] = torch.as_tensor(keypoints_original, dtype=torch.float32)
img_original = F.to_tensor(img_original)
# demo=True일 경우, 원본 이미지와 변환된 이미지를 비교하기 위해 원본 이미지를 반환하기 위한 블록
if self.demo:
return img, target, img_original, target_original
else:
return img, target
def __len__(self):
return len(self.imgs_files)
if __name__ == "__main__":
root_path = "./keypoint_dataset"
train_dataset = KeypointDataset(f"{root_path}/train")
for item in train_dataset:
print(item)
visualize.py
import cv2
import albumentations as A
from Customdataset import KeypointDataset
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
import numpy as np
from utils import collate_fn
train_transform = A.Compose([
A.Sequential([
A.RandomRotate90(p=1), # 랜덤 90도 회전
A.RandomBrightnessContrast(brightness_limit=0.3, contrast_limit=0.3, brightness_by_max=True,
always_apply=False, p=1) # 랜덤 밝기 및 대비 조정
], p=1)
], keypoint_params=A.KeypointParams(format='xy'),
bbox_params=A.BboxParams(format="pascal_voc", label_fields=['bboxes_labels']) # 키포인트의 형태를 x-y 순으로 지정
)
root_path = "./keypoint_dataset/"
dataset = KeypointDataset(f"{root_path}/train/", transform=train_transform, demo=True)
# demo=True 인자를 먹으면 Dataset이 변환된 이미지에 더해 transform 이전 이미지까지 반환하도록 지정됨
data_loader = DataLoader(dataset, batch_size=1, shuffle=True, collate_fn=collate_fn)
iterator = iter(data_loader)
# for문으로 돌릴 수 있는 복합 자료형들은 iterable (반복 가능) 속성을 갖고 있음
# iter()로 그러한 자료형을 감싸면 iterator (반복자) 가 되고,
# next(iterator)를 호출하면서 for문을 돌리듯이 내부 값들을 순회할 수 있게 됨
batch = next(iterator)
# iterator에 대해 next로 감싸서 호출을 하게 되면,
# for item in iterator의 예시에서 item에 해당하는 단일 항목을 반환함
# 아래 4줄에 해당하는 코드와 같은 의미
# batch_ = None
# for item in data_loader:
# batch_ = item
# break
keypoints_classes_ids2names = {0: "Head", 1: "Tail"}
# bbox 클래스는 모두 접착제 튜브 (Glue tube) 로 동일하지만, keypoint 클래스는 위의 dict를 따름
def visualize(image, bboxes, keypoints, image_original=None, bboxes_original=None, keypoints_original=None):
# pyplot을 통해서 bbox와 키포인트가 포함된
# 원본 이미지와 변환된 이미지를 차트에 띄워서 대조할 수 있는 편의함수
fontsize = 18
# cv2.putText에 사용될 글씨 크기 변수
for bbox in bboxes:
# bbox = xyxy
start_point = (bbox[0], bbox[1])
# 사각형의 좌측 상단
end_point = (bbox[2], bbox[3])
# 사각형의 우측 하단
image = cv2.rectangle(image.copy(), start_point, end_point, (0, 255, 0), 2)
# 이미지에 bbox 좌표에 해당하는 사각형을 그림
for kpts in keypoints:
# keypoints : JSON 파일에 있는 keypoints 키의 값 (즉, keypoints 최상위 리스트)
# kpts : keypoints 내부의 각 리스트 (즉, 각 bbox의 키포인트 리스트)
for idx, kp in enumerate(kpts):
# kp : kpts 내부의 각 리스트 (즉, 키포인트 리스트 내부의 xy좌표쌍, 키포인트 점)
image = cv2.circle(image.copy(), tuple(kp), 5, (255, 0, 0), 10)
# 현재 키포인트에 점을 찍음
image = cv2.putText(image.copy(), f" {keypoints_classes_ids2names[idx]}", tuple(kp),
cv2.FONT_HERSHEY_SIMPLEX, 2, (255, 0, 0), 3, cv2.LINE_AA)
# 현재 키포인트가 Head인지 Tail인지 위에 선언한 dict에 해당하는 문자를 집어넣음
# 변환된 이미지만을 확인할 경우, 원본 이미지가 없을 것이므로 그대로 이미지 처리 끝냄
if image_original is None and keypoints_original is None:
plt.figure(figsize=(40, 40))
# 이미지를 그릴 차트 선언
plt.imshow(image)
# 위에서 bbox와 키포인트를 그린 이미지를 출력
else:
for bbox in bboxes_original:
# bbox = xyxy
start_point = (bbox[0], bbox[1])
# 사각형의 좌측 상단
end_point = (bbox[2], bbox[3])
# 사각형의 우측 하단
image_original = cv2.rectangle(image_original.copy(), start_point, end_point, (0, 255, 0), 2)
# 이미지에 bbox 좌표에 해당하는 사각형을 그림
for kpts in keypoints_original:
# keypoints : JSON 파일에 있는 keypoints 키의 값 (즉, keypoints 최상위 리스트)
# kpts : keypoints 내부의 각 리스트 (즉, 각 bbox의 키포인트 리스트)
for idx, kp in enumerate(kpts):
# kp : kpts 내부의 각 리스트 (즉, 키포인트 리스트 내부의 xy좌표쌍, 키포인트 점)
image_original = cv2.circle(image_original.copy(), tuple(kp), 5, (255, 0, 0), 10)
# 현재 키포인트에 점을 찍음
image_original = cv2.putText(image_original.copy(), f" {keypoints_classes_ids2names[idx]}", tuple(kp),
cv2.FONT_HERSHEY_SIMPLEX, 2, (255, 0, 0), 3, cv2.LINE_AA)
# 현재 키포인트가 Head인지 Tail인지 위에 선언한 dict에 해당하는 문자를 집어넣음
f, ax = plt.subplots(1, 2, figsize=(40, 20))
# 두 장의 이미지를 1행 2열, 즉 가로로 길게 보여주는 subplots 생성
ax[0].imshow(image_original)
# 첫번째 subplot에는 원본 이미지를 출력
ax[0].set_title("Original Image", fontsize=fontsize)
# 이미지 제목
ax[1].imshow(image)
# 두번째 subplot에는 변환이 완료된 이미지를 출력
ax[1].set_title("Transformed Image", fontsize=fontsize)
plt.show()
if __name__=="__main__":
visualize_image_show = True
visualize_targets_show = True
image = (batch[0][0].permute(1, 2, 0).numpy() * 255).astype(np.uint8)
# CustomDataset에서 Tensor로 변환했기 때문에 다시 plt에 사용할 수 있도록 numpy 행렬로 변경
# img, target, img_original, target_original = batch이므로, batch[0]는 img를 지칭
# batch[0][0]에 실제 이미지 행렬에 해당하는 텐서가 있을것 (batch[0][1]에는 dtype 등의 다른 정보가 있음)
bboxes = batch[1][0]['boxes'].detach().cpu().numpy().astype(np.int32).tolist()
# target['boxes']에 bbox 정보가 저장되어있으므로, 해당 키로 접근하여 bbox 정보를 획득
keypoints = []
for kpts in batch[1][0]['keypoints'].detach().cpu().numpy().astype(np.int32).tolist():
keypoints.append([kp[:2] for kp in kpts])
# 이미지 평면상 점들이 필요하므로, 3번째 요소로 들어있을 1을 제거
image_original = (batch[2][0].permute(1, 2, 0).numpy() * 255).astype(np.uint8)
# batch[2] : image_original
bboxes_original = batch[3][0]['boxes'].detach().cpu().numpy().astype(np.int32).tolist()
# batch[3] : target
keypoints_original = []
for kpts in batch[3][0]['keypoints'].detach().cpu().numpy().astype(np.int32).tolist():
keypoints_original.append([kp[:2] for kp in kpts])
if visualize_image_show:
visualize(image, bboxes, keypoints, image_original, bboxes_original, keypoints_original)
if visualize_targets_show and visualize_image_show == False:
print("Original targets: \n", batch[3], "\n\n")
# original targets: (줄바꿈) original targets dict 출력 (두줄 내림)
print("Transformed targets: \n", batch[1])
main.py
import torch
import torchvision
import albumentations as A
from engine import train_one_epoch, evaluate
from utils import collate_fn
from torch.utils.data import DataLoader
from Customdataset import KeypointDataset
from torchvision.models.detection import keypointrcnn_resnet50_fpn
from torchvision.models.detection.rpn import AnchorGenerator
def get_model(num_keypoints, weights_path=None):
# 필요한 모델에 대해 키포인트 개수를 정의하고, 기존 모델이 있는 경우 로드하는 편의함수
anchor_generator = AnchorGenerator(sizes=(32, 64, 128, 256, 512), aspect_ratios=(0.25, 0.5, 0.75, 1.0, 2.0, 3.0, 4.0))
# 여러 input size에 대해 feature map으로 넘어갈 때 적절한 비율 변환율을 도와주는 객체
model = keypointrcnn_resnet50_fpn(
pretrained=False, # 모델 자체는 pretrain된 것을 사용하지 않음
# 현재는 학습용 코드이기 때문에, pretrain 모델을 fine-tuning 하지않고 처음부터 가중치 업데이트를 하도록 설정
pretrained_backbone=True, # backbone만 pretrain된 것을 사용함
# backbone은 모델 설계상 이미 pretrain 됐을 것으로 상정했기 때문에
# 실제 가중치 없데이트가 주로 일어날 부분에 비해 pretrain 여부가 크게 상관있지 않음
num_classes=2, # 무조건 배경 클래스를 포함함
num_keypoints=num_keypoints,
rpn_anchor_generator=anchor_generator)
if weights_path: # 기존 모델이 있는 경우
state_dict = torch.load(weights_path)
model.load_state_dict(state_dict)
return model
train_transform = A.Compose([
A.Sequential([
A.RandomRotate90(p=1), # 랜덤 90도 회전
A.RandomBrightnessContrast(brightness_limit=0.3, contrast_limit=0.3, brightness_by_max=True,
always_apply=False, p=1) # 랜덤 밝기 및 대비 조정
], p=1)
], keypoint_params=A.KeypointParams(format='xy'),
bbox_params=A.BboxParams(format="pascal_voc", label_fields=['bboxes_labels']) # 키포인트의 형태를 x-y 순으로 지정
)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
KEYPOINTS_FOLDER_TRAIN = "./keypoint_dataset/train/"
# train dataset이 있는 경로
train_dataset = KeypointDataset(KEYPOINTS_FOLDER_TRAIN, transform=train_transform)
train_dataloader = DataLoader(train_dataset, batch_size=6, shuffle=True, collate_fn=collate_fn)
model = get_model(num_keypoints=2)
model.to(device)
optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9, weight_decay=0.0005)
# momentum : 이전에 가중치를 업데이트한 기울기를 얼마나 반영할 것인가?
# weight_decay : 가중치 감소율
lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=5, gamma=0.3)
# stepLR : step_size epoch 마다 lr이 기준 lr * gamma 로 변경됨 -> 즉 5 epoch 마다 lr이 0.3배씩 줄어듬
num_epochs = 20
# 최대 학습 횟수
# 현재 engine에서 받아온 train_one_epoch 함수는 손실함수를 넣지 않아도 되도록 처리된 함수이므로,
# 손실함수의 정의 는 생략됨
for epoch in range(num_epochs):
train_one_epoch(model, optimizer, train_dataloader, device, epoch, print_freq=1000)
# 학습이 진행되는 함수
lr_scheduler.step()
# 학습 1 epoch가 끝날때마다 scheduler 역시 업데이트
if epoch % 10 == 0:
torch.save(model.state_dict(), f"./keypointsrcnn_weights_{epoch}.pth")
# 10 epochs마다 가중치 저장
torch.save(model.state_dict(), "./keypointsrcnn_weights_last.pth")
# 모든 epoch가 끝나면 last.pth까지 저장
# os listdir
import os
# 이미지가 저장된 디렉토리 경로
img_dir = './사과/'
# 디렉토리 내 모든 파일 목록 가져오기
file_list = os.listdir(img_dir)
print(file_list)
# 단점 정렬 되지 않습니다.
sorted를 써도 마찬가지
# 만약 정렬 하고 싶다면 sort 함수 사용
# os listdir
import os
# 이미지가 저장된 디렉토리 경로
img_dir = './사과/'
# 디렉토리 내 모든 파일 목록 가져오기
file_list = sorted(os.listdir(img_dir))
print(file_list)
import glob
import os
file_list = glob.glob(os.path.join("./사과/", "*.jpg"))
print(file_list)
# 을 활용해야 제대로 정렬된 상태로 가져옴
폴더안의 폴더도 for문으로 가능함
#### os.walk 를 이용한 폴더에서 이미지 파일 가져오기 함수 구현
os.walk() 하위의 폴더들을 for문으로 탐색할 수 있게 해줍니다. 인자로 전달된 path에 대해서 다음 3개의 값이 있는 tuple을 넘겨줍니다.
- root : dir과 files가 있는 path
- dirs : root 아래에 있는 폴더들
- files : root 아래에 있는 파일들
def get_img_paths(root_path): # 하위에 있는 경로 모두 탐색
file_paths = []
for (path, dir, files) in os.walk(root_path):
for file in files:
ext = os.path.splitext(file)[-1].lower()
formats = ['.bmp', '.jpg', '.jpeg', '.png', '.tif', '.tiff', '.dng']
if ext in formats:
file_path = os.path.join(path, file)
file_paths.append(file_path)
return file_paths
file_paths = get_img_paths("./사과/")
print(file_paths)
# 정렬 하고 싶다면 natsort.natsorted 이용
# file_paths_sort = natsort.natsorted(file_paths)
# print(file_list_sort)
2. FAST를 사용하여 빠르게 특징점을 검출하고, BRIEF를 사용하여 특징점 주변의 특징을 설명합니다.
3. 회전에 불변한 특징점 검출 및 설명을 제공합니다. 4. ORB는 SIFT 및 SURF와 비교적 더 빠르지만, 일반적으로 덜 정확한 결과를 제공합니다.
import cv2
# 동영상 파일 열기
cap = cv2.VideoCapture("MS/CV/0612 객체탐지,추적/data/vtest.avi")
# ORB 객체 생성
orb = cv2.ORB_create()
while True:
# 프레임 읽기
ret, frame = cap.read()
if not ret:
break
# 그레이스케일로 변환
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 특징점 검출
keypoints = orb.detect(gray, None)
# 특징점 그리기
frame = cv2.drawKeypoints(frame, keypoints, None, (0, 150, 220), flags=0)
# 출력
cv2.imshow("ORB", frame)
if cv2.waitKey(30) & 0xFF == ord("q"):
break
cap.release()
cv2.destroyAllWindows()
를 쓰면 불필요한 특징점도 많이 나온다.
Mask를 활용하여 특징점 제거하기 2
import cv2
import numpy as np
# 동영상 파일 읽기
cap = cv2.VideoCapture("MS/CV/0612 객체탐지,추적/data/vtest.avi")
# ORB 객체 생성
orb = cv2.ORB_create()
# 특징점 최소 크기 설정
min_keypoint_size = 10
# 중복 특징점 제거 기준거리
duplicate_threshold = 10
while True:
# 프레임 읽기
ret, frame = cap.read()
if not ret:
break
# 그레이스케일로 변환
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 특징점 검출
keypoints = orb.detect(gray, None)
# 특징점 크기가 일정 크기 이상인 것만 남기기
keypoints = [kp for kp in keypoints if kp.size > min_keypoint_size] # 리스트 컴프리헨션(파이썬문법)
# 중복된 특징점 제거
mask = np.ones(len(keypoints), dtype=bool) # 1차원 배열
for i, kp1 in enumerate(keypoints):
if mask[i]:
for j, kp2 in enumerate(keypoints[i + 1:]):
if (
mask[ i + j + 1]
and np.linalg.norm(np.array(kp1.pt) - np.array(kp2.pt)) < duplicate_threshold # .pt : OpenCV의 KeyPoint 객체의 속성중 하나
):
mask[ i + j + 1] = False
# kp1.pt와 kp2.pt는 각각 두 개의 키포인트 객체인 kp1과 kp2의 위치 좌표를 나타냅니다.
"""
먼저, mask라는 길이가 keypoints의 길이와 동일한 불리언 배열을 생성합니다. 이 배열은 모든 원소를 True로 초기 화합니다.
그런 다음, keypoints 리스트를 반복하면서 현재 키포인트와 나머지 키포인트들 간의 거리를 계산하여 중복된 키포 인트를 확인합니다.
중복된 키포인트인 경우에는 해당 키포인트에 해당하는 mask의 원소를 False로 설정합니다.
마지막으로, mask를 기반으로 중복되지 않은 키포인트들만을 새로운 리스트로 필터링하여 keypoints를 업데이트 합니다.
이를 통해 중복된 특징점을 제거하고 유니크한 특징점만을 남기는 작업을 수행합니다.
"""
keypoints = [kp for i, kp in enumerate(keypoints) if mask[i]]
# 특징점 그리기
frame = cv2.drawKeypoints(frame, keypoints, None, (0, 200, 150), flags=0)
cv2.imshow("ORB", frame)
# 'q'를 누르면 종료
if cv2.waitKey(60) == ord('q'):
break
# 해제
cap.release()
cv2.destroyAllWindows()