공부/Digital Twin Bootcamp

TIL_211231_Vision 인식

2021. 12. 31. 17:38
목차
  1. 이미지 변환
파이참 다운로드

파이참 : 파이썬할 때 많이 쓰는 IDE

커뮤니티 버전으로 설치

 

Anaconda Powershell Prompt (anaconda3) 업데이트
conda update conda
conda update --all
: procced 계속 yes 해주면 업데이트 끝
  python 치면 파이썬 실행 가능
meta라는 이름의 가상환경 설정
conda create --name meta python=3.9.7
meta 실행
conda activate meta

base : 기본환경
현재 가상환경(meta)이라 cv2 모듈이 존재하지 않음 확인 가능

opencv 설치
pip install opencv-python
설치 환경 확인 가능
conda env list

 

파이참 설정

create 하면 완료

* 바로 실행 : ctrl + f5

* 선택 실행 : ctrl + shift + f10

* 파이썬은 snake_case를 많이 쓴다

 

어제 과제

import cv2
import numpy as np

# 이미지 반환 함수
def createImage(h_red): # 이미지 배열 반환
    dst = cv2.bitwise_and(hsv, hsv, mask = h_red)
    # hsv이미지와 hsv 이미지를 bitswise_and 연산/ 연산 결과는 dst 변수에 담김
    # bitwise_and 연산의 마스크는 h_red (and 연산 할 부분)

    dst = cv2.cvtColor(dst, cv2.COLOR_HSV2BGR) # HSV2BGR
    # hsv 연산이 끝난 dst를 bgr로 변환해 다시 dst에 담음
    return dst

cv2.namedWindow("Palette") # Palette 이름의 윈도우(창) 생성
cv2.createTrackbar("Hue", "Palette", 0, 179, lambda x:x) # Palette 윈도우에 Hue 이름의 트랙바 생성

src = cv2.imread('tomato.jpg') # 이미지 가져옴
hsv = cv2.cvtColor(src, cv2.COLOR_BGR2HSV) # 색상 검출을 위해 HSV 채널로 변환
h, s, v = cv2.split(hsv) # hue값 쓰기 위해 hsv 채널 이미지를 분리함

while True:
    hue_val = cv2.getTrackbarPos("Hue", "Palette")
    # Hue 트랙바가 움직일 때마다 움직여서 받아온 값을 hue_val에 넣는다
    
    h_red = cv2.inRange(h, 0, hue_val)
    # hue_val 사용하여 h_red 마스크 생성

    cv2.imshow("Palette", createImage(h_red))
    # Palette 윈도우에 creatImage 함수 반환 결과(이미지)를 보여줌
    
    if cv2.waitKey(33) & 0xFF == ord('q'): # 33ms마다 키입력 대기, q 입력 받으면 종료
        break

cv2.destroyAllWindows() # 모든 윈도우 release(메모리 해제)

결과

 

 

이미지 연산

이미지를 연산할 수 있는 함수들이 있다.

- 절댓값 함수

- 절댓값 차이 함수

- 비교 함수

  dst = cv2.compare(src1, src2, cmpop) : cmpop 조건에 맞으면 255 반환 아니면 0

  dst = src1 == src2   dst = src1 >= scv2 : 이런 방식도 가능하지만 True, False 형식으로 나온다

- 선형 방정식 해 찾기 cv2.solve

  간단히 말하면 두 직선의 교점을 찾는다

- 각 요소별 비트단위 연산

  비트끼리 연산

  cv2.bitwise_and, or, xor, not(mask 사용 가능)

  AND, OR, XOR, NOT => &, |, ^, ~ (이렇게도 표현)

 

흐림 효과

커널과 고정점 참고

- 커널 : 특정 픽셀과 해당 픽셀 주변을 포함한 작은 크기의 공간, 신호 처리에서는 필터 라고도 한다.

- 고정점 : 컨벌루션된 값을 할당한 지점

- 컨벌루션 : 원본 픽셀을 특정 커널을 이용해 새로운 픽셀 값을 만들어 내는 것 참고

                외삽법

 

실습

cv2.bilateralFilter

원본
효과 적용

