파이참 다운로드
파이참 : 파이썬할 때 많이 쓰는 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
opencv 설치
pip install opencv-python
설치 환경 확인 가능
conda env list
파이참 설정
* 바로 실행 : 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 (이거 같은데 링크)
'공부 > [TIL] 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 |