레이블이 리눅스인 게시물을 표시합니다. 모든 게시물 표시
레이블이 리눅스인 게시물을 표시합니다. 모든 게시물 표시

2023년 4월 10일 월요일

파이썬 기반 PDF 보고서 생성 및 텍스트 마이닝하기

이 글은 파이썬 기반 PDF 보고서 파일을 생성하거나, 원하는 텍스트를 추출하여 데이터 마이닝하는 오픈소스 도구를 간략히 정리한다.

오피스에서 작업을 하다보면, PDF파일을 많이 다루게된다. 이 경우, 노가다가 필요한 수작업이 많은데, 파이썬 라이브러리를 사용하면, 이러한 오피스 작업을 자동화할 수 있다. 

이 글은 fpdf, pyPDF2 라이브러리를 사용해, 보고서 형식을 PDF로 출력하거나, 반대로 PDF의 정보를 추출하는 방법을 구현해 본다. 

생성된 PDF 보고서 예시

라이브러리 소개

fpdf (github)는 PDF 문서를 생성하는 라이브러리로 최초 PHP로 개발되었다가 파이썬으로 포팅된 도구이다. 다음 기능을 지원한다.

  • PDF 쓰기 지원
  • PNT, GIF, JPG 지원
  • html2pdf 템플릿 지원

PyPDF2는 PDF파일 병합, 절단, 변환 등을 지원한다. 주요 기능은 다음과 같다.
  • PDF 데이터 처리
  • PDF 암호화
  • PDF 데이터 추출 

라이브러리 설치

다음 명령을 터미널에 입력해 필요한 라이브러리를 설치한다.

pip install fpdf, pyPDF2, lorem, pandas, seaborn, re

사용방법

파이썬 파일을 하나 만든 후, 다음을 코딩해 넣는다.

from fpdf import FPDF
import lorem 
import pandas as pd

# 표 출력을 위해, 데이터 프레임 생성
df = pd.DataFrame(
          {'feature 1' : ['cat 1', 'cat 2', 'cat 3', 'cat 4'],
           'feature 2' : [400, 300, 200, 100]
          })

# 차트 그림 생성
import matplotlib.pyplot as plt
import seaborn as sns
fig, ax = plt.subplots(1,1, figsize = (6, 4))
sns.barplot(data =  df, x = 'feature 1', y = 'feature 2')
plt.title("Chart")
plt.savefig('./example_chart.png', 
           transparent=False,  
           facecolor='white', 
           bbox_inches="tight")

# PDF 클래스 생성
ch = 8
class PDF(FPDF):
    def __init__(self):
        super().__init__()
    def header(self):
        self.set_font('Arial', '', 12)
        self.cell(0, 8, 'Header', 0, 1, 'C')
    def footer(self):
        self.set_y(-15)
        self.set_font('Arial', '', 12)
        self.cell(0, 8, f'Page {self.page_no()}', 0, 0, 'C')

# 셀을 생성하고, 각 셀에 보고서 내용 설정
pdf = PDF()
pdf.add_page()
pdf.set_font('Arial', 'B', 24)
pdf.cell(w=0, h=20, txt="Title", ln=1)
pdf.set_font('Arial', '', 16)
pdf.cell(w=30, h=ch, txt="Date: ", ln=0)
pdf.cell(w=30, h=ch, txt="01/01/2022", ln=1)
pdf.cell(w=30, h=ch, txt="Author: ", ln=0)
pdf.cell(w=30, h=ch, txt="Max Mustermann", ln=1)
pdf.ln(ch)
pdf.multi_cell(w=0, h=5, txt=lorem.paragraph())
pdf.image('./example_chart.png', x = 10, y = None, w = 100, h = 0, type = 'PNG', link = '') # 차트 이미지 입력
pdf.ln(ch)
pdf.multi_cell(w=0, h=5, txt=lorem.paragraph())
pdf.ln(ch)

# 표 헤더 및 내용 설정
pdf.set_font('Arial', 'B', 16)
pdf.cell(w=40, h=ch, txt='Feature 1', border=1, ln=0, align='C')
pdf.cell(w=40, h=ch, txt='Feature 2', border=1, ln=1, align='C')
# Table contents
pdf.set_font('Arial', '', 16)
for i in range(len(df)):
    pdf.cell(w=40, h=ch, txt=df['feature 1'].iloc[i], border=1, ln=0, align='C')
    pdf.cell(w=40, h=ch, txt=df['feature 2'].iloc[i].astype(str), border=1, ln=1, align='C')

# PDF 저장
pdf.output("example.pdf")

그 결과는 다음과 같다.


PDF에서 텍스트 추출
다음 파이썬 코드를 입력한다.
import re, PyPDF2

def extract_abstract(pdf_fname = 'sample.pdf'):  # sample.pdf 파일을 준비한다. 이 경우는 논문의 ABSTRACT 부분을 추출하는 것으로 코딩되었다.
    pdf_file = open(pdf_fname, 'rb')
    pdf_reader = PyPDF2.PdfReader(pdf_file)

    first_page = pdf_reader.pages[0]
    text = first_page.extract_text()

    abstract_start = text.find('Abstract')  # abstract keyword 검색
    if abstract_start == -1:
        abstract_start = text.find('ABSTRACT')

    abstract = ''
    if abstract_start == -1:
        return abstract

    abstract_start += len('Abstract')
    introduction_start = text.find('Introduction')  # introduction keyword 검색
    if introduction_start == -1:
        introduction_start = text.find('Background')
    if introduction_start == -1:
        return abstract
    introduction_start -= 4

    abstract_end = introduction_start if introduction_start != -1 else len(text)
    abstract = text[abstract_start:abstract_end]

    # remove new line characters
    abstract = abstract.replace('\n', ' ')

    # remove digits and special characters
    # abstract = re.sub(r'[^a-zA-Z0-9().,% ]', '', abstract)
    print(abstract)

extract_abstract()

실행하면 다음과 같이 PDF로부터 추출된 ABSTRACT 텍스트 결과를 확인할 수 있다.

레퍼런스


2023년 3월 9일 목요일

가장 간단한 파이토치 도커 이미지 빌드 및 확장하기

이 글은 가장 간단한 파이토치 도커 이미지 생성하는 방법을 남긴다. 

도커 이미지를 생성하기 전에 고려사항이 있다.
1. 생성할 도커 이미지 내 설치할 패키지, 라이브러리 간 버전 호환성 및 의존성 확인
2. 기존 이미지를 기반으로 확장해 생성할 경우, 기존 이미지의 패키지 버전 호환성 확인
3. 생성할 도커 이미지의 크기 만큼 디스크 용량 확인