- 단순 흐림 (cv2.blur와 cv2.boxFilter 함수는 동일한 결과를 낸다)

  dst = cv2.blur(src, ksize, anchor = None, borderType = None)

- 중간값

  dst = cv2.medianBlur(src, ksize)

- 가우시안 (많이 씀)

  dst = cv2.GaussianBlur(src, ksize, sigmaX, sigmaY = None, borderType = None)

- 양방향 필터

  dst = cv2.bilateralFilter(src, d, sigmaColor, sigmaSpace, dst = None, borderType = None)

 

* ksize = 커널 사이즈, height와 weigth를 설정해 해당 구역의 평균을 구함

 

 

샤프닝

결과

 


이미지 변환

이미지 피라미드(꼭대기로 갈수록 이미지 크기가 작아진다) 설명

업샘플링(이미지 확대)

다운샘플링(이미지 축소)

무조건 좌우가 두배씩 줄고 늘어남

한번에 이미지 크기를 조정할 수 없고 무조건 피라미드 단계를 밟아 다운샘플링/업샘플링을 해야하기 때문에 resize를 많이 쓴다.

# 이미지 피라미드
import cv2
src = cv2.imread("ferris-wheel.jpg")
dst = src.copy()

for i in range(3):
    dst=cv2.pyrDown(dst)

cv2.imshow("src", src)
cv2.imshow("dst", dst)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

resize

절대크기(dsize)로 크기 조절

dsize가 없으면 fx, fy로 크기 조절 참고

#resize
import cv2
src = cv2.imread("car.png")

dst = src[280:310, 240:405]
dst = cv2.resize(dst, dsize=(256,256), interpolation=cv2.INTER_NEAREST)

cv2.imshow("src", src)
cv2.imshow("dst", dst)
cv2.waitKey(0)
cv2.destroyAllWindows()

결과

 

 

대칭 & 회전

- 대칭 : 반사(reflection)의 의미를 가짐 | 변환할 행렬(이미지)에 대해 2X2 행렬을 왼쪽 곱셈 진행=> dst = cv2.flip(src, flipCode)

- 회전 : 단순한 회전은 2x2 행렬로 회전 가능 => matrix = cv2.getRotationMatrix2D(center, angle, scale) => 2,3 회전 행렬

           회전 후의 이미지 크기, 중심 다시 계산해줘야함

# 이런 식으로 함수로 만들어 편하게 꺼내 쓸 수 있다

def rotate(image, angle, scale):
    """
    input data
    image : source image
    angle : 회전 각도
    scale : 회전 후 이미지 비율
    """
    
    import math
    import cv2

    src = cv2.imread(image)

    height, width, _ = src.shape
    center = (width / 2, height / 2)
    matrix = cv2.getRotationMatrix2D(center, angle, scale)

    radians = math.radians(angle)
    sin = math.sin(radians)
    cos = math.cos(radians)
    
    # 회전 후 이미지 크기
    bound_w = int((height * scale * abs(sin)) + (width * scale * abs(cos)))
    bound_h = int((height * scale * abs(cos)) + (width * scale * abs(sin)))

    matrix[0, 2] += ((bound_w / 2) - center[0])
    matrix[1, 2] += ((bound_h / 2) - center[1])

    dst = cv2.warpAffine(src, matrix, (bound_w, bound_h))
    return dst

* 매개변수는 적을수록 좋다

 

 

기하학적 변환

이미지를 확대, 축소, 위치 변경, 회전, 왜곡 하는 등 이미지의 형태를 변환하는 것

- 아핀변환 : 이미지를 멀리 보이는 것처럼 찌그러뜨리기 / 변환 전,후 좌표 세개

- 원근변환 : 변환 전,후 좌표 네개

# 기하학적 변환  - 원근 변환

import numpy as np
import cv2

src = cv2.imread("clouds.jpg")
cv2.namedWindow('dst', flags=cv2.WINDOW_NORMAL)
height, width,_ = src.shape #height, width=src.shape[:2]

pts1 = np.float32([[0,0], [0,height], [width, 0], [width, height]]) # 변환 전 좌표 4개
pts2 = np.float32([[300,300], [0,height-200], [800, 200], [width-100, height-100]]) # 변환 후 좌표 4개

