# 본 자료는 이수안 교수님(https://suanlab.com/)의 자료를 기반으로 수정 및 보완하여 제작되었습니다.
# 제작자 : 김민수(rlaalstn1504@naver.com)
import cv2
import numpy as np
import matplotlib.pyplot as plt
from google.colab import files
from google.colab.patches import cv2_imshow
다양한 도형을 그릴 수 있음
도형을 그리는 좌표가 해당 범위를 넘어가면 이미지에 표현되지 않음
img = np.zeros((512,512,3), np.uint8)
plt.imshow(img)
plt.show()

cv.line()
Parameters
img : 그림을 그릴 이미지 파일
start : 시작 좌표
end : 종료 좌표
color : BGR형태의 Color (ex; (255, 0, 0) -> Blue)
thickness (int) : 선의 두께. pixel
img = cv2.line(img, (0,0), (511,511), (255,0,0), 5) # 시작, 끝, 컬러(BGR), 두께
plt.imshow(img) # 왜 붉은 색 선일까? -> B에 그렸으나 R로 인식
plt.show()

cv2.rectangle()
Parameters
img : 그림을 그릴 이미지
start : 시작 좌표
end : 종료 좌표
color : BGR형태의 Color(ex; (255, 0, 0) -> Blue)
thickness (int) : 선의 두께. pixel
img = cv2.rectangle(img, (0,0), (250,350), (0,255,0), 3)
plt.imshow(img)
plt.show()

cv2.circle()
Parameters
img : 그림을 그릴 이미지
center : 원의 중심 좌표(x, y)
radian : 반지름
color : BGR형태의 Color
thickness : 선의 두께, -1 이면 원 안쪽을 채움
img = cv2.circle(img, (447, 63), 63, (0,0,255), -1)
plt.imshow(img)
plt.show()

img = cv2.circle(img, (63,447), 63, (0,255,255), 2)
plt.imshow(img)
plt.show()

cv2.ellipse()
Parameters
img : image
center : 타원의 중심
axes : 중심에서 가장 큰 거리와 작은 거리
angle : 타원의 기울기 각
startAngle : 타원의 시작 각도
endAngle : 타원이 끝나는 각도
color : 타원의 색
thickness : 선 두께. -1이면 안쪽을 채움
# 타원을 이미지에 그리기 (회색 타원)
# 중심 좌표: (255, 255), 축 길이: (100, 50), 회전 각도: 0, 호의 시작각도: 10도, 끝각도: 255도, 채우기: -1
img = cv2.ellipse(img, (255,255), (100,50), 0, 10, 255, -1)
plt.imshow(img)
plt.show()

# 타원을 이미지에 그리기 (흰색 타원)
# 중심 좌표: (255, 255), 축 길이: (150, 50), 회전 각도: 45, 호의 시작각도: 0도, 끝각도: 360도, 두께: 2
img = cv2.ellipse(img, (255,255), (150,50), 45, 0, 360, (255,255,255), 2)
plt.imshow(img)
plt.show()

# 타원을 이미지에 그리기 (파란색 부분 타원)
# 중심 좌표: (255, 255), 축 길이: (150, 10), 회전 각도: 135, 호의 시작각도: 0도, 끝각도: 270도, 두께: 2
img = cv2.ellipse(img, (255,255), (150,10), 135, 0, 270, (0,0,255), 2)
plt.imshow(img)
plt.show()

cv2.polylines()
Parameters
img : image
pts (array) : 연결할 꼭지점 좌표
isClosed : 닫힌 도형 여부
color : Color
thickness : 선 두께
이미지에 표현하기 위해 점 좌표를 3차원 행렬로 변환.
# 폴리라인(다각형)을 그리기
# 점들의 좌표 정의 (정수형 배열로 변환)
pts = np.array([[10,5], [20,30], [70,20], [50,10]], np.int32)
print(pts.shape)
(4, 2)
# 점들의 형태를 (n, 2, 1)에서 (n, 1, 2)로 변환
pts = pts.reshape((-1,2,1)) # 3차원 행렬로 변환하기 위해
print(pts.shape)
# 폴리라인 그리기, 닫힌 형태(True), 색상: 오렌지, 두께: 5
img = cv2.polylines(img, [pts], True, (0, 155, 255), 5)
(4, 2, 1)
plt.imshow(img)
plt.show()