이 글은 2번 방식으로 도커 이미지를 생성하는 방법을 간략히 정리한다. 기타, 도커에 대한 설치 등 상세 내용은 다음 링크를 참고한다. 

도커 이미지 확장 빌드 예제
이 경우는 기존 이미지를 이용해 패키지를 설치, 확장 후 새 이미지를 생성한다. 이 경우는 보통 딥러닝 개발 환경 등 설치하기 까다롭고 버전 호환성 문제가 자주 발생하는 경우에 사용한다.

파이토치, CUDA의 특정 버전으로 생성된 이미지를 도커 허브에서 검색한다. 

터미널에서 도커 이미지를 생성할 폴더를 만든다. 그리고 Dockerfile을 편집한다.
mkdir docker
gedit Dockerfile

다음과 같이 도커 명령을 입력한 후 저장한다. 
FROM pytorch/pytorch:1.12.1-cuda11.3-cudnn8-devel
MAINTAINER taewook

RUN apt-get update
RUN apt-get install -y libx11-6 libgl1-mesa-glx
RUN pip install torch tensorboard numpy

이때 도커의 레이어 빌드 메커니즘을 고려해, RUN 을 잘 조합해 정의한다. 

참고로, 도커는 레이어 구조로 설치 패키지를 관리하기 때문에, 이미 빌드된 레이어를 재활용해, 도커 빌드 속도를 크게 개선할 수 있다. 아울러, 빌드된 레이어를 재활용해, 빌드될 이미지를 조합할 수 있다.
도커 레이어 구조

터미널에서 다음과 같이 도커 이미지를 빌드한다.
docker bulid --tag docker_image .


레퍼런스

부록: 자주 사용하는 도커 명령어
docker build -t app_program_docker_image .
docker save -o app_program_docker_image.tar cbc
docker system prune -a -f
docker ps -a
docker image rm -f
docker rmi <image id> --force
docker run -v c:/dep_build/input:/app_program/input -v c:/dep_build/output:/app_program/output app_program_docker_image_name conda run -n venv_app python ./bin/user_app.py --input ./input --output ./output

부록: 도커 이미지 빌드 명령어 예시
FROM image_name
ENV PIP_ROOT_USER_ACTION=ignore
ENV PATH /usr/
RUN apt-get install python3-pip -y
RUN apt-get install python3.9
RUN pip install --upgrade pip 
CMD ["bash"]
CMD tail -f /dev/null

2023년 3월 5일 일요일

우분투 파이썬 패키지 버전 의존성 불일치 문제 해결 방법

이 글은 우분투 파이썬 패키지 버전 의존성 불일치 문제 해결 방법을 간략히 정리한다.

패키지 버전 디펜던시(의존성) 에러

우분투에서 개발하다보면, 여러 패키지 간 버전이 일치하지 않아, import가 실패하는 경우가 빈번히 발생한다. 특히, 오픈소스를 github에서 다운받아 사용할 경우, 사용된 패키지 버전이 명시되지 않았을 때는 직접 해당 버전을 탐색해 설치해야 한다.

의존성 에러

이 경우, 원하는 패키지 버전을 탐색하기 위해서는 pip package history를 확인해야 한다. 서로 유사한 패키지 릴리즈 시점의 버전을 찾는다.

패키지 버전 히스토리

그리고, 다음과 같이 버전을 명시해 설치하도록 한다.

pip install torch==1.10.1 torchvision==0.11.2 torchautdio==0.10.1

pip install scikit-learn matplotlib yacs timm==0.4.9

참고로 본인이 개발한 프로그램은 virtualenv, docker 등을 통해 개발환경을 반듯이 가상화해야 개발팀 전체가 사용할 때 문제가 없다. 아울러, 개발된 프로그램의 패키지 버전도 pip freeze로 기록해 놓는 습관을 가지는 것이 좋다.

참고

2023년 2월 8일 수요일

개발 후 자동화된 통합/배포/운영을 위한 DevOps CI/CD 파이프라인 및 오픈소스 도구 소개

이 글은 개발 후 자동화된 통합/배포/운영을 위한 DevOps CI(Continuous Integration)/CD(Continuous Delivery) 파이프라인과 오픈소스 도구를 간략히 소개한다.


CI/CD 파이프라인은 자동화된 테스트, 버전 관리, 지속적인 통합, 제공 및 배포를 포함하는 개발 방법론이다. 통합은 자동화를 통해 애플리케이션을 빠르고 효과적으로 배포할 수 있다. CI/CD는 여러 자동화된 테스트/배포 도구를 포함한다. 이 도구를 통해, 개발 배포 과정을 자동화할 수 있지만, 이 기술을 사용하기 이전에 작업하는 방식, 절차, 습관의 변화가 필수적이다.

지속적인 통합/배포 개념
CI/CD는 소프트웨어 개발 프로세스를 자동화하여, 배포 시간을 몇 달에서 몇 시간으로 단축할 수 있다.

지속적 통합에는 코드베이스에 대한 지속적인 코드 통합 프로세스가 포함된다. 사소한 코드 변경으로 인해 원하지 않는 결과가 발생하므로, 소규모 통합을 자주 수행하는 것이 중요하다. 사람 개입 없이 빌드 자동화를 통해 모든 통합의 오류를 확인한다.

이는 CI 서버 및 코드 관리 시스템을 비롯한 자동화 도구에 크게 의존한다. 이를 통해 문제를 조기에 감지하고, 제거할 수 있다. 지속적인 코드 통합은 팀 협업을 촉진하고, 개발자가 고품질 소프트웨어를 신속하게 제공할 수 있도록 한다.

CD는 버그 수정, UI/기능 개선과 같은 작고 빈번한 소프트웨어 변경 사항을 배포하는 방법론이다. 기본적으로 CI가 끝나는 지점에서 CD가 시작된다. 본질적으로 CD에는 지속적인 통합 코드의 스테이징, 테스트 및 배포가 포함된다. CD는 전적으로 운영 영역에 속한다.

CD는 소프트웨어가 CD/CI 파이프라인을 통과할 때, 오류 및 보안 취약성을 식별하기 위한 자동화된 회귀 테스트, 성능 모니터링을 자동 실행한다. 

지속적 통합/배포 도구
애자일 개발 방법론 채택이 증가함에 따라, 여러 CI/CD 도구가 시장에 출시되었다. 