matrix = cv2.getPerspectiveTransform(pts1, pts2)

dst = cv2.warpPerspective(src, matrix, (width, height))

cv2.imshow("dst", dst)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

원본 / 결과

 

circle로 위치 표시

# 기하학적 변환  - 원근 변환

import numpy as np
import cv2

src = cv2.imread("clouds.jpg")
cv2.namedWindow('dst', flags=cv2.WINDOW_NORMAL)
height, width,_ = src.shape #height, width=src.shape[:2]


cv2.namedWindow('dst', flags=cv2.WINDOW_NORMAL)
cv2.namedWindow('src', flags=cv2.WINDOW_NORMAL)

# 원 설정
cv2.circle(src, (0,0), 10, (255, 0, 0), 10) # B
cv2.circle(src, (0,height), 10, (0, 255, 0), 10) # G
cv2.circle(src, (width,0), 10, (0, 0, 255), 10) # R
cv2.circle(src, (width,height), 10, (0, 255, 255), 10) # Y

cv2.imshow('src', src)


pts1 = np.float32([[0,0], [0,height], [width, 0], [width, height]]) # 변환 전 좌표 4개
pts2 = np.float32([[300,300], [0,height-200], [800, 200], [width-100, height-100]]) # 변환 후 좌표 4개

matrix = cv2.getPerspectiveTransform(pts1, pts2)

dst = cv2.warpPerspective(src, matrix, (width, height))

cv2.imshow("dst", dst)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

 

모폴로지 변환(많이 씀)

영상이나 이미지를 형태학적 관점에서 접근하는 기법

기본적인 모폴로지 변환에는 팽창, 침식이 있음

=> 팽창-침식 후 남은 영역을 이용해 사진의 외곽선을 그리는 등 가능

- 구조 요소를 활용하여 커널의 형태 설정 가능 cv2.getStructuringElement(shape, ksize, anchor = None)

  shape : 직사각형, 십자가, 타원 형태 등 존재

#모폴로지 변환

import cv2

src = cv2.imread("dandelion.jpg", cv2.IMREAD_GRAYSCALE)

kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5), anchor=(-1, -1)) # 순서대로 타원, 사이즈, 고정점
dst = cv2.erode(src, kernel, iterations=3) # 침식 반복 횟수

cv2.imshow("src", src)
cv2.imshow("dst", dst)
cv2.waitKey(0)
cv2.destroyAllWindows()

원본
결과

 

 

 

과제

1. 어제 마스크 총 두개를 썼는데 그걸 이용한 트랙바 생성(hue 마스크 2개)

2. 이진화는 hue 마스크 2개 적용하는 이미지를 이용해서

3. 이진화 이미지를 밑에 붙일 것 / 임계값에 따라 모양 바꿔주기(trackbar)

4. 키보드 특정키로 윈도우창 움직이기

5. esc, q로 종료

 

참고 코드

창 크기 조절 : cv2.namedWindow('window_name', flags=cv2.WINDOW_NORMAL)

 

결과 예제

 

내가 한 결과

100% 똑같진 않지만 나름...구현했다.

윈도우 이동 구현은 링크를 참고해 성공했다. 참고한 링크

근데 문제가 생겼다. 윈도우를 이동하지 않으면 트랙바 값의 결과가 반영이 되질 않는다.

import cv2
import numpy as np

# 이미지 반환 함수
def createImage(complete_red):
    dst = cv2.bitwise_and(hsv, hsv, mask=complete_red)
    dst = cv2.cvtColor(dst, cv2.COLOR_HSV2BGR)

    return dst # 이미지 반환

src = cv2.imread("tomato.jpg") # 이미지 가져옴
hsv = cv2.cvtColor(src, cv2.COLOR_BGR2HSV) # 색상 검출을 위해 HSV 채널로 변환
# gray = createImage() # 결과를 이분화하기 위해 graysacle 변환
x, y = 0, 0 # x, y 값

h, s, v = cv2.split(hsv)  # hue값 쓰기 위해 hsv 채널 이미지를 분리함

