1. OpenCV의 채널 처리 함수에 대해 아는 대로 기술하시오.
컬러 영상은 파란색, 녹색, 빨간색의 각기 독립적인 2창원 정보를 합쳐 놓은 배열로 정의가 가능하며, 이를 표현하기 위해 채널이라는 개념이 도입됐다. 일련의 3개 원소로 하나의 컬러 화소가 구성되며, numpy에서는 화소 단위로 순회한다. 이 컬러 배열을 분리하면 각 채널을 단일채널 행렬로 구성할 수 있으며, 세부적인 영상처리에 이용될 수 있다. (교재 162p)
cv2의 채널 처리 함수로는 cv2.merge()와 cv2.split()이 있으며, 각 함수는 아래와 같다.
cv2.merge(mv[,dst]) → dst ▪ 설명: 여러 개의 단일채널 배열을 다채널 배열로 합성한다. |
||
인수 설명 | mv | 합성될 입력 혹은 배열 벡터, 합성될 단일채널 배열들의 크기와 깊이(depth)가 동일해야 함 |
dst | 입력 배열과 같은 크기와 같은 깊이의 출력 배열 | |
cv2.split(m[, mv]) → mv ▪ 설명: 다채널 배열을 여러 개의 단일채널 배열로 분리한다. |
||
인수 설명 | m | 입력되는 다채널 배열 |
mv | 분리되어 반환되는 단일채널 배열들의 벡터 |
(교재 165p)
2. OpenCV의 사칙 연산을 수행하는 함수와 연산의 수행 방법에 대해 기술하시오.
두 배열의 원소간(per-element) 사칙 연산을 수행하도록 cv2.add(), cv2.substract(), cv2.multiply(), cv2.divide() 함수를 제공한다. 이 함수들은 두 행렬의 원소간에 연산을 수행한다. 이때, 마스크 행렬을 이용해서 특정 영역만 연산을 수행할 수 있다.
(교재 208p)
cv2.add(src, src2[,dst[, mask[, dtype]]]) → dst ▪ 설명: 두 개의 배열 혹은 배열과 스칼라의 각 원소 간 합을 계산한다. 입력 인수 src1, src2 중 하나는 스칼라값일 수 있다. ▪ 수식: dsti=saturatesrc1i+src2i if maski≠0 dsti=saturatesrc1+src2i if maski≠0 dsti=saturatesrc1i+src2 if maski≠0 |
||
인수 설명 | src1 | 첫 번째 입력 배열 혹은 스칼라 |
src2 | 두 번째 입력 배열 혹은 스칼라 | |
dst | 계산된 결과의 출력 배열 | |
mask | 연산 마스크: 0이 아닌 마스크 원소의 위치만 연산 수행(8비트 단일채널) | |
dtype | 출력 배열의 깊이 | |
cv2.substract(src1, src2[, dst[, mask[, dtype]]]) → dst ▪ 설명: 두 개의 배열 혹은 배열과 스칼라의 각 원소 간 차분을 계산한다. add()함수의 인수와 동일하다. ▪ 수식: dsti=saturatesrc1i-src2i if maski≠0 dsti=saturatesrc1-src2i if maski≠0 dsti=saturatesrc1i-src2 if maski≠0 |
||
cv2.multiply(src1, src2[, dst[, scale[, dtype]]]) → dst ▪ 설명: 두 배열의 각 원소 간 곱을 계산한다. ▪ 수식: dsti=saturatescale⋅src1i⋅src2i |
||
인수 설명 | scale | 두 배열의 운소 간 곱할 때 추가로 곱해주는 배율 |
cv2.divide(src1, src2[, dst[, scale[, dtype]]]) → dst ▪ 설명: 두 배열의 각 원소 간 나눗샘을 수행한다. ▪ 수식: dsti=saturatescale⋅src1i/src2(i) |
||
cv2.divide(scale, src2[, dst[, dtype]]) → dst ▪ 설명: 스칼라값과 행렬원소간 나눗셈을 수행한다. ▪ 수식: dsti=scale/src2(i) |
||
cv2.addWeighted(src1, alpha, src2, beta, gamma[, dst[, dtype]]) → dst ▪ 설명: 두 배열의 각 원소에 가중치를 곱한 수에 각 원소 간 합 즉, 가중된(weighted) 합을 계산한다. ▪ 수식: dsti=saturatesrc1i⋅alpha+src2i⋅beta+gamma |
||
인수 설명 | alpha | 첫 번째 배열의 모든 원소에 대한 가중치 |
beta | 두 번째 배열의 모든 원소에 대한 가중치 | |
gamma | 두 배열의 원소 간 합에 추가로 더해주는 스칼라 |
(교재 170-171p)
3. 행렬(ndarray)을 초기화하는 방법들에 대해서 기술하고, 각 방법으로 선언하시오.
numpy에서 다양한 행렬 초기화 메소드를 제공한다. np.zeros()는 0으로 채워진 행렬을 초기화하며, np.ones()는 1로 채워진 행렬, np.full()은 지정된 값으로 채워진 행렬을 반환한다.
np.zeros() | np.zeros((2,4), np.uint8) # 0으로 채워진 2행 4열 uint8 자료형의 행렬 생성 |
np.ones() | np.ones((2,4), np.uint8) # 1으로 채워진 2행 4열 uint8 자료형의 행렬 생성 |
np.full() | np.full((2,4), 30, np.uint8) # 30으로 채워진 2행 4열 uint8 자료형의 행렬 생성 |
4. 사칙 연산이나 논리 비트 연산에서 마스킹(masking)을 사용할 수 있다. 마스크 행렬에 대한 의미와 사용법에 대해서 설명하시오.
사칙 연산 함수, 논리 비트 연산 함수에서 입력 인수로 mask를 제공한다. Mask는 8비트 단일채널 배열을 가지며, 마스크 배열의 원소가 0이 아닌 좌표만 계산을 수행한다.
예를 들어 마스크 배열이 [[0, 1], [1, 1]]의 구조를 가진다면 값이 0인 (0, 0) 좌표는 해당 함수가 수행하는 연산을 수행하지 않는다.
[사용 예시]
소스코드 |
import numpy as np, cv2 src1 = np.arange(4, dtype=np.uint8).reshape(2,2) src2 = np.arange(4, 8, dtype=np.uint8).reshape(2,2) mask = np.array([0,1,1,1], dtype=np.uint8).reshape(2,2) add_without_mask = cv2.add(src1, src2) add_with_mask = cv2.add(src1, src2, mask=mask) print('add_without_mask =\n', add_without_mask) print('add_with_mask =\n', add_with_mask) |
5. cv2.reduce() 함수에 대해서 설명하고, 특히 축소 시에 사용하는 연산 옵션에 대해서 상세히 설명하시오.
cv2.reduce(src, dim, rtype[, dst[, dtype]]) → dst ▪ 설명: 행렬을 열방향/행방향으로 옵션 상수(rtype)에 따라 축소한다. |
||
인수 설명 | src | 2차원 입력 배열(np.float32, np.float64에서만 수행 가능) |
dst | 출력 벡터, 감소방향과 타입은 dim, dtype 인수에 따라 정해짐 | |
dim | 행렬이 축소소딜 때 차원 감소 첨자
|
|
rtype | 축소 연산 종류 |
|
dtype | 감소된 벡터의 자료형 |
6. 다음 예시 코드는 컴파일 에러가 발생한다. 에러가 발생하는 부분을 수정하고 실행 결과를 적으시오.
소스코드 |
import numpy as np, cv2 # m1과 m2를 list에서 ndarray로 수정 m1 = np.array([1,2,3,1,2,3]) m2 = np.array([3,3,4,2,2,3]) m3 = m1 + m2 m4 = m1 - m2 print("[m1] = %s" % m1) print("[m2] = %s" % m2) print("[m3] = %s" % m3) print("[m3] = %s" % m3) |
소스코드 |
import numpy as np, cv2 data = [1,2,3,4,5,6,7,8,9,10,11,12] # reshape(2,3,2)를 하면 2채널이 되어 rgb 3개의 채널로 분리가 불가함. 따라서 reshape(2,2,3)으로 수정 m1 = np.array(data).reshape(2,2,3) r,g,b = cv2.split(m1) print("[m1] = %s" % m1) print("[r, g, b] = %s, %s, %s" % (r,g,b)) |
7. 다음의 컬러 영상파일(logo.jpg)을 입력 받아서 RGB의 3개 채널을 분리하고, 각 채널을 컬러 영상을 윈도우에 표시해 보자. 즉 Red 채널은 빨간색으로, green 채널은 초록색으로, Blue 채널은 파란색으로 표현되도록 다음의 프로그램을 완성하시오.
소스코드 |
import numpy as np, cv2 logo = cv2.imread('images/logo.jpg', cv2.IMREAD_COLOR) if logo is None: raise Exception("영상파일 읽기 오류") blue, green, red = cv2.split(logo) zeros = np.zeros(blue.shape, dtype=np.uint8) blue_img = cv2.merge([blue, zeros, zeros]) green_img = cv2.merge([zeros, green, zeros]) red_img = cv2.merge([zeros, zeros, red]) # 윈도우 자동 정렬 titles = ['logo']+[_+"_img" for _ in ["blue", "green", "red"]] x, y = 100, 100 for title in titles: cv2.namedWindow(title, cv2.WINDOW_AUTOSIZE) cv2.moveWindow(title, x, y) x += blue.shape[1] + 10 cv2.imshow(title, eval(title)) cv2.waitKey(0) cv2.destroyAllWindows() |
8. 다음 영상에서 특정 영역의 타원반을 복사하여 새 창에 표시하는 프로그램을 완성하시오.
소스코드 |
import numpy as np, cv2 image = cv2.imread("images/color.jpg", cv2.IMREAD_COLOR) if image is None: raise Exception("영상파일 읽기 오류") mask = np.zeros(image.shape[:2], np.uint8) center = (190, 170) cv2.ellipse(mask, center, (30,60), 0,0,360,255, -1) dst = cv2.bitwise_and(image, image, mask=mask) # 윈도우 정렬 x = y = 100 for title in ['image', 'dst']: cv2.namedWindow(title, 1) cv2.moveWindow(title, x, y) x += image.shape[1] + 10 cv2.imshow(title, eval(title)) cv2.waitKey(0) cv2.destroyAllWindows() |
9. 3행, 6열의 행렬을 생성하고, 행렬의 원소를 초기화한 후에 cv2.reduce() 함수를 이용해서 가로 방향과 세로 방향으로 감축하여 평균을 구한 결과를 출력하시오.
소스코드 |
import numpy as np, cv2 arr = np.random.rand(3, 6) * 1000//10 col_avg = cv2.reduce(arr, dim=0, rtype=cv2.REDUCE_AVG) # 행방향 축소 -> 열 평균 row_avg = cv2.reduce(arr, dim=1, rtype=cv2.REDUCE_AVG) # 열방향 축소 -> 행 평균 print('arr = \n', arr) print('열 평균 = ', col_avg.flatten().round(2)) print('행 평균 = ', row_avg.flatten().round(2)) |
10. PC 카메라로 영상을 읽어서 특정 부분(관심 영역)의 합과 평균을 구하는 프로그램을 작성하시오.
소스코드 |
import numpy as np, cv2 capture = cv2.VideoCapture(1) if capture.isOpened() == False: raise Exception("카메라 연결 안됨") def print_info(frame, slices=(slice(200, 400), slice(100, 300))): target = frame[slices] cv2_mean = cv2.mean(target) target_sum = np.zeros(3) for y in range(target.shape[0]): for x in range(target.shape[1]): target_sum += target[y][x] user_mean = target_sum / (target.shape[0] * target.shape[1]) print("cv2로 구한 평균 = ", cv2_mean) print("원소 순회로 구한 평균 = ", tuple(user_mean)) print("관심영역 합 = ", target_sum) while True: ret, frame = capture.read() if not ret: break if cv2.waitKey(30) >= 0: break print('[밝은 부분]') print_info(frame, (slice(200, 400), slice(100, 300))) print('[어두운 부분]') print_info(frame, (slice(-200, -100), slice(-200, -100))) exposure = capture.get(cv2.CAP_PROP_EXPOSURE) title = "View Frame from Camera" cv2.imshow(title, frame) # 윈도우에 영상 띄우기 capture.release() cv2.destroyAllWindows() |
11. PC 카메라로 영상을 받아들여서 다음과 같이 윈도우의 특정 영역에서 재생하시오.
소스코드 |
import numpy as np, cv2 capture = cv2.VideoCapture(1) if capture.isOpened() == False: raise Exception("카메라 연결 안됨") bg = np.zeros((300, 400, 3), dtype=np.uint8) mask = np.zeros((300, 400), dtype=np.uint8) cv2.rectangle(mask, (30, 30), (30+320, 30+240), 255, -1) while True: ret, frame = capture.read() if not ret: break if cv2.waitKey(30) >= 0: break target = cv2.resize(frame, (400, 300)) dst = cv2.bitwise_and(target, target, mask=mask) cv2.rectangle(dst, (30, 30), (30+320, 30+240), (0,0,255), 2) exposure = capture.get(cv2.CAP_PROP_EXPOSURE) title = "View Frame from Camera" cv2.imshow(title, dst) # 윈도우에 영상 띄우기 capture.release() cv2.destroyAllWindows() |
12. 영상파일을 읽어서 메인 윈도우에 다음과 같이 출력하시오.
소스코드 |
image = cv2.imread('images/abs_test1.jpg', cv2.IMREAD_GRAYSCALE) if image is None: raise Exception("영상파일 읽기 오류") mask1 = np.zeros(image.shape[:2], image.dtype) cv2.rectangle(mask1, (50, 50, 100, 100), 255, -1) cv2.add(image, 50, image, mask1) target = image[200:300, 200:300] noimage = np.zeros(target.shape[:2], target.dtype) avg = cv2.mean(image)[0]/2.0 target = cv2.addWeighted(target, 2.0, noimage, 0, -avg) image[200:300, 200:300] = target cv2.imshow("12", image) cv2.waitKey(0) cv2.destroyAllWindows() |
13. cv2.sortIdx() 함수를 활용해서 다음의 조건에 부합하도록 벡터의 원소를 정렬하시오.
소스코드 |
import numpy as np, cv2 def print_rects(rects): print("-" * 46) print("사각형 원소\t\t랜덤 사각형 정보\t\t 넓이") print("-" * 46) for i, (x,y, w,h, a) in enumerate(rects): print("rects[%i] = [(%3d,%3d) from (%3d,%3d)] %5d" %(i, w, h, x, y, a)) print() rands = np.zeros((10, 5), np.uint16) starts = cv2.randn(rands[:, :2 ], 100, 50) ends = cv2.randn(rands[:, 2:-1], 300, 50) sizes = cv2.absdiff(starts, ends) areas = sizes[:, 0] * sizes[:, 1] rects = rands.copy() rects[:, 2:-1] = sizes rects[:,-1] = areas idx = cv2.sortIdx(areas, cv2.SORT_EVERY_COLUMN + cv2.SORT_ASCENDING).flatten() print_rects(rects) print_rects(rects[idx.astype('int')]) |
14. 다음의 연립방정식을 가우시안 소거법의 역함수를 계산해서 해를 구하는 프로그램을 작성하시오.
소스코드 |
import numpy as np, cv2 data = [ 3, 6, 3, -5, 6, 1, 2,-3, 5] m1 = np.array(data, np.float32).reshape(3,3) m2 = np.array([2, 10, 28], np.float32) ret, inv = cv2.invert(m1, cv2.DECOMP_LU) if ret: ret, dst = cv2.solve(m1, m2, cv2.DECOMP_LU) # 연립방정식 풀이 print("[inv] = \n%s\n" % inv) print("[dst] =", dst.flatten()) # 행렬을 벡터로 변환 else: print("역행렬이 존재하지 않습니다.") |
15. 5.6절에서 예제_5.6.2의 사각형 회전하기 예제를 확장하여 그 사각형의 중심점을 기준으로 45도 회전시키는 프로그램을 완성하시오.
소스코드 |
import numpy as np, cv2 pts1 = np.array([(200,50,1), (400, 50, 1), (400, 250, 1), (200, 250, 1)], np.float32) theta = 45 * np.pi / 180 m = np.array([[np.cos(theta), -np.sin(theta), 0], [np.sin(theta), np.cos(theta), 0], [0,0,1]], np.float32) delta = (pts1[2] - pts1[0])// 2 center = pts1[0] + delta print('center=',center) t1 = np.eye(3, dtype=np.float32) t2 = np.eye(3, dtype=np.float32) t1[:2, 2] = -center[:2] t2[:2, 2] = center[:2] m2 = cv2.gemm(t2, m, 1, None, 0) m2 = cv2.gemm(m2, t1, 1, None, 0) pts2 = cv2.gemm(pts1, m2, 1,None, 1, flags=cv2.GEMM_2_T) for i, (pt1, pt2) in enumerate(zip(pts1, pts2)): print("pts1[%d] = %s, pts2[%d] = %s"%(i, pt1, i, pt2)) image = np.full((400, 500, 3), 255, np.uint8) cv2.polylines(image, [np.int32(pts1[:,:2])], True, (0, 255, 0), 2) cv2.polylines(image, [np.int32(pts2[:,:2])], True, (255, 0, 0), 3) cv2.imshow("image", image) cv2.waitKey(0) cv2.destroyAllWindows() |
Docs에서 쓴거 그대로 옮겼더니 수식이 다 깨졌다.
'Programming > Python' 카테고리의 다른 글
[OpenCV] OpenCV-Python으로 배우는 영상 처리 및 응용 - 6장 연습문제 풀이 (1) | 2025.07.01 |
---|---|
[OpenCV] OpenCV-Python으로 배우는 영상 처리 및 응용 - 4장 연습문제 풀이 (6) | 2025.06.30 |
[Jupyter extension] Jupyter 확장프로그램 개발 환경 구축 (1) | 2024.09.02 |
[OpenCV] 채널 별 배열 구조 + 이미지 투명화 (1) | 2023.10.17 |