https://fpcv.cs.columbia.edu/ 

https://partnerhelp.netflixstudios.com/hc/ko-kr/articles/360060570713--%EB%8D%B0%EB%93%9C-%ED%94%BD%EC%85%80-%EC%A0%95%EC%9D%98-%EC%88%98%EC%A0%95-%EB%B0%A9%EB%B2%95-%EB%B0%8F-%EB%B0%A9%EC%A7%80-%EB%8C%80%EC%B1%85

 

MMdetection을 사용하기 위한 초기 환경설정

 

1. 상환경 구

conda create -n mmdetection python=3.11 # ( 3.7 이상 버전이면 ok)

 

2. Pytorch 설

conda install pytorch torchvision torchaudio pytorch-cuda=11.7 -c pytorch -c nvidia

 

3. MMCV-Full 설


자신이 설한 cuda 버전으로 설합니다. 우리는 위에 11.7 했으므로 11.7

pip install mmcv==2.0.0 -f https://download.openmmlab.com/mmcv/dist/cu117/torch2.0/index.html

가이드 주소 : https://mmcv.readthedocs.io/en/latest/get_started/installation.html#install-mmcv

 

 

4. Github 에서 mmdetection zip 다로드 후 파이참 세팅 및 위에서 만상환경으로 인터프린 트 설정

https://github.com/open-mmlab/mmdetection/archive/refs/heads/main.zip

or 

git clone https://github.com/open-mmlab/mmdetection.git

 

5. install MMDetection (수동 설치)   / cd명령어로 저장한 파일로 경로이동

pip install -r requirements/build.txt

pip install -v -e .

pip mmdet # 설치가 잘 됐는지 확인사살

'ComputerVision > [ObjectDetection]' 카테고리의 다른 글

[ObjectDetection] Keypoint 탐지  (0) 2023.08.01

keypoint가 라벨링된 json 파일(train)을 이용하여 이미지에 적용하여 학습하여

나머지 이미지(test)로 keypoint 찾기

 

# keypointrcnn_resnet50_fpn 모델 이용한 Keypoint 실습

PyTorch 제공하는 Object detection reference training scripts 다운로드

 - 다운로드 사이트  https://github.com/pytorch/vision/tree/main/references/detection 

 

 

 

Customdataset.py

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까지 저장

'ComputerVision > [ObjectDetection]' 카테고리의 다른 글

MMdetection 설치 A-Z  (0) 2023.08.03

알고리즘은 컴퓨터 전 및 이미지 처리에서 주요한 역할을 합니다.

 

 

ORB (Oriented FAST and Rotated BRIEF):

 

 

1. FAST(FeaturesfromAcceleratedSegmentTest)기와BRIEF(BinaryRobustIndependent Elementary Features) 자의 합입니다.

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()

 

 

 

 

알고리즘은 컴퓨터 전 및 이미지 처리에서 주요한 역할을 합니다.

SIFT (Scale-Invariant Feature Transform):

 

1. 크기 및 회전에 변한 특설명 알고리즘입니다.

2. 다양한 크기와 회전에서 특점을 하고,점 주변의 지역 특을 기합니다.

3. 점 간의 일기 위해 특점 기자를 사합니다.
4. SIFT
우 강력한 특및 일알고리즘으로 알져 있습니다.

 

 

import cv2

# 동영상 파읽 열기
cap = cv2.VideoCapture("MS/CV/0612 객체탐지,추적/data/slow_traffic_small.mp4")

# sift 객체 생성
sift = cv2.SIFT_create()