cv2.namedWindow("Palette", flags=cv2.WINDOW_NORMAL) # 윈도우 창 생성 Palette

cv2.createTrackbar("lower_value", "Palette", 0, 179, lambda x:x)
cv2.createTrackbar("upper_value", "Palette", 0, 179, lambda x:x)
cv2.createTrackbar("threshold", "Palette", 0, 255, lambda x:x)

while True:
    #Palette 윈도우의 lower_value 트랙바 움직일 때마다 값을 가져옴
    hue_val1 = cv2.getTrackbarPos("lower_value", "Palette")
    lower_red = cv2.inRange(h, hue_val1, hue_val1)

    hue_val2 = cv2.getTrackbarPos("upper_value", "Palette")
    upper_red = cv2.inRange(h, 0, hue_val2)

    # 배열 병합
    complete_red = cv2.addWeighted(lower_red, 1.0, upper_red, 1.0, 0.0)

    threshold = cv2.getTrackbarPos("threshold", "Palette")
    _, binary = cv2.threshold(createImage(complete_red), 0, threshold, cv2.THRESH_BINARY)

    result = np.vstack((createImage(complete_red), binary))
    cv2.imshow("Palette", result)

    # 키보드 키로 윈도우 이동
    cv2.moveWindow("Palette", x, y)
    key=cv2.waitKey(33) & 0xFF # 키보드 입력 대기
    if key == ord('a'):
        x -= 100
    elif key == ord('s'):
        y += 100
    elif key == ord('w'):
        y -= 100
    elif key == ord('d'):
        x += 100
    elif key == ord('q') or key == 27: # q 아님 esc로 윈도우 종료
        break
    cv2.moveWindow("Palette", x, y)

    # if cv2.waitKey(33) == ord('q') or cv2.waitKey(33) == 27: # q 아님 esc로 윈도우 종료
    #     break

cv2.destroyAllWindows()

=> 아마 실행 순서 문제인 것 같아 살펴보고 위치를 조정하여 해결했다. 그리고 이름 바꿔서 hue 값을 조정하는 트랙바를 각각 두개씩, 4개로 늘렸다.

import cv2
import numpy as np

# 이미지 반환 함수
def createImage(complete_red):
    dst = cv2.bitwise_and(hsv, hsv, mask=complete_red)
    dst = cv2.cvtColor(dst, cv2.COLOR_HSV2BGR)
    return dst # 이미지 반환

## 이미지 grayscale로 반환...
# def grayImage(createImage):
#     dst2 = cv2.cvtColor(createImage(complete_red), cv2.COLOR_BGR2GRAY)
#     return dst2

src = cv2.imread("tomato.jpg") # 이미지 가져옴
hsv = cv2.cvtColor(src, cv2.COLOR_BGR2HSV) # 색상 검출을 위해 HSV 채널로 변환
# gray = createImage() # 결과를 이분화하기 위해 graysacle 변환
x, y = 0, 0 # x, y 값

h, s, v = cv2.split(hsv)  # hue값 쓰기 위해 hsv 채널 이미지를 분리함

cv2.namedWindow("Palette", flags=cv2.WINDOW_NORMAL) # 윈도우 창 생성 Palette

cv2.createTrackbar("lower_value1", "Palette", 0, 179, lambda x:x)
cv2.createTrackbar("lower_value2", "Palette", 0, 179, lambda x:x)
cv2.createTrackbar("upper_value1", "Palette", 0, 179, lambda x:x)
cv2.createTrackbar("upper_value2", "Palette", 0, 179, lambda x:x)
cv2.createTrackbar("threshold", "Palette", 0, 255, lambda x:x)