Jenkins
Jenkins는 사용자가 실시간으로 테스트하고 보고할 수 있는 오픈 소스 교차 플랫폼 Java 기반 도구이다. 사용하기 쉬운 Jenkins는 Azure , Google Cloud, AWS 및 DigitalOcean 을 포함한 주요 클라우드 플랫폼과의 원활한 통합을 제공한다. 

CI/CD 파이프라인에는 개발자가 도메인별 언어를 사용하여 모델링 및 구현에 사용할 수 있는 유용한 도구 세트가 포함되어 있다.

MacOS, Windows 및 Linux 플랫폼에서 사용할 수 있는 Jenkins는 강력한 커뮤니티와 1500개 이상의 플러그인으로 구성된 플러그인 생태계를 보유하고 있다. 오픈 소스이고 무료이며 강력하고 확장 가능하므로 대규모 조직과 신생 기업 모두가 선호한다.

Sippable
소프트웨어 회사 Jfrog가 개발한 Shippable은 DevOps 엔지니어가 예측 가능한 소프트웨어 릴리스를 만드는 데 사용할 수 있는 CI/CD 도구 중 하나이다. Docker 지원을 제공하는 Shippable 자동화 플랫폼은 언제 어디서나 애플리케이션의 개발, 테스트, 프로비저닝 및 배포를 간소화한다.

HipChat, Slack, Kubernetes, Google Container Engine, Amazon ECS, Docker, Bitbucket, GitHub, Nose, xUnit, JUnit, Cucumber 등 도구와 원활하게 통합된다.

GitLab CI
GitLab CI는 Windows, macOS 및 Linux와 같은 여러 플랫폼에서 사용할 수 있다. 친숙한 UI로 설계된 GitLab CI는 지속적인 통합, 제공, 코드 검토 및 배포를 포함한 다양한 기능을 간단한 대시보드에 담았다.

GitLab CI는 Shell Executor를 통해 빌드를 트리거할 수 있다. 

TeamCity
JetBrains에서 개발한 TeamCity는 Jira Software, Azure DevOps, NuGet, Maven, Visual Studio Team Services 및 Docker와의 통합을 지원하는 무료 오픈소스 CI/CD 도구이다.

TeamCity는 Linux 및 Windows에서 사용할 수 있으며, 병렬 빌드를 실행하여, 사용자에게 다양한 환경에서 동시에 빌드를 실행할 수 있는 유연성을 제공한다.

TeamCity는 vSphere, VMWare, Google Cloud 및 AWS를 포함한 다양한 클라우드 플랫폼에서 잘 작동한다. Kotlin 기반 DSL(도메인 스펙 언어)을 사용하여 파이프라인을 정의할 수 있다.

Buildkite
Buildkite는 개발자에게 온프레미스 플랫폼에서 확장 가능하고 안전한 파이프라인을 실행할 수 있는 기능을 제공한다. 사용자는 Docker, macOS, Linux, Windows에 Buildkite를 설치하고 중앙 집중식 플랫폼의 여러 에이전트에서 병렬로 작업을 실행할 수 있다. 사용자가 GraphQL API를 활용하는 도구를 구축할 수 있다.

Codefresh
Kubernetes 에서 개발된 Codefresh는 GitOps를 완벽하게 지원한다. Puppet , Pulumi, Terraform, Slack, GitLab, Bitbucket 및 GitHub 와 같은 잘 알려진 도구와의 통합을 제공한다 .

Kubernetes 대시보드를 통해 Codefresh는 프로그래머에게 Azure, Amazon Web Services, Kubernetes를 포함한 다양한 플랫폼에서 가시성, 확장성 및 뛰어난 속도를 제공한다.

Codefresh Runner라는 CLI(Command Line Interface)를 통해, 코드 배포, 빌드 트리거 및 테스트 실행을 위한 확장 가능하고 효과적인 방법을 제공한다.

Bitrise
Bitrise는 모바일 앱 개발에 사용되는 대부분의 프로그래밍 언어(예: Flutter, Reactive Native, Iconic, Cordova, Objective C, Swift 및 Kotlin)에 대한 강력한 지원을 제공한다. Bitrise는 오픈 소스 프로젝트로 개별 모바일 애플리케이션 개발자에게 무료로 제공된다. 

Bitrise는 사용자가 값비싼 하드웨어를 설치하지 않아도 되도록 해준다. GitLab Enterprise, GitHub, Bitbucket, GitLab 및 GitHub Enterprise와 같은 임시, 공용 및 개인용 Git 서비스와 잘 연결된다. 

AutoRABIT
AutoRABIT은 Salesforce 플랫폼용 맞춤형 솔루션을 제공하는 CI/CD 도구 중 하나이다.

대화식의 사용자 친화적인 인터페이스를 제공하며, 120개 이상의 메타데이터 유형을 지원한다. AutoRABIT은 사용자가 Salesforce 지향 도구를 시스템에 통합하는 데 필요한 노력을 피할 수 있도록 도와준다.

Strider
MongoDB를 데이터베이스로 사용하는 Strider는 JavaScript/NodeJS로 작성되었다. Ruby, NodeJS 및 Python을 포함하여 광범위한 언어를 지원한다.

사용자는 Strider를 Bitbucket, Heroku, GitHub Enterprise, GitLab, GitHub 및 Slack과 통합하여 알림을 받을 수 있다. Strider의 내장 플러그인으로 다양한 기능을 쉽게 확장하고, 사용자 정의할 수 있다.

파이프라인의 보안
CI/CD 파이프라인은 팀이 사람의 실수를 줄이고, 개발팀의 결과물을 운영하는 데 도움이 된다. 하지만, 악의적인 행위의 표적이 될 수도 있다. 따라서 CI/CD 파이프라인 보호를 고려할 필요가 있다.

암호 관리는 CI/CD(지속적인 통합 및 배포) 중요한 보안 방법이다. 암호는 암호, 토큰 및 개인 키와 같이 노출되어서는 안 되는 민감한 정보이다. 권한이 없는 개인이나 시스템이 파이프라인에 접속할 수 없도록, 안전한 위치에 암호가 저장되어야 한다. 이를 위한, 인기 있는 암호 관리 도구는 Hashicorp Vault, AWS Secrets Manager 및 Azure Key Vault 등이 있다.

다른 보안 방법은 개발 산출물 컨텐츠 자체를 암호화하는 것이다. AES-256과 같은 보안 암호화 알고리즘을 사용하고, 암호화 키를 정기적으로 교체하여, 시간이 지나도 컨텐츠가 안전하게 유지되도록 한다.