while True:
    # 프레임 읽기
    ret, frame = cap.read()
    if not ret:
        break

    # 그레이스케일로 변환
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # 특징점 검출
    keypoints, descriptors = sift.detectAndCompute(gray, None)

    # 특징점 그리기
    frame = cv2.drawKeypoints(frame, keypoints, None,
                              flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

    # 화면에 출력
    cv2.imshow("frame", frame)

    # 'q'를 누르면 종료
    if cv2.waitKey(60) == ord('q'):
        break

# 해제
cap.release()
cv2.destroyAllWindows()

 

 

개수제한 >>>

 

1. 이미지 대비 밝기 일정이상의 차이가 있을때만 보이게 하면서

2. 포인터를 수를 일정 수로 제한해서 포인터의 개수를 특정 객체에만 나타나게 설정

 

import cv2

cap = cv2.VideoCapture("MS/CV/0612 객체탐지,추적/data/slow_traffic_small.mp4")

# SIFT 객체 생성
sift = cv2.SIFT_create(contrastThreshold=0.02)   # 이미지 대비의 밝기 차이

# 특지점 개수 제한 설정
max_keypoints = 100  # 100개의 특징점만 검출

while True:
    # 프레임 읽기
    ret, frame = cap.read()
    if not ret:
        break

    # 그레이스케일로 변환
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # 특징점 검출
    keypoints, descriptors = sift.detectAndCompute(gray, None)

    # 특징점 제한
    if len(keypoints) > max_keypoints:
        keypoints = sorted(keypoints, key=lambda x: -
                           x.response)[:max_keypoints]


    """
    keypoints = sorted(keypoints, key=lambda x: -x.response): 
    keypoints 리스트를 x.response를 기준으로 내림차순으로 정렬
    이는 키포인트의 response 값을 기준으로 정렬하겠다는 의미
    response 값이 큰 키포인트일수록 우선순위를 가지게 됩니다

    [:max_keypoints]: 정렬된 keypoints 리스트에서 처음부터 max_keypoints까지의 키포인트만 선택 
    이를 통해 최대 키포인트 수를 제한합니다.
    """
    
    
    # 특징점 그리기
    frame = cv2.drawKeypoints(frame, keypoints, None,
                              flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

    # 프레임 출력
    cv2.imshow("frame", frame)

    # 'q'를 누르면 종료
    if cv2.waitKey(60) == ord('q'):
        break

# 해제
cap.release()
cv2.destroyAllWindows()

 

 

# 객체 추적 알고리즘 소

움직이는 객체식별하고, 그 위적하는 방법을 의미합니다. 이를 해 동상이나 실시간 스트리데 이터에서 객체움직지하고, 객체적하는데 사됩니다. 다음은 일적으로 사되는 몇 가지 객 체 추적 알고리즘입니다.

 

점 기반 추(Feature-based Tracking):

  1. 점기반 추적은객 체의 특점을 하고, 이를 기으로 객체적합니다.
  2. SIFT (Scale-Invariant Feature Transform), SURF (Speeded-Up Robust Features), ORB (Oriented FAST and Rotated BRIEF) 등의 알고리즘을 사하여 특점을 합니다.
  3. 이 특점들은 다음 프에서 아지며, 객체움직적합니다.
  4. 점의 일도와 거리를 기으로 가장 좋은는 알고리즘을 사합니다.

 

# 점 기반 추

점 기반 추(Feature-based Tracking)에서 일적으로 르는 서는 다음과 같습니다

기화 :
1.
이미지로 변합니다.
2. Shi-Tomasi
기 또는 다방법을 사하여 적 지점을합니다.

3. 적할 특점의 기위를 저장합니다.

 

 

import cv2
import numpy as np

# 동영상 파일 읽기
cap = cv2.VideoCapture("MS/CV/0612 객체탐지,추적/data/slow_traffic_small.mp4")

# 코너 검출기 파라미터 설정
feature_params = dict(maxCorners=100, qualityLevel=0.3,
                      minDistance=7, blockSize=7)
"""
maxCorners: 검출할 최대 특징점 수를 지정. 여기서는 최대 100개의 특징점을 검출하도록 설정
qualityLevel: 특징점으로 인정되기 위한 품질 기준. 0과 1 사이의 값,높을수록 특징점으로 인정되기 위한 조건이 엄격
minDistance: 검출된 특징점들 간의 최소 거리를 나타냄 이 거리보다 가까운 특징점은 제거
blockSize: 특징점 검출 시 사용되는 이웃 픽셀 블록의 크기. 특징점 검출 알고리즘에서 사용되는 계산에 영향을 미치는 요소
"""


# 광학 흐름 파라미터 설정
lk_params = dict(winSize=(30, 30), maxLevel=2, criteria=( # 알고리즘 중지기준
    cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))  # eps정확도가 criteria[0]보다 작을때까지 or count는 반복회수가 criteria[1]보다 작을때 까지 추적
"""
winSize=(30, 30): 이동 벡터를 추정하기 위해 사용되는 윈도우(window)의 크기
윈도우 크기는 광학 흐름 알고리즘에서 이웃 픽셀들 간의 관계를 분석하는 데 사용되는 영역의 크기를 조절 

maxLevel=2: 피라미드 계층의 수
광학 흐름 알고리즘은 다단계의 피라미드 구조를 사용하여 이미지의 해상도를 다양하게 처리


"""
    

# 첫 프레임 읽기
ret, next_frame = cap.read()
prev_gray = cv2.cvtColor(next_frame, cv2.COLOR_BGR2GRAY)

# 초기 추적 지점 선택
prev_corners = cv2.goodFeaturesToTrack(prev_gray, mask=None, **feature_params)
prev_points = prev_corners.squeeze()

# 추적 결과를 표시하기위한 색상 설정
color = (0, 200, 0)

while True:
    # 다음 프레임 읽기
    ret, next_frame = cap.read()
    if not ret:
        break

    # 현재 프레임 변환
    gray = cv2.cvtColor(next_frame, cv2.COLOR_BGR2GRAY)

    # 옵티컬 플로우 계산
    next_points, status, err = cv2.calcOpticalFlowPyrLK(
        prev_gray, gray, prev_points, None, **lk_params)

    # 추적 결과를 좋은 포인트로 필터링
    for i, (prev_point, next_point) in enumerate(zip(prev_points, next_points)):
        x_prev, y_prev = prev_point.astype(np.int32)
        x_next, y_next = next_point.astype(np.int32)

        # cv2.line(prev_frame, (x_prev, y_prev), (x_next, y_next), color, 2)
        cv2.circle(next_frame, (x_next, y_next), 3, color, 3)

    # 화면에 출력
    cv2.imshow("OpticalFlow-LK", next_frame)

    # 다음 프레임을 위한 프레임과 포인트 저장
    prev_gray = gray.copy()
    prev_points = next_points

    # 'q'를 누르면 종료
    if cv2.waitKey(60) == ord('q'):
        break

# 해제
cap.release()
cv2.destroyAllWindows()

# 객체 추적 알고리즘 소

움직이는 객체식별하고, 그 위적하는 방법을 의미합니다. 이를 해 동상이나 실시간 스트리데 이터에서 객체움직지하고, 객체적하는데 사됩니다. 다음은 일적으로 사되는 몇 가지 객 체 추적 알고리즘입니다.

 

만 필터 (Kalman Filter Tracking):


1.
만필터는 상태정에 사되는 필터, 객체추적에도 적됩니다.
2.
예측과 실제관을 기으로 객체의 위와 속도를 정합니다.
3.
예측과 관간의 오를 고하여 정한 위를 업데이트합니다.
4.
이를 움직이는 객체의 경로를 적하고, 예측불확실성을 줄입니다.

 

측정 행렬(Measurement Matrix): 측정 행렬은 시스템의 상태를 측정값으로 변환하는 역할을 합니다. 측정 행렬은 상태 벡터와 측정 벡터 사이의 선형 변환을 정의합니다. 일반적으로 센서로부터 얻은 측정값을 상태 벡터의 선형 조합으로 표현하는 데 사용됩니다. 측정 행렬은 시스템의 동작과 측정 방식에 따라 결정되며, 변하지 않는 고정된 행렬입니다.

전이 행렬(Transition Matrix): 전이 행렬은 시스템의 상태를 현재 상태에서 다음 상태로 변환하는 역할을 합니다. 전이 행렬은 시스템의 동적 모델을 반영하여 상태 벡터를 현재 상태에서 다음 상태로 예측하는 데 사용됩니다. 전이 행렬은 시간에 따라 변할 수 있으며, 시스템의 동작을 모델링하는 데 사용되는 다른 행렬들과 연결되어 시스템의 상태 추정에 영향을 미칩니다.

 

import cv2
import numpy as np

# kalman filter
kalman = cv2.KalmanFilter(4,2)  # 상태백터(위치와 속도 등)를 4차원으로 지정, 측정벡터(센서신호) 2차원으로 세팅) 

# 측정 행렬을 설정합니다. 이 행렬은 측정 벡터를 상태 벡터로 변환하는 역할
kalman.measurementMatrix = np.array([[1,0,0,0],[0,1,0,0]],np.float32)  
# 전이 행렬을 설정합니다. 이 행 렬은 시간에 따라 상태 벡터가 어떻게 변화하는지를 나타냄
kalman.transitionMatrix = np.array([[1,0,1,0],[0,1,0,1],[0,0,1,0],[0,0,0,1]],np.float32) 
# 프로세스 잡음 공분산 행렬을 설정합니다. 이 값은 시스템의 불확실성을 모델링
kalman.processNoiseCov = np.array([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],np.float32) * 0.05


# 칼만 필터 추적 실습
# 영상 파일 열기
cap = cv2.VideoCapture("MS/데이터전처리/0612/data/slow_traffic_small.mp4")

# 첫 프레임에서 추적할 객체 선택
ret, frame = cap.read()
print(ret, frame) # 경로가 잘못된 경우 ret = False, frame = None / 정상일경우 ret = True, frame = 영상프레임

x, y, w, h = cv2.selectROI(frame, False, False)

"""
"Select Object": 객체를 선택할 때 표시될 창의 제목입니다.
frame: 이미지나 비디오 프레임을 나타내는 변수입니다.
False: 선택한 영역을 자동으로 크롭하지 않고, 선택한 영역의 좌표와 크기를 반환합니다.
False: 마우스 이벤트를 사용하여 객체의 영역을 선택할 수 있도록 합니다.
"""
print("선택한 박스의 좌표 >>", x, y, w, h)

# 객체 추적을 위한 초기 추정 위치 설정
# 객체의 x,y,x방향속도(초기0), y방향속도(초기0)로 지정  # 일단 속도를 알 수 없으니 0으로 초기화
kalman.statePre = np.array([[x],[y],[0],[0]],np.float32)

while True:
    # 프레임 읽기
    ret, frame = cap.read()
    if not ret:
        break

    # 칼만 필터로 예측한 위치 계산
    kalman.correct(np.array([[np.float32(x)+w/2],[np.float32(y)+h/2]]))
    kalman.predict()

    # 칼만 필터로 추정된 객체 위치
    predicted_bbox = tuple(map(int, kalman.statePost[:2,0]))
    """
    map(int, kalman.statePost[:2,0]): 상태 벡터의 값들을 int 함수를 사용하여 정수로 변환 
    map 함수는 시퀀스의 모든 요소에 함수를 적용하여 새로운 시퀀스를 생성 
    여기서는 상태 벡터의 값을 int 함수를 사용하여 정수로 변환
    """

    # 추정된 위치에 사각형 그리기
    cv2.rectangle(frame, (predicted_bbox[0]-w//2, predicted_bbox[1]-h//2), 
                        (predicted_bbox[0]+w//2, predicted_bbox[1]+h//2), 
                        (0,255,255), 2)

    # 화면에 출력
    cv2.imshow("frame", frame)


    # 'q'를 누르면 종료
    if cv2.waitKey(60) == ord('q'):
        break

# 해제
cap.release()
cv2.destroyAllWindows()

 

 

 

import cv2
import numpy as np

# kalman filter
kalman = cv2.KalmanFilter(4,2)
kalman.measurementMatrix = np.array([[1,0,0,0],
                                    [0,1,0,0]],np.float32)
kalman.transitionMatrix = np.array([[1,0,1,0],[0,1,0,1],[0,0,1,0],[0,0,0,1]],np.float32)
kalman.processNoiseCov = np.array([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],np.float32) * 0.05

# 칼만 필터 추적 실습
# 영상 파일 열기
cap = cv2.VideoCapture("MS/데이터전처리/0612/data/slow_traffic_small.mp4")

# 첫 프레임에서 추적할 객체 선택
ret, frame = cap.read()
print(ret, frame) # 경로가 잘못된 경우 ret = False, frame = None / 정상일경우 ret = True, frame = 영상프레임

x, y, w, h = cv2.selectROI(frame, False, False)
print("선택한 박스의 좌표 >>", x, y, w, h)

# 객체 추적을 위한 초기 추정 위치 설정
# 객체의 x,y,x방향속도(초기0), y방향속도(초기0)
kalman.statePre = np.array([[x],[y],[0],[0]],np.float32)

while True:
    # 프레임 읽기
    ret, frame = cap.read()
    if not ret:
        break

    # 칼만 필터로 예측한 위치 계산
    kalman.correct(np.array([[np.float32(x)+w/2],[np.float32(y)+h/2]]))
    kalman.predict()

    # 칼만 필터로 추정된 객체 위치
    predicted_bbox = tuple(map(int, kalman.statePost[:2,0]))

    # 추정된 위치에 사각형 그리기
    cv2.rectangle(frame, (predicted_bbox[0]-w//2, predicted_bbox[1]-h//2), 
                        (predicted_bbox[0]+w//2, predicted_bbox[1]+h//2), 
                        (0,255,255), 2)

    # 화면에 출력
    cv2.imshow("frame", frame)


    # 'q'를 누르면 종료
    if cv2.waitKey(60) == ord('q'):
        break

# 해제
cap.release()
cv2.destroyAllWindows()

 

# 객체 추적 알고리즘 소

움직이는 객체식별하고, 그 위적하는 방법을 의미합니다. 이를 해 동상이나 실시간 스트리데 이터에서 객체움직지하고, 객체적하는데 사됩니다. 다음은 일적으로 사되는 몇 가지 객 체 추적 알고리즘입니다.

 

평균 이동 추적 (Mean Shift Tracking):

1. 평균 이동추적은 객체의 히스토그램을 기반으로 추적하는 알고리즘입니다.
2. 초기위치를 선택하고, 해당 위치의 픽셀분포를 히스토그램으로 계산합니다.
3. 다음 프레임에서 히스토그램 유사도를 측정하여 객체의 새로운 위치를 찾습니다.

4. 이 과정을 반복하여 객체움직적합니다.

우선 전체 코드

 

import cv2

# 평균 이동 추적을 위한 초기 사각형 설정
track_window = None  # 객체의 위치 정보 저장할 변수
roi_hist = None  # 히스토그램을 저장할 변수
trem_crit = (cv2.TERM_CRITERIA_EPS|cv2.TERM_CRITERIA_COUNT, 10,1)  # 반복횟수 10

"""
cv2.TERM_CRITERIA_EPS: 반복 알고리즘의 정지 기준 중 하나로, 
주어진 정지 기준 오차(epsilon)를 충족하는 경우 알고리즘을 중지합니다.
cv2.TERM_CRITERIA_COUNT: 반복 알고리즘의 정지 기준 중 하나로, 
주어진 반복 횟수(count)를 충족하는 경우 알고리즘을 중지합니다.
따라서 cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT는 이 두 가지 정지 기준을 모두 사용하겠다는 의미입니다. 
이러한 정지 기준을 설정한 후에는 알고리즘이 주어진 정지 기준에 도달하면 반복을 중지하게 됩니다.

또한, (10, 1)은 알고리즘의 최대 반복 횟수와 정지 기준 오차(epsilon) 값을 나타냅니다. 
예를 들어, 여기서는 최대 10번의 반복과 1의 정지 기준 오차를 설정하였습니다. 
따라서 알고리즘은 최대 10번 반복하거나 주어진 오차가 1 이하로 감소할 때까지 실행됩니다.
"""


# 영상 파일 열기 
cap = cv2.VideoCapture("MS/데이터전처리/0612/data/slow_traffic_small.mp4")

# 첫 프레임에서 추적할 객체 선택
ret, frame = cap.read()  # 영상 있냐없냐 T/F가 ret , Frame 저장
x, y, w, h = cv2.selectROI("selectROI", frame, False, False)
print("선택한 박스의 좌표 >>", x, y, w, h)


# 추적할 객체 초기 히스토그램 계산
roi = frame[y:y+h, x:x+w]  # 관심구역
hsv_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV) # (Hue 색상, Saturation 채도, Value 명도)
roi_hist = cv2.calcHist([hsv_roi], [0], None, [180], [0,180]) # 히스토그램 계산
cv2.normalize(roi_hist, roi_hist, 0, 255, cv2.NORM_MINMAX) # 히스토그램을 0부터 255로 정규화 (정확도 높일려고)

# 추적할 객체의 초기 윈도우 설정 
track_window = (x, y, w, h)
# cv2.imshow("roi test", roi)
# cv2.waitKey(0)

# 평균 이동 추적
while True:
    # 프레임 읽기
    ret, frame = cap.read()
    if not ret:  
        break

    # 추적할 객체에 대한 채도 히스토그램 역투영계산
    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
    backproj = cv2.calcBackProject([hsv], [0], roi_hist, [0,180], 1)
    """
    [hsv]: 역투영을 계산할 대상 이미지의 HSV 채널입니다. HSV 채널은 색상 정보를 갖고 있어 역투영에 활용됩니다.
    [0]: 역투영에 사용할 채널 인덱스입니다. 여기서는 색상 채널(Hue)인 0번 인덱스를 사용합니다.
    roi_hist: ROI(Region of Interest)의 히스토그램입니다. 이 히스토그램은 역투영에 사용됩니다.
    [0,180]: 히스토그램의 범위를 나타냅니다. 여기서는 Hue 채널의 범위가 0부터 180까지로 설정되었습니다.
    1: 확대 비율(scale)입니다. 픽셀의 값을 히스토그램의 값으로 나누어 정규화합니다.
    """
    

    # 평균 이동으로 새 위치 검색
    ret, track_window = cv2.meanShift(backproj, track_window, trem_crit)

    # 검색된 새 위치에 사각형 그리기
    x, y, w, h = track_window
    print("새 위치 좌표 >>", x, y, w, h)
    cv2.rectangle(frame, (x,y), (x+w, y+h), (0,255,0), 2)
    """
    frame: 이미지 또는 비디오 프레임을 나타내는 변수입니다.
    (x, y): 사각형의 왼쪽 위 모서리의 좌표입니다.
    (x + w, y + h): 사각형의 오른쪽 아래 모서리의 좌표입니다. 여기서 w는 사각형의 너비, h는 사각형의 높이입니다.
    (0, 255, 0): 사각형의 선 색상을 나타내는 RGB 값입니다. (R, G, B) 형식으로 표현됩니다. 여기서 (0, 255, 0)은 녹색을 나타냅니다.
    2: 사각형 선의 두께를 나타냅니다.
    """
    

    # 화면에 출력
    cv2.imshow("backproj", backproj)
    cv2.imshow("frame", frame)

    # 'q'를 누르면 종료
    if cv2.waitKey(60) == ord('q'):
        exit()

# 해제
cap.release()
cv2.destroyAllWindows()

 

import cv2

# 평균 이동 추적을 위한 초기 사각형 설정
track_window = None  # 객체의 위치 정보 저장할 변수
roi_hist = None  # 히스토그램을 저장할 변수
trem_crit = (cv2.TERM_CRITERIA_EPS|cv2.TERM_CRITERIA_COUNT, 10,1)  # 반복횟수 10

# 영상 파일 열기 
cap = cv2.VideoCapture("MS/데이터전처리/0612/data/slow_traffic_small.mp4")

# 첫 프레임에서 추적할 객체 선택
ret, frame = cap.read()
x, y, w, h = cv2.selectROI("selectROI", frame, False, False)
print("선택한 박스의 좌표 >>", x, y, w, h)


# 추적할 객체 초기 히스토그램 계산
roi = frame[y:y+h, x:x+w]
hsv_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV) # (Hue, Saturation, Value)
roi_hist = cv2.calcHist([hsv_roi], [0], None, [180], [0,180]) # 히스토그램 계산
cv2.normalize(roi_hist, roi_hist, 0, 255, cv2.NORM_MINMAX) # 히스토그램을 0부터 255로 정규화 (정확도 높일려고)

# 추적할 객체의 초기 윈도우 설정 
track_window = (x, y, w, h)
# cv2.imshow("roi test", roi)
# cv2.waitKey(0)

# 평균 이동 추적
while True:
    # 프레임 읽기
    ret, frame = cap.read()
    if not ret:
        break

    # 추적할 객체에 대한 채도 히스토그램 역투영계산
    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
    backproj = cv2.calcBackProject([hsv], [0], roi_hist, [0,180], 1)

    # 평균 이동으로 새 위치 검색
    ret, track_window = cv2.meanShift(backproj, track_window, trem_crit)

    # 검색된 새 위치에 사각형 그리기
    x, y, w, h = track_window
    print("새 위치 좌표 >>", x, y, w, h)
    cv2.rectangle(frame, (x,y), (x+w, y+h), (0,0,255), 2)

    # 화면에 출력
    cv2.imshow("backproj", backproj)
    cv2.imshow("frame", frame)

    # 'q'를 누르면 종료
    if cv2.waitKey(60) == ord('q'):
        break

# 해제
cap.release()
cv2.destroyAllWindows()

 

 

 

https://webnautes.tistory.com/1257

 

OpenCV Python 강좌 - 모폴로지 연산 (Morphological Operations)

OpenCV에서 제공하는 Erosion, Dilation, Opening, Closing 연산하는 함수들을 다룹니다. 보통 바이너리 이미지(Binary Image)에서 흰색으로 표현된 오브젝트의 형태를 개선하기 위해 사용됩니다. 마지막 업데

webnautes.tistory.com

을 먼저보고오는게 이해가 더 잘됨

1.  threshold 

경계면 구분을 원활하게 진행함

 

import cv2
import matplotlib.pyplot as plt
import numpy as np

image_path= "./data/a.png"

image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
"""
230보다 큰 값은 모두 255로 설정되고 작은값은 0으로 (흰색으로 바뀜)
"""

_, mask = cv2.threshold(image, 230, 255, cv2.THRESH_BINARY_INV)

image_list = np.hstack([
    image,
    mask
])

plt.imshow(image_list, 'gray')
plt.show()

 

 

 

2. 확장 침식 실험 - 02

import cv2
import matplotlib.pyplot as plt
import numpy as np

image_path= "./data/a.png"

image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
_, mask = cv2.threshold(image, 230, 255, cv2.THRESH_BINARY_INV)

                        
kernel = np.ones((3,3), np.uint8)
dilation = cv2.dilate(mask, kernel, iterations=2) # iterations 반복횟수
erosion = cv2.erode(mask, kernel)                     

image_list = np.hstack([
    image,
    mask,
    dilation,  # 팽창
    erosion   # 침식
])

plt.imshow(image_list, 'gray')
plt.title('org img to mask, dilation, erosion')
plt.show()

 

 

image_path= "./data/a.png"

image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
_, mask = cv2.threshold(image, 230, 255, cv2.THRESH_BINARY_INV)

                        
kernel = np.ones((3,3), np.uint8)
dilation = cv2.dilate(mask, kernel, iterations=2) # iterations 반복횟수
erosion = cv2.erode(mask, kernel)
opening = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
closing = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)

image_list = np.hstack([
    image,
    mask,
    dilation, 
    erosion,  
    opening,  
    closing
])

plt.imshow(image_list, 'gray')
plt.title('org img to mask, dilation, erosion, opening, closing')
plt.show()

 

3. 확장 침식 실험 - 03

 

image_path= "./data/a.png"

image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
_, mask = cv2.threshold(image, 230, 255, cv2.THRESH_BINARY_INV)

                        
kernel = np.ones((3,3), np.uint8)
dilation = cv2.dilate(mask, kernel, iterations=2) # iterations 반복횟수
erosion = cv2.erode(mask, kernel)
opening = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
closing = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)

mg = cv2.morphologyEx(mask, cv2.MORPH_GRADIENT, kernel)   # 팽창과 침식의 차  / 경계, 윤곽선, 테두리 등을 추출에 주로 쓰임
th = cv2.morphologyEx(mask, cv2.MORPH_TOPHAT, kernel)     #                 이미지의 밝은 영역에서 뚜렷한 경계를 강조

image_list = np.hstack([
    image,
    mask,
    dilation, 
    erosion,  
    opening,  
    closing
])

image_list_01 = np.hstack([
    mg,
    th
])

plt.imshow(image_list, 'gray')
plt.title('org img to mask, dilation, erosion, opening, closing')
plt.show()

plt.imshow(image_list_01, 'gray')
plt.title('org img to mg, th')
plt.show()

 

1.  커스텀 필터로 data폴더안에 있는 모든 .png파일 불러오기

import cv2
import matplotlib.pyplot as plt
import numpy as np
import os
import glob


image_path = "./data"
image_list = glob.glob(os.path.join(image_path, "*.png"))  # data 폴더안에 .png를 모두 불러옴

for path in image_list :
    print(path)
    image = cv2.imread(path, 1)
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    
    # custom filter 
    filter = np.array([[3,-2, -2 ], [-3, 7, -5], [5, -1, 0]])
    
    """
    각각의 a, b, c는 필터의 가중치  값을 나타냅니다. 이 값은 이미지의 각 픽셀과 이웃한 픽셀 간의 계산에 사용됩니다. 
    필터는 이미지의 각 픽셀에 적용되며, 
    필터 윈도우 내의 픽셀 값과 필터의 가중치를 곱한 후 합산합니다.
    """
    
    # custom filter 적용 
    custom_filter_img = cv2.filter2D(image, -1, filter)  # -1 : 출력이미지의 뎁스를 입력이미지와 동일하게
    
    plt.imshow(custom_filter_img)
    plt.show()

2.  세피아 필터적용

image_path = "./data"
image_list = glob.glob(os.path.join(image_path, "*.png"))

for path in image_list :
    print(path)
    image = cv2.imread(path, 1)
    
    # 효과필터 적용
    filter_ = np.array([[0.272, 0.534, 0.131], [0.119,0.686, 0.168], [0.393, 0.769, 0.189]])
    sepia_img = cv2.transform(image, filter_)
    
    plt.imshow(sepia_img)
    plt.show()
    
    
    """
    cv2.transform 함수는 이미지의 전체적인 변환 작업에 사용되며, 
    cv2.filter2D 함수는 이미지 필터링 작업을 통해 픽셀 값을 변형하는 데 사용됩니다.
    """

 

 

3. 엠보스효과

 

import cv2
import matplotlib.pyplot as plt
import numpy as np
import os
import glob

image_path = "./data"
image_list = glob.glob(os.path.join(image_path, "*.png"))

for path in image_list :
    print(path)
    image = cv2.imread(path, 1)
    
    # 엠보스 효과
    filter_temp = np.array([[0, 1, 0], [0, 0, 0], [0, -1, 0]])
    
    emboss_img = cv2.filter2D(image, -1, filter_temp)
    emboss_img = emboss_img + 128
    
    plt.imshow(emboss_img)
    plt.show()

학습을 시키기위해 이미지 데이터를 1차원 벡터로 변환하는  과정

 

import cv2
import matplotlib.pyplot as plt

image_path = "./data/box.png"

### 경계선 찾기
# 1. 픽셀 강도의 중간값을 계산
image_gray = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)

image_10x10 = cv2.resize(image_gray, (10,10)) # image 10x10 픽셀 크기로 변환 
image_10x10.flatten()  # 이미지 데이터를 1차원 벡터로 변환

plt.imshow(image_10x10, 'gray')
plt.show()

 

 

#### 머신러닝 특성 컬러 이미지 처리 결과


image_color = cv2.imread("./data/iamge01.png")
image_color = cv2.cvtColor(image_color, cv2.COLOR_BGR2RGB)

# image size 10 x 10
image_10x10_temp = cv2.resize(image_color, (10,10)) # image 10x10 픽셀 크기로 변환 
a = image_10x10_temp.flatten() # 이미지 데이터를 1차원 백터로 변환
print(a, a.shape)

# image size 100 x 100
image_100x100_temp = cv2.resize(image_color, (100,100)) # image 10x10 픽셀 크기로 변환 
b = image_255x255_temp.flatten() # 이미지 데이터를 1차원 백터로 변환


plt.imshow(image_10x10_temp)
plt.show()

plt.imshow(image_100x100_temp)
plt.show()

 

 

1. 이미지회전 

- cv2.rotate() 함수활용

- 옵션(이미지, cv2.ROTATE_{각도}_(COUNTER)CLOCKWISE)

import cv2
import matplotlib.pyplot as plt
import numpy as np

image_path = "./data/cat.png"
image = cv2.imread(image_path)

# RGB 타입으로 변경 
image_org_ = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

# 이미지 회전
image_90 = cv2.rotate(image_org_, cv2.ROTATE_90_CLOCKWISE)
image_180 = cv2.rotate(image_org_, cv2.ROTATE_180)
image_270 = cv2.rotate(image_org_, cv2.ROTATE_90_COUNTERCLOCKWISE)

plt.imshow(image_90)
plt.show()

plt.imshow(image_180)
plt.show()

plt.imshow(image_270)
plt.show()

 

2. 상하좌우 반전 

- cv2.flip()

- cv2.flip(이미지, 1 or 0)  1은 좌우반전, 0은 상하반전

 

import cv2
import matplotlib.pyplot as plt
import numpy as np

image_path = "./data/cat.png"
image = cv2.imread(image_path)

# RGB 타입으로 변경 
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

# 1은 좌우 반전 / 0번은 상하 반전
dst_1 = cv2.flip(image, 1)
dst_2 = cv2.flip(image, 0)

plt.imshow(dst_1)
plt.show()

plt.imshow(dst_2)
plt.show()

1. 블러처리 (이미지블러, 가우시안블러)

1. 이미지블러 cv2.blur() 함수활용

import cv2
import matplotlib.pyplot as plt

image_path = "./data/cat.png"
image = cv2.imread(image_path)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

image_blurry = cv2.blur(image, (5,5)) # cv2.blur() 를 활용함
# 33 55 77   # 블러강도를 나타냄  

plt.imshow(image)
plt.show()

plt.imshow(image_blurry)
plt.show()

 

 

2. 가우시안블러 cv2.Gaussianblur() 함수활용 

이걸 더 많이씀

import cv2
import matplotlib.pyplot as plt
import numpy as np

image_path = "./data/cat.png"
image = cv2.imread(image_path)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

image_g = cv2.GaussianBlur(image, (9,9), 0)   # cv2.GaussianBlur() 를 사용함  
# (9,9): 주변 커널수-> 세기조절이 됨
# 0 : 표준편차

plt.imshow(image)
plt.show()

plt.imshow(image_g)
plt.show()

 

 

 

 

2. 이미지 선명하게하기

import cv2
import matplotlib.pyplot as plt
import numpy as np

image_path = "./data/cat.png"
image = cv2.imread(image_path)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

kernel_ = np.array([[0,-1,0],
                    [-1,5,-1],
                    [0,-1,0]
                   ])

image_sharp = cv2.filter2D(image, -1, kernel_)

plt.imshow(image)
plt.show()

plt.imshow(image_sharp)
plt.show()

 

3. 이미지 대비높이기

1 . 그레이스케일 일때  : 히스토그램 평활화 cv2.equalizeHist()에 BGR2Gray 활용 : 

import cv2
import matplotlib.pyplot as plt
import numpy as np

image_path = "./data/cat.png"
image = cv2.imread(image_path)
image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 단일 이미지에서만 적용가능!
image_enhanced = cv2.equalizeHist(image_gray)

plt.imshow(image_gray, 'gray')
plt.show()

plt.imshow(image_enhanced, 'gray')
plt.show()

 

2. 컬러일때 : RGB에 YUV를 추가함

import cv2
import matplotlib.pyplot as plt
import numpy as np

image_path = "./data/cat.png"
image = cv2.imread(image_path)

# RGB 타입으로 변경 
image_org = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

# YUV 타입으로 변경 
image_yuv = cv2.cvtColor(image, cv2.COLOR_BGR2YUV)

# 히스토그램 평활화 적용 
image_yuv[:,:,0] = cv2.equalizeHist(image_yuv[:,:,0])

# YUV > RGB 
image_rgb = cv2.cvtColor(image_yuv , cv2.COLOR_YUV2RGB)


plt.imshow(image_yuv)
plt.show()

plt.imshow(image_org)
plt.show()

plt.imshow(image_rgb)
plt.show()

4. 이진화(흑백)

 

import cv2
import matplotlib.pyplot as plt
import numpy as np

image_path = "./data/cat.png"
image = cv2.imread(image_path)

image_gray_01 = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 이미지 이진화 
max_output_value = 255 
neighborhood_size = 99
subtract_from_mean = 10
image_binarized = cv2.adaptiveThreshold(
                                image_gray_01,
                                max_output_value,
                                cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                                cv2.THRESH_BINARY, # cv2.THRESH_BINARY_INV(반전)
                                neighborhood_size,
                                subtract_from_mean
)

plt.imshow(image_binarized, 'gray')
plt.show()

1.  이미지 타입, 크기 확인하기

import cv2
image_path = "./data/cat.png"

# image read 
image = cv2.imread(image_path)

# image 타입 
image_type = type(image)
print("이미지 타입 >> " , image_type)

# 이미지 크기 확인 
image_height, image_width, image_channel = image.shape
print(image_height, image_width, image_channel)

# 이미지 타입 >>  <class 'numpy.ndarray'>
# 162 310 3

 

2. 이미지 리사이징

import cv2
import matplotlib.pyplot as plt

image_path = "./data/cat.png"

image = cv2.imread(image_path)

image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)  # cv2로 불러오면  BGR을 RGB로 바꿔줘야함
# https://itcreator.tistory.com/141 에 적어둠

# 이미지 크기 조절 
image_resize = cv2.resize(image, (244,244))

# 이미지 크기 확인 
image_height_, image_width_, _ = image_resize.shape


# 원본 이미지 
plt.imshow(image)
plt.show()

 

# 리사이즈 이미지 
print(image_height_, image_width_)
# 244 244

plt.imshow(image_resize)
plt.show()

# 저장하기

import cv2
image_resize = cv2.cvtColor(image_resize, cv2.COLOR_BGR2RGB)  
cv2.imwrite("./data/resize_image.png", image_resize)  # cv2.imwrite로 저장하기

# True라고 뜨면 저장된거

 

3. 이미지 자르기(슬라이싱)

import cv2
import matplotlib.pyplot as plt

image_path = "./data/cat.png"
image = cv2.imread(image_path)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

image_cropped = image[10:,:200]   #  [행시작:행끝, 열시작:열끝]

plt.imshow(image)
plt.show()

 

plt.imshow(image_cropped)
plt.show()

 

 

3-1. 이미지자르기(좌표)

import cv2
import matplotlib.pyplot as plt

image_path = "./data/cat.png"
image = cv2.imread(image_path)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)


x1, y1 = 50, 50 # 좌측 상단 모서리 좌표 
x2, y2 = 150,150 # 우측 하단 모서리 좌표 

cropped_image_test = image[y1:y2, x1:x2]   # y가 먼저인 이유는 좌표평면에서 행렬의 행은 y값임
plt.imshow(cropped_image_test)
plt.show()

 

 

 

 

 

 

2. 변환하기

3. 리사이즈

4. 크롭  (슬라이싱,좌표)

 

1.  이미지 불러오기 (png파일)

# !pip install opencv-python==4.5.5.64
import cv2
from matplotlib import pyplot as plt 


img = cv2.imread("./data/cat.png")    # cv2 라이브러리의 imread() 함수로 불러옴

# 색상의 문제 발생 -> BGR -> RGB 
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

plt.imshow(img)
plt.show()

2. 이미지 저장하기(.npy)

넘파이를 이용하여 배열로 저장하기

.npy 파일로 저장하는 방법

import numpy as np
np.save("./data/image.npy", img)  #  현재위치에 있는 data 폴더에 image.npy로 이미지만들기


# 만약에 해당폴더가 없으면 폴더생성하는 파이썬 코드
import os

def createDirectory(directory):
    try:
        if not os.path.exists(directory):
            os.makedirs(directory)
    except OSError:
        print("Error: Failed to create the directory.")

2-1. 이미지 저장하기(PIL)

PIL은 간단한 이미지 처리와 변환 작업에 사용하기 좋음 / 컴퓨터 비전 작업에서는 OpenCV를 쓰는게 맞음

#!pip install Pillow
import numpy as np
from PIL import Image

# 이미지 불러오기 
img = Image.open("./data/cat.png").convert('L') # 그레이 스케일 변환
print(img)

img_array = np.array(img)

np.save("./data/pil_image.npy", img_array)  # .npy로 저장
img_array1 = np.load("./data/pil_image.npy")  # .npy 불러오기

# 다시 PIL 이미지로 변환 
img = Image.fromarray(np.uint8(img_array1))    # pil의 Image라이브러리 fromarray()로 변환
img.save("./data/image_numpy.png")            # pil로 png로 변환       


org_size = os.path.getsize("./data/cat.png")
np_save_size = os.path.getsize("./data/pil_image.npy")
pil_save_size = os.path.getsize("./data/image_numpy.png")

# 용량차이 확인하기 
print(org_size, "bytes")
print(np_save_size, "bytes")
print(pil_save_size, "bytes")


# 원본 : 6468 bytes
# .npy로 저장 : 50348 bytes   
# PIL를 활용하여 png로 저장 26808 bytes

 

3. 이미지 불러오기 (.npy)

img_data = np.load("./data/image.npy")  # 넘파이의 load()로 불러옴

plt.imshow(img_data)
plt.show()

 

 

메타에서 SAM(Segment Anything Model)이라는 모델을 무려 오픈소스(Apache-2.0 license) 공개하였습니다. 이미지 개체를 인식하여 영역을 분할하는 모델로 기존 대비 훨씬 많은 데이터로 학습하여 뛰어난 성능을 보여줍니다.

 

SAM 소개 페이지

https://segment-anything.com/ https://ai.facebook.com/blog/segment-anything-foundation-model-image-segmentation

 

 

SAM 깃허브  

https://github.com/facebookresearch/segment-anything

 

 

SAM 데모 써보기

https://segment-anything.com/demo

감도가 높을수록 노이즈가 많다고 알고있지? 

SNR 과 ISO 관계 이해하기

 

https://petapixel.com/2017/03/22/find-best-iso-astrophotography-dynamic-range-noise/

 

How to Find the Best ISO for Astrophotography: Dynamic Range and Noise

ISO is one of the three major exposure settings in the exposure triangle of a digital camera -- shutter time, f/number, and ISO. Of the three, it is ISO

petapixel.com

 

 

 

기종별 최적 감도(ISO)

http://dslr-astrophotography.com/iso-dslr-astrophotography/

 

예시) 시뮬레이터

https://www.dpreview.com/reviews/image-comparison/fullscreen?attr134_0=sony_a7iii&attr134_1=nikon_z6&attr134_2=sony_a7iii&attr134_3=nikon_z6&attr136_0=7&attr136_1=7&attr136_2=1&attr136_3=1&normalization=compare&widget=601&x=0.8672117644150164&y=0.14915352500398193

 

Image comparison: Digital Photography Review

 

www.dpreview.com

 

 

https://paperswithcode.com/trends

- 나도코딩 OpenCV @ https://bit.ly/3AyaDB1
- OpenCV 공식 튜토리얼 (Basics + ObjectDetection & PoseEstimation, 3h) @ https://j.mp/3xlNxKX
- Hand tracking / Gesture volume control / AI Trainer using CPU @ https://j.mp/32L77mU & https://j.mp/3wWGUif
- Python OpenCV4로 배우는 CV & ML (CNN/DNN/SVM/KNN 포함) @ http://j.mp/2WRedpa
- OpenCV 튜토리얼 (유튜브 3h, 문서스캔/차량번호판탐지 포함) @ https://j.mp/39mvKuw
- OpenCV FreeCodeCamp (유튜브 3h, 얼굴탐지/심슨캐릭터분류 포함) @ https://j.mp/2LyqWZy
- 영상처리 OpenCV 강의 @ https://goo.gl/PxfXj3
- [dlib & opencv 기반 프로젝트] 얼굴인식 @ https://j.mp/2JVSzan / 성별&나이인식 @ https://j.mp/2JVSzan / 눈깜빡임 감지 @ https://j.mp/3c91Ub2
- Face detection 예시코드 & 성능비교 (Opencv/dlib/mtcnn/S3FD/retinaface) @ https://j.mp/2FuTFLX
- YOLO v5 커스텀 학습 (빵형) @ https://bit.ly/3Hq8eKv

+ Recent posts