while True:
    #Palette 윈도우의 lower_value 트랙바 움직일 때마다 값을 가져옴
    hue_val1 = cv2.getTrackbarPos("lower_value1", "Palette")
    hue_val2 = cv2.getTrackbarPos("lower_value2", "Palette")
    lower_red = cv2.inRange(h, hue_val1, hue_val2)

    hue_val3 = cv2.getTrackbarPos("upper_value1", "Palette")
    hue_val4 = cv2.getTrackbarPos("upper_value2", "Palette")
    upper_red = cv2.inRange(h, hue_val3, hue_val4)

    # 키보드 키로 윈도우 이동
    cv2.moveWindow("Palette", x, y)
    key=cv2.waitKey(33) & 0xFF # 키보드 입력 대기
    if key == ord('a'):
        x -= 100
    elif key == ord('s'):
        y += 100
    elif key == ord('w'):
        y -= 100
    elif key == ord('d'):
        x += 100
    elif key == ord('q') or key == 27: # q 아님 esc로 윈도우 종료
        break
    cv2.moveWindow("Palette", x, y)

    # 배열 병합
    complete_red = cv2.addWeighted(lower_red, 1.0, upper_red, 1.0, 0.0)

    threshold = cv2.getTrackbarPos("threshold", "Palette")
    _, binary = cv2.threshold(createImage(complete_red), 0, threshold, cv2.THRESH_BINARY)

    result = np.vstack((createImage(complete_red), binary))
    cv2.imshow("Palette", result)

    # if cv2.waitKey(33) == ord('q') or cv2.waitKey(33) == 27: # q 아님 esc로 윈도우 종료
    #     break

cv2.destroyAllWindows()

하고 싶은 데 못한 것

- 이진화할 때 grayscale : 변수에 통째로 담으려고 했는데 오류가 났다. 아마 compelete_red가 밖으로 나오지 못해서 그런 것 같은데...

- 똑같은 trackbar 2개 : 똑같은 걸 두번씩 넣어봤는데 안된다...물론 4개의 트랙바의 이름을 다 바꿔서 하긴 했지만 과제 결과물엔 이름이 똑같은 것 같은데 어떻게 한건지 모르겠다.

 

성공한 것

- hue 마스크 조절(아마도)

- 마스크 조절한 이미지를 받아서 이진화

- 이진화한 결과를 밑에 이어붙이기

- wasd로 window 키 움직이기

- q, esc로 종료하기

 

 

강사님이 공개하신 코드

import cv2
import numpy as np


def createImage(lower_mask, upper_mask, threshold):
    complete_mask = cv2.addWeighted(lower_mask, 1.0, upper_mask, 1.0, 0.0)
    dst = cv2.bitwise_and(hsv, hsv, mask=complete_mask)
    dst = cv2.cvtColor(dst, cv2.COLOR_HSV2BGR)

    gray = cv2.cvtColor(dst, cv2.COLOR_BGR2GRAY)
    _, binary = cv2.threshold(gray, threshold, 255, cv2.THRESH_BINARY)
    binary = cv2.cvtColor(binary, cv2.COLOR_GRAY2BGR)
    dst = np.vstack((dst, binary))

    return dst


src = cv2.imread("tomato.jpg")
hsv = cv2.cvtColor(src, cv2.COLOR_BGR2HSV)

h, s, v = cv2.split(hsv)
window_x, window_y = 0, 0

cv2.namedWindow("Palette", flags=cv2.WINDOW_NORMAL)
cv2.createTrackbar("lower_mask_min_value", "Palette", 0, 179, lambda x: x)
cv2.createTrackbar("lower_mask_max_value", "Palette", 5, 179, lambda x: x)
cv2.createTrackbar("upper_mask_min_value", "Palette", 170, 179, lambda x: x)
cv2.createTrackbar("upper_mask_max_value", "Palette", 179, 179, lambda x: x)

cv2.createTrackbar("threshold", "Palette", 127, 255, lambda x: x)

while True:
    lower_mask_min_value = cv2.getTrackbarPos("lower_mask_min_value", "Palette")
    lower_mask_max_value = cv2.getTrackbarPos("lower_mask_max_value", "Palette")

    upper_mask_min_value = cv2.getTrackbarPos("upper_mask_min_value", "Palette")
    upper_mask_max_value = cv2.getTrackbarPos("upper_mask_max_value", "Palette")

    threshold = cv2.getTrackbarPos("threshold", "Palette")

    lower_mask = cv2.inRange(h, lower_mask_min_value, lower_mask_max_value)
    upper_mask = cv2.inRange(h, upper_mask_min_value, upper_mask_max_value)

    cv2.imshow("Palette", createImage(lower_mask, upper_mask, threshold))

    key = cv2.waitKey(33)

    if key == ord('a'):
        window_x -= 10
    elif key == ord('s'):
        window_y += 10
    elif key == ord('w'):
        window_y -= 10
    elif key == ord('d'):
        window_x += 10
    elif key == ord('q') or key == 27:  # 'q' 이거나 'esc' 이면 종료
        break
        cv2.destroyAllWindows()
    cv2.moveWindow("Palette", window_x, window_y)  # 안배움