마무리
CI/CD를 통해 애플리케이션을 신속하고 효과적으로 배포할 수 있다. CI/CD는 여러 자동화된 테스트/배포 도구를 포함한다. 이 도구를 통해, 개발 배포 과정을 자동화할 수 있다. 다만, 어떤 기술이든 제대로 활용하기 전에 우리가 작업하는 방식, 절차, 습관의 변화는 필수적일 것이다.

레퍼런스

2023년 2월 5일 일요일

사물인터넷 데쉬보드 Thingsboard IoT 소개

이 글은 Thingsboard 사물인터넷 데쉬보드를 간단히 소개한다. 이 라이브러리를 이용하면, 손쉽게 아래와 같은 데쉬보드를 만들 수 있다. 이와 유사한 것들은 Grafana, Node-RED 등이 있다. 리액트 등 프론드엔드 테마는 여기를 참고한다.

씽스보드 화면 예시

설치
다음 설치 메뉴얼을 따라 씽스보드를 설정한다.
사용법
다음 명령을 이용해, 서비스를 실행한다.
netstat -tulpen |grep 80
sudo kill -9 $(sudo lsof -t -i:8080)
sudo service thingsboard start
service --status-all


그럼 서버가 실행되며, 이를 통해, 데쉬보드를 인터넷으로 서비스할 수 있다.

에러가 발생하면, 다음 명령으로 확인할 수 있다.
cat /var/log/thingsboard/thingsboard.log | grep ERROR

마무리
이와 같이, javascript node.js 기반 다양한 데쉬보드 프론트엔드 테마 오픈소스 라이브러리가 있으며, 이를 이용해, 복잡하고 동적인 데쉬보드를 손쉽게 개발할 수 있다. 이를 메터포트 등 그래픽 뷰어를 이용해 커스텀하면 디지털트윈과 같은 시스템을 손쉽게 개발할 수 있다.

레퍼런스

우분투 리눅스 user is not in the sudoers file 에러 해결 방법

이 글은 우분투 user is not in the sudoers file 에러 수정 방법을 간략히 정리한다. 

이 에러는 root 권한에서 다음과 같이 멍청한? 짓을 할 때 발생할 수 있다. 

sudo usermod -g docker $USER


이 짓을 설명하자면, 현재 루트 권한 사용자를 docker란 그룹으로 옮김으로써 스스로 sudo 그룹을 탈퇴시켜버려 모든 명령어를 실행할 수 없게 하란 뜻이다. 원래 입력하고자 하는 명령은 다음과 같은 것이었다.
sudo usermod -aG docker $USER

이런 유사한? 짓을 했다면, sudo 로 실행되는 명령 apt update, apt-get 을 사용하지 못하는 벌을 받게 되어 있다(아~ T.T). 

user is not in the sudoers file 

구글링하면, sudo 권한을 잃었는 데, usermod를 실행해 유저를 추가하라는 등 넌센스 답들이 넘쳐난다. 멘붕하지 말자. 다음을 잘 따라하면, 다시 멘탈을 되찾을 수 있다.

1. 우선, 우분투를 재부팅한다.
2. GNU GRUB 메뉴에서 Advanced options for Ubuntu 를 선택한다.

3. 다음 메뉴에서 recovery mode 로 표시된 항목 아무거나 선택한다.

4. Recovery Menu에서 다음 root 메뉴를 선택한다. 그럼 루트 권한으로 터미널이 실행될 것이다. 만약, "Press Enter for maintenance" 프롭프트가 보이면 엔터를 친다.

5. 루투 권한으로 실행된 터미널에서 아래 명령을 입력해, 파일 시스템과 마운트하고, sudo 그룹에 유저를 다시 추가해 준다.
mount -o rw,remount /
usermod -aG sudo <user name>

6. exit 명령을 입력하고, 보여진 Recovery Menu에서 resume메뉴를 선택해, 재부팅한다.

7. 다시 우분투 리눅스로 로그인하면, 다음과 같이 sudo 그룹 명령을 실행할 수 있다.

한번의 실수로 한시간을 날렸다(아 ~ㅆㅂ). 앞으로는 아무 생각없이 이런짓 하지 말자.

참고: group user 명령
groups
whoami
cat /etc/group | grep $USER
cat /etc/passwd | grep 'docker'
sudo gpasswd -a $USER docker
newgrp docker
exit

2022년 11월 13일 일요일

우분투 22.04, GTX 3090 기반 딥러닝 개발 환경 설치하기

이 글은 우분투 22.04, GTX 3090 기반 딥러닝 개발 환경 설치하는 순서를 간단히 요약한다.

  1. 우분투 22.04 설치
  2. 우분투 업데이트
  3. 디펜던시 패키지 설치
  4. 개발 도구 설치
  5. 파이썬 3 설치
  6. Git 설치
  7. NVIDIA 470 드라이버 설치
  8. CUDA 설치
  9. 머신러닝 환경 설치
상세한 내용은 레퍼런스를 참고하라. 

행운을 빈다.


레퍼런스

2022년 7월 8일 금요일

우분투 apt-get update 시 NO_PUBKEY 문제 해결 방법

이 글은 우분투 apt-get update 시 NO_PUBKEY 문제 해결 방법을 간략히 정리한다. 

가끔 다음과 같이 실행하면, NO_PUBKEY나 EXPKEYSIG 에러 발생하는 경우가 있다.
sudo apt-get update 

에러 화면

이 경우는 업데이트 저장 사이트가 변경되거나 등의 이유로 해당 키값이 적절히 등록되어 있지 않아 발생하는 것이다. 

아래 명령으로 각 키값을 등록한다.
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-key [NO_PUBKEY 에러 키값]

EXTKEYSIG 에러는 다음 명령으로 해당 키를 삭제한다. 
sudo apt-key del [EXTKEYSIGN 에러 키값]

그럼 다음과 같이 정상적으로 업데이트된다. 

레퍼런스

2022년 5월 3일 화요일

플러그인 개발을 위한 자바 스크립트와 C++ 연동 및 실행 방법

이 글은 자바스크립트(Javascript)와 C++ 연동 방법에 대한 내용을 간략히 공유한다. 가끔, 플러그인 등을 개발할 때, 외부 스크립트 코드와 연동해야할 경우가 있다. 이와 관련해, 필요한 내용이 무엇인지 알아본다.

자바스크립트 동작 방식은 다음과 같다.
자바스크립트 내부 구조
자바스크립트 실행 상태 예시

아래는 C++에서 자바스크립트를 호출하는 방법을 보여준다. 
#include "quickjspp.hpp"
#include <iostream>

