250911 [ Day 48 ] - OpenCV (4)

TaeHyun·2025년 9월 11일

TIL

목록 보기
50/182

시작하며

오늘은 그동안 배웠던 OpenCV의 기능을 최대한으로 활용한 실습 문제를 하나 풀었는데 생각보다 많이 어려웠다. 크게 3가지의 기능을 구현해보는 내용이었는데, 2단계까지는 어떻게 하다 보니 구현했는데 마지막 3단계 구현을 끝내 못했다. 최종적으로 구현된 코드를 계속 보면서 공부를 더 해봐야겠다.

마우스 이벤트

  • cv2.setMouseCallback(winName, onMouse)
  • onMouse(event) : 마우스 이벤트를 처리하는 콜백 함수, 첫번째 인자로 이벤트를 받아줌
# 원본 사용 (누적)
img = cv.imread(DOG_PATH)

name = "Mouse Event"
cv.namedWindow(name)

def on_mouse(event, x, y, flags, _):
    if event == cv.EVENT_LBUTTONDOWN:
        cv.circle(img, (x,y), 20, (255,255,0), cv.FILLED)
        cv.imshow(name, img)

cv.setMouseCallback(name, on_mouse)

cv.imshow(name, img)

cv.waitKey(0)
cv.destroyAllWindows()
cv.waitKey(1)
# 복사본 사용 (갱신)
img = cv.imread(DOG_PATH)

name = "Mouse Event"
cv.namedWindow(name)

def on_mouse(event, x, y, flags, _):
    if event == cv.EVENT_LBUTTONDOWN:
        copied = img.copy()
        cv.circle(copied, (x,y), 20, (255,255,0), cv.FILLED)
        cv.imshow(name, copied)

cv.setMouseCallback(name, on_mouse)

cv.imshow(name, img)

cv.waitKey(0)
cv.destroyAllWindows()
cv.waitKey(1)
# 마우스 이벤트 + 원근 변환
PRACTICE_PATH = "../images/book2.jpg"

img = cv.imread(PRACTICE_PATH)

name = "Mouse Event(Practice)"
point_list = []

def show_result():
    width = int(np.linalg.norm(np.array(point_list[0]) - (point_list[1])))
    height = int(np.linalg.norm(np.array(point_list[0]) - (point_list[3])))

    src = np.array(point_list, dtype=np.float32)
    dst = np.array([[0,0], [width,0], [width,height], [0,height]], dtype=np.float32)
    mat = cv.getPerspectiveTransform(src, dst)
    result = cv.warpPerspective(img, mat, (width, height))
    
    cv.imshow("warped", result)

drawing = False

def on_mouse(event, x, y, flags, _):
    global drawing
    coppied = img.copy()
    point = (x,y)

    if event == cv.EVENT_LBUTTONDOWN:
        drawing = True
        point_list.append(point)

    if drawing:
        prev_point = None
        for p in point_list:
            cv.circle(coppied, p, 10, (0,0,255), cv.FILLED)
            if prev_point:
                cv.line(coppied, prev_point, p, (0,0,255), 5)
            prev_point = p

        if len(point_list) == 4:
            point = point_list[0]
            show_result()
        cv.line(coppied, point_list[-1], point, (0,0,255), 5)

    cv.imshow(name, coppied)

cv.namedWindow(name)

cv.setMouseCallback(name, on_mouse)
cv.imshow(name, img)

cv.waitKey(0)
cv.destroyAllWindows()
cv.waitKey(1)

경계선 검출

  • 경계선(edge) : 이미지에서 색이 갑자기 바뀌는 지점을 연결한 것(경계)
  • cv2.Canny(img, threshold1, threshold2)
    • threshold1 : 약한 경계 임계값
    • threshold2 : 강한 경계 임계값

경계선 검출 과정

  1. 이미지를 그레이 스케일로 변환
  2. 블러 처리 등을 통해 노이즈를 제거
  3. Canny 알고리즘을 적용

기본 구현

img = cv.imread(MOUNTAIN_PATH, cv.IMREAD_GRAYSCALE)
canny = cv.Canny(img, 50, 150)

cv.imshow("img", img)
cv.imshow("canny", canny)

cv.waitKey(0)
cv.destroyAllWindows()
cv.waitKey(1)

트랙바를 이용한 구현

img = cv.imread(MOUNTAIN_PATH, cv.IMREAD_GRAYSCALE)

name = "Canny"
cv.namedWindow(name)

cv.createTrackbar("threshold1", name, 50, 255, lambda x:x)
cv.createTrackbar("threshold2", name, 150, 255, lambda x:x)

while True:
    threshold1 = cv.getTrackbarPos("threshold1", name)
    threshold2 = cv.getTrackbarPos("threshold2", name)

    canny = cv.Canny(img, threshold1, threshold2)

    cv.imshow(name, canny)

    if cv.waitKey(1) == ord("q"):
        break

cv.destroyAllWindows()
cv.waitKey(1)

캠 화면에서 구현

cap = cv.VideoCapture(0)

name = "Canny"
cv.namedWindow(name)

cv.createTrackbar("threshold1", name, 50, 255, lambda x:x)
cv.createTrackbar("threshold2", name, 150, 255, lambda x:x)

while cap.isOpened():
    ret, frame = cap.read()

    if not ret:
        break

    threshold1 = cv.getTrackbarPos("threshold1", name)
    threshold2 = cv.getTrackbarPos("threshold2", name)

    gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
    canny = cv.Canny(gray, threshold1, threshold2)

    cv.imshow(name, canny)

    if cv.waitKey(1) == ord("q"):
        break

cap.release()
cv.destroyAllWindows()
cv.waitKey(1)

중첩으로 블러 사용

  • Canny 알고리즘에 이미 가우시안 블러가 적용되어있음
  • 추가적으로 노이즈 제거 필요 시 중첩으로 블러 적용
img = cv.imread("../images/dog.jpg", cv.IMREAD_GRAYSCALE)

blur = cv.GaussianBlur(img, (5,5), 0)

canny = cv.Canny(img, 50, 150)
canny_blur = cv.Canny(blur, 50, 150)

cv.imshow("img", canny)
cv.imshow("blur", canny_blur)

cv.waitKey(0)
cv.destroyAllWindows()
cv.waitKey(1)

윤곽선 검출

  • 윤곽선(Contour) : 같은 색상(빛의 강도)을 가진 영역의 경계선을 연결한 곡선

윤곽선 그리는 방법

  1. 엣지를 찾는다 (Threshold/Canny)
  2. 윤곽선을 생성해서 데이터로 저장 (findContours)
  3. 이미지 위에 윤곽선을 그림 (drawContours)

기본 구현

img = cv.imread(DOG_PATH)
coppied = img.copy()

# 그레이 스케일로 변환
gray = cv.cvtColor(coppied, cv.COLOR_BGR2GRAY)

# 이진화
ret, binary = cv.threshold(gray, -1, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)

# 윤곽선 찾기
contours, hierachy = cv.findContours(binary, cv.RETR_CCOMP, cv.CHAIN_APPROX_NONE)

# 윤곽선 그리기
cv.drawContours(img, contours, -1, (0,255,0), 2)

cv.imshow("Contours", img)

cv.waitKey(0)
cv.destroyAllWindows()
cv.waitKey(1)

마치며

조금씩 어려운 내용이 나오기는 하지만 OpenCV 사용이 어느 정도 적응이 된 것 같아서 괜찮게 따라가고 있는 것 같다.

NOTION

MY NOTION (OpenCV. 03)

profile
Hello I'm TaeHyunAn, Currently Studying Data Analysis

0개의 댓글