cv2.destroyAllWindows()

궁금증 모두 해결되었다 :

그레이스케일...그냥 이미지 처리할 때 넣으면 되는 것이었다....

이름 네개 다 다른 것이었다...

threshold 위치도 틀렸다(나름 여기저기 넣어보다가 여긴가? 한건데 머쓱하네)아직 완벽하게 이해 못하는 게 티가 난다.

그래도 강사님 코드 보고 보니까 좀 알 것 같은 느낌이다.

되짚어 볼 것 : 왜 내 코드는 이상할까ㅋㅋ

 


 

느낀 점

느끼기에 어제보다 허들이 훨씬 높은 내용이었다.

근데 어떻게든 이해하려고 노력 중이다.

그래도 어려운만큼 재미도 있다ㅎ

 

지금 개발 공부할 때 과정은

구글링해서 설명을 본다 -> 그 설명의 모르는 항목 발견 -> 그 항목을 구글링해서 설명을 본다 -> 그 설명의 모르는 항목 발견 -> ...

거의 이렇다.

아무래도 아는 게 아직 많이 적어서 당연한 과정이다. 다행히도 이런 공부 방법이 나랑 맞는 것 같다.

 

월요일부턴 캠을 이용해 얼굴 인식을 해볼 예정인데, 관심 있으면 미리 검색해보라 하신 키워드 적어둔다.

얼굴인식 오픈소스 : real time face recorder (근데 이렇게 치면 게임에서 쓰는 방식이 많이 나오는 것 같은데...)

real time face id attendance recorder(이건가?) real time face detector (이거 같은데 링크)

'공부 > Digital Twin Bootcamp' 카테고리의 다른 글

TIL_220104_Vision 인식  (1) 2022.01.04
TIL_210103_Vision 인식  (0) 2022.01.03
TIL_211230_VISION 인식  (0) 2021.12.30
TIL_211229_VISION 인식  (0) 2021.12.29
TIL_211227_Frontend  (1) 2021.12.27
  1. 이미지 변환
'공부/Digital Twin Bootcamp' 카테고리의 다른 글
  • TIL_220104_Vision 인식
  • TIL_210103_Vision 인식
  • TIL_211230_VISION 인식
  • TIL_211229_VISION 인식
Ail_
Ail_
Ail_
log
Ail_
  • 분류 전체보기 (182)
    • 사설 (11)
      • 강연 (5)
      • * (3)
      • 회고 (3)
    • 공부 (161)
      • Just do it (3)
      • TIL (66)
      • Game Bootcamp (31)
      • Digital Twin Bootcamp (39)
    • 노션 (3)
    • 프로젝트 (23)
      • Game Bootcamp (1)
      • Digital Twin Bootcamp (21)
      • 경기청년 갭이어 (1)

인기 글

최근 글

태그

  • 데이터베이스
  • 이펙트
  • TypeScript
  • 공격
  • 대시
  • node.js
  • 개발일지
  • SQL 첫걸음
  • 오블완
  • 멋쟁이사자처럼
  • Chat
  • unity
  • 유니티 게임 개발
  • 노션
  • 배포
  • 피격
  • 플레이어
  • 개발회고
  • 한입 크기로 잘라 먹는 타입스크립트
  • 티스토리챌린지
  • mysql 설치
  • SQL첫걸음
  • Do it! 자료구조와 함께 배우는 알고리즘 입문 : 파이썬 편
  • notion
  • 회고
  • 템플릿
  • C#
  • 부트캠프
  • 인터랙티브 웹 UX·UI
  • 유니티

최근 댓글

전체
오늘
어제
hELLO · Designed By 정상우.
Ail_
TIL_211231_Vision 인식
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.