# 또 다른 다각형 정의 및 폴리라인 그리기
pts2 = np.array([[150,5], [200,30], [100,70], [50,20]], np.int32)
print(pts2.shape)
(4, 2)
# (n, 2, 1) 형태에서 (n, 1, 2)로 변환
pts2 = pts2.reshape((-1,1,2))
print(pts2.shape)
# 폴리라인 그리기, 닫힌 형태(True), 색상: 연한 보라색, 두께: 4
img = cv2.polylines(img, [pts2], True, (172, 200, 255), 4)
(4, 1, 2)
plt.imshow(img)
plt.show()

cv2.putText()
Parameters
img : image
text : 표시할 문자열
org : 문자열이 표시될 위치. 문자열의 bottom-left corner 점
font : font type. CV2.FONT_XXX
fontSacle : Font Size
color : fond color
# 텍스트 추가
# 텍스트: 'OpenCV', 좌표: (10, 500), 폰트: SIMPLEX, 크기: 4, 색상: 흰색, 두께: 3
img = cv2.putText(img, 'OpenCV', (10, 500), cv2.FONT_HERSHEY_SIMPLEX, 4, (255,255,255), 3)
plt.imshow(img)
plt.show()

img = np.zeros((540,540), np.uint8)
plt.imshow(img, cmap='gray')
plt.show()

정답 :
img = cv2.line(img, (0, 0), (270, 540), 255, 4) # 왼쪽 위 → 중앙 아래
img = cv2.line(img, (0, 0), (540, 270), 255, 4) # 왼쪽 위 → 중앙 오른쪽
img = cv2.line(img, (540, 0), (0, 270), 255, 4) # 오른쪽 위 → 중앙 왼쪽
img = cv2.line(img, (540, 0), (270, 540), 255, 4) # 오른쪽 위 → 중앙 아래
img = cv2.line(img, (0, 540), (270, 0), 255, 4) # 왼쪽 아래 → 중앙 위
img = cv2.line(img, (0, 540), (540, 270), 255, 4) # 왼쪽 아래 → 중앙 오른쪽
img = cv2.line(img, (540, 540), (0, 270), 255, 4) # 오른쪽 아래 → 중앙 왼쪽
img = cv2.line(img, (540, 540), (270, 0), 255, 4) # 오른쪽 아래 → 중앙 위
필요에 따라 적절한 처리
resize(), flip(), getAffineTransform(), warpAffine() 등 다양한 메서드 존재
코드 출처 : https://opencv-python.readthedocs.io/en/latest/index.html
# 이미지 다운로드 주소 : https://upload.wikimedia.org/wikipedia/ko/2/24/Lenna.png
files.upload()
image = cv2.imread('Lenna.png')
print(image.shape)
cv2_imshow(image)

cv2.resize()
사이즈가 변하면 pixel사이의 값을 결정을 해야함
보간법(Interpolation method)
사이즈를 줄일 때 : cv2.INTER_AREA
사이즈를 크게 할 때 : cv2.INTER_CUBIC , cv2.INTER_LINEAR
Parameters
img : Image
dsize : Manual Size. 가로, 세로 형태의 tuple(ex; (100,200))
fx : 가로 사이즈의 배수. 2배로 크게하려면 2. 반으로 줄이려면 0.5
fy : 세로 사이즈의 배수
interpolation : 보간법
cv2_imshow(image)

'''
cv2.resize(src, dsize[, dst[, fx[, fy[, interpolation]]]])
여기서 각 인자는 다음을 의미합니다:
src: 크기를 조절하려는 원본 이미지입니다.
dsize: 새로운 이미지의 크기입니다. (width, height) 형식의 튜플로 지정할 수 있습니다.
dst: 선택적으로, 크기를 조절한 이미지를 저장할 곳을 지정합니다. 이 인자를 생략하면 함수가 새로운 이미지를 반환합니다.
fx: 선택적으로 가로 방향 크기의 배율 요인입니다.
fy: 선택적으로 세로 방향 크기의 배율 요인입니다.
interpolation: 선택적으로 크기 조절에 사용할 보간법을 지정합니다. 기본값은 cv2.INTER_LINEAR로, 선형 보간법을 사용합니다.
다른 옵션으로는 cv2.INTER_NEAREST, cv2.INTER_AREA, cv2.INTER_CUBIC, cv2.INTER_LANCZOS4 등이 있습니다.
'''
height, width = image.shape[:2]
print(height, width)
shrink = cv2.resize(image, (0,0), fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA) # 새 이미지의 크기를 직접 지정하지 않은 것
print(shrink.shape)
expand1 = cv2.resize(image, (width*2, height*2), interpolation=cv2.INTER_CUBIC) # 크기 2배 확대
print(expand1.shape)
expand2 = cv2.resize(image, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC) # 크기 2배 확대 (명시적 크기 미지정)
print(expand2.shape)
220 220
(110, 110, 3)
(440, 440, 3)
(440, 440, 3)
cv2_imshow(shrink) # 축소된 이미지 표시
cv2_imshow(expand1) # 확대된 이미지 표시 (방식 1)
cv2_imshow(expand2) # 확대된 이미지 표시 (방식 2)



이미지의 위치를 변경
cv2.warpAffine()
Parameters
src : Image
M : 변환 행렬
dsize (tuple) : output image size(ex; (width=columns, height=rows)
'''
[1, 0, 10], [0, 1, 20]
1: 축 방향의 스케일링 비율을 나타내며, 이 경우 1이므로 x축 방향의 크기 변화가 없음을 의미합니다.
0: y축으로부터 x축으로의 기울기(회전)를 나타냅니다. 0이므로 이 방향으로의 회전이 없음을 의미합니다.
10: x축 방향으로의 이동(변환)을 나타내며, 이 값은 이미지를 오른쪽으로 10 픽셀 이동시킵니다.
0: x축으로부터 y축으로의 기울기(회전)를 나타냅니다. 0이므로 이 방향으로의 회전이 없음을 의미합니다.
1: y축 방향의 스케일링 비율을 나타내며, 이 경우 1이므로 y축 방향의 크기 변화가 없음을 의미합니다.
20: y축 방향으로의 이동(변환)을 나타내며, 이 값은 이미지를 아래로 20 픽셀 이동시킵니다.
'''
# 이미지 이동 (Translation)
rows, cols = image.shape[:2]
M = np.float32([[1, 0, 10], [0, 1, 20]]) # 이동 행렬: x축 10 픽셀, y축 20 픽셀 이동
dst = cv2.warpAffine(image, M, (cols, rows)) # 이동 적용
cv2_imshow(dst) # 이동된 이미지 표시

물체를 평면상의 한 점을 중심으로 𝜃 만큼 회전하는 변환
양의 각도는 시계반대방향으로 회전
cv2.getRotationMatrix2D()
Parameters
center : 이미지의 중심 좌표
angle : 회전 각도
scale : scale factor
# 이미지 회전
rows, cols, _ = image.shape
M = cv2.getRotationMatrix2D((cols / 2, rows / 2), 45, 1) # 중심에서 45도 회전
rotated_img = cv2.warpAffine(image, M, (cols, rows)) # 회전 적용
cv2_imshow(rotated_img) # 회전된 이미지 표시

대칭 변환
좌우 대칭 (좌우 반전)
상하 대칭 (상하 반전)
입력 영상과 출력 영상의 픽셀이 1:1 매칭이므로 보간법이 필요 없음
cv2.flip()
Parameters
src : 입력 영상
flipCode : 대칭 방법을 결정하는 flag 인자
양수이면 좌우 대칭
0이면 상하 대칭
음수이면 상하, 좌우 대칭을 모두 실행
result2 = cv2.flip(image, 0) # 수직 뒤집기
result3 = cv2.flip(image, 1) # 수평 뒤집기
cv2_imshow(result2) # 수직 뒤집힌 이미지 표시
cv2_imshow(result3) # 수평 뒤집힌 이미지 표시


기하학적 도형의 크기와 각도를 변경할 수 있지만, 원점 간의 상대적인 위치(선의 평행성과 점들 간의 비율)는 보존
이동, 확대, Scale, 반전까지 포함된 변환
cv2.getAffineTransform()

# 원본 이미지의 크기와 채널 정보 가져오기
# rows: 이미지의 높이(픽셀 수), cols: 너비(픽셀 수), ch: 채널 수 (컬러 이미지라서 3)
rows, cols, ch = image.shape
# Affine 변환을 위한 원본 좌표와 이동 좌표 설정
# 원본 좌표: pts1 (Affine 변환 전의 3개 점)
pts1 = np.float32([[100, 50], [200, 50], [100, 100]])
# 이동 좌표: pts2 (Affine 변환 후의 3개 점)
pts2 = np.float32([[100, 150], [200, 100], [100, 200]])
# Affine 변환에서 사용하는 3개의 점 시각화를 위해 원본 이미지에 원 그리기
# 첫 번째 점 (200, 100) 빨간색 점
cv2.circle(image, (200, 100), 10, (255, 0, 0), -1) # 색상: 빨강 (BGR 형식), 두께: -1 (채우기)
# 두 번째 점 (400, 100) 초록색 점
cv2.circle(image, (400, 100), 10, (0, 255, 0), -1) # 색상: 초록 (BGR 형식), 두께: -1 (채우기)
# 세 번째 점 (200, 200) 파란색 점
cv2.circle(image, (200, 200), 10, (0, 0, 255), -1) # 색상: 파랑 (BGR 형식), 두께: -1 (채우기)
# 원본 좌표(pts1)와 이동 좌표(pts2)를 기반으로 Affine 변환 행렬 계산
# M: 2x3 변환 행렬, 이 행렬은 점들의 위치를 기반으로 전체 이미지를 변환하는 데 사용됨
M = cv2.getAffineTransform(pts1, pts2)
# Affine 변환 적용
# cv2.warpAffine(): 이미지를 변환 행렬(M)을 기반으로 변환
# cols, rows: 결과 이미지의 크기 (원본 이미지와 동일하게 설정)
dst = cv2.warpAffine(image, M, (cols, rows))
# 변환된 이미지(dst) 출력
cv2_imshow(dst) # Google Colab에서 이미지를 표시하는 함수
# 변환된 이미지의 크기 확인
print(dst.shape) # 변환 후에도 원본 이미지 크기(높이, 너비, 채널)가 유지됨

(220, 220, 3)
# 문제 1: 이미지 크기 조절
# 목표: 주어진 이미지의 크기를 사용자가 지정한 크기로 조절하는 기능을 구현합니다.
# 설명: 이미지를 200x200 크기로 조절해보세요. 다양한 보간법을 적용하며 결과를 비교합니다.
# 이미지 불러오기
img = cv2.imread('Lenna.png')
# 크기 조절
# 이미지 표시
plt.imshow(cv2.cvtColor(resized_img, cv2.COLOR_BGR2RGB))
plt.show()

# 문제 2: 이미지 뒤집기
# 목표: 주어진 이미지를 수평, 수직으로 뒤집는 기능을 구현합니다.
# 설명: 이미지를 수평, 수직으로 뒤집고 결과를 표시합니다.
# # 수평 뒤집기
# 수직 뒤집기
# 결과 표시
plt.imshow(cv2.cvtColor(flipped_img_hor, cv2.COLOR_BGR2RGB))
plt.show()
plt.imshow(cv2.cvtColor(flipped_img_ver, cv2.COLOR_BGR2RGB))
plt.show()


# 문제 3: 이미지 회전
# 목표: 주어진 이미지를 특정 각도로 회전시키는 기능을 구현합니다.
# 설명: 이미지를 45도 회전시키고 결과를 표시합니다.
# 회전을 위한 변환 행렬 생성
rows, cols, _ = img.shape
# 이미지 회전
# 결과 표시
plt.imshow(cv2.cvtColor(rotated_img, cv2.COLOR_BGR2RGB))
plt.show()

# 정답
# 문제 1
resized_img = cv2.resize(img, (200, 200), interpolation=cv2.INTER_LINEAR)
# 이미지 표시
plt.imshow(cv2.cvtColor(resized_img, cv2.COLOR_BGR2RGB))
plt.show()
# 문제 2
# # 수평 뒤집기
flipped_img_hor = cv2.flip(img, 1)
# 수직 뒤집기
flipped_img_ver = cv2.flip(img, 0)
# 결과 표시
plt.imshow(cv2.cvtColor(flipped_img_hor, cv2.COLOR_BGR2RGB))
plt.show()
plt.imshow(cv2.cvtColor(flipped_img_ver, cv2.COLOR_BGR2RGB))
plt.show()
# 문제 3
rows, cols, _ = img.shape
M = cv2.getRotationMatrix2D((cols/2, rows/2), 45, 1)
# 이미지 회전
rotated_img = cv2.warpAffine(img, M, (cols, rows))
# 결과 표시
plt.imshow(cv2.cvtColor(rotated_img, cv2.COLOR_BGR2RGB))
plt.show()