class MyClass
{
public:
    MyClass() {}
    MyClass(std::vector<int>) {}

    double member_variable = 5.5;
    std::string member_function(const std::string& s) { return "Hello, " + s; }
};

void println(qjs::rest<std::string> args) {
    for (auto const & arg : args) std::cout << arg << " ";
    std::cout << "\n";
}

int main()
{
    qjs::Runtime runtime;
    qjs::Context context(runtime);
    try
    {
        // C++클래스 정의 
        auto& module = context.addModule("MyModule");
        module.function<&println>("println");
        module.class_<MyClass>("MyClass")
                .constructor<>()
                .constructor<std::vector<int>>("MyClassA")
                .fun<&MyClass::member_variable>("member_variable")
                .fun<&MyClass::member_function>("member_function");

        // 자바스크립트 모듈 임포트
        context.eval(R"xxx(
            import * as my from 'MyModule';
            globalThis.my = my;
        )xxx", "<import>", JS_EVAL_TYPE_MODULE);

        // 자바스크립트 코드 호출 및 실행.
        // C++에서 정의된 클래스를 자바스크립트에서 생성, 호출할 수 있다.
        context.eval(R"xxx(
            let v1 = new my.MyClass();
            v1.member_variable = 1;
            let v2 = new my.MyClassA([1,2,3]);
            function my_callback(str) {
              my.println("at callback:", v2.member_function(str));
            }
        )xxx");

        // callback
        auto cb = (std::function<void(const std::string&)>) context.eval("my_callback");
        cb("world");
    }
    catch(qjs::exception)
    {
        auto exc = context.getException();
        std::cerr << (std::string) exc << std::endl;
        if((bool) exc["stack"])
            std::cerr << (std::string) exc["stack"] << std::endl;
        return 1;
    }
}

이 코드 실행을 위해서는 quickjspp 라이브러리가 필요하다. 아래 링크를 참고해 설치하고, 실행하면 된다. 

레퍼런스

2022년 3월 10일 목요일

Ubuntu 리눅스 포트 죽이기

이 글은 Ubuntu 리눅스 포트 죽이는 방법을 간단히 정리한 것이다. 가끔 포트가 다른 프로그램에 의해 열려져 있어, 해당 포트를 사용하지  못할 때 사용하면 된다.

netstat -nap | grep :8080

sudo kill $(sudo lsof -t -i:8080)



2022년 2월 25일 금요일

리눅스 우분투 인터넷 서비스 포트 종료시키기

이 글은 리눅스 우분투 인터넷 서비스 포트 종료시키는 방법을 간략히 보여준다.

예를 들어, 3000번으로 열린 포트를 닫을려면, 터미널에서 다음 명령을 입력한다.

sudo kill -9 $(sudo lsof -t -i:3000)

2021년 9월 22일 수요일

우분투 리눅스 기반 프로그램 디버거 소개 및 사용법

이 글은 우분투 리눅스 기반 프로그램 디버거 소개 및 사용법을 공유한다. 개발하던 프로그램이 제대로 동작하지 않으면, 디버깅이 필요하다. 여기서는 리눅스에서 가장 많이 사용되는 디버거인 GDB, KDbg, Eclipse를 소개하고, 사용방법을 간단히 공유한다.

디버그란
디버그는 컴퓨터로 실행되는 프로그램에 오류가 있을 경우, 이 오류를 해결하는 방법 및 행위를 말한다. 최초의 컴퓨터 버그는 1947년 실제 컴퓨터 부품 내에 들어가 오동작을 일으킨 벌래였다.
최초의 컴퓨터 버그(1947)

디버그를 위해서는 다음과 같은 기본적인 기능이 필요하다. 여기서, 브레이크포인트(break point)는 디버깅 실행 중 멈추는 지점을 의미한다. 모든 디버거는 이와 같은 기본 기능을 지원한다.
  • 브레이크포인트 설정
  • 브레이크포인트까지 계속 실행
  • 명령행 한단계 실행
  • 함수 진입
  • 함수 빠져나오기
  • 현재 변수값 확인
GDB는 GNU 소프트웨어 기본 디버거이다. GDB는 유닉스 기반에서 개발되어, 리눅스에도 이식되어 있다. 설치를 위해 우분투 터미널에서 다음 명령을 입력한다.
sudo apt-get install gdb

설치 후, 실행파일이 있는 폴더에서 다음과 같이 명령을 입력한다.
gdb --args [프로그램명] [입력인자1] [입력인자2...]

그럼 다음과 같이 디버그가 실행된다. 이제, gdb 명령어를 이용해 디버깅을 하면 된다.

gdb 명령창이 실행되면, b main 명령으로 main 엔트리에 브레이크포인트를 설정한다. r 명령을 입력하면, 브레이크포인트까지 실행될 것이다. p argc 명령으로 argc 변수값을 확인해 본다. 이런식으로 디버깅을 할 수 있다.

이외, 주요 명령어는 다음과 같다. 
b main - Puts a breakpoint at the beginning of the program
b - Puts a breakpoint at the current line
b N - Puts a breakpoint at line N
b +N - Puts a breakpoint N lines down from the current line
b fn - Puts a breakpoint at the beginning of function "fn"
d N - Deletes breakpoint number N
info break - list breakpoints
r - Runs the program until a breakpoint or error
c - Continues running the program until the next breakpoint or error
f - Runs until the current function is finished. step out
s - Runs the next line of the program. step in
s N - Runs the next N lines of the program
n - Like s, but it does not step into functions. next
u N - Runs until you get N lines in front of the current line
p var - Prints the current value of the variable "var"
bt - Prints a stack trace
u - Goes up a level in the stack
d - Goes down a level in the stack
q - Quits gdb

KDBG는 GNU 소프트웨어 디버그용 무료 오픈소스 프로그램이다. KDE 아키텍처를 이용해 GUI(Graphic User Interface)를 지원한다.

사용을 위해서는 우분투 터미널에서 다음 명령을 입력한다. 소스를 다운로드 받고, 빌드 및 설치한다. 
sudo apt install extra-cmake-modules
git clone -b maint https://github.com/j6t/kdbg.git 
cd kdbg/
git tag -l
git checkout kdbg-3.0.1
cmake .
sudo make install

만약, CMake에서 에러가 발생하면, 해당 패키지를 apt-get 으로 설치한 후, 다시 시도하여 빌드한다. 빌드에 성공하면 다음과 같이 관련 모듈이 설치된다.

kdbg를 명령창에 입력하면, 다음과 같이 GUI가 표시된다. 소스와 실행파일 위치를 메뉴를 통해 선택하고 Run을 하면 디버깅을 할 수 있다. 브레이크 포인트는 편집창 왼쪽 줄 옆에 클릭하면된다. 변수는 Expression 창에서 그 값을 확인할 수 있다.

이클립스는 IBM의 웹스피어 스튜디오란 개발자 지원 도구에서 파생된 것으로, 엔진부분이 오픈소스로 공개된을 사용해 개발된 것이다. 현재는 이클립스 재단에서 통합 관리한다.


사용을 위해, 여기에서 프로그램을 다운받아 설치한다.
설치 후, 이클립스를 실행하고, 간단한 C++ 어플리케이션 프로젝트를 생성한다. 이름은 hello로 한다. 사용방법은 Visual Studio와 유사하게, 메뉴나 프로젝트 탐색창에서 Build, Run, Debug를 실행할 수 있도록 되어 있다.

디버깅을 위해, 프로젝트 탐색기의 프로젝트를 선택하고, cpp파일을 만든다. 해당 소스 파일 내에 hello world 출력 코드를 코딩하고, 빌드한다. 

#include <stdio.h>

int main()
{
char* szHello = "hello world! from stjohns";
printf(szHello);
return 0;
}

빌드가 성공하면, 실행 및 디버깅 환경 설정을 다음과 같이 실행한다. 

소스 편집기 창의 라인번호 왼쪽을 더블클릭해, 브레이크포인트를 만든 후 디버깅을 실행하면 다음과 같이 해당 지점에 실행이 멈추고, 변수값 등을 확인할 수 있다.
 
기존 프로젝트를 가져와서 디버깅을 하고 싶으면, File > import > C/C++ > Existing Code as Makefile Project 를 선택하고, 프로젝트의 Makefile이 있는 루트 폴더를 선택하면 된다. 그럼, 다음과 같이 프로젝트에서 생성된 실행파일을 디버깅할 수 있다. 아울러, 디버그 심벌이 있으면, 소스코드는 함께 표시된다. 참고로, 디버그 심벌이 제대로 연결되지 않으면, 해당 부분의 소스 라인은 디버그할 수 없다. 

pdb
python디버거이다. 소스코드를 디버깅할 수 있으며, 앞서 언급한 gdb와 유사한 명령어를 지원한다.
명령은 다음과 같다.
python -m pdb example.py

PDB를 사용하여 디버깅 모드로 진입 후, 다음 명령어를 사용해 디버깅하면 된다.
help: 도움말
next: 다음 문장 이동
print: 변수값 화면 표시
list: 소스코드 리스트 출력. 현재 위치 화살표 표시
where: 콜스택 출력
continue: 계속 실행. 다음 중단점에 멈추거나 중단점 없으면 끝까지 실행
step: Step Into. 함수 내부 진입
return: 현재 함수의 리턴 직전까지 실행
!변수명 = 값: 변수에 값 재설정

pdb 사용 예시

printf
오래전에 아는 선배가 제일 좋은 디버깅 도구로 추천한 것이 printf였다. 앞의 디버깅도구들은 심벌을 로딩하지 못하는 환경에서는 디버깅이 어렵다. 이 경우, 에러가 있을만한 코드를 printf로 표시해 나가면서 디버깅하면 된다.
printf은 가장 단순하면서도, 가장 확실하게 디버깅할 수 있는 도구이다.

참고

2021년 6월 8일 화요일

리눅스에서 VSCode, CMake와 GDB 개발환경 만들기

이 글은 리눅스에서 VSCode, CMake와 GDB 개발환경 만들기 방법을 간단히 정리한다.
리눅스에서 개발환경은 윈도우보다 좋지는 않다. 옛날에는 Eclipse 정도가 최신이었지만, 지금은 VSCode, PyCharm 등이 있어 비쥬얼한 개발환경 지원이 좋아졌다. 이 글은 우분투에서 VSCode에 대한 설치 및 사용 방법만 언급한다.

VS Code, CMake 및 GDB 설치 
VS Code 용 C ++ 확장을 설치한다. 확장보기 윈도우( Ctrl + Shift + X )에서 'c ++'를 검색하여 C / C ++ 확장을 설치할 수 있다.

g++ 컴파일러를 사용하여 Linux에서 소스 코드를 컴파일한다. GDB를 사용하여 디버깅한다. 이러한 도구는 Ubuntu에 기본적으로 설치되지 않으므로 설치해야한다. 터미널 창을 열고 다음 명령을 입력하라.
gcc -v
sudo apt-get update

다음 명령으로 GNU 컴파일러 도구와 GDB 디버거를 설치한다.
sudo apt-get install build-essential gdb

Hello World 만들기
다음과 같이 터미널 창에서 프로젝트 저장용 빈 폴더를 만든다. 
mkdir projects
cd projects
mkdir helloworld
cd helloworld
code .

이 code .명령은 "작업 공간"이 되는 현재 작업 폴더에서 VS Code를 실행한다. 작업 공간 의 폴더에 세 개의 파일을 만든다.

tasks.json (컴파일러 빌드 설정)
launch.json (디버거 설정)
c_cpp_properties.json (컴파일러 경로 및 IntelliSense 설정)
Hello World 소스 코드 파일

파일 탐색기 제목 표시 줄에서 새 파일을 선택하고 파일 이름을 helloworld.cpp로 한다. 다음 소스 코드를 붙여 넣는다.

#include <iostream>
#include <vector>
#include <string>

using namespace std;

int main()
{
    vector<string> msg {"Hello", "C++", "World", "from", "VS Code", "and the C++ extension!"};

    for (const string& word : msg)
    {
        cout << word << " ";
    }
    cout << endl;
}

이제 Ctrl + S 를 눌러 파일을 저장하라. 파일이 VS Code 사이드 바에 있는 파일 탐색기에 나열된다.

helloworld.cpp 빌드
주 메뉴에서 터미널 > 기본 빌드 작업 구성을 선택한다. g++ 빌드 환경을 설정한다. 그러면 폴더에 tasks.json파일이 생성된다. 새 tasks.json파일은 아래 JSON과 유사하다.
{
  "version": "2.0.0",
  "tasks": [
    {
      "type": "shell",
      "label": "g++ build active file",
      "command": "/usr/bin/g++",
      "args": ["-g", "${file}", "-o", "${fileDirname}/${fileBasenameNoExtension}"],
      "options": {
        "cwd": "/usr/bin"
      },
      "problemMatcher": ["$gcc"],
      "group": {
        "kind": "build",
        "isDefault": true
      }
    }
  ]
}

빌드 실행
빌드 작업을 실행하려면, Ctrl + Shift + B를 누르거나 터미널 기본 메뉴에서 빌드 작업을 실행한다.

helloworld.cpp 디버그
F5키를 l눌러 프로그램을 디버깅한다. 주 메뉴에서 실행 > 구성 추가 ... 를 선택한 다음 C ++ (GDB / LLDB) 를 선택한다. 미리 정의된 디버깅 구성에 대한 드롭 다운이 표시된다. g++ 빌드 및 디버그 활성 파일을 선택한다.

VS Code는 launch.json파일을 만들고 편집기에서 열고 'helloworld'를 빌드하고 실행한다.
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "g++ build and debug active file",
      "type": "cppdbg",
      "request": "launch",
      "program": "${fileDirname}/${fileBasenameNoExtension}",
      "args": [],
      "stopAtEntry": false,
      "cwd": "${workspaceFolder}",
      "environment": [],
      "externalConsole": false,
      "MIMode": "gdb",
      "setupCommands": [
        {
          "description": "Enable pretty-printing for gdb",
          "text": "-enable-pretty-printing",
          "ignoreFailures": true
        }
      ],
      "preLaunchTask": "g++ build active file",
      "miDebuggerPath": "/usr/bin/gdb"
    }
  ]
}

디버깅 세션 시작
F5 키를 누르 거나 주 메뉴에서 실행> 디버깅 시작을 선택한다. 디버깅 제어판에서 Step over 아이콘을 클릭하거나 누른다. 통합 터미널의 디버그 콘솔 탭에서 GDB가 출력하는 진단 정보 출력을 볼 수 있다.

레퍼런스

    2021년 4월 13일 화요일

    딥러닝 기반 실시간 객체 인식 YOLO v5 설치 및 사용기

    이 글은 우분투 20.04기반 YOLO v5 설치 및 사용 방법을 간단히 기술한다. YOLO v5는 PyTorch기반으로 동작하며, 기존 YOLO v3보다 객체 인식 속도 및 품질이 크게 개선되었다. 

    YOLO v5 성능

    2020년 불과 몇 개월 사이에 YOLO v4, YOLO v5 및 PP-YOLO라는 세 가지 주요 버전의 YOLO가 릴리즈되었다. YOLO v5는 YOLO 개발자가 개발한 것이 아니기에 사람들사이에서 논쟁거리가 있었다. 하지만, 결론적으로 여러가지 성능향상이 있었기에, 많은 사람들이 v5 버전을 사용하고 있다. 참고로, 욜로 버전 역사는 다음과 같다.
    • Joseph Redmon의 YOLO 버전
    버전 1: 통합 된 실시간 객체 감지(2016)
    버전 2: 더 좋고, 더 빠르고, 더 강력함(2017)
    버전 3: 점진적 개선(2018)
    • Joseph Redmon의 컴퓨터 비전 연구 중단 발표(2020.2)
    • Alexey Bochkovskiy의 YOLO 버전
    버전 4: Darknet 기반 Tesla V100에서 65FPS의 실시간 속도. COCO 데이터 세트에서 43.5%의 AP값 획득
    • Glenn Jocher의 YOLO 버전(2020.5)
    버전 5: Darknet의 포크가 아닌, PyTorch 구현. 모자이크 데이터 확대 및 자동 학습 경계 상자 앵커가 포함됨. Tesla P100에서, 이미지 당 0.007 초 객체 예측. 평균 140 FPS 주장.
    YOLO v4 성능

    개발환경 설치

    다음 순서로 개발 환경을 설치한다.

    이제, 텐서플로우나 케라스 예제 코드가 쥬피터 노트북에서 제대로 실행되면 환경 설정 성공한 것이다.
    import tensorflow as tf
    print(tf.reduce_sum(tf.random.normal([1000, 1000])))

    import tensorflow as tf
    print("# GPUs Available: ", len(tf.config.experimental.list_physical_devices('GPU')))


    YOLO v5 설치
    버전5는 원 개발자가 아닌 PyTorch로 YOLO개발했던 개발자가 맡아 진행한 것이다. 그러므로, PyTorch를 사용한다. 이에 대한 상세한 스토리는 여기를 참고한다. 다음을 참고해 욜로 버전5를 설치한다.
    이제 YOLO 설치 폴더에서 다음 명령을 입력한다.

    다음과 같이 객체 인식 처리가 되면 성공한 것이다.


    주요 코드 분석
    주요 코드를 분석해 본다. 동작은 다음과 같다. 
    import torch.distributed as dist #파이토치 임포트
    import torch.nn as nn
    ...
    from models.yolo import Model  # 욜로 모델 임포트

    def train(hyp, opt, device, tb_writer=None):
        # 설정
        if rank in [-1, 0]:
            opt.hyp = hyp  # 하이퍼 파라메터 추가
            run_id = torch.load(weights).get('wandb_id') if weights.endswith('.pt') and os.path.isfile(weights) else None
            if wandb_logger.wandb:
                weights, epochs, hyp = opt.weights, opt.epochs, opt.hyp  # WandbLogger might update weights, epochs if resuming

        nc = 1 if opt.single_cls else int(data_dict['nc'])  # 클래스 수
        names = ['item'] if opt.single_cls and len(data_dict['names']) != 1 else data_dict['names']  # class names

        # 모델 정의
        pretrained = weights.endswith('.pt')
        if pretrained:
            with torch_distributed_zero_first(rank):
                attempt_download(weights)  # download if not found locally
            ckpt = torch.load(weights, map_location=device)  # 체크 모델 로드
            model = Model(opt.cfg or ckpt['model'].yaml, ch=3, nc=nc, anchors=hyp.get('anchors')).to(device)  # create
            model.load_state_dict(state_dict, strict=False)  # 모델 로딩
        else:
            model = Model(opt.cfg, ch=3, nc=nc, anchors=hyp.get('anchors')).to(device)  # create
        train_path = data_dict['train']
        test_path = data_dict['val']

        # 최적화
        nbs = 64  # nominal batch size
        accumulate = max(round(nbs / total_batch_size), 1)  # accumulate loss before optimizing
        hyp['weight_decay'] *= total_batch_size * accumulate / nbs  # scale weight_decay

        pg0, pg1, pg2 = [], [], []  # optimizer parameter groups

        if opt.adam:  # 최적화 함수
            optimizer = optim.Adam(pg0, lr=hyp['lr0'], betas=(hyp['momentum'], 0.999))  # adjust beta1 to momentum
        else:
            optimizer = optim.SGD(pg0, lr=hyp['lr0'], momentum=hyp['momentum'], nesterov=True)

        if opt.linear_lr:
            lf = lambda x: (1 - x / (epochs - 1)) * (1.0 - hyp['lrf']) + hyp['lrf']  # linear
        else:
            lf = one_cycle(1, hyp['lrf'], epochs)  # cosine 1->hyp['lrf']
        scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lf)
        # plot_lr_scheduler(optimizer, scheduler, epochs)

        # EMA
        ema = ModelEMA(model) if rank in [-1, 0] else None

        start_epoch, best_fitness = 0, 0.0
        if pretrained:
            # Epochs
            start_epoch = ckpt['epoch'] + 1
            if opt.resume:
                assert start_epoch > 0, '%s training to %g epochs is finished, nothing to resume.' % (weights, epochs)

            del ckpt, state_dict

        # Image sizes
        imgsz, imgsz_test = [check_img_size(x, gs) for x in opt.img_size]  # verify imgsz are gs-multiples

        # 모델 파라메터
        hyp['box'] *= 3. / nl  # scale to layers
        hyp['cls'] *= nc / 80. * 3. / nl  # scale to classes and layers
        hyp['obj'] *= (imgsz / 640) ** 2 * 3. / nl  # scale to image size and layers
        hyp['label_smoothing'] = opt.label_smoothing
        model.nc = nc  # attach number of classes to model
        model.hyp = hyp  # attach hyperparameters to model
        model.gr = 1.0  # iou loss ratio (obj_loss = 1.0 or iou)
        model.class_weights = labels_to_class_weights(dataset.labels, nc).to(device) * nc  # attach class weights
        model.names = names

        for epoch in range(start_epoch, epochs):  # epoch 
            model.train()  # 모델 훈련

            for i, (imgs, targets, paths, _) in pbar:  # 배치 -------------------------------------------------------------
                ni = i + nb * epoch  # number integrated batches (since train start)
                imgs = imgs.to(device, non_blocking=True).float() / 255.0  # uint8 to float32, 0-255 to 0.0-1.0


    detect.py 주요 코드는 다음과 같다. 
    def detect(save_img=False):
        source, weights, view_img, save_txt, imgsz = opt.source, opt.weights, opt.view_img, opt.save_txt, opt.img_size
        save_img = not opt.nosave and not source.endswith('.txt')  # save inference images
        webcam = source.isnumeric() or source.endswith('.txt') or source.lower().startswith(
            ('rtsp://', 'rtmp://', 'http://', 'https://'))

        # 초기화
        device = select_device(opt.device)
        half = device.type != 'cpu'  # half precision only supported on CUDA

        # 모델 로드
        model = attempt_load(weights, map_location=device)  # load FP32 model
        stride = int(model.stride.max())  # model stride
        imgsz = check_img_size(imgsz, s=stride)  # check img_size

        # 클래스 이름 및 색상 
        names = model.module.names if hasattr(model, 'module') else model.names
        colors = [[random.randint(0, 255) for _ in range(3)] for _ in names]

        # 예측 실행
        if device.type != 'cpu':
            model(torch.zeros(1, 3, imgsz, imgsz).to(device).type_as(next(model.parameters())))  # run once
        t0 = time.time()
        for path, img, im0s, vid_cap in dataset:
            img = torch.from_numpy(img).to(device)
            img = img.half() if half else img.float()  # uint8 to fp16/32
            img /= 255.0  # 0 - 255 to 0.0 - 1.0
            if img.ndimension() == 3:
                img = img.unsqueeze(0)

            # Inference
            t1 = time_synchronized()
            pred = model(img, augment=opt.augment)[0]

            # Apply Classifier
            if classify:
                pred = apply_classifier(pred, modelc, img, im0s)  # 객체 인식 예측

            # 객체 탐지 프로세스
            for i, det in enumerate(pred):  # detections per image
                p = Path(p)  # to Path
                save_path = str(save_dir / p.name)  # img.jpg
                gn = torch.tensor(im0.shape)[[1, 0, 1, 0]]  # normalization gain whwh
                if len(det):
                    # Rescale boxes from img_size to im0 size
                    det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round()

                # Stream results
                if view_img:
                    cv2.imshow(str(p), im0)
                    cv2.waitKey(1)  # 1 millisecond

    if __name__ == '__main__':
        parser = argparse.ArgumentParser()  # 객체 탐지 예측 파라메터 정의
        parser.add_argument('--weights', nargs='+', type=str, default='yolov5s.pt', help='model.pt path(s)')
        parser.add_argument('--source', type=str, default='data/images', help='source')  # file/folder, 0 for webcam
        parser.add_argument('--img-size', type=int, default=640, help='inference size (pixels)')
        parser.add_argument('--conf-thres', type=float, default=0.25, help='object confidence threshold')
        parser.add_argument('--iou-thres', type=float, default=0.45, help='IOU threshold for NMS')
        parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
        parser.add_argument('--view-img', action='store_true', help='display results')
        parser.add_argument('--save-txt', action='store_true', help='save results to *.txt')
        parser.add_argument('--save-conf', action='store_true', help='save confidences in --save-txt labels')
        parser.add_argument('--nosave', action='store_true', help='do not save images/videos')
        parser.add_argument('--classes', nargs='+', type=int, help='filter by class: --class 0, or --class 0 2 3')
        parser.add_argument('--agnostic-nms', action='store_true', help='class-agnostic NMS')
        parser.add_argument('--augment', action='store_true', help='augmented inference')
        parser.add_argument('--update', action='store_true', help='update all models')
        parser.add_argument('--project', default='runs/detect', help='save results to project/name')
        parser.add_argument('--name', default='exp', help='save results to project/name')
        parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')
        opt = parser.parse_args()
        print(opt)
        check_requirements(exclude=('pycocotools', 'thop'))

        with torch.no_grad():
            if opt.update:  # update all models (to fix SourceChangeWarning)
                for opt.weights in ['yolov5s.pt', 'yolov5m.pt', 'yolov5l.pt', 'yolov5x.pt']:  # 가중치에 따른 예측
                    detect()
                    strip_optimizer(opt.weights)
            else:
                detect()

    마무리
    YOLO v5는 실행 속도 측면에서 분명한 이점이 있다. YOLO v5 모델은 작은 개체를 감지하는 데 더 나은 성능을 보이면서, 빠르게 실행된다. 다음은 다른 딥러닝 모델과 성능 차이를 나타낸다.