1. 이미지 사이즈 변환¶
- 이미지 변환 또한, 변형과 동일하게 전처리 과정으로 주로 활용된다.
- 이미지 변형은 데이터가 감소하지는 않지만, 이미지를 해석하기 위해 특징을 검출하기 쉬운 데이터로 만드는 것이다.
- 이미지 변환은 이미지 데이터의 갯수를 늘리거나 줄여 알고리즘 연산량을 줄이는 것을 주 목적으로 삼는다.
이미지 변환을 유형별로 분류하면 다음과 같다.
- 강체(Rigid) 변환
- 유사(Similarity) 변환
- 선형(Linear) 변환
- 아핀(Affine) 변환
- 원근(Perspective Transformation) 변환
- 강체 변환(유클리디언 변환) : 변환 기준점으로부터 크기와 각도가 보존되는 변환 -> 평행이동, 회전
- 강체 : 강체(剛體, Rigid body)란 물리학에서 형태가 고정되어 변하지 않는 물체를 가리킨다.
- 유사 변환 : 강체변환 + 크기 변환
- 선형 변환 : 벡터 공간에서의 이동
- 아핀 변환 : 선형 변환 + 이동 변환
- 가우시안 피라미드 :
- 업샘플링/다운샘플링 사용
- 업샘플링 : 입력 이미지에 새로운 행과 열을 추가하여 짝수 행과 짝수 열로 만들어 하위 이미지를 만든다. (MN -> 2M2N)
- 다운샘플링 : 짝수 행과 열을 제거해서 상위 이미지를 만든다. (MN -> M/2 N/2)
- 단계마다 이미지 면적의 차이는 4배가 된다.
- 이러한 연산은 서로 역연산처럼 보이지만, 그렇지 않다.(없는 데이터를 생성 vs 있는 데이터를 제거)
- 업샘플링 후 다시 다운샘플링하면, 이미지 피라미드가 적용된 이미지는 원본 이미지와 동일한 데이터를 갖고 있지 않다.
- 라플라시안 피라미드 :
- 감산 영상을 통해 생성
- $G_0 = Image$
$G_{i+1} = Down(G_i)$
$L_i = G_i - Up(G_{i+1})$
- 가우시안 피라미드 이미지에서 업샘플링된 이미지를 감산해서, 가우시안 피라미드의 차이를 계산한다.
- 즉, 가우시안 피라미드 레이어 간의 차이를 구하는 것을 의미한다.
- 이미지 경계선을 찾고, 그것을 단서로 이미지를 압축한다.
이미지 확대¶
Q1. python opencv의 이미지 확대 함수는 어떻게 표현되나요?
dst = cv2.pyrUp(src, dstSize=None, borderType=None)
- 입력 이미지(src)의 행과 열을 2배로 키워 이미지를 확대하는 변환 함수
- 입력 이미지에 새로운 행과 열을 추가해서 짝수 행과 짝수 열을 만들고, 0의 값을 할당한다.
- 할당된 0에 새로운 값을 넣어주어야 하므로 색상이 결정되지 않은 픽셀은 근삿값으로 채워 가우시안 필터로 컨볼루션을 진행한다.
- 이미지를 업샘플링한 후, 흐림 효과를 적용한다.
- dstSize(출력 이미지 크기) : 출력 이미지의 크기
- 가우시안 필터는 4의 값을 활용해 정규화한다.
- 새로 삽입된 요소는 모두 0 픽셀값을 가지고 있으며, 각 차원의 방향으로 2배씩 업샘플링을 수행했기 때문에(가로 2 * 세로 2)
- 평균 밝기로 복원하기 위해 4로 정규화된 커널을 사용한다.
- 출력 이미지의 크기는 일반적으로 2배 확대한 이미지가 되므로 가로2, 세로2의 크기가 된다.
- 출력 이미지의 크기를 할당해서 사용하려면
$ \left\vert dstsize.width - src.cols*2 \right\vert \leqq (dstsize.width mod 2) $
(출력 이미지의 너비 - 현재 이미지의 가로 2배) <= 출력 이미지의 너비를 2로 나눈 나머지보다 작아야 함 == 2배여야 함
이미지 축소¶
Q2. python opencv 이미지 축소 함수는 어떻게 표현되나요?
dst = cv2.pyrDown(src, dstSize=None, borderType=None)
- 입력 이미지의 행과 열을 2배로 축소해서 이미지를 축소하는 변환 함수
- 이미지에 흐림 효과를 적용한 후
- 다운샘플링을 진행한다.
- 이 때, 이미지를 2배 작게 만들어야 하므로 홀수 크기 이미지에는 +1을 해서 짝수로 만들고 2를 진행한다.
- 이미지 축소 함수는 $G_i$ 이미지를 컨벌루션하고, 모든 짝수 행과 짝수 열을 제거해 $G_{i+1}$을 생성한다.
- 생성된 레이어(축소된 이미지)는 입력 이미지의 $\frac{1}{!}$에 해당하는 면적을 갖는다.
- 출력이미지를 지정한다면 다음 조건을 충족해야 한다.
$ \left\vert dstsize.width*2 - src.cols \right\vert \leqq 2 $
Q3. 하나의 이미지를 불러와, 예제 6.2(python opencv에서의 이미지 축소)를 시험해보세요.
import cv2
import os
import matplotlib.pyplot as plt
image_path = os.getenv('HOME') + '/ShowMeTheCV/Jeongeun/PostingPic/5_practice.jpg'
src = cv2.imread(image_path)
print('불러온 이미지 사이즈 :' , src.shape)
plt.imshow(src)
불러온 이미지 사이즈 : (384, 512, 3)
<matplotlib.image.AxesImage at 0x7f8e62706390>
dst = src.copy()
for i in range(2):
dst = cv2.pyrDown(dst)
plt.imshow(dst)
print('축소한 이미지 크기', dst.shape)
축소한 이미지 크기 (96, 128, 3)
이미지 크기 조절¶
- 이미지 크기 변형은 단순한 연산이 아니다.
- 이미지를 확대하는 경우 픽셀에 대한 보간법, 이미지를 축소하는 경우 픽셀에 대한 병합법이 수행된다.
- 앞에서 수행한 이미지 확대/축소의 경우, 2배 확대/축소만 가능하므로 일반적으로 다른 함수를 활용하여 이미지 크기를 조절해야 한다.
- 이 이미지 크기 조절 함수는 사용자가 원하는 크기로 이미지를 변환한다.
- 이미지의 크기를 사용자가 원하는 절대 크기로 변경하는 것
- 이미지의 크기를 비율에 맞게 상대 크기로 변경하는 것
Q4. python opencv의 이미지 크기 조절 함수는 어떻게 표현되나요?
dst = cv2.resize(src, dsize, fx=None, fy=None, interpolation=None)
- 절대크기(dsize)
- 상대크기(fx, fy)
- 상대 크기를 사용할 때도 절대 크기에는 값을 할당해줘야 하는데, fx,fy에 할당된 크기가 dsize에 할당되지 때문이다.
- 절대 크기가 우선이며, 필수 요소이다.
interpolation(보간법)¶
- 이미지 피라미드는 변경될 크기가 고정되어 있어 가우시안/라플라시안 피라미드를 통해 처리할 수 있지만,
- 이미지 크기 조절 함수는 어떤 크기로 변환될 지 알 수 없으므로 보간법을 활용한다.
- 이미지의 비율을 변경하면 존재하지 않는 영역에 새로운 픽셀값을 매핑하거나 존재하는 픽셀을 압축해서 새 값을 할당해야 한다.
- 이는 이미지 상에 존재하는 픽셀 데이터 $(x_i, y_i)$에 대한 근사함수 $f(x,y)$를 구하여 새로운 함수 값을 구하는 것으로 이해할 수 있다.
- 이미지 확대 : 이미 있는 픽셀들을 결과 이미지의 새 좌표로 매핑하고, 군데군데 빈 값을 보간을 통해 메꿔준다.
- 이미지 축소 : 입력 이미지의 픽셀이 결과 이미지의 좌표로 매핑될 때, 가장 근사값을 갖는 좌표로 픽셀값이 매핑된다.
- 하지만 이 때 사용되는 픽셀들은 대부분 분수 픽셀(실수 좌표의 픽셀) 이다.
- 그러므로, 새 값을 찾을 때 보간법의 유형에 따라 어떤 값으로 메꿀지가 결정된다.
- 가장 가까운 이웃 보간법 : 분수 픽셀 위치에서 가장 가까운 원본 픽셀을 결과 이미지의 픽셀값으로 사용
- 쌍선형 보간법 : 분수 픽셀 위치에서 2*2 크기의 주변 원본 픽셀과 가까운 거리에 따라 선형적으로 가중치를 할당해서 결과 이미지의 픽셀값으로 사용한다.
- 영역 보간법 : 픽셀 간의 관계를 고려한다.(결과 이미지의 픽셀 위치를 입력 이미지의 픽셀 위치에 배치하고, 겹치는 영역의 평균을 구해 결과값으로 사용한다.)
- 바이 큐빅 보간법
- 이미지를 확대할 때는 주로 쌍선형 보간법이나 바이큐빅 보간법을 사용한다.
- 이미지를 축소할 때는 영역 보간법을 주로 활용한다.
Q5. 이미지를 불러와 사이즈를 조절해보세요.(예제 6.4(python opencv의 이미지 크기 조절))
import cv2
import os
import matplotlib.pyplot as plt
image_path = os.getenv('HOME') + '/ShowMeTheCV/Jeongeun/PostingPic/5_practice.jpg'
src = cv2.imread(image_path)
print('불러온 이미지 사이즈 :' , src.shape)
plt.imshow(src)
불러온 이미지 사이즈 : (384, 512, 3)
<matplotlib.image.AxesImage at 0x7f8e605ee910>
#일정 영역을 관심 영역으로 설정
dst = src[250:350, 450:550]
plt.imshow(dst)
<matplotlib.image.AxesImage at 0x7f8e604bb610>
dst = cv2.resize(dst, dsize=(256,256), interpolation=cv2.INTER_NEAREST)
plt.imshow(dst)
<matplotlib.image.AxesImage at 0x7f8e60426250>
2. 대칭&회전¶
- 대칭은 기하학적인 측면에서 반사(reflection)의 의미를 갖는다.
- 대칭은 변환할 행렬에 대해 2*2 행렬을 왼쪽 곱셈한다. 즉, 'p'형태의 그림에 Y축 대칭을 적용해 'q'를 만든다.
- 원본에 반사 행렬을 적용하여 값을 뒤집는다.
Q6. python opencv의 대칭 함수는 어떻게 표현되나요?
cv2.flip(src, flipCode)
- 대칭 축 플래그 : XY(상하좌우대칭), X(상하대칭), Y(좌우대칭)
회전¶
- 선형 변환 중의 하나
- 종류
- 회전 변환 행렬 : 임의의 점을 중심으로 물체를 회전시킨다.
- 좌푯값을 회전시키는 회전 행렬
- 좌표축을 회전시키는 회전 행렬
- 회전 변환 행렬 : 임의의 점을 중심으로 물체를 회전시킨다.
Q7. 예제 6.6(python opencv)을 테스트하여 이미지를 대칭/회전시켜보세요.
import math
src = cv2.imread(image_path)
height, width, _ = src.shape
#중심점을 기준으로 회전해야 하므로 중점 픽셀을 구해줌
center = (width/2, height/2)
angle=90
scale=0.5
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))
plt.imshow(dst)
<matplotlib.image.AxesImage at 0x7f8e60395c50>
3. 기하학적 변환¶
- 기하학적 변환이란, 이미지를 인위적으로 확대, 축소, 위치변경, 회전, 왜곡하는 등 이미지의 형태를 변환하는 것을 의미한다.
- == 이미지를 구성하는 픽셀 좌표값의 위치를 재배치하는 과정이다.
- 이차원 공간에서의 기하학적 변환에는
- 아핀 변환(Affine Transformation)
- 원근 변환(Perspective Transformation)
이 해당된다.
아핀 변환(Affine)¶
- 아핀 변환 행렬의 기본적인 변환 형태는 3*3 형태이다.
- 하지만, 아핀 변환은 선의 수평성을 유지하며, 변환 전에 서로 평행했던 선은 변환 후에도 평행하다.
Q8. python opencv의 아핀 변환 함수는 어떻게 생겼나요?
m = cv2.getAffineTransform(src, dst)
- src == 변환 전 세 개의 픽셀좌표
- dst == 변환 후 세 개의 픽셀좌표
- m = 아핀 맵 행렬
dst = cv2.warpAffine(src, M, dsize, dst=None, flags=None, borderMode=None, borderValue=None)
- 입력 이미지(src)
- 아핀 맵 행렬(M)
- 출력이미지(dst)
- 출력이미지 크기(dsize)
- 보간법(flags)
- 테두리 외삽법(borderMode)
- 테두리 색상(borderValue)
Q17. affine 변환 예제를 확인하고, 4가지 예제 중 하나를 골라 이미지에 적용해보세요.
import numpy as np
import cv2
def affine_transform():
src = cv2.imread('tekapo.bmp')
if src is None:
print('Image load failed!')
return
rows = src.shape[0]
cols = src.shape[1]
src_pts = np.array([[0, 0],
[cols - 1, 0],
[cols - 1, rows - 1]]).astype(np.float32)
dst_pts = np.array([[50, 50],
[cols - 100, 100],
[cols - 50, rows - 50]]).astype(np.float32)
affine_mat = cv2.getAffineTransform(src_pts, dst_pts)
dst = cv2.warpAffine(src, affine_mat, (0, 0))
cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()
def affine_translation():
src = cv2.imread('tekapo.bmp')
if src is None:
print('Image load failed!')
return
affine_mat = np.array([[1, 0, 150],
[0, 1, 100]]).astype(np.float32)
dst = cv2.warpAffine(src, affine_mat, (0, 0))
cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()
def affine_shear():
src = cv2.imread('tekapo.bmp')
if src is None:
print('Image load failed!')
return
rows = src.shape[0]
cols = src.shape[1]
mx = 0.3
affine_mat = np.array([[1, mx, 0],
[0, 1, 0]]).astype(np.float32)
dst = cv2.warpAffine(src, affine_mat, (int(cols + rows * mx), rows))
cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()
def affine_scale():
src = cv2.imread('rose.bmp')
if src is None:
print('Image load failed!')
return
dst1 = cv2.resize(src, (0, 0), fx=4, fy=4, interpolation=cv2.INTER_NEAREST)
dst2 = cv2.resize(src, (1920, 1280))
dst3 = cv2.resize(src, (1920, 1280), interpolation=cv2.INTER_CUBIC)
dst4 = cv2.resize(src, (1920, 1280), interpolation=cv2.INTER_LANCZOS4)
cv2.imshow('src', src)
cv2.imshow('dst1', dst1[400:800, 500:900])
cv2.imshow('dst2', dst2[400:800, 500:900])
cv2.imshow('dst3', dst3[400:800, 500:900])
cv2.imshow('dst4', dst4[400:800, 500:900])
cv2.waitKey()
cv2.destroyAllWindows()
def affine_rotation():
src = cv2.imread('tekapo.bmp')
if src is None:
print('Image load failed!')
return
cp = (src.shape[1] / 2, src.shape[0] / 2)
affine_mat = cv2.getRotationMatrix2D(cp, 20, 1)
dst = cv2.warpAffine(src, affine_mat, (0, 0))
cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()
def affine_flip():
src = cv2.imread('eastsea.bmp')
if src is None:
print('Image load failed!')
return
cv2.imshow('src', src)
for flip_code in [1, 0, -1]:
dst = cv2.flip(src, flip_code)
desc = 'flipCode: %d' % flip_code
cv2.putText(dst, desc, (10, 30), cv2.FONT_HERSHEY_SIMPLEX,
1.0, (255, 0, 0), 1, cv2.LINE_AA)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()
if __name__ == '__main__':
affine_transform()
affine_translation()
affine_shear()
affine_scale()
affine_rotation()
affine_flip()
import os
import cv2
import matplotlib.pyplot as plt
image_path = os.getenv('HOME') + '/ShowMeTheCV/Jeongeun/PostingPic/pooh.jpg'
image = cv2.imread(image_path)
plt.imshow(image)
<matplotlib.image.AxesImage at 0x7feb780f5710>
def affine_flip(image_path):
src = cv2.imread(image_path)
if src is None:
print('Image load failed!')
return
for flip_code in [1, 0, -1]:
dst = cv2.flip(src, flip_code)
desc = 'flipCode: %d' % flip_code
cv2.putText(dst, desc, (10, 30), cv2.FONT_HERSHEY_SIMPLEX,
1.0, (255, 0, 0), 1, cv2.LINE_AA)
plt.imshow(dst)
affine_flip(image_path)
원근 변환¶
- 원근 변환 행렬의 형태는 3*3이다.
- 원근 변환 행렬은 아핀 변환에서 유지되는 수평성은 유지되지 않는다.
- 원근 변환은 뒤틀림이나 원근 왜곡을 포함 하므로 더 많은 미지수를 요구한다.

Q9. python opencv의 원근 맵 행렬 생성 함수와, 원근 변환 함수는 어떻게 생겼나요?
m = cv2.getPerspectiveTransform(src, dst)
- 변환 전 네 개의 픽셀좌표(src)
- 변환 후 네 개의 픽셀좌표(dst)
Q10. 예제 6.8(python opencv의 원근 변환)를 참고하여, 이미지를 원근 변환 시켜보세요.
dst = cv2.warpPerspective(src, M, dsize, dst=None, flags=None, borderMode=None, borderValue=None)
- 입력 이미지(src)
- 아핀 맵 행렬(M)
- 출력이미지(dst)
- 출력이미지 크기(dsize)
- 보간법(flags)
- 테두리 외삽법(borderMode)
- 테두리 색상(borderValue)
import numpy as np
src= cv2.imread(image_path)
width, height, _ = target.shape
src_pts = np.array([[0.0, 0.0], [width, 0.0], [width, height], [0.0, height]], dtype=np.float32)
dst_pts = np.array([[10, 80], [200, 80], [40, 150], [350,110]], dtype=np.float32)
M = cv2.getPerspectiveTransform(src_pts, dst_pts)
dst = cv2.warpPerspective(src, M, (width, height), borderValue=(255,0,0))
plt.imshow(dst)
<matplotlib.image.AxesImage at 0x7feb74762dd0>
4. 모폴로지 변환¶
- 모폴로지 변환이란, 영상이나 이미지를 형태학적 관점에서 접근하는 기법을 의미한다.
- 모폴로지 변환은 주로 영상 내 픽셀 값 대체에 사용되는데, 이를 통해 노이즈 제거, 요소 결합 및 분리, 강도 피크 검출 등에 이용할 수 있다.
- 모폴로지 변환에서는
- 집합의 포함 관계,
- 이동(translation),
- 대칭(reflection),
- 여집합(complement),
- 차집합(difference)
성질을 활용한다.
- 기본적인 모폴로지 변환으로는 팽창과 침식 이 있다.
- 팽창과 침식은 이미지와 커널의 콘볼루션 연산이다.
- 모폴로지 변환을 통해 피크 를 검출하거나, 그래디언트 를 정의할 수 있다.
- 팽창은 커널 영역 안에 있는 모든 픽셀의 값을 커널 내부의 극대값 으로 대체한다.
- 구조 요소를 활용해 이웃한 픽셀을 최대 픽셀값으로 대체한다.
- 팽창 연산을 적용하면 어두운 영역이 줄어들고, 밝은 영역이 늘어난다.
- 커널의 크기나 반복 횟수에 따라 밝은 영역이 늘어나 스펙클이 커지고, 객체 내부의 홀이 사라진다.
- 노이즈 제거 후 줄어든 크기를 복구하고자 할 때 주로 사용한다.
- 스펙클 : 작은 반점
- 침식은 커널 영역 안에 있는 모든 픽셀의 값을 커널 내부의 극소값 으로 대체한다.
- 구조 요소를 활용해 이웃한 픽셀을 최소 픽셀값으로 대체한다.
- 밝은 영역이 줄어들고, 어두운 영역이 늘어난다.
- 스펙클이 줄어들고, 객체 내부의 홀이 커진다.
- 노이즈 제거에 주로 사용한다.
Q11. python opencv의 구조 요소 생성 함수는 어떻게 생겼나요?
kernel = cv2.getStructuringElement(shape, size)
shape = 커널 형태 ksize = 커널 사이즈
- 구조요소 생성 함수는 커널의 형태를 설정할 수 있으며, 직사각형, 십자가, 타원 모양으로 구조 요소를 생성한다.
Q12. python opencv의 팽창 함수는 어떻게 생겼나요?
dst = cv2.dilate(src, kernel, anchor=None, iterations=None, borderType=None, borerValue=None)
Q13. python opencv의 침식 함수는 어떻게 생겼나요?
dst = cv2.erode(src, kernel, anchor=None, iterations=None, borderType=None, borerValue=None)
Q14. 예제 6.10(python opencv의 모폴로지 침식)을 활용하여 이미지를 변경시켜 보세요.
image_path = os.getenv('HOME') + '/ShowMeTheCV/Jeongeun/PostingPic/test.jpg'
src = cv2.imread(image_path)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5), anchor=(-1,-1))
dst = cv2.erode(src, kernel, iterations=5)
plt.imshow(src)
<matplotlib.image.AxesImage at 0x7feb745b8c90>
모폴로지 연산¶
- 모폴로지 연산은 모폴로지 변환 + 고급 형태학을 적용하는 변환 연산 이다.
- 입력 이미지가 이진화 된 이미지라면 팽창과 침식 연산으로도 우수한 결과를 얻는다.
- 하지만, 일반적인 이미지는 다중 채널 이미지를 이용하기 때문에 더 복잡한 연산이 필요하다.
- 이 때, 모폴로지 연산을 활용해 우수한 결과를 얻을 수 있다.
Q15.python opencv의 모폴로지 연산 함수는 어떻게 생겼나요? 각 부분은 어떤 의미를 갖나요?
dst = cv2.morphologyEx(src, op, kernel, anchor=None, iterations=None, borderType=None, borerValue=None)
1. 열림 연산¶
열림 연산 : 팽창 연산자 + 침식 연산자의 조합. 침식 연산 이후 팽창 연산을 적용한다.
2. 닫힘 연산¶
닫힘 연산 : 팽창 연산자 + 침식 연산자의 조합. 팽창 연산 후 침식 연산을 적용한다.
3. 그래디언트 연산¶
그래디언트 연산 : 팽창 연산자 + 침식 연산자의 조합. 입력 이미지에 각각 팽창, 침식을 적용하고 감산을 진행한다.
- 각각의 연산을 감산하게 되면, 객체의 가장자리가 반환된다.
- 그래디언트는 밝은 영역의 가장자리를 분리하며 그레이스케일 이미지가 가장 급격하게 변하는 곳에서 가장 높은 결과를 반환한다.
- (즉, 선을 뚜렷하게 나타나게 한다.)
4. 탑햇 연산¶
탑햇 연산 : 입력 이미지에 열림 연산을 적용한 이미지를 감산한다. (이미지 + 열림 연산 + 감산)
5. 블랙햇 연산¶
블랙햇 연산 : 입력 이미지에 닫힘 연산을 적용한 이미지를 감산한다. (이미지 + 닫힘 연산 + 감산)
6. 히트미스 연산¶
히트미스 연산 : 히트미스 연산은 단일 채널 이미지에 적용되며, 주로 이진화 이미지에 적용한다.
- 내부 요소의 값은 0 혹은 1만 의미가 있다.
- 0은 해당 픽셀을 무시한다, 1은 해당 픽셀을 고려한다는 의미이다.
- 이 특성 덕분에 히트미스 연산을 __모서리를 검출하는 데 활용하기도 한다.__
'OpenCV > 쇼미더CV' 카테고리의 다른 글
쇼미더 CV_일곱번째 날 (0) | 2021.06.20 |
---|---|
쇼미더 CV_여섯번째 날 (0) | 2021.06.20 |
쇼미더 CV_넷째 날 (0) | 2021.06.20 |
쇼미더CV_셋째 날 (0) | 2021.06.20 |
쇼미더 CV_첫째 날 (0) | 2021.06.20 |