컴퓨터 영상 처리/OpenCV

OpenCV_Basic (1)

Zoo_10th 2024. 3. 22.

1. 이미지 파일 저장

1-1. 이미지 저장하기

1) 이미지 읽기

cv2.imread() 함수를 사용하여 이미지 파일을 읽어온다. 여기서 cv2.IMREAD_GRAYSCALE 옵션을 사용하면 이미지를 그레이스케일로 로드한다.

import cv2
img = cv2.imread('cats.jpg', cv2.IMREAD_GRAYSCALE)

2) 이미지 표시

cv2.imshow() 함수를 사용하여 이미지를 화면에 표시한다.

cv2.imshow('IMG', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

3) 이미지 저장

cv2.imwrite() 함수를 사용하여 이미지를 파일로 저장한다. 이 함수는 파일 경로와 이미지 데이터를 인자로 받으며, 저장이 성공적으로 완료되면 True를 반환한다. JPG와 PNG 형식으로 저장하는 방법이다.

result_jpg = cv2.imwrite('cats_gray.jpg', img)
print(result_jpg)  # JPG 형식으로 저장
result_png = cv2.imwrite('cats_gray.png', img)
print(result_png)  # PNG 형식으로 저장

1-2. 동영상 저장하기

1) 동영상 파일 읽기

cv2.VideoCapture() 함수를 사용하여 동영상 파일을 읽어온다.

cap = cv2.VideoCapture('chess.mp4')

2) 코덱 설정 및 VideoWriter 객체 생성

cv2.VideoWriter_fourcc() 함수로 코덱을 설정하고, cv2.VideoWriter()를 사용하여 저장할 동영상의 속성을 설정한다.

fourcc = cv2.VideoWriter_fourcc(*'DIVX')
width = round(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = round(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv2.CAP_PROP_FPS)
out = cv2.VideoWriter('output.avi', fourcc, fps, (width, height))

3) 동영상 읽기 및 저장

cap.read()로 동영상의 각 프레임을 읽어오고, cv2.imshow()로 화면에 표시한다. cv2.VideoWriter.write() 메소드를 사용하여 각 프레임을 새로운 동영상 파일에 저장한다.

while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
        break
    out.write(frame)  # 프레임 저장
    cv2.imshow('video', frame)
    if cv2.waitKey(25) == ord('q'):
        break

4) 자원 해제

사용이 끝난 후, cv2.VideoCapture와 cv2.VideoWriter 객체를 release() 메소드로 해제한다. 마지막으로 모든 OpenCV 창을 닫는다.

out.release()
cap.release()
cv2.destroyAllWindows()

2. 이미지 스레시홀딩 (Thresholding)

이미지 처리에서 스레시홀딩은 이미지를 단순화하여 분석하기 쉽게 만드는 기술이다. 특히, 이미지를 두 가지 색상(보통은 검정과 흰색)으로 변환하여 객체를 강조하거나 배경을 제거할 수 있다. 

2-1. 전역 스레시홀딩 (Global Thresholding)

전역 스레시홀딩은 전체 이미지에 하나의 문턱값을 적용하여 이미지를 이진화하는 방법이다. 문턱값보다 큰 픽셀은 하나의 값(보통 흰색)으로, 작은 픽셀은 다른 값(보통 검정색)으로 설정된다.

1) 이미지 읽기 

그레이 스케일로 이미지를 로드

img = cv2.imread('../img/gray_gradient.jpg', cv2.IMREAD_GRAYSCALE)

2) NumPy를 사용한 이진화

 - 원본과 같은 크기의 검정색 이미지를 생성

 - 문턱값(127)보다 큰 픽셀은 흰색(255)으로 변경

thresh_np = np.zeros_like(img)
thresh_np[img > 127] = 255

3) OpenCV를 사용한 이진화

 - cv2.threshold 함수로 문턱값을 적용한 이진화를 수행

 - 함수는 적용된 문턱값과 결과 이미지를 반환

ret, thresh_cv = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)

4) 결과 출력: 원본 이미지와 이진화된 이미지를 비교하여 출력

imgs = {'Original': img, 'NumPy API':thresh_np, 'cv2.threshold': thresh_cv}
import cv2
import numpy as np
import matplotlib.pylab as plt

img = cv2.imread('../img/gray_gradient.jpg', cv2.IMREAD_GRAYSCALE) #이미지를 그레이 스케일로 읽기

#NumPy API로 바이너리 이미지 만들기
thresh_np = np.zeros_like(img)   # 원본과 동일한 크기의 0으로 채워진 이미지
thresh_np[ img > 127] = 255      # 127 보다 큰 값만 255로 변경

#OpenCV API로 바이너리 이미지 만들기
ret, thresh_cv = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) 
print(ret)  # 127.0, 바이너리 이미지에 사용된 문턱 값 반환

#원본과 결과물을 matplotlib으로 출력
imgs = {'Original': img, 'NumPy API':thresh_np, 'cv2.threshold': thresh_cv}
for i , (key, value) in enumerate(imgs.items()):
    plt.subplot(1, 3, i+1)
    plt.title(key)
    plt.imshow(value, cmap='gray')
    plt.xticks([]); plt.yticks([])

plt.show()

2. 오츠 스레시홀딩 (Otsu's Thresholding)

오츠 방법은 이미지의 히스토그램을 분석하여 최적의 문턱값을 자동으로 결정한다. 주로 배경과 객체의 명암 대비가 뚜렷하지 않은 이미지에 적합하다.

2-1. 오츠 알고리즘(Otsu's algorithm)

오츠 알고리즘(Otsu's algorithm)은 자동으로 이미지의 최적의 문턱값을 결정하는 방법으로, 이미지를 배경과 전경(객체) 두 부분으로 분리하는 이진화 작업에 주로 사용된다. 1979년 노부유키 오츠(Nobuyuki Otsu)에 의해 개발되었으며, 이미지의 히스토그램 정보를 기반으로 한다. 이 알고리즘은 이미지 내의 픽셀 강도 분포를 분석하여, 전체 픽셀을 두 그룹으로 나누는 문턱값을 자동으로 계산한다. 계산된 문턱값은 두 그룹 간의 분산을 최대화하는 값이다.

2-2. 오츠 알고리즘 작동 원리

1) 히스토그램 계산: 먼저, 이미지의 그레이스케일 히스토그램을 계산한다. 히스토그램은 이미지 내 각각의 픽셀 값(예: 0~255)이 몇 번 나타나는지를 나타내는 그래프이다. 

2) 클래스 분리: 가능한 모든 문턱값을 고려하여, 해당 문턱값을 기준으로 픽셀 집합을 두 그룹(전경과 배경)으로 나눈다.

3) 클래스 간 분산 계산: 각 문턱값마다 클래스 간 분산(between-class variance)을 계산한다. 클래스 간 분산은 두 그룹의 평균값 차이를 기반으로 하며, 이 값이 크다는 것은 두 그룹이 잘 분리되어 있음을 의미한다.

4) 최대 클래스 간 분산 찾기: 모든 가능한 문턱값에 대해 계산된 클래스 간 분산 중에서 최대값을 찾는다. 이 최대값을 제공하는 문턱값이 최적의 문턱값으로 선택된다.

5) 이진화 적용: 계산된 최적의 문턱값을 사용하여 이미지를 이진화한다. 문턱값보다 큰 픽셀은 하나의 값(예: 흰색)으로, 작거나 같은 픽셀은 다른 값(예: 검정색)으로 설정된다.

2-3. 장점과 한계

 - 장점: 오츠 알고리즘의 가장 큰 장점은 사용자의 개입 없이 이미지로부터 자동으로 문턱값을 결정할 수 있다는 점이다. 따라서, 다양한 조명 조건 또는 다른 명암 대비를 가진 이미지에 대해 일관된 결과를 얻을 수 있다.

 - 한계: 오츠 알고리즘은 이미지의 전경과 배경이 뚜렷이 구분되고, 히스토그램이 양봉 구조를 보일 때 가장 잘 작동한다. 그러나 전경과 배경이 비슷한 밝기를 가지거나, 여러 객체가 복잡하게 얽혀 있는 경우에는 최적의 문턱값을 찾기 어려울 수 있다. 또한, 노이즈가 많은 이미지에서도 성능이 저하될 수 있다.

1) 이미지 읽기

 - 그레이 스케일로 이미지를 로드

img = cv2.imread('../img/scaned_paper.jpg', cv2.IMREAD_GRAYSCALE)

2) 오츠 스레시홀딩 적용

 - cv2.threshold 함수에 cv2.THRESH_OTSU 옵션을 함께 사용하여 오츠 방법으로 이진화를 수행

 - 함수는 선택된 최적의 문턱값과 결과 이미지를 반환

t, t_otsu = cv2.threshold(img, -1, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

3) 결과 출력

 - 원본 이미지와 오츠 방법으로 이진화된 이미지를 비교하여 출력

imgs = {'Original': img, 't:130': t_130, 'otsu:%d' % t: t_otsu}
import cv2
import numpy as np
import matplotlib.pylab as plt

# 이미지를 그레이 스케일로 읽기
img = cv2.imread('../img/scaned_paper.jpg', cv2.IMREAD_GRAYSCALE) 
# 경계 값을 130으로 지정  ---①
_, t_130 = cv2.threshold(img, 130, 255, cv2.THRESH_BINARY)        
# 경계 값을 지정하지 않고 OTSU 알고리즘 선택 ---②
t, t_otsu = cv2.threshold(img, -1, 255,  cv2.THRESH_BINARY | cv2.THRESH_OTSU) 
print('otsu threshold:', t)                 # Otsu 알고리즘으로 선택된 경계 값 출력

imgs = {'Original': img, 't:130':t_130, 'otsu:%d'%t: t_otsu}
for i , (key, value) in enumerate(imgs.items()):
    plt.subplot(1, 3, i+1)
    plt.title(key)
    plt.imshow(value, cmap='gray')
    plt.xticks([]); plt.yticks([])

plt.show()

3. 이미지 합성

3-1. 알파 블렌딩 (Alpha Blending)

알파 블렌딩은 두 이미지를 서로 다른 비율로 혼합하여 하나의 이미지로 합성하는 기술이다. 이 과정에서 알파 값(투명도)을 조절함으로써, 한 이미지에서 다른 이미지로 자연스럽게 전환되게 만들 수 있다.

1) 라이브러리 준비 : OpenCV와 NumPy 라이브러리를 불러온다.

import cv2
import numpy as np

2) 이미지 로드: 합성하고자 하는 두 이미지를 로드한다.

img1 = cv2.imread('../img/man_face.jpg')
img2 = cv2.imread('../img/lion_face.jpg')

3) 트랙바 이벤트 핸들러 정의: 트랙바를 움직였을 때 호출될 함수를 정의한다. 이 함수는 트랙바의 위치에 따라 알파 값을 조절하고, cv2.addWeighted() 함수를 사용하여 두 이미지를 혼합한다.

def onChange(x):
    alpha = x / 100
    dst = cv2.addWeighted(img1, 1-alpha, img2, alpha, 0)
    cv2.imshow(win_name, dst)

4) 이미지 표시 및 트랙바 생성: 첫 번째 이미지를 화면에 표시하고, 트랙바를 생성한다. 트랙바는 0부터 100까지의 값을 가지며, 이는 0%에서 100%까지의 알파 값을 의미한다.

win_name = 'Alpha blending'
trackbar_name = 'fade'

cv2.imshow(win_name, img1)
cv2.createTrackbar(trackbar_name, win_name, 0, 100, onChange)


5) 실행 및 종료: 프로그램이 사용자의 입력을 기다리도록 하고, 키 입력이 있을 때 모든 창을 종료한다.

cv2.waitKey()
cv2.destroyAllWindows()

 - 알파 블렌딩은 사진 편집, 이미지 합성, 영상 효과 등 다양한 분야에서 사용된다.

 - 사용자 인터페이스에서 부드러운 전환 효과를 주기 위해 알파 블렌딩을 활용할 수 있다.

 - 게임 개발에서 캐릭터나 배경의 투명도를 조절하기 위해 사용된다.

import cv2
import numpy as np

win_name = 'Alpha blending'     # 창 이름
trackbar_name = 'fade'          # 트렉바 이름

# 트렉바 이벤트 핸들러 함수
def onChange(x):
    alpha = x/100
    dst = cv2.addWeighted(img1, 1-alpha, img2, alpha, 0) 
    cv2.imshow(win_name, dst)


# 합성 영상 읽기
img1 = cv2.imread('../img/man_face.jpg')
img2 = cv2.imread('../img/lion_face.jpg')

# 이미지 표시 및 트렉바 붙이기
cv2.imshow(win_name, img1)
cv2.createTrackbar(trackbar_name, win_name, 0, 100, onChange)

cv2.waitKey()
cv2.destroyAllWindows()

4. 원근 변환 (Perspective Transformation)

원근 변환은 이미지의 시점을 변경하는 데 사용되는 강력한 기술이다. 이를 통해 이미지의 특정 부분을 확대하거나, 다른 각도에서 보는 것처럼 이미지의 시점을 변경할 수 있다.

원근 변환(Perspective Transformation)은 이미지의 시점을 변경하여, 마치 다른 각도에서 보는 것과 같은 효과를 내는 이미지 처리 기술이다. 이 과정은 3D 공간에서의 변환을 2D 이미지에 적용하여, 물체가 더 가깝거나 먼 것처럼 보이게 한다. 원근 변환을 수행하기 위해, 먼저 변환 전후의 대응되는 점 쌍(최소 4쌍)이 필요하다. 이 점들을 이용하여 변환 행렬을 계산한 다음, 이 행렬을 이미지에 적용하여 원근 효과를 준다.

4-1. 원근 변환의 수학적 기반

(x, y)는 원본 이미지의 좌표, (x', y')는 변환된 이미지의 좌표, (w')는 동차 좌표(Homogeneous coordinate)로, 최종 좌표는 (x'/w', y'/w')로 계산됩니다. (a_ij)는 변환 행렬의 요소이다.

원근 변환을 위한 행렬은 변환 전후의 점 쌍으로부터 계산할 수 있다. OpenCV에서는 cv2.getPerspectiveTransform() 함수를 사용하여 이 행렬을 계산할 수 있다.

4-2. 원근 변환의 구현

1) 점 쌍 정의: 변환을 적용할 원본 이미지의 점과 변환된 이미지에서 해당 점이 위치할 점을 정의합니다. 최소 4쌍의 점이 필요하다.

2) 변환 행렬 계산: cv2.getPerspectiveTransform() 함수를 사용하여 변환 행렬을 계산한다.

3) 원근 변환 적용: 계산된 변환 행렬을 cv2.warpPerspective() 함수에 적용하여 이미지에 원근 변환을 수행한다.

import cv2
import numpy as np

win_name = "scanning"
img = cv2.imread("../img/paper.jpg")
rows, cols = img.shape[:2]
draw = img.copy()
pts_cnt = 0
pts = np.zeros((4,2), dtype=np.float32)

def onMouse(event, x, y, flags, param):  #마우스 이벤트 콜백 함수 구현 ---① 
    global  pts_cnt                     # 마우스로 찍은 좌표의 갯수 저장
    if event == cv2.EVENT_LBUTTONDOWN:  
        cv2.circle(draw, (x,y), 10, (0,255,0), -1) # 좌표에 초록색 동그라미 표시
        cv2.imshow(win_name, draw)

        pts[pts_cnt] = [x,y]            # 마우스 좌표 저장
        pts_cnt+=1
        if pts_cnt == 4:                       # 좌표가 4개 수집됨 
            # 좌표 4개 중 상하좌우 찾기 ---② 
            sm = pts.sum(axis=1)                 # 4쌍의 좌표 각각 x+y 계산
            diff = np.diff(pts, axis = 1)       # 4쌍의 좌표 각각 x-y 계산

            topLeft = pts[np.argmin(sm)]         # x+y가 가장 값이 좌상단 좌표
            bottomRight = pts[np.argmax(sm)]     # x+y가 가장 큰 값이 좌상단 좌표
            topRight = pts[np.argmin(diff)]     # x-y가 가장 작은 것이 우상단 좌표
            bottomLeft = pts[np.argmax(diff)]   # x-y가 가장 큰 값이 좌하단 좌표

            # 변환 전 4개 좌표 
            pts1 = np.float32([topLeft, topRight, bottomRight , bottomLeft])

            # 변환 후 영상에 사용할 서류의 폭과 높이 계산 ---③ 
            w1 = abs(bottomRight[0] - bottomLeft[0])    # 상단 좌우 좌표간의 거리
            w2 = abs(topRight[0] - topLeft[0])          # 하당 좌우 좌표간의 거리
            h1 = abs(topRight[1] - bottomRight[1])      # 우측 상하 좌표간의 거리
            h2 = abs(topLeft[1] - bottomLeft[1])        # 좌측 상하 좌표간의 거리
            width = max([w1, w2])                       # 두 좌우 거리간의 최대값이 서류의 폭
            height = max([h1, h2])                      # 두 상하 거리간의 최대값이 서류의 높이
            
            # 변환 후 4개 좌표
            pts2 = np.float32([[0,0], [width-1,0], 
                                [width-1,height-1], [0,height-1]])

            # 변환 행렬 계산 
            mtrx = cv2.getPerspectiveTransform(pts1, pts2)
            # 원근 변환 적용
            result = cv2.warpPerspective(img, mtrx, (int(width), int(height)))
            cv2.imshow('scanned', result)
cv2.imshow(win_name, img)
cv2.setMouseCallback(win_name, onMouse)    # 마우스 콜백 함수를 GUI 윈도우에 등록 ---④
cv2.waitKey(0)
cv2.destroyAllWindows()

5. 블러링과 필터

블러링은 이미지에서 노이즈를 줄이거나, 세부 사항을 덜 뚜렷하게 만들어 이미지를 부드럽게 처리하는 기술이다. 블러링은 다양한 필터를 적용하여 수행할 수 있으며, 평균 블러링은 그 중 가장 기본적인 방법 중 하나이다.

5-1. 평균 블러링 (Average Blurring)

평균 블러링은 이미지에 있는 각 픽셀에 대해 주변 픽셀의 평균 값을 취하여 적용하는 방법이다. 이 과정은 이미지의 노이즈를 줄이고, 세부 사항을 흐리게 만든다.

1)  라이브러리 준비

 - OpenCV와 NumPy 라이브러리를 불러온다.

import cv2
import numpy as np

2) 이미지 로드

 - 블러링을 적용할 이미지를 로드한다.

file_name = '../img/self.jpg'
img = cv2.imread(file_name)

3) cv2.blur() 함수 사용

 - OpenCV의 cv2.blur() 함수를 사용하여 평균 블러링을 적용한다. 이 함수는 블러링의 크기를 결정하는 커널 크기를 매개변수로 받는다.

blur1 = cv2.blur(img, (10,10))

4) cv2.boxFilter() 함수 사용

 - cv2.boxFilter() 함수를 사용하여 유사하게 평균 블러링을 적용할 수 있다. 1은 출력 이미지의 깊이가 입력 이미지와 동일함을 의미한다.

blur2 = cv2.boxFilter(img, -1, (10,10))

5) 결과 출력

 - 원본 이미지와 블러링 처리된 이미지를 나란히 표시하여 비교한다.

merged = np.hstack((img, blur1, blur2))
cv2.imshow('blur', merged)

평균 블러링은 블러링 효과를 적용하는 가장 기본적인 방법 중 하나로, 이미지 처리에서 다양하게 활용된다. 다만, 세밀한 조정이 필요한 경우 다른 블러링 기술이나 필터를 사용하는 것이 더 나은 결과를 얻을 수 있다.

import cv2

ksize = 30              # 블러 처리에 사용할 커널 크기
win_title = 'mosaic'    # 창 제목
img = cv2.imread('img/self.jpg')    # 이미지 읽기

while True:
    x,y,w,h = cv2.selectROI(win_title, img, False) # 관심영역 선택
    if w > 0 and h > 0:         # 폭과 높이가 음수이면 드래그 방향이 옳음 
        roi = img[y:y+h, x:x+w]   # 관심영역 지정
        roi = cv2.blur(roi, (ksize, ksize)) # 블러(모자이크) 처리
        img[y:y+h, x:x+w] = roi   # 원본 이미지에 적용
        cv2.imshow(win_title, img)
    else:
        break
cv2.destroyAllWindows()

5-2. 경계 검출 (Edge Detection)

경계 검출은 이미지에서 객체의 윤곽이나 모양을 찾는 과정이다. 이는 객체 인식, 이미지 분할, 이미지 복원 등 다양한 이미지 처리 분야에서 중요한 기초 작업이다. 경계 검출을 위한 주요 방법 중 하나는 미분 필터와 라플라시안 필터를 사용하는 것이다.

1) 미분 필터

미분 필터는 이미지의 픽셀 간 밝기 값 변화율을 이용하여 경계를 찾는다. 이미지에서의 밝기 값 변화가 큰 곳이 바로 경계가 된다. 가로 방향과 세로 방향의 미분을 통해 수평 및 수직 방향의 경계를 감지할 수 있다.

① 미분 커널 생성

 - 수평 방향(gx)과 수직 방향(gy)의 미분을 위한 커널을 정의한다.

② 필터 적용

 - cv2.filter2D() 함수를 사용하여 미분 필터를 이미지에 적용한다. 각 방향의 미분 결과를 얻는다.

③ 결과 출력

 - 원본 이미지와 수평, 수직 방향의 경계 검출 결과를 함께 출력한다.

merged = np.hstack((img, edge_gx, edge_gy))
cv2.imshow('edge', merged)
import cv2
import numpy as np

img = cv2.imread("../img/sudoku.jpg")

#미분 커널 생성 ---①
gx_kernel = np.array([[ -1, 1]])
gy_kernel = np.array([[ -1],[ 1]])

# 필터 적용 ---②
edge_gx = cv2.filter2D(img, -1, gx_kernel)
edge_gy = cv2.filter2D(img, -1, gy_kernel)
# 결과 출력
merged = np.hstack((img, edge_gx, edge_gy))
cv2.imshow('edge', merged)
cv2.waitKey(0)
cv2.destroyAllWindows()

2) 라플라시안 필터

라플라시안 필터는 이미지의 두 번째 미분을 이용한 방법으로, 경계의 갑작스러운 변화를 감지한다. 이 방법은 모든 방향의 경계를 동시에 감지할 수 있어 유용하다.

① 라플라시안 필터 적용

 - cv2.Laplacian() 함수를 사용하여 라플라시안 필터를 이미지에 적용한다.

edge = cv2.Laplacian(img, -1)

② 결과 출력

 - 원본 이미지와 라플라시안 필터를 적용한 결과를 출력한다.

merged = np.hstack((img, edge))
cv2.imshow('Laplacian', merged)
import cv2
import numpy as np

img = cv2.imread("../img/sudoku.jpg")

# 라플라시안 필터 적용 ---①
edge = cv2.Laplacian(img, -1)

# 결과 출력
merged = np.hstack((img, edge))
cv2.imshow('Laplacian', merged)
cv2.waitKey(0)
cv2.destroyAllWindows()
import cv2
import numpy as np

# 카메라 장치 연결
cap = cv2.VideoCapture(0)   
while cap.isOpened():
    # 프레임 읽기
    ret, frame = cap.read()
    # 속도 향상을 위해 영상크기를 절반으로 축소
    frame = cv2.resize(frame, None, fx=0.5, fy=0.5, \
                        interpolation=cv2.INTER_AREA)
    if cv2.waitKey(1) == 27: # esc키로 종료
        break
    # 그레이 스케일로 변경    
    img_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    # 잡음 제거를 위해 가우시안 플러 필터 적용(라플라시안 필터 적용 전에 필수)
    img_gray = cv2.GaussianBlur(img_gray, (9,9), 0)
    # 라플라시안 필터로 엣지 거출
    edges = cv2.Laplacian(img_gray, -1, None, 5)
    # 스레시홀드로 경계 값 만 남기고 제거하면서 화면 반전(흰 바탕 검은 선)
    ret, sketch = cv2.threshold(edges, 70, 255, cv2.THRESH_BINARY_INV)
    
    # 경계선 강조를 위해 팽창 연산
    kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (3,3))
    sketch = cv2.erode(sketch, kernel)
    # 경계선 자연스럽게 하기 위해 미디언 블러 필터 적용
    sketch = cv2.medianBlur(sketch, 5)
    # 그레이 스케일에서 BGR 컬러 스케일로 변경
    img_sketch = cv2.cvtColor(sketch, cv2.COLOR_GRAY2BGR)

    # 컬러 이미지 선명선을 없애기 위해 평균 블러 필터 적용
    img_paint = cv2.blur(frame, (10,10) )
    # 컬러 영상과 스케치 영상과 합성
    img_paint = cv2.bitwise_and(img_paint, img_paint, mask=sketch)
    
    # 결과 출력
    merged = np.hstack((img_sketch, img_paint))
    cv2.imshow('Sketch Camera', merged)
    
cap.release()
cv2.destroyAllWindows()
728x90

'컴퓨터 영상 처리 > OpenCV' 카테고리의 다른 글

OpenCV 기초  (0) 2024.03.19

댓글