안경잡이개발자

728x90
반응형

  동영상 편집에서 색 보정 작업을 위해서 RGB 곡선(RGB Curve)은 굉장히 많이 사용되는 효과 중 하나입니다. RGB 각각의 패널에 대하여 컨트롤을 진행할 수 있습니다. (이때 보통은 조정 레이어를 먼저 추가한 뒤에, 그 위에 다음과 같이 RGB 곡선 효과를 입힙니다.)

 

 

  효과를 줄 때는 영상 스타일마다 다르지만, 보통 빨강과 녹색은 조금씩 줄이고, 파랑은 조금 키우는 방식을 많이 사용합니다. 그리고 영상이 전체적으로 어둡게 찍혔다면, 흰색 선을 위로 키워서 밝게 만들 수 있습니다.

 

 

  특히 역동적인 영상인 경우 파랑색(blue)가 더 두드러지게 하는 경우가 많습니다.

 

  또한 개인적으로 RGB 곡선을 제외하고도 Lumetri 색상(Color) 또한 많이 사용합니다.

728x90
반응형

728x90
반응형

  동일한 입력에 대하여 매번 모델이 다른 결과를 뱉는다면, 아래의 유형에 해당되는지 체크한다.

 

유형 1. torch 설정

 

  기본적인 torch 설정을 바꿔줄 수 있다.

 

torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False

 

유형 2. 모델의 랜덤성

 

  StyleGAN 등의 모델에서는 랜덤성(randomness)이 포함되어 있다. 이러한 레이어를 identity() 레이어로 치환하는 방법으로 랜덤성을 제거할 수 있다.

 

class Identity(nn.Module):
    def __init__(self):
        super(Identity, self).__init__()
        
    def forward(self, x):
        return x

 

유형 3. eval() 모드

 

  ResNet 등의 모델에서는 Batch Normalization을 사용한다. 이러한 레이어에서는 train() 모드와 eval() 모드에서의 파라미터가 다르게 적용된다. 그래서 test time에 항상 eval() 모드를 사용하는 방식으로 랜덤성을 제거할 수 있다.

728x90
반응형

728x90
반응형

  Jupyter Notebook에서는 다음과 같이 코드를 불러올 수 있다. 하지만 아래와 같이 불러온 라이브러리의 코드가 수정된다면 어떻게 Jupyter 상에서 반영할 수 있을까? Jupyter Notebook 상에서는 곧바로 반영이 어렵기 때문에 reload()를 수행하여 문제를 해결할 수 있다.

 

 

  가장 간편한 방법은 다음과 같이 autoreload를 사용하는 것이다. 이렇게 하면 Jupyter상에서 불러오는 코드에 대하여 자동으로 reload를 진행한다.

 

%load_ext autoreload
%autoreload 2

 

  사용 예시는 다음과 같다.

 

728x90
반응형

728x90
반응형

  흔히 다른 사람의 저장소를 Fork하기 위해서는 [Fork] 버튼을 눌러 자신의 저장소로 옮긴다. (이때 부모 저장소가 private이라면, 당연히 자신이 Fork한 저장소 또한 private 저장소 형태로 복제가 완료된다.)

 

 

  단, 자신의 저장소에서 작업을 하다 보면 부모 저장소의 최신 코드와 conflict하는 문제가 생길 수 있다. 예를 들어 다음과 같이 최신 5개의 커밋(commit)이 부모 저장소에 발생했는데, 나의 Fork한 저장소에서는 없기 때문에 코드상의 차이가 발생한다고 알려준다.

 

 

  이럴 때는 일단 git clone을 받아서 자신의 로컬로 코드를 옮겨오자.

 

 

  이후에 upstream을 추가하고 fetch를 진행한다.

 

 

  그리고 merge 명령어를 사용하여 코드를 merge한다.

 

 

  이제 부모 저장소에서 최근에 업데이트된 코드가 자신의 repository에도 적용된다. (push를 해야 할 수도 있다.)

 

※ 참고 ※

 

  만약 Public 저장소를 포크(fork)했는데, 부모 저장소의 코드가 새롭게 갱신되는 경우 다음과 같은 메시지가 나올 수 있다. 이때는 [Fetch upstream]을 눌러 [Fetch and merge] 버튼을 눌러 최신 상태로 갱신할 수 있다.

 

728x90
반응형

728x90
반응형

  ※ 본 포스트는 2021년 기준으로 작성되었습니다.

 

  경기도 청년기본소득을 신청하기 위해서는 [잡아바] 사이트에 방문하면 된다. 기본적인 준비물주민등록초본이다. 이것도 PDF 파일로 온라인 제출하면 되므로, 온라인으로 간단히 신청할 수 있다. 주민등록초본을 PDF로 뽑는 방법은 다음의 링크에서 확인하면 된다.

 

  ▶ 주민등록초본 PDF 파일 얻는 방법: ndb796.tistory.com/515

 

주민등록등본(초본) 인터넷으로 발급받아 출력하는 방법

우리는 다양한 상황에서 주민등록등본이 필요합니다. 참고로 주민등록등본(초본)을 인터넷으로 발급받을 때는 무료입니다. 또한 인터넷으로는 즉시 발급받아 출력할 수 있기 때문에 굉장히

ndb796.tistory.com

 

  주민등록초본 PDF 파일이 준비되었으면 잡아바 사이트에 방문하여 청년 기본 소득을 신청하자.

 

  ▶ 잡아바 링크: apply.jobaba.net/

 

  여기에 방문해서 신청서를 작성하면 된다. 경기도 청년기본소득 페이지에 방문한 뒤에 [신청하러 가기] 버튼을 누르면 신청하러 갈 수 있다.

 

 

  확인해 보면 다음과 같이 만 24세 청년에게 100만 원을 지원해 준다고 한다. 자신이 조건에 부합하는지 확인한다. 필자의 경우 만 24세에 해당하며 경기도에 10년이 훌쩍 넘는 기간 동안 거주했으므로, 조건에 부합한다.

 

 

  [신청하기] 버튼을 누르면 기본적으로 회원가입을 요구한다. 통합접수시스템에서 회원가입을 진행하면 된다.

 

 

  회원가입 및 로그인이 완료되면 다음과 같이 [신청하기] 페이지에서 자가진단표를 작성할 수 있다. 앞서 언급했듯이 필자는 경기도에 거주한 기간이 10년은 훌쩍 넘었기 때문에 신청 요건을 만족한다. 자가진단표를 잘 읽고, 자신에게 해당하는 내용에 맞게 체크하면 된다.

 

 

  이후에 약관에 동의하면 된다. 이후에 구체적인 신청서를 작성하면 되는데, 2021년에는 한 번에 100만 원을 일괄적으로 지급한다고 한다. 그래서 굉장히 편리한 것 같다.

 

 

  이어서 [첨부파일] 탭에서 자신의 주민등록초본 PDF 파일을 업로드하면 된다.

 

 

  결과적으로 신청이 완료되면 [신청현황] 페이지에서 다음과 같은 화면을 확인할 수 있다.

 

728x90
반응형

728x90
반응형

  프린터기를 이용하고 있을 때 삭제 중이나 스풀링 상태에서 계속 멈춰있는 경우가 있다. 일반적으로 출력하고자 하는 문서의 용량이 클 때 종종 확인할 수 있는 문제 상황이다. 문서를 [취소] 하더라도 계속해서 이 상태에 머물러 있다면 굉장히 답답하게 느껴질 수 있다.

 

 

  이런 문제를 해결하기 위해서 가장 먼저 컴퓨터와 프린터기를 재부팅해보자. 만약에 그렇게 해도 해결이 안 된다면 다음과 같이 해결해보자. 윈도우 키 + R을 눌러서 [실행] 창을 열어 다음과 같이 services.msc를 입력한다.

 

 

  이후에 Print Spooler의 서비스를 [중지]한다.

 

 

  이후에 Windows 10 운영체제를 기준으로 다음의 경로에 가본다.

 

C:\Windows\System32\spool\PRINTERS

 

  여기에서 스풀링(spooling) 파일을 확인할 수 있는데, 이 파일들을 모두 제거하면 강제로 작업을 취소할 수 있다. 또한 이 파일들도 제거가 안 될 수 있다. 구글 크롬이나 문서 PDF 프로그램이 열려 있는 경우에 그럴 수 있는데, 이 경우에는 컴퓨터를 재부팅 하는 것이 좋다.

 

 

  결과적으로 모든 .SPL 확장자를 제거한 뒤에 다시 Print Spooler 서비스(service)[시작]하면 된다.

 

728x90
반응형

728x90
반응형

  구글 코랩(Google Colab)무료 GPU를 지원하는 구글의 개발환경 서비스다. 개인적으로는 딥러닝 테스트 코드를 돌려보거나 MNIST나 CIFAR-10과 같이 간단한 데이터셋을 학습할 때 효과적으로 사용할 수 있다고 생각한다. (물론 ImageNet과 같은 large-scale 데이터셋을 활용할 정도로 자원이 충분하지는 않다.) 구글 코랩에서는 깃허브(GitHub)와 쉽게 연동할 수 있는데, 깃허브에 저장된 노트북(notebook) 파일을 곧장 불러올 수 있다.

 

  1. 깃허브에 저장된 노트북 파일 불러오기

 

  깃허브에 저장된 노트북 파일을 불러오는 것은 간단하다. [노트 열기]를 누른 뒤에 [GitHub] 탭으로 이동한 뒤에 사용자를 입력한다. 이때 [비공개 저장소 포함] 버튼을 클릭하면 비공개(private) 저장소에 저장된 노트북 파일을 불러와 사용할 수 있다.

 

 

  개인적으로 간단한 소스코드는 노트북을 이용하는 경우가 많아서, 이것은 굉장히 유용하다. 구글 코랩 노트북 파일을 내보내기 할 때에도 비공개(private) 저장소로 한 번에 내보낼 수 있다. 또 다른 방법으로는 .ipynb 파일을 저장한 뒤에 GitHub에 업로드하는 방식을 사용할 수도 있다. 아무튼 Google Colab을 이용하면 GitHub과 하나의 .ipynb 파일을 이용해 연동을 수행하기에는 매우 편리하다고 생각한다.

 

  2. git clone 명령을 이용하여 Google Colab 내부로 소스코드 불러오기

 

  단순히 하나의 주피터 노트북(notebook) 파일만 이용한다면 앞서 언급한 방법을 사용할 수 있다. 하지만 특정한 저장소(repository)에 포함된 모든 소스코드를 Google Colab으로 불러와 사용하고 싶다면, git clone 명령어를 사용하는 것이 효과적이다. 이때 public repository라면 단순히 git clone 명령어를 사용할 수 있다. 하지만 private repository라면 SSH 키를 생성하고 깃허브(GitHub) 사이트에 등록할 필요가 있다.

 

  ▶ SSH를 이용해 깃허브(GitHub) 계정에 접근하는 방법: ndb796.tistory.com/561

 

SSH Key를 이용해 GitHub 계정의 저장소(repository) 코드에 접근하기

기본적으로 외부에 공개되면 안 되는 중요한 코드는 깃허브(GitHub)의 private repository에 저장하는 것이 일반적이다. 당연히 오픈소스 형태로 외부에 공개된 public repository라면 git clone 명령어를 이

ndb796.tistory.com

 

  위 포스트에 적힌 방법에 따라서 SSH 개인키와 공개키를 생성하여, 공개키를 깃허브(GitHub)에 등록하도록 하자. SSH 키가 등록된 이후에는 Google Colab 상에서 다음과 같은 코드를 실행하면 된다.

 

from google.colab import files

# 개인키 업로드
uploaded = files.upload()

# .ssh 폴더 생성 및 개인키 넣기
!mkdir -p ~/.ssh/
!mv id_rsa ~/.ssh/id_rsa
!chmod 400 ~/.ssh/id_rsa

# 알려진 호스트(known hosts)에 GitHub 호스트 주소 넣기
!ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts
!ssh -T git@github.com

# private repository에서 소스코드 다운로드
!git clone git@github.com:ndb796/Watermarking-Deepfakes

 

  실제 Google Colab 상에서는 다음과 같이 실행할 수 있다. 매우 간단하게 깃허브(GitHub) 내부의 private repository에서 코드를 가져온 것을 확인할 수 있다.

 

728x90
반응형

728x90
반응형

  기본적으로 외부에 공개되면 안 되는 중요한 코드는 깃허브(GitHub) private repository에 저장하는 것이 일반적이다. 당연히 오픈소스 형태로 외부에 공개된 public repository라면 git clone 명령어를 이용해 전체 소스코드를 가져올 수 있지만, private repository에 저장된 소스코드는 가져오기 어렵다. private repository의 경우 SSH key를 이용해 소스코드를 가져오는 것이 일반적인데, 당연히 깃허브(GitHub) 내에서 SSH 키(key) 설정을 진행해야 한다.

 

  구체적인 과정은 ① SSH 개인키와 공개키를 생성한 뒤에, ② 내 GitHub에 SSH 공개키를 등록하는 것이다. 그러면 나중에 SSH 개인키를 이용해 내 GitHub에 접근할 수 있다. 리눅스에서 SSH 키를 생성하는 방법은 간단하다. ssh-keygen 명령어를 입력하면 된다.

 

 

  이제 리눅스에서 자신의 홈 디렉터리(home directory)에 다음과 같이 개인키와 공개키가 생성된다.

 

 

  여기에서 .pub 확장자가 붙은 것이 공개키이므로, 이 파일의 내용을 복사하여 GitHub에 넣어주면 된다. 이때 개인키가 유출되지 않도록 조심해야 한다.

 

 

  결과적으로 이러한 공개키(public key)의 내용을 그대로 특정한 저장소(repository)의 keys 설정 페이지에 넣어주면 된다. 다음과 같이 [Key] 탭에 붙여넣기 하여 [Add key] 버튼을 누르면 등록된다. 참고로 특정한 private repository의 keys 설정 페이지는 다음의 경로에서 확인할 수 있다.

 

https://github.com/{사용자 이름}/{저장소 이름}/settings/keys

 

 

  그러면 결과적으로 다음과 같이 키가 등록된 것을 알 수 있다.

 

 

  이제 특정 리눅스 컴퓨터에서 GitHub에 접속하여 private repository의 코드를 받아오고자 한다면 환경 설정 목적으로 다음과 같은 명령어를 입력할 수 있다. 다음과 같이 해당 SSH 키를 이용해 접속할 수 있도록 등록하는 것이다.

 

ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts
ssh -T git@github.com

 

  이후에 다음과 같이 git clone 명령어를 이용하여 소스코드를 받아오면 된다.

 

git clone git@github.com:{사용자 이름}/{저장소 이름}

 

  필자의 경우 private repository인 Watermarking-Deepfakes라는 이름의 저장소에 있는 코드를 받아왔다.

 

728x90
반응형

728x90
반응형

  믹싱(mixing) 과정에서 굉장히 많이 사용되는 도구로는 컴프레서(compressor)가 있다. 일반적으로 압축기라고 불리기도 하는 이 도구는, 보컬 믹싱이나 강의 영상 믹싱 과정에서 매우 많이 사용된다. 흔히 보컬에서는 컴프레서를 이용해 큰 목소리로 내는 소리와 작은 목소리로 내는 소리의 차이가 덜 나도록 만드는 것이 일반적이다. 당연히 듣는 입장에서 편하게 노래를 들을 수 있을 것이다. 더불어 컴프레서를 사용하여 threshold를 조절하면 체감상 단단한 목소리가 나는 것처럼 느껴진다. 그래서 목소리가 더 명료하게 만들어진다.

 

 

  [효과] 탭으로 이동한 뒤에 멀티밴드 압축기(Multi-band Compressor)를 선택한다.

 

 

  이후에 [효과 컨트롤] 탭으로 이동하여 [편집]을 열어 구체적인 설정을 진행할 수 있다.

 

 

  기본적으로 많이 사용되는 설정은 미리 준비되어 있다. 예를 들어 내가 강의 영상을 제작할 때 많이 사용하는 사전 설정으로는 [거친 음 제거], [저음 향상], [팝 마스터]가 있다. 강의 내용이 너무 졸리다 싶으면 고음을 강조하기 위해 [고음 향상]을 선택하는 경우도 있다. 직접 다양한 설정을 선택해 본 뒤에 가장 마음에 드는 적절한 설정을 선택하면 된다.

 

 

  또한 추가적으로 노이즈 제거(Denoiser)를 적용하는 것도 좋다. 강의를 찍다 보면 다양한 노이즈(noise)가 끼게 되는 경우가 많기 때문이다. 다음과 같이 [효과] 탭에서 [노이즈 제거]를 선택한다.

 

 

  이후에 필자의 경우 5%에서 10% 정도의 강도를 적용하는 경우가 많다. 너무 강도를 높이면 많은 양의 노이즈가 목소리와 함께 제거될 수 있기 때문이다.

 

 

  참고로 이러한 오디오 관련 기능을 적용하는 경우 인코딩(encoding) 속도가 현저하게 느려질 수 있다.

 

 

  (+추가) 참고로 영상 내 다수의 클립들의 오디오의 크기가 서로 다를 수 있다. 이럴 때는 여러 개의 클립에 대하여 표준화를 진행할 수 있는데, 영문 버전 어도비 프리미어에서는 Normalization이라는 용어로 번역되어 있다. 방법은 매우 간단하다. 여러 개의 오디오를 묶은 뒤에 [오디오 게인]을 누른다.

 

 

  이후에 [모든 최고점을 다음으로 표준화] 버튼을 눌러 표준화를 진행할 수 있다. 이를 이용하면, 상대적으로 소리가 작은 클립은 소리가 커지고, 상대적으로 소리가 큰 클립은 소리가 작아진다. 다만 길이가 짧은 클립 중에서 과도하게 목소리가 작아지거나 커지는 경우가 있다.

 

  그래서 필자의 경우 Normalization을 이용하는 것보다는 전체적으로 오디오를 확인한 뒤에, 소리가 작은 클립들은 키우는 방식으로 수동으로 조절하는 것이 더 낫다고 판단하고 있다.

 

 

  혹은 영상에서 오디오만 따로 추출(extract)하여, 다른 편집 앱을 이용해 오디오를 처리한 뒤에 다시 삽입할 수도 있다. 이를 위해서는 가장 먼저 [파일] - [내보내기] - [미디어]를 확인한다.

 

 

  이후에 [AAC 오디오]를 선택하고 [오디오 내보내기]에 정상적으로 체크가 되어있는지 확인한다.

 

728x90
반응형

728x90
반응형

  아래 코드는 PyTorch에서 ImageFolder와 유사한 기능을 직접 구현해야 할 때 사용할 수 있는 코드다. ImageFolder는 기본적으로 (이미지, 레이블) 형태로 특정한 이미지 데이터를 가져올 수 있다. 이때 이미지 대신에 numpy 형태로 가져오거나 원하는 형식의 확장자를 사용하도록 하려면, 이러한 클래스를 직접 작성해 사용할 수 있을 것이다.

 

import torch.utils.data as data

from PIL import Image
import os
import os.path

IMG_EXTENSIONS = [
    '.jpg', '.JPG', '.jpeg', '.JPEG',
    '.png', '.PNG', '.ppm', '.PPM', '.bmp', '.BMP',
]


def is_image_file(filename):
    return any(filename.endswith(extension) for extension in IMG_EXTENSIONS)


def find_classes(dir):
    classes = [d for d in os.listdir(dir) if os.path.isdir(os.path.join(dir, d))]
    classes.sort()
    class_to_idx = {classes[i]: i for i in range(len(classes))}
    return classes, class_to_idx


def make_dataset(dir, class_to_idx):
    images = []
    dir = os.path.expanduser(dir)
    for target in sorted(os.listdir(dir)):
        d = os.path.join(dir, target)
        if not os.path.isdir(d):
            continue

        for root, _, fnames in sorted(os.walk(d)):
            for fname in sorted(fnames):
                if is_image_file(fname):
                    path = os.path.join(root, fname)
                    item = (path, class_to_idx[target])
                    images.append(item)

    return images


def pil_loader(path):
    # open path as file to avoid ResourceWarning (https://github.com/python-pillow/Pillow/issues/835)
    with open(path, 'rb') as f:
        with Image.open(f) as img:
            return img.convert('RGB')


def accimage_loader(path):
    import accimage
    try:
        return accimage.Image(path)
    except IOError:
        # Potentially a decoding problem, fall back to PIL.Image
        return pil_loader(path)


def default_loader(path):
    from torchvision import get_image_backend
    if get_image_backend() == 'accimage':
        return accimage_loader(path)
    else:
        return pil_loader(path)


class ImageFolder(data.Dataset):
    """A generic data loader where the images are arranged in this way: ::

        root/dog/xxx.png
        root/dog/xxy.png
        root/dog/xxz.png

        root/cat/123.png
        root/cat/nsdf3.png
        root/cat/asd932_.png

    Args:
        root (string): Root directory path.
        transform (callable, optional): A function/transform that  takes in an PIL image
            and returns a transformed version. E.g, ``transforms.RandomCrop``
        target_transform (callable, optional): A function/transform that takes in the
            target and transforms it.
        loader (callable, optional): A function to load an image given its path.

     Attributes:
        classes (list): List of the class names.
        class_to_idx (dict): Dict with items (class_name, class_index).
        imgs (list): List of (image path, class_index) tuples
    """

    def __init__(self, root, transform=None, target_transform=None,
                 loader=default_loader):
        classes, class_to_idx = find_classes(root)
        imgs = make_dataset(root, class_to_idx)
        if len(imgs) == 0:
            raise(RuntimeError("Found 0 images in subfolders of: " + root + "\n"
                               "Supported image extensions are: " + ",".join(IMG_EXTENSIONS)))

        self.root = root
        self.imgs = imgs
        self.classes = classes
        self.class_to_idx = class_to_idx
        self.transform = transform
        self.target_transform = target_transform
        self.loader = loader

    def __getitem__(self, index):
        """
        Args:
            index (int): Index

        Returns:
            tuple: (image, target) where target is class_index of the target class.
        """
        path, target = self.imgs[index]
        img = self.loader(path)
        if self.transform is not None:
            img = self.transform(img)
        if self.target_transform is not None:
            target = self.target_transform(target)

        return img, target


    def __len__(self):
        return len(self.imgs)

 

  ▶ 출처: chsasank.github.io/vision/_modules/torchvision/datasets/folder.html

 

728x90
반응형