이미지 처리에서 중요한 개념인 이진화, 변환 기법에 대해 살펴보겠습니다. 특히 OpenCV를 이용해 직접 코드를 구현해 볼 수 있도록 실습 예제도 준비했습니다.
ROI는 영상 내에서 특정 관심 영역을 설정하는 것을 의미합니다. 예를 들어, 이미지 내에서 태양의 영역을 설정해 그 부분만 다루고 싶을 때 ROI를 사용합니다.
import cv2 import numpy as np #이미지 읽기 img = cv2.imread('./sun.jpg') #태양 영역 선택 (임의로 선택한 좌표) x, y, w, h = 182, 17, 120, 118 #태양 영역 복사 sun_region = img[y:y+h, x:x+w].copy() #원형 마스크 생성 mask = np.zeros((h, w), dtype=np.uint8) center = (w // 2, h // 2) radius = min(w // 2, h // 2) cv2.circle(mask, center, radius, 255, -1) #마스크를 이용해 원형 부분만 남기기 sun_region_masked = cv2.bitwise_and(sun_region, sun_region, mask=mask) #복사한 태양 영역을 새로운 위치에 붙여넣기 new_img = img.copy() new_img[y:y+h, 300:300+w] = cv2.bitwise_and(new_img[y:y+h, 300:300+w], new_img[y:y+h, 300:300+w], mask=cv2.bitwise_not(mask)) new_img[y:y+h, 300:300+w] += sun_region_masked #결과 이미지 표시 cv2.imshow('Sun Image', new_img) cv2.imwrite('./sun_with_copy.jpg', new_img) cv2.waitKey(0) cv2.destroyAllWindows()결과
이진화는 픽셀 값을 흑백으로 나누는 작업입니다. 배경과 객체를 분리하거나 관심 영역을 강조하는 데 유용합니다.
OpenCV에서 사용하는 이진화 함수:
cv2.threshold()
cv2.adaptiveThreshold()
import cv2 import matplotlib.pyplot as plt img = cv2.imread('./cells.png', cv2.IMREAD_GRAYSCALE) hist = cv2.calcHist([img], [0], None, [256], [0, 255]) #임계값을 100으로 설정하여 이진화 _, dst1 = cv2.threshold(img, 100, 255, cv2.THRESH_BINARY) # 임계값을 210으로 설정하여 이진화 _, dst2 = cv2.threshold(img, 210, 255, cv2.THRESH_BINARY) cv2.imshow('Original Image', img) cv2.imshow('Threshold 100', dst1) cv2.imshow('Threshold 210', dst2) plt.plot(hist) plt.show() cv2.waitKey(0)결과
과
오츠 알고리즘은 자동으로 임계값을 찾아 이진화하는 기법입니다. 이미지의 히스토그램을 분석하여 최적의 임계값을 결정합니다.
import cv2 img = cv2.imread('./rice.png', cv2.IMREAD_GRAYSCALE) #오츠 알고리즘을 사용한 이진화 th, dst = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU) print('Calculated Threshold:', th) cv2.imshow('Original Image', img) cv2.imshow('Otsu Thresholding', dst) cv2.waitKey(0) cv2.destroyAllWindows()결과
영상 변환은 이미지의 크기나 모양을 변환하는 작업입니다. 다음은 영상 이동(Translation), 크기 변환(Scaling), 회전(Rotation)에 대한 예제입니다.
import cv2 import numpy as np #이미지 이동 (Translation) img = cv2.imread('./dog.bmp') aff = np.array([[1, 0, 200], [0, 1, 100]], dtype=np.float32) dst = cv2.warpAffine(img, aff, (0, 0)) cv2.imshow('Original Image', img) cv2.imshow('Translated Image', dst) cv2.waitKey(0)결과
이미지를 회전시키기 위해 회전 변환 행렬을 사용합니다. 중심을 기준으로 이미지를 회전시킬 수 있습니다.
import cv2 import numpy as np #이미지 읽기 img = cv2.imread('./dog.bmp') #회전 중심 좌표 (이미지 중심) center = (img.shape[1] // 2, img.shape[0] // 2) #회전 각도와 스케일 설정 angle = 45 # 45도 회전 scale = 1.0 # 스케일 유지 #회전 변환 행렬 생성 rotation_matrix = cv2.getRotationMatrix2D(center, angle, scale) #회전 적용 rotated_img = cv2.warpAffine(img, rotation_matrix, (img.shape[1], img.shape[0])) #결과 이미지 출력 cv2.imshow('Original Image', img) cv2.imshow('Rotated Image', rotated_img) cv2.waitKey(0) cv2.destroyAllWindows()결과
가우시안 블러는 이미지를 부드럽게 만들기 위해 사용되는 필터입니다. 이미지에서 노이즈를 제거하거나 흐림 효과를 적용할 때 사용됩니다.
import cv2 #이미지 읽기 img = cv2.imread('./dog.bmp') #가우시안 블러 적용 (커널 크기 7x7) blurred_img = cv2.GaussianBlur(img, (7, 7), 0) # 결과 이미지 출력 cv2.imshow('Original Image', img) cv2.imshow('Gaussian Blur', blurred_img) cv2.waitKey(0) cv2.destroyAllWindows()
히스토그램 평활화는 이미지의 밝기 분포를 조정하여 명암 대비를 개선하는 기법입니다. 특히 저조도 이미지에서 효과적입니다.
import cv2 #이미지 읽기 (그레이스케일) img = cv2.imread('./dark_image.jpg', cv2.IMREAD_GRAYSCALE) #히스토그램 평활화 적용 equalized_img = cv2.equalizeHist(img) #결과 이미지 출력 cv2.imshow('Original Image', img) cv2.imshow('Equalized Image', equalized_img) cv2.waitKey(0) cv2.destroyAllWindows()
원근 변환(Perspective Transformation)은 이미지에서 특정 영역을 다른 시점에서 촬영된 것처럼 변환하는 방법입니다. 이 변환은 주로 이미지를 왜곡시키거나 특정 각도에서 보이는 것처럼 만들 때 사용됩니다.
import cv2 import numpy as np img = cv2.imread('./pic.jpg') w, h = 600, 400 srcQuad = np.array([[369, 172], [1228, 156], [1424, 846], [207, 801]], np.float32) dstQuad = np.array([[0, 0], [w, 0], [w, h], [0, h]], np.float32) pers = cv2.getPerspectiveTransform(srcQuad, dstQuad) dst = cv2.warpPerspective(img, pers, (w, h)) cv2.imshow('img', img) cv2.imshow('dst', dst) cv2.waitKey()결과
사용자가 마우스를 사용해 직접 변환 영역의 좌표를 조정할 수 있는 프로그램을 만들 수도 있습니다. 이 방법은 보다 유연하게 원근 변환을 적용할 수 있게 해줍니다. 아래 코드는 사용자가 마우스로 드래그하여 4개의 포인트를 조정할 수 있도록 구현한 예제입니다
예제)
import cv2 import numpy as np import sys def drawROI(img, corners): cpy = img.copy() c1 = (192, 192, 255) c2 = (128, 128, 255) for pt in corners: cv2.circle(cpy, tuple(pt.astype(int)), 25, c1, -1) cv2.line(cpy,tuple(corners[0].astype(int)), tuple(corners[1].astype(int)),c2,2) cv2.line(cpy, tuple(corners[1].astype(int)), tuple(corners[2].astype(int)), c2, 2) cv2.line(cpy, tuple(corners[2].astype(int)), tuple(corners[3].astype(int)), c2, 2) cv2.line(cpy, tuple(corners[3].astype(int)), tuple(corners[0].astype(int)), c2, 2) return cpy ptOld = None def onMouse(event, x, y, flags, param): global srcQuad, dragSrc, ptOld, img if event == cv2.EVENT_LBUTTONDOWN: for i in range(4): if cv2.norm(srcQuad[i] - (x, y)) < 25: dragSrc[i] = True ptOld = (x, y) break if event == cv2.EVENT_LBUTTONUP: for i in range(4): dragSrc[i] = False if event == cv2.EVENT_MOUSEMOVE: for i in range(4): if dragSrc[i]: srcQuad[i] = (x, y) cpy = drawROI(img, srcQuad) cv2.imshow('img',cpy) ptOld = (x, y) break img = cv2.imread('./namecard.jpg') h, w = img.shape[:2] #A4용지 크기: 210 * 297mm dh = 500 dw = round(dh * 297 / 210) srcQuad = np.array([[30, 30], [30, h-30], [w-30, h-30], [w-30, 30]], np.float32) dstQuad = np.array([[0, 0], [0, dh], [dw, dh], [dw, 0]], np.float32) dragSrc = [False, False, False, False] disp = drawROI(img, srcQuad) cv2.imshow('img', disp) cv2.setMouseCallback('img', onMouse) while True: key = cv2.waitKey() if key == 13: break elif key == 27: sys.exit() pers = cv2.getPerspectiveTransform(srcQuad, dstQuad) dst = cv2.warpPerspective(img, pers, (dw, dh), flags=cv2.INTER_CUBIC) cv2.imshow('dst',dst) cv2.waitKey()결과