728x90

OpenCV Extra 2D Features Framework 정의

OpenCV Extra 2D Features Framework는 OpenCV 기본 라이브러리에서 제공하지 않는 고급 특징 추출(Feature Detection) 및 특징 기술자(Descriptor) 알고리즘을 제공하는 확장 모듈입니다. 이 기능은 OpenCV의 확장 저장소인 opencv_contrib에 포함되어 있으며, 컴퓨터 비전 분야에서 객체 인식, 이미지 매칭, 로봇 비전, 증강현실(AR) 등의 다양한 응용에 활용됩니다.


기본 OpenCV 패키지에는 ORB, FAST 등 일부 특징 검출 알고리즘이 포함되어 있지만, 연구나 고급 프로젝트에서는 더 다양한 알고리즘이 필요한 경우가 많습니다. 이를 해결하기 위해 제공되는 기능이 바로 Extra 2D Features Framework입니다.

대표적으로 다음과 같은 알고리즘을 제공합니다.

  • SIFT (Scale-Invariant Feature Transform)
  • SURF (Speeded-Up Robust Features)
  • BRIEF
  • FREAK
  • LATCH
  • DAISY

이 알고리즘들은 이미지에서 의미 있는 특징점(Keypoint)을 검출하고, 이를 설명하는 특징 벡터(Descriptor)를 생성하여 이미지 간의 유사성을 분석하는 데 사용됩니다.

OpenCV Extra 2D Features Framework 설명

OpenCV에서 Extra 2D Features는 cv2.xfeatures2d 네임스페이스를 통해 사용할 수 있습니다. 이 모듈은 기본 OpenCV 패키지에는 포함되어 있지 않으며 opencv-contrib-python 패키지를 설치해야 사용할 수 있습니다.

1. 설치 방법

pip install opencv-contrib-python

설치 후 다음과 같이 모듈을 확인할 수 있습니다.

import cv2
print(dir(cv2.xfeatures2d))

2. 주요 특징

1) 다양한 특징 기술자 제공
기본 OpenCV보다 훨씬 다양한 feature descriptor 알고리즘을 사용할 수 있습니다.

2) 연구 및 산업 프로젝트 활용
SLAM, 로봇 비전, 3D Reconstruction 등 다양한 컴퓨터 비전 프로젝트에서 활용됩니다.

3) 강력한 특징 매칭 성능
이미지의 회전, 크기 변화, 조명 변화에도 비교적 안정적인 특징을 추출합니다.

4) OpenCV와 높은 호환성
기존 OpenCV Feature2D 인터페이스와 동일하게 사용할 수 있어 코드 통합이 쉽습니다.

OpenCV Extra 2D Features 활용 예제

다음 예제는 SIFT 알고리즘을 사용하여 이미지의 특징점을 검출하고 시각화하는 Python OpenCV 코드입니다.

import cv2

# 이미지 읽기
img = cv2.imread("image.jpg")

# 특징 추출 정확도를 높이기 위해 grayscale 변환
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# SIFT 객체 생성
# Extra 2D Features Framework에서 제공되는 대표 알고리즘
sift = cv2.SIFT_create()

# 특징점(Keypoint)과 Descriptor 계산
# detectAndCompute 함수는 특징점 검출과 descriptor 생성을 동시에 수행
keypoints, descriptors = sift.detectAndCompute(gray, None)

# 특징점 시각화
# DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS 옵션을 사용하면
# 특징점의 크기와 방향 정보까지 표시됩니다.
result = cv2.drawKeypoints(
    img,
    keypoints,
    None,
    flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS
)

# 결과 출력
cv2.imshow("SIFT Keypoints", result)
cv2.waitKey(0)
cv2.destroyAllWindows()

위 코드는 이미지에서 특징점을 검출하고 이를 시각적으로 확인할 수 있도록 표시합니다. 이 방식은 이미지 매칭, 객체 인식, 파노라마 생성, 영상 추적과 같은 다양한 컴퓨터 비전 응용에서 활용됩니다.


OpenCV Extra 2D Features Framework는 OpenCV의 기능을 확장하여 고급 특징 추출 알고리즘을 제공하는 매우 중요한 컴퓨터 비전 도구입니다. 특히 이미지 매칭이나 객체 인식과 같은 프로젝트에서는 높은 정확도의 특징 검출이 필요하기 때문에 SIFT, SURF, FREAK과 같은 알고리즘이 자주 사용됩니다.

 

Extra 2D Features - SIFT Keypoints Result

728x90
728x90

OpenCV RGB Depth Processing 정의

OpenCV RGB Depth Processing은 RGB 이미지와 Depth(깊이) 데이터를 함께 활용하여 객체의 거리와 공간 구조를 분석하는 컴퓨터 비전 기술입니다. 일반적인 RGB 이미지는 색상 정보만 제공하지만 Depth 데이터는 카메라와 객체 사이의 실제 거리를 픽셀 단위로 제공합니다.


RGB와 Depth 데이터를 결합하면 단순한 이미지 인식에서 벗어나 공간 이해(Spatial Understanding)가 가능한 비전 시스템을 구축할 수 있습니다. 이러한 기술은 로봇 비전, 자율주행, 증강현실(AR), 3D 스캐닝 등 다양한 분야에서 활용되고 있습니다. 대표적으로 Intel RealSense, Microsoft Kinect, LiDAR 기반 센서 등이 RGB-D 데이터를 제공하며 OpenCV를 활용하면 이러한 데이터를 쉽게 처리하고 분석할 수 있습니다.

OpenCV RGB Depth Processing 설명

RGB Depth Processing의 핵심은 색상 이미지와 깊이 데이터를 동시에 분석하여 실제 공간 정보를 추출하는 것입니다. OpenCV에서는 다음과 같은 방식으로 RGB-D 처리를 구현할 수 있습니다.

1. RGB 이미지와 Depth 데이터 결합

RGB 카메라는 색상 정보를 제공하고 Depth 센서는 각 픽셀의 거리 정보를 제공합니다. 두 데이터를 결합하면 특정 객체가 어디에 있으며 얼마나 떨어져 있는지 파악할 수 있습니다. 예를 들어 사람을 인식한 후 해당 픽셀의 Depth 값을 확인하면 사람까지의 거리를 계산할 수 있습니다.

2. Depth 기반 객체 분리

Depth 데이터는 배경과 객체를 분리하는 데 매우 유용합니다. 특정 거리 범위만 필터링하면 원하는 객체만 추출할 수 있습니다.

  • 0 ~ 1m 거리 → 손 인식
  • 1 ~ 3m 거리 → 사람 인식

이러한 방식은 배경 제거, 제스처 인식, 로봇 장애물 회피 등에 활용됩니다.

3. 3D 공간 분석

RGB-D 데이터를 사용하면 다양한 고급 기능을 구현할 수 있습니다. 특히 로봇 비전이나 자율주행 시스템에서는 RGB-D 데이터가 매우 중요한 역할을 합니다.

  • 3D 포인트 클라우드 생성
  • 객체 거리 측정
  • 공간 맵핑(SLAM)
  • 로봇 경로 탐색

OpenCV RGB Depth Processing 예제(Python)

다음 예제는 RGB 이미지와 Depth 이미지를 사용하여 특정 거리 범위의 객체만 추출하는 간단한 Python 예제입니다.

import cv2
import numpy as np

# RGB 이미지와 Depth 이미지 불러오기
rgb = cv2.imread("rgb_image.png")        # 컬러 이미지
depth = cv2.imread("depth_image.png", cv2.IMREAD_GRAYSCALE)  # Depth 이미지

# Depth 범위 설정 (예: 가까운 객체만 선택)
min_depth = 50
max_depth = 150

# Depth 범위에 해당하는 영역만 마스크 생성
mask = cv2.inRange(depth, min_depth, max_depth)

# RGB 이미지에 마스크 적용
result = cv2.bitwise_and(rgb, rgb, mask=mask)

# 결과 출력
cv2.imshow("RGB Image", rgb)
cv2.imshow("Depth Mask", mask)
cv2.imshow("Filtered Object", result)

cv2.waitKey(0)
cv2.destroyAllWindows()

코드 설명

1. RGB와 Depth 이미지 로드

rgb = cv2.imread("rgb_image.png")
depth = cv2.imread("depth_image.png", cv2.IMREAD_GRAYSCALE)

RGB 이미지는 색상 정보를 포함하며 Depth 이미지는 각 픽셀의 거리 값을 나타냅니다.

2. 관심 거리 범위 설정

min_depth = 50
max_depth = 150

Depth 값이 해당 범위에 있는 객체만 선택하도록 설정합니다.

3. Depth 기반 마스크 생성

mask = cv2.inRange(depth, min_depth, max_depth)

설정한 Depth 범위에 해당하는 픽셀만 흰색으로 표시되는 마스크를 생성합니다.

4. RGB 이미지에 마스크 적용

result = cv2.bitwise_and(rgb, rgb, mask=mask)

Depth 조건에 맞는 영역만 RGB 이미지에 적용하여 특정 거리의 객체만 추출합니다.

OpenCV RGB Depth Processing 활용 사례

  • 로봇 비전 : 장애물 감지 및 경로 탐색
  • 자율주행 : 차량 주변 환경 인식
  • 증강현실(AR) : 공간 인식 기반 객체 배치
  • 3D 스캐닝 : 실제 환경을 디지털 3D 모델로 변환
  • 제스처 인식 : 손 동작 기반 인터페이스

 

 

RGB-Depth Processing: RGB Image, Depth Mask, Filtered Object

728x90
728x90

Face Anonymization 설명

실시간 얼굴 비식별화(Face Anonymization)는 영상이나 이미지에서 사람의 얼굴을 자동으로 감지한 뒤, 특정 알고리즘을 통해 개인을 식별할 수 없도록 처리하는 기술을 의미합니다. 일반적으로 블러(Blur), 모자이크(Pixelation), 마스킹(Masking) 등의 방법을 활용하여 얼굴 특징을 흐리게 만듭니다. 최근 CCTV, 영상 분석 시스템, AI 데이터 수집 등 다양한 분야에서 개인정보 보호 규정이 강화되면서 얼굴 비식별화 기술의 중요성이 높아지고 있습니다. 특히 컴퓨터 비전 라이브러리인 OpenCV를 활용하면 비교적 간단한 코드로 실시간 얼굴 탐지와 비식별화 처리를 구현할 수 있습니다.

OpenCV를 활용한 얼굴 비식별화 원리

OpenCV에서 얼굴 비식별화를 구현하는 기본적인 과정은 다음과 같습니다.

  1. 얼굴 탐지(Face Detection) : 영상에서 얼굴 위치를 찾습니다. 보통 Haar Cascade나 DNN 기반 모델을 사용합니다.
  2. ROI(Region of Interest) 추출 : 탐지된 얼굴 영역만 따로 추출합니다.
  3. 비식별화 처리 : 추출된 얼굴 영역에 블러 또는 모자이크 필터를 적용합니다.
  4. 영상에 다시 반영 : 처리된 영역을 원본 영상에 다시 합성합니다.

이 과정을 영상 프레임마다 반복하면 실시간 얼굴 비식별화 시스템을 구축할 수 있습니다.

대표적인 비식별화 방법은 다음과 같습니다.

  • Gaussian Blur : 자연스럽게 얼굴을 흐리게 처리
  • Pixelation : 모자이크 형태로 얼굴 특징 제거
  • Mask Overlay : 특정 색상 또는 이미지로 얼굴 가림

실제 서비스에서는 Gaussian Blur와 얼굴 탐지 모델 조합이 가장 많이 사용됩니다.

OpenCV 실시간 얼굴 비식별화 예제 (Python)

아래 예제는 웹캠 영상을 받아 얼굴을 탐지한 후 Gaussian Blur를 적용하여 실시간으로 비식별화하는 코드입니다.

import cv2

# Haar Cascade 얼굴 탐지 모델 로드
face_cascade = cv2.CascadeClassifier(
    cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
)

# 웹캠 실행
cap = cv2.VideoCapture(0)

while True:
    ret, frame = cap.read()
    if not ret:
        break

    # 그레이스케일 변환 (얼굴 탐지 성능 향상)
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # 얼굴 탐지
    faces = face_cascade.detectMultiScale(
        gray,
        scaleFactor=1.3,
        minNeighbors=5,
        minSize=(30, 30)
    )

    # 탐지된 얼굴마다 비식별화 처리
    for (x, y, w, h) in faces:

        # 얼굴 영역 추출 (ROI)
        face_region = frame[y:y+h, x:x+w]

        # Gaussian Blur 적용
        # (99,99)는 블러 강도를 의미하며 값이 클수록 더 흐려짐
        blurred_face = cv2.GaussianBlur(face_region, (99, 99), 30)

        # 원본 영상에 다시 삽입
        frame[y:y+h, x:x+w] = blurred_face

    # 결과 출력
    cv2.imshow("Real-time Face Anonymization", frame)

    # 'q' 키 입력 시 종료
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# 리소스 해제
cap.release()
cv2.destroyAllWindows()

CascadeClassifier는 Haar Cascade 기반 얼굴 탐지 모델을 불러오는 기능이며, detectMultiScale() 함수는 영상에서 얼굴 위치를 탐지하여 좌표 정보를 반환합니다. 이후 GaussianBlur() 함수를 이용해 얼굴 영역에 블러 처리를 적용하여 개인 식별이 어렵도록 만듭니다. 이 방식은 얼굴 영역만 선택적으로 처리하기 때문에 전체 영상 처리보다 효율적이며 실시간 영상 처리에서도 빠르게 동작합니다.


OpenCV를 이용하면 비교적 간단한 코드로 실시간 얼굴 비식별화 시스템을 구현할 수 있습니다. 얼굴 탐지와 블러 처리를 결합하면 CCTV, 영상 스트리밍, AI 데이터 수집 과정에서 개인정보를 보호할 수 있습니다. 최근에는 Haar Cascade 외에도 DNN 기반 얼굴 탐지 모델이나 YOLO 기반 객체 탐지 모델을 활용하여 더욱 높은 정확도의 비식별화 시스템을 구축하기도 합니다.

728x90
728x90

LangChain과 OpenCV의 정의

LangChain은 LLM(대규모 언어 모델)을 기반으로 외부 도구, API, 데이터베이스와 연결하여 확장 가능한 AI 애플리케이션을 구축할 수 있도록 돕는 프레임워크입니다. Tool, Agent, Chain 개념을 활용하여 모델이 외부 기능을 직접 호출하도록 설계할 수 있습니다.

OpenCV(Open Source Computer Vision Library)는 이미지 처리 및 컴퓨터 비전 분야에서 널리 사용되는 오픈소스 라이브러리입니다. 객체 탐지, 영상 필터링, 특징점 매칭, 영상 분할 등 다양한 기능을 제공합니다.

LangChain과 OpenCV를 함께 활용하면 자연어 명령을 이해하고 이미지 처리 작업을 자동으로 수행하는 AI 시스템을 구축할 수 있습니다.

LangChain에서 OpenCV를 호출하는 구조 설명

LangChain에서 OpenCV를 호출하는 일반적인 구조는 다음과 같습니다.

  1. OpenCV 기반 이미지 처리 함수 작성
  2. 해당 함수를 LangChain Tool로 래핑
  3. Agent를 통해 자연어 → Tool 호출 → 결과 반환 구조 구성

핵심은 LLM이 직접 OpenCV를 실행하는 것이 아니라, Python 함수 형태로 정의된 Tool을 호출한다는 점입니다.

예를 들어 다음과 같은 자연어 요청이 가능합니다.

  • 이미지를 그레이스케일로 변환해줘
  • 엣지를 검출해줘
  • 이미지에서 얼굴을 찾아줘

이러한 요청을 Agent가 해석한 뒤 적절한 OpenCV 함수를 실행합니다. 이 방식은 자연어 기반 영상 처리 자동화와 멀티모달 AI 시스템 구축에 매우 적합합니다.

LangChain + OpenCV 예제(Python)

import cv2
from langchain.tools import tool
from langchain.agents import initialize_agent, AgentType
from langchain.chat_models import ChatOpenAI

# OpenCV 이미지 처리 함수 정의
@tool
def detect_edge(image_path: str) -> str:
    """
    주어진 이미지 경로에서 Canny Edge Detection을 수행합니다.
    결과 이미지는 edge_result.jpg로 저장합니다.
    """
    # 이미지 읽기
    img = cv2.imread(image_path)

    # 그레이스케일 변환
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # Canny 엣지 검출
    edges = cv2.Canny(gray, 100, 200)

    # 결과 저장
    output_path = "edge_result.jpg"
    cv2.imwrite(output_path, edges)

    return f"엣지 검출 완료. 저장 경로: {output_path}"


# LLM 초기화
llm = ChatOpenAI(temperature=0)

# Agent 생성 (Tool 연결)
agent = initialize_agent(
    tools=[detect_edge],
    llm=llm,
    agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
    verbose=True
)

# 자연어로 OpenCV 호출
agent.run("sample.jpg에서 엣지를 검출해줘")

코드 설명

  • @tool 데코레이터로 OpenCV 함수를 LangChain Tool로 등록합니다.
  • Agent는 자연어 요청을 분석하여 detect_edge 함수를 자동 호출합니다.
  • OpenCV는 실제 이미지 처리를 수행하고 파일을 저장합니다.
  • LLM은 최종 결과 메시지를 사용자에게 반환합니다.

LangChain과 OpenCV를 결합하면 이미지 이해부터 처리, 설명 생성까지 하나의 파이프라인으로 구성할 수 있습니다. LangChain에서 OpenCV를 호출하는 방식의 핵심은 Tool 기반 함수 래핑과 Agent 구조입니다. 이를 통해 자연어 인터페이스 기반의 영상 처리 자동화 시스템을 효율적으로 구축할 수 있습니다.

728x90
728x90

1. OpenClaw 정의

OpenClaw는 Clawdbot 환경에서 외부 데이터 수집, 명령 자동화, 확장 기능 구현을 지원하는 오픈 인터페이스 기반 모듈입니다.

  • Clawdbot 기능 확장
  • 자동화된 데이터 수집 및 처리
  • 외부 API 연동
  • 봇 응답 로직 커스터마이징

즉, OpenClaw는 Clawdbot의 확장 엔진 역할을 수행하며, 고급 자동화 및 지능형 응답 시스템을 구현할 수 있도록 설계되었습니다.

2. OpenClaw 상세 설명

① 이벤트 기반 트리거 구조
사용자 입력 또는 시스템 이벤트 발생 시 OpenClaw가 이를 감지하고 정의된 액션을 실행합니다.

② 플러그인 확장 방식
모듈 단위로 기능을 추가할 수 있어 유지보수가 용이합니다.

③ API 연동 지원
REST API 기반 외부 서비스와 연동이 가능하여 실시간 데이터 활용이 가능합니다.

  • 실시간 주가 조회
  • 서버 상태 모니터링
  • 자동 리포트 생성
  • 로그 분석 자동화

또한 비동기 처리 구조를 기반으로 설계되어 대규모 요청 환경에서도 안정적으로 동작하며, 확장성이 뛰어납니다. 이러한 구조 덕분에 OpenClaw는 단순 챗봇 기능을 넘어 업무 자동화 플랫폼으로 확장할 수 있습니다.

3. OpenClaw 활용 예제(Python)

# OpenClaw 모듈 불러오기
from openclaw import ClawExtension
import requests

# 확장 클래스 정의
class WeatherExtension(ClawExtension):
    
    # 사용자가 /weather 명령어 입력 시 실행
    async def on_command(self, command, args):
        if command == "weather":
            
            # 외부 API 호출
            response = requests.get(
                "https://api.weatherapi.com/v1/current.json",
                params={
                    "key": "YOUR_API_KEY",
                    "q": args
                }
            )
            
            # JSON 데이터 파싱
            data = response.json()
            temp = data["current"]["temp_c"]
            
            # Clawdbot에 응답 반환
            return f"{args}의 현재 기온은 {temp}°C 입니다."

위 코드는 ClawExtension을 상속받아 확장 모듈을 생성하고, 특정 명령어 입력 시 외부 API를 호출하여 데이터를 가공한 후 사용자에게 반환하는 구조입니다. 이를 통해 사내 시스템 연동, 데이터 자동 조회, 알림 자동화, 보고서 자동 생성 등 다양한 고급 기능을 구현할 수 있습니다.

4. 실무 활용 전략

  1. 기능 단위로 모듈화 설계
  2. API 호출은 비동기 처리 권장
  3. 예외 처리 로직 필수 구현
  4. 로그 기록 시스템 연동
  5. 보안 키 관리 철저

특히 운영 환경에서는 API 키 암호화 및 요청 제한 제어가 매우 중요합니다. Clawdbot에서 OpenClaw는 단순 확장 모듈을 넘어 자동화 및 시스템 통합의 핵심 엔진 역할을 수행합니다. 이벤트 기반 구조와 플러그인 방식 덕분에 높은 확장성과 유연성을 제공하며, 실무 자동화에 최적화된 구조를 갖추고 있습니다.

728x90
728x90

Binary Descriptor 설명

Binary Descriptor는 이미지의 특징점을 이진 비트(Binary Bit) 형태로 표현하는 특징 기술자(Feature Descriptor)입니다. OpenCV에서는 ORB, BRIEF, BRISK, FREAK과 같은 Binary Descriptor를 제공하며, 주로 빠른 연산 속도와 낮은 메모리 사용량이 요구되는 실시간 컴퓨터 비전 환경에서 활용됩니다. 기존의 SIFT, SURF와 같은 부동소수점 기반 디스크립터와 달리, Binary Descriptor는 0과 1로 구성된 비트열을 사용하여 특징을 표현하므로 Hamming Distance를 이용한 빠른 매칭이 가능합니다.

OpenCV Binary Descriptor의 특징과 장점

Binary Descriptor는 다음과 같은 특징을 가지고 있습니다.

  • 연산 속도가 빠름: 비트 연산 기반으로 모바일 및 임베디드 환경에 적합합니다.
  • 메모리 사용량이 적음: 디스크립터 크기가 작아 대규모 특징점 처리에 유리합니다.
  • 실시간 처리에 최적화: SLAM, 객체 추적, 증강현실(AR) 등 실시간 응용에 널리 사용됩니다.

대표적인 Binary Descriptor는 다음과 같습니다.

  • ORB (Oriented FAST and Rotated BRIEF): OpenCV에서 가장 많이 사용되는 기본 Binary Descriptor
  • BRISK: 스케일 불변성과 회전 불변성을 지원
  • BRIEF: 매우 빠르지만 회전 불변성은 미지원

이 중 ORB는 성능과 안정성의 균형이 좋아 OpenCV 프로젝트에서 가장 많이 선택됩니다.

OpenCV ORB Binary Descriptor 예제(Python)

다음은 OpenCV에서 ORB Binary Descriptor를 사용하여 특징점을 검출하고 디스크립터를 생성하는 예제입니다.


import cv2

# 이미지 로드 (그레이스케일로 읽어오는 것이 일반적입니다)
image = cv2.imread("input.jpg", cv2.IMREAD_GRAYSCALE)

# ORB 객체 생성
# nfeatures는 검출할 최대 특징점 개수를 의미합니다
orb = cv2.ORB_create(nfeatures=500)

# 특징점(keypoints)과 Binary Descriptor 계산
keypoints, descriptors = orb.detectAndCompute(image, None)

# 결과 확인
print("검출된 특징점 개수:", len(keypoints))
print("Descriptor shape:", descriptors.shape)

위 코드에서 detectAndCompute() 함수는 특징점 검출과 디스크립터 생성을 동시에 수행합니다. 생성된 descriptors는 이진 형태의 배열이며, 이후 BFMatcher + Hamming Distance를 이용해 이미지 매칭에 활용할 수 있습니다. Binary Descriptor는 객체 인식, 이미지 매칭, 영상 추적과 같은 다양한 컴퓨터 비전 문제에서 속도와 효율성이 중요한 경우 매우 효과적인 선택이 됩니다.

728x90
728x90

ArUco Marker 설명

ArUco Marker는 컴퓨터 비전 분야에서 널리 사용되는 이차원(2D) 이진 마커(Binary Marker)입니다. OpenCV에서 공식적으로 지원하는 기능 중 하나로, 카메라 영상에서 특정 마커를 빠르고 정확하게 인식할 수 있도록 설계되었습니다. 각 마커는 고유한 ID를 가지며, 이를 통해 위치 추적, 자세 추정(Pose Estimation), 증강현실(AR), 로봇 비전 등 다양한 분야에서 활용됩니다.

OpenCV ArUco Marker 특징

OpenCV의 aruco 모듈은 마커 생성부터 검출, ID 인식, 카메라 좌표계 기준의 위치 및 회전 추정까지 통합적으로 제공합니다.

ArUco Marker의 주요 특징은 다음과 같습니다.

  • 빠른 검출 속도로 실시간 영상 처리에 적합
  • 고유 ID 제공으로 다수의 마커 동시 인식 가능
  • 카메라 캘리브레이션과 연계한 거리 및 각도 계산 가능
  • 조명 변화에 비교적 강인한 흑백 패턴 구조

특히 로봇이나 드론 분야에서는 ArUco Marker를 기준점으로 활용하여 자기 위치 인식(Localization)과 경로 보정에 널리 사용됩니다.

OpenCV ArUco Marker 활용 예제

아래는 OpenCV와 Python을 이용하여 ArUco Marker를 검출하고 ID를 출력하는 기본 예제 코드입니다.


import cv2
import cv2.aruco as aruco

# 기본 카메라 열기
cap = cv2.VideoCapture(0)

# ArUco 사전(Dictionary) 선택
aruco_dict = aruco.getPredefinedDictionary(aruco.DICT_4X4_50)

# 마커 검출 파라미터 생성
parameters = aruco.DetectorParameters()

while True:
    ret, frame = cap.read()
    if not ret:
        break

    # 그레이스케일 변환 (검출 성능 향상)
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # ArUco 마커 검출
    corners, ids, rejected = aruco.detectMarkers(
        gray, aruco_dict, parameters=parameters
    )

    # 마커가 검출된 경우
    if ids is not None:
        # 검출된 마커 테두리와 ID 시각화
        aruco.drawDetectedMarkers(frame, corners, ids)
        print("Detected IDs:", ids.flatten())

    cv2.imshow("ArUco Marker Detection", frame)

    # ESC 키 입력 시 종료
    if cv2.waitKey(1) & 0xFF == 27:
        break

cap.release()
cv2.destroyAllWindows()

위 예제에서는 실시간 카메라 영상에서 ArUco Marker를 검출하고, 마커의 테두리와 ID를 화면에 시각적으로 표시합니다. 이 구조를 확장하면 거리 측정, Pose Estimation, AR 오브젝트 합성까지 구현할 수 있습니다.

 

  • 아래 마커는 DICT 4X4_50 마커이며, 웹켐 연결 되어 있는 PC에서 위 예제 코드를 이용하여 쉽게 테스트 해 볼 수 있습니다.

DICT 4X4 50 Marker

728x90
728x90

1. OpenCV Video 처리

OpenCV는 이미지뿐만 아니라 동영상(Video) 처리 기능도 강력하게 제공합니다. Video 읽기(Read)와 쓰기(Write)는 파일, 웹캠, 네트워크 스트림 등 다양한 영상 소스를 다루는 핵심 기능으로, 영상 분석·객체 검출·트래킹·AI 기반 영상 처리의 출발점이 됩니다. OpenCV에서는 VideoCaptureVideoWriter 클래스를 통해 이를 간단하게 구현할 수 있습니다.

2. OpenCV Video 읽고 쓰기 원리와 설명

2.1 VideoCapture를 이용한 동영상 읽기

VideoCapture 클래스는 동영상 파일이나 카메라 장치를 프레임 단위로 읽어옵니다. 프레임은 이미지(Mat) 형태로 반환되며, 일반적인 OpenCV 이미지 처리 함수와 동일하게 활용할 수 있습니다.

  • 파일 입력: cv2.VideoCapture("video.mp4")
  • 웹캠 입력: cv2.VideoCapture(0)
  • 주요 메서드: isOpened(), read(), get()

2.2 VideoWriter를 이용한 동영상 저장

VideoWriter 클래스는 처리된 프레임을 다시 동영상 파일로 저장하는 역할을 합니다. 코덱(FourCC), FPS, 프레임 크기를 정확히 설정하는 것이 중요합니다. 대표적인 코덱으로는 XVID, MJPG, mp4v가 있으며, 저장 해상도는 입력 프레임 크기와 반드시 동일해야 합니다.

3. OpenCV Video 읽고 쓰기 예제(Python)

아래 예제는 동영상을 읽어 그레이스케일로 변환한 후 새로운 파일로 저장하는 기본적인 Video 처리 예제입니다.


import cv2

# 1. 동영상 파일 열기
cap = cv2.VideoCapture("input.mp4")

# 동영상이 정상적으로 열렸는지 확인
if not cap.isOpened():
    print("비디오 파일을 열 수 없습니다.")
    exit()

# 2. 원본 동영상 정보 가져오기
fps = cap.get(cv2.CAP_PROP_FPS)
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

# 3. VideoWriter 설정
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter("output.mp4", fourcc, fps, (width, height), isColor=False)

while True:
    # 4. 프레임 단위 영상 읽기
    ret, frame = cap.read()
    if not ret:
        break

    # 5. 프레임 처리 (그레이스케일 변환)
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # 6. 처리된 프레임 저장
    out.write(gray)

    # 결과 화면 출력
    cv2.imshow("Gray Video", gray)
    if cv2.waitKey(1) & 0xFF == 27:
        break

# 7. 자원 해제
cap.release()
out.release()
cv2.destroyAllWindows()

4. OpenCV Video 처리 활용 사례

  • AI 객체 검출 결과 영상 저장
  • CCTV 영상 분석 및 이상 상황 감지
  • 주행 영상 및 산업용 영상 데이터 전처리
  • 실시간 스트리밍 영상 분석

OpenCV의 Video I/O 기능은 OpenVINO, YOLO, DNN 모델과 결합하여 실무 수준의 AI 영상 처리 파이프라인을 구축하는 데 핵심적인 역할을 합니다.

728x90
728x90

관련 설명은 앞에 블로그 "OpenCV Graph API 설명과 활용 방법"를 참고해 보시면 됩니다. Python에서도 G-API를 활용하면 가독성 높은 파이프라인 구성과 연산 최적화가 가능하여 이미지 처리 코드의 유지보수성이 크게 향상됩니다.

OpenCV G-API Python 이미지 처리 예제

아래 예제는 컬러 이미지를 입력받아 그레이스케일 변환 후 캐니 엣지 검출을 수행하는 OpenCV G-API Python 이미지 처리 파이프라인입니다.


import cv2 as cv

# 그래프 입력 정의
in_gmat = cv.GMat()

# 그래프 연산 정의
gray = cv.gapi.RGB2Gray(in_gmat)
edges = cv.gapi.Canny(gray, 50, 150)

# 그래프 컴파일
graph = cv.GComputation(cv.GIn(in_gmat), cv.GOut(edges))

# 이미지 로드
input_img = cv.imread("input.jpg")

# 그래프 실행
output = graph.apply(cv.gin(input_img))

# 결과 출력
cv.imshow("G-API Edges", output)
cv.waitKey(0)
cv.destroyAllWindows()

예제 코드 설명

cv.GMat은 실제 이미지 데이터가 아닌 그래프 상의 입력 노드를 의미합니다. RGB2Gray와 Canny 함수는 즉시 실행되지 않고 그래프 연산으로 등록됩니다.


GComputation 객체는 전체 이미지 처리 파이프라인을 정의하며, apply 호출 시 그래프가 최적화되어 한 번에 실행됩니다. 이 방식은 불필요한 중간 결과 생성을 줄이는 데 효과적입니다.

 

 

처리 결과는 아래와 같이 원본 이미지와 처리 이미지이며 속도에 최적화 되어 있습니다. Canny Edge 결과를 볼 수 있습니다.

 

OpenCV G-API Canny Edge Result

728x90
728x90

OpenVINO 설명

OpenVINO(Open Visual Inference and Neural Network Optimization)는 Intel에서 제공하는 딥러닝 추론 최적화 툴킷입니다. 주로 컴퓨터 비전, 음성 인식, 자연어 처리 모델을 CPU, GPU, VPU 등 다양한 하드웨어에서 고속으로 추론할 수 있도록 지원합니다.

OpenVINO의 핵심 강점은 학습된 딥러닝 모델을 하드웨어 친화적인 중간 표현(IR, Intermediate Representation)으로 변환하고, 이를 기반으로 지연 시간(latency) 감소와 처리량(throughput) 향상을 동시에 달성할 수 있다는 점입니다. TensorFlow, PyTorch, ONNX 등 주요 프레임워크에서 학습한 모델을 그대로 활용할 수 있어 산업 현장에서의 적용성이 매우 높습니다.

특히 엣지 디바이스나 서버 환경에서 GPU 없이 CPU만으로도 안정적인 추론 성능을 제공하기 때문에 스마트 팩토리, 영상 분석, 지능형 CCTV, 의료 영상 처리 등 다양한 분야에서 활용되고 있습니다.

OpenVINO 설치 방법

OpenVINO는 Windows, Linux, macOS를 모두 지원하며, 최근에는 Python 기반 설치가 가장 많이 사용됩니다. 아래는 Python 환경 기준의 대표적인 설치 방법입니다. 참고로 C/C++ 환경에서도 사용 가능합니다.

1. 시스템 요구사항

  • Python 3.8 이상
  • pip 최신 버전
  • Intel CPU 권장 (타 CPU에서도 동작 가능)

2. pip를 이용한 설치

가장 간단한 방법은 pip를 이용하는 것입니다.

pip install openvino

설치가 완료되면 아래 코드로 Python에서 정상적으로 로드되는지 확인합니다.

import openvino as ov
print(ov.__version__)

3. 모델 변환 도구

OpenVINO는 모델 변환을 위해 Model Optimizer 기능을 제공합니다. ONNX 모델을 사용하는 경우 추가 설치 없이 바로 활용 가능합니다.

OpenVINO 활용 방법

OpenVINO는 크게 모델 변환, 추론 엔진 실행, 결과 후처리 단계로 활용됩니다.

1. 모델 로딩 및 컴파일

import openvino as ov

core = ov.Core()
model = core.read_model("model.onnx")
compiled_model = core.compile_model(model, "CPU")

2. 추론 실행

import numpy as np

input_tensor = np.random.rand(1, 3, 224, 224).astype(np.float32)
infer_request = compiled_model.create_infer_request()
infer_request.infer({0: input_tensor})
output = infer_request.get_output_tensor().data

OpenVINO는 OpenCV DNN 모듈과도 잘 결합되며, 기존 영상 처리 파이프라인에 비교적 쉽게 통합할 수 있습니다. OpenVINO는 단순한 추론 프레임워크를 넘어 딥러닝 모델의 성능을 극대화하는 실전 도구입니다. 특히 비용 효율적인 CPU 기반 AI 시스템을 구축하고자 한다면 OpenVINO를 활용해보는 것도 하나의 방법이 될 수 있습니다.

728x90
728x90

엣지 컴퓨팅은 데이터를 중앙 서버나 클라우드가 아닌 ‘데이터가 발생하는 지점(Edge)’에서 직접 처리하는 기술입니다. 카메라, IoT 센서, 임베디드 기기(라즈베리파이 등)와 같은 엣지 디바이스에서 데이터를 실시간 처리함으로써 다음과 같은 장점이 있습니다.

  • 지연(Latency) 감소 → 실시간 처리 가능
  • 대역폭 절약 → 서버 전송 데이터 최소화
  • 보안 강화 → 민감한 영상이 클라우드로 가지 않음
  • 운영 비용 절감 → 클라우드 사용량 감소

OpenCV는 가벼우면서도 빠르게 동작해 엣지 디바이스에서도 효율적으로 실행할 수 있으므로 엣지 컴퓨팅 환경에서 가장 널리 사용되는 컴퓨터 비전 라이브러리입니다.

 

엣지 컴퓨팅에서 자주 사용하는 OpenCV 핵심 기능 설명

 

1. 이미지 전처리(Image Preprocessing)

엣지 장치에서는 빠른 연산이 중요하기 때문에 간단하면서도 효율적인 필터/변환을 많이 사용합니다.

  • cv2.resize() : 이미지 크기 축소/확대
  • cv2.cvtColor() : BGR ↔ Gray, HSV 변환
  • cv2.GaussianBlur() : 노이즈 제거
  • cv2.threshold() : 이진화 처리

 

2. 객체 감지(Object Detection)

엣지 환경에서는 가벼운 알고리즘이 사용됩니다.

  • Haar Cascade (cv2.CascadeClassifier)
  • HOG + SVM (cv2.HOGDescriptor())
  • MobileNet-SSD 등 경량 DNN 모델 (cv2.dnn)

 

3. 영상 스트리밍 처리

엣지 디바이스 카메라에서 실시간 프레임 처리를 지향합니다.

  • cv2.VideoCapture(0) : 카메라 입력
  • cv2.imencode() : 네트워크 전송용 포맷 변환
  • cv2.imshow() : 디스플레이용

 

4. OpenCV DNN 모듈 활용 (경량 모델)

엣지 환경에서는 TensorFlow Lite, ONNX Runtime과 함께 가벼운 모델을 로드해 inference를 수행합니다.

  • cv2.dnn.readNetFromONNX()
  • cv2.dnn.blobFromImage()
  • net.forward()

 

5. 라즈베리파이, Jetson Nano 등에서 최적화

  • OpenCV 빌드 시 NEON, VFPV3 활성화
  • cv2.setNumThreads(2)로 CPU 사용량 제한
  • GPU가 있다면 CUDA 지원 OpenCV 사용

 

엣지 컴퓨팅에서 OpenCV 사용 예제

첫번째 예는 라즈베리파이에서 실시간 얼굴 검출이며, 두번째는 MobileNet을 이용한 경량 객체 검출, 세번째는 영상 압축 후 서버 전송 파이썬 예제 입니다.

 

import cv2

# Haar Cascade 불러오기
cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")

# 카메라 영상 읽기
cap = cv2.VideoCapture(0)

while True:
    ret, frame = cap.read()
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    faces = cascade.detectMultiScale(gray, 1.3, 5)

    for (x, y, w, h) in faces:
        cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)

    cv2.imshow("Edge Face Detection", frame)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

 

import cv2
import numpy as np

# MobileNet-SSD 로드
net = cv2.dnn.readNetFromCaffe(
    "deploy.prototxt",
    "mobilenet_ssd.caffemodel"
)

cap = cv2.VideoCapture(0)

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

    blob = cv2.dnn.blobFromImage(frame, 0.007843, (300, 300), 127.5)
    net.setInput(blob)
    detections = net.forward()

    for i in range(detections.shape[2]):
        confidence = detections[0, 0, i, 2]
        if confidence & 0.4:
            box = detections[0, 0, i, 3:7] * np.array(
                [frame.shape[1], frame.shape[0], frame.shape[1], frame.shape[0]]
            )
            (startX, startY, endX, endY) = box.astype("int")

            cv2.rectangle(frame, (startX, startY), (endX, endY), (255, 0, 0), 2)

    cv2.imshow("Edge Object Detection", frame)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

 

import cv2
import requests

cap = cv2.VideoCapture(0)

SERVER_URL = "http://server-ip/upload"

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

    # JPEG 압축
    _, buffer = cv2.imencode('.jpg', frame)
    files = {'image': buffer.tobytes()}

    # 서버로 전송
    requests.post(SERVER_URL, files=files)

 

엣지 컴퓨팅 환경에서는 속도·경량화·실시간 처리가 핵심이며, OpenCV는 이러한 요구에 적합한 기능을 제공합니다. 라즈베리파이, Jetson, ARM 기반 보드 등 다양한 엣지 디바이스에서 OpenCV를 활용하여 객체 감지, 영상 처리, DNN 추론 등의 다양한 작업을 효율적으로 수행할 수 있습니다.

728x90
728x90

프로세스 마이닝은 시스템에 남겨진 로그 등의 과거 데이터를 통해 업무 프로세스가 어떻게 이뤄지는지 분석하고 비교하여 최적화 할 수 있는 분석 기법이라고 했습니다. 기본적인 설명은 이전 블로그 링크를 참고해 보시면 좋을 것 같습니다.

 

2025.11.09 - [프로그래밍 기술] - 데이터 분석 프로세스 마이닝 (Process Mining) 개요

 

실제 이벤트 로그를 이용하여 프로세스 모델을 만들고 시각화하는 예제를 파이썬 라이브러리인 PM4Py를 통해 설명하겠습니다. PM4Py(Process Mining for Python)는 오픈소스 프로세스 마이닝 라이브러리이며, 이벤트 로그로부터 프로세스 모델 생성, 시각화, 적합성 검사, 병목 분석 및 KPI분석을 지원합니다.

 

설치는 pip을 이용하며, 윈도우와 맥, 리눅스 모두를 지원합니다.

  • pip install pm4py 

아래는 이벤트 로그를 활용한 PM4Py의 가단한 예제입니다. 로그는 CSV 포맷으로 로드됩니다. 주위할 점은 로그를 구성할 때 “case_id”, “activity”, “timestamp”는 필수적으로 존재해야 합니다. 프로세스 마이닝의 개념 자체가 시간 흐름에 따른 활동 분석이 목적이기 때문입니다. 모델 생성 알고리즘은 다양하며, 예제에서는 휴리스틱 마이너(Heuristic Miner) 방법을 사용했습니다. 휴리스틱 마이너 방법은 실제 로그에 담겨진 정보에 누락이나 오류, 반복과 드문 경로 등이 존재할 때 좋은 성능을 낼 수 있는 방법으로 알려져 있습니다.

 

# 이벤트 로그(CSV)로부터 프로세스 모델 생성 및 시각화
import pm4py

# 1. event log read ----------------------
log = pm4py.read_csv('event_log.csv')

# 2. column name define ------------------
log = pm4py.format_dataframe(
    log,
    case_id='case_id',
    activity_key='activity',
    timestamp_key='timestamp'
)

# 3. process discovery (Heuristic Miner) ---
net, im, fm = pm4py.discover_petri_net_heuristics(log)

# 4. model display -------------------------
# 각 activity를 노드로 표시하고 실제 실행 경로를 기반으로 연결선 표시
pm4py.view_petri_net(net, im, fm)

# 5. process map display ------------------
# 각 activity 간 이동 횟수, 평군 시간 등 시간적 요약
pm4py.view_process_map(log)

 

 

추가로 아래는 기존 모델과 실제 로그를 비교해 위반 탐지를 위한 적합성 검사(Conformance Checking)와 각 활동 간 평균 처리시간 계산(Throughput Time)을 위한 예제 코드입니다. 

 

# Conformance Checking (규정 위반 탐지)
from pm4py.algo.conformance.tokenreplay import algorithm as token_replay

replayed_traces = token_replay.apply(log, net, im, fm)
print(replayed_traces)

 

# Throughput time (각 활동 평균 처리 시간)
from pm4py.statistics.traces.generic.log import case_statistics

durations = case_statistics.get_all_case_durations(log, parameters={"timestamp_key": "timestamp"})
print("평균 처리시간:", sum(durations)/len(durations))
728x90
728x90

유전 알고리즘(GA)은 최적화 문제에 활용될 수 있습니다. 당연히 영상처리 기술들에도 적용이 가능하며, 윤곽선 검출에 대표 기술 중에 하나인 캐니 엣지 탐색(Canny Edge Detection)에 적용해 보겠습니다. 캐니 엣지 탐색 방법에서는 윤곽선 검출을 위해 두 개의 임계값인 Threshold1과 Threshold2를 사용하며, Threshold1 이하는 엣지가 아닌 영역이며 Threshold2 이상은 엣지영역으로 구분하고 Thresholds의 사이는 Canny Procedure에 의해 엣지 유무를 판단하게 됩니다.

 

2025.10.26 - [영상처리 기술] - 영상처리 유전 알고리즘 Genetic Algorithm 이해

2018.01.15 - [영상처리 도구] - OpenCV 윤곽선 검출 Edge Detection

 

유전 알고리즘의 기본과 윤곽선 검출에 대해서는 기존 블로그를 참고해 보시면 도움이 될 수 있습니다. 아래는 OpenCV와 파이썬을 활용한 캐니 엣지 탐색 알고리즘의 파라미터 Threshold1과 Threshold2 최적화 예제입니다. 개체(염색체)는 [Threshold1, Threshold2]로 설정이 되고 적합도(Fitness) 평가에는 당연히 캐니 엣지 탐색 방법과 그 윤곽선 수로 Score를 계산합니다.

 

기존 블로그에서도 언급을 했지만 반복 수행을 통해 최적해를 찾는 점과 교차율과 돌연변이율 등 매개변수 설정이 있다는 것은 설정 차이를 통해 Solution에 차이가 생길 수 있다는 점을 인식해야 하며 이런 이유로 항상 최적해를 보장하지 않는다는 점을 알고 유전 알고리즘을 활용해야 합니다.

 

import cv2
import numpy as np
import random

# ------- gray image read  ---------
img = cv2.imread('image.tif', cv2.IMREAD_GRAYSCALE)

# ------ 1. fitness define ------------
def fitness(threshold1, threshold2):
    edges = cv2.Canny(img, threshold1, threshold2)
    score = np.sum(edges > 0) # total number of edges
    return score

# --------2. initialization --------------
def random_chromosome():
    return [random.randint(0, 100), random.randint(100, 255)]  # [t1, t2]

population_size = 20
population = [random_chromosome() for _ in range(population_size)]

# ------ 3. GA parameters --------------
generations = 30
mutation_rate = 0.2

# ------- 4. repetition ----------------
for gen in range(generations):
    fitness_scores = [fitness(ch[0], ch[1]) for ch in population] # fitness calc.
    
    # selection (선택)
    sorted_pop = [x for _, x in sorted(zip(fitness_scores, population), reverse=True)]
    parents = sorted_pop[:int(population_size/2)]

    # Crossover (교차)
    children = []
    while len(children) < population_size - len(parents):
        p1, p2 = random.sample(parents, 2)
        cross_point = random.randint(0, 1)
        child = p1[:cross_point+1] + p2[cross_point+1:]
        children.append(child)
    
    # Mutation (돌연변이)
    for child in children:
        if random.random() < mutation_rate:
            idx = random.randint(0, 1)
            child[idx] = random.randint(0, 255)
    
    # Replacement (대체)
    population = parents + children

    # best sol
    best_fit = max(fitness_scores)
    best_chrom = population[np.argmax(fitness_scores)]
    print(f"세대 {gen+1}: 최고점 ={best_fit}, 임계값={best_chrom}")

# ------- display --------------------
best_t1, best_t2 = best_chrom
best_edge = cv2.Canny(img, best_t1, best_t2)

cv2.imshow("Canny Best Edges", best_edge)
cv2.waitKey(0)
cv2.destroyAllWindows()

  

728x90
728x90

위너필터는 신호처리와 영상처리 시 잡음을 제거하고 원래 신호를 복원할 때 사용하는 적응형 필터 중에 하나 입니다. 1949년 Norbert Wiener가 제안한 방법으로 프로세스 사이의 평균제곱오차(Mean Square Error)를 최소화 합니다. 위너 필터는 주파수 영역에서 계산되며 저주파에서는 실제 영상의 정보가 많기 때문에 보존하고 고주파에서는 잡음이 많을 가능성이 있기 때문에 억제 합니다. 결국 주파수별 신화와 잡음비(SNR)에 따라 가변적으로 동작하는 적응형 필터라고 말할 수 있습니다.

 

이미지에 블러링 효과와 잡음이 동시에 결합된 경우에 많이 사용됩니다. 블러(흐림) 전달함수를 H, H의 켤레복소수 H*, 잡음의 파워 스펙트럼 Snn, 원영상의 파워 스펙트럼 Sxx라고 할 때 수식은 아래의 같습니다.

 

 

특징으로는 신호대 잡음비(SNR)의 통계적 특성을 고려한 최적의 적응형 필터이며 영상의 복원과 잡음제거에 대표적인 필터 중에 하나 입니다. 반대로 신호대 잡음의 스펙트럼 정보를 미리 알아야 한다는 점은 단점으로 실제 영상처리에서 정확한 스펙트럼을 알기 어려워 근사치를 사용하게 됩니다.

 

수학적 접근으로 간략히 정리해 보면, 어떤 공간에서 선형적인 움직임과 잡음을 포함한 이미지를 g라고 해보겠습니다. 원래 이미지를 f, 움직임에 대한 효과(PSF)를 h, 잡음을 n이라고 하면 아래와 같이 표현됩니다. 푸리에 주파수 영역으로 변환을 하면 각 G, H, F, N으로 표현될 수 있습니다.

 

g(x,y) = h(x,y)*f(x,y) + h(x,y) → G(u,v) = H(u,v)F(u,v) + N(u,v)

 

목표는 위너 필터를 W라고 했을 때, F’(u,v) = W(u,v)G(u,v)에 평균제곱오차(MSE)를 최소화하는 것입니다. 중간 과정은 제외하면 최종적으로 아래 수식을 구하게 됩니다. 

 

앞에 언급했듯이 입력으로 들어오는 잡음비를 정확히 알 수 없으므로 실무에서는 Sn/Sf (Snn/Sxx)를 상수 K로 두고 근사값으로 적용합니다. 아래는 위너 필터에 대한 Python 예제 코드니 살펴 보시면 이해에 도움이 될 수 있으며, 코드에서는 Noise/Signal 비율 K가 0.01이 적용되었습니다.

 

# wiener filter example ------------------------------
import cv2
import numpy as np
import matplotlib.pyplot as plt

# image read -----------------------------------------
img = cv2.imread("image.tif", cv2.IMREAD_GRAYSCALE)
img = img.astype(np.float32) / 255.0 # 0~1 normalization

# blur PSF --------------------------------------------
def motion_blur_psf(length, angle, shape): 
psf = np.zeros(shape, dtype=np.float32) 
center = (shape[0] // 2, shape[1] // 2) 
x = np.linspace(-length // 2, length // 2, length) 
y = np.zeros_like(x) 
coords = np.vstack((x, y)).T 
rot = cv2.getRotationMatrix2D((0, 0), angle, 1) # rotation effect
coords = coords @ rot[:, :2].T 
for (dx, dy) in coords.astype(int): 
px, py = int(center[0] + dx), int(center[1] + dy) 
if 0 <= px < shape[0] and 0 <= py < shape[1]: 
psf[px, py] = 1 
psf /= psf.sum()  
return psf
psf = motion_blur_psf(15, 30, img.shape)

# noise -----------------------------------------------
H = np.fft.fft2(np.fft.ifftshift(psf)) # PSF - FFT
G_blur = np.fft.ifft2(np.fft.fft2(img) * H).real
noise = np.random.normal(0, 0.01, img.shape)
g = np.clip(G_blur + noise, 0, 1)

# wiener -----------------------------------
def wiener_filter(G, H, K): 
H_conj = np.conj(H) 
return (H_conj / (np.abs(H)**2 + K)) * G

G = np.fft.fft2(g) # fft transform
K = 0.01 # NSR (Noise-to-Signal Ratio)
F_hat = wiener_filter(G, H, K)

f_hat = np.fft.ifft2(F_hat).real # inverse fft
f_hat = np.clip(f_hat, 0, 1)

# display ---------------------------
plt.figure(figsize=(12,4))
plt.subplot(1,3,1), plt.imshow(img, cmap="gray"), plt.title("Original"), plt.axis("off")
plt.subplot(1,3,2), plt.imshow(g, cmap="gray"), plt.title("Blur + Noise"), plt.axis("off")
plt.subplot(1,3,3), plt.imshow(f_hat, cmap="gray"), plt.title("Wiener Restored"), plt.axis("off")
plt.show()
728x90
728x90

칼만 필터는 어떤 시스템에 잡음이 포함되어 있는 측정치에 기반하여 상태를 추정하는 재귀 필터라고 했습니다. 기본적인 내용은 이전 블로그 2025.08.24 - [영상처리 기술] - 칼만 필터 Kalman Filter 이해와 활용(1) 를 먼저 참고해 보시면 이해하기 쉬울 듯 합니다.

 

어떤 움직이는 물체를 모델로 해서 위치를 추정해 보는 예를 살펴보겠습니다. 기본적으로 칼만 필터의 수행은 예측(Prediction), 측정(Measurement), 그리고 추정(Update)의 반복이라고 했습니다. 우리가 추적하고 싶은 물체는 아래와 같은 초기 조건로 가정합니다.

 

(초기 조건)

  • 시작 추정 위치 x_0 = 10 m
  • 초기 오차 P_0 = 1.0
  • 속도 u = 1.0 m/s
  • 시간 간격 Δt = 1.0 s
  • 프로세스 노이즈 Q = 0.1
  • 측정 노이즈 R = 0.5 (GPS 오차)
  • 첫 번째 GPS 측정값 z_1 = 11.2, 두 번째 GPS 측정값 z_2 = 12.4
  • 상태 전이 모델(상태 공간 모형) x_k = x_k-1 + u·Δt 

첫 번째 계산 (첫 번째 루프)

1) 예측: 

  • (위치) x’_1 = x_0 + u·Δt = 10m + 1.0m/s * 1.0s = 11.0m
  • (오차 공분산) P’_1 = P_0 + Q = 1.0 + 0.1 = 1.1

2) 측정 및 업데이트

  • (칼만 이득) K_1 = P’_1 / (P’_1+R) = 1.1 / (1.1+0.5) = 0.6875
  • (위치) x_1 = x’_1+K_1(z_1-x’_1) = 11.0+0.6875(11.2-11.0) = 11.1375
  • (오차 공분산) P_1=(1-K_1)*P’_1 = (1-0.6875)*1.1 = 0.34375

두 번째 계산 (두 번째 루프)

1) 예측

  • (위치) x’_2 = x_1 + u·Δt = 11.1375 + 1.0 = 12.1375
  • (오차 공분산) P’_2 = P_1 + Q = 0.34375 + 0.1 = 0.44375

2) 측정 및 업데이트

  • (칼만 이득) K_2 = 0.44375 / (0.44375 + 0.5) = 0.4702
  • (위치) x_2 = 12.1375 + 0.4702*(12.4-12.1375) = 12.261
  • (오차 공분산) P_2 = (1-0.4702)*0.44375 = 0.235 

 

첫 번째 1초 후 추정 위치 x는 11.1375m, 오차 P는 0.34375, 두 번째 2초 후에는 추정 위치 12.261m, 오차 P는 0.235로 계산 되었고 시간에 따라 오차가 작아지는 것을 알 수 있습니다. 이는 추정 신뢰도가 상승 한다는 의미로도 볼 수 있습니다. 아래는 위 예제의 1차원 위치 추정을 위한 칼만 필터 파이썬 코드니 테스트 해보시면 이해하는데 도움이 될 수 있습니다.

 

# Kalman Filter 1D Exmaple
import numpy as np
import matplotlib.pyplot as plt

num_steps = 10
true_position = 10.0
velocity = 1.0 # 속도

process_noise = 0.1   # 프로세스 노이즈 Q
measurement_noise = 0.5  # 센서 노이즈 R

# 초기 추정값
x_est = 10.0  # 추정 위치
P = 1.0       # 추정 오차 공분산

true_positions = [true_position]
measured_positions = []
estimated_positions = [x_est]
errors = [P]

# Kalman Filter Loop
for step in range(1, num_steps + 1):
    # 실제 위치 업데이트 (노이즈 없는 이동)
    true_position += velocity
    true_positions.append(true_position)

    # 측정값 (노이즈 포함 GPS)
    z = true_position + np.random.normal(0, np.sqrt(measurement_noise))
    measured_positions.append(z)

    # 예측 단계
    x_pred = x_est + velocity
    P_pred = P + process_noise

    # 칼만 이득 계산
    K = P_pred / (P_pred + measurement_noise)

    # 업데이트 단계
    x_est = x_pred + K * (z - x_pred)
    P = (1 - K) * P_pred

    # 저장
    estimated_positions.append(x_est)
    errors.append(P)

# 결과 디스플레이
plt.figure(figsize=(10, 6))
plt.plot(true_positions, label="True Position", color='green', linestyle='--')
plt.plot(range(1, num_steps + 1), measured_positions, label="Measured (GPS)", color='red', linestyle=':', marker='o')
plt.plot(estimated_positions, label="Estimated (Kalman Filter)", color='blue', marker='s')
plt.xlabel("Time Step")
plt.ylabel("Position")
plt.title("Kalman Filter Position Estimation (1D)")
plt.legend()
plt.grid(True)
plt.show()
728x90
728x90

허프 변환(Hough Transform)은 컴퓨터 비전, 이미지 처리 및 패턴 인식 분야에서 사용되는 특징 추출 기술들 중 하나 입니다. 1972년에 발표된 오래된 이론으로 처음에는 이미지 내에 직선 성분을 찾기 위한 방법이었지만 추후 원과 타원 추출 방법으로 확장 되었습니다. 현 글에서는 허프 변환을 이용한 직선 추출 방법에 대해 설명하겠습니다.

 

일반적인 에지 탐색 등을 활용한 직선 성분 추출 시 잡음 등에 의해 중간 중간이 끊겨서 완전한 직선을 탐색하기 어려운 경우가 많습니다. 이 때, 허프 변환을 활용하면 일부 좌표 손실 상황에서도 직선 형태를 강건하게 검출할 수 있다는 장점이 있습니다. 일반적으로 카테시안(데카르트) 좌표계에서 직선식 표현하면 1)와 같으며 기울기 m의 경우 무한대가 나올 수 있어 활용하는데 문제가 될 수 있습니다. 그래서 허프 변환에서는 2)와 같이 극좌표계로 변환하여 직선 성분을 탐색합니다.  

 

1) y = mx + b // m: 기울기, b: y절편

2) r = xcosθ + ysinθ // r: 원점에서 직선까지 수직 거리, θ: 수직선과 x축 사이의 각도

 

 

 

허프 변환은 일부 가려짐이나 잡음에 강한 특성을 가지는 반면 계산량이 많아 질 수 있다는 점과 파라미터 설정이 중요할 수 있습니다. 그림에서처럼 이미지 평면에 점 A, B, C가 존재할 때, 각 점에 대해 각도 별(0~180도)로 거리(r)와 각도(theta)를 구하게 됩니다. 각 점을 지나는 극좌표점을 비교해 보면 60도 일때 가장 비슷한 수치를 얻게되며 파랑색 직선을 얻을 수 있습니다.

 

OpenCV에서는 직선 탐색을 위한 허프변환에 HoughLines()와 HoughLinesP()의 두 가지 함수를 제공합니다. 전자는 기본 허프 변환이며, 후자는 확률적 허프 변환(Probabilistic Hough Transform)입니다. 파이썬 예제 코드를 통해 파라미터 설정을 참고해보면 이해하기 쉬울 수 있습니다.

 

lines = cv2.HoughLines(image, rho, theta, threshold)

- image: 8-bit, Canny 등 에지 탐색 된 이진화 이미지

- rho(r): 거리 해상도(1이면 1-pixel 간격)

- theta: 각도 해상도 (0~180), 라디안 값으로 적용 

- threshold: 겹치는 점들의 축적값에 대한 임계값

 

lines = cv2.HoughLinesP(image, rho, theta, threshold, minLineLength, maxLineGap)

- minLineLength: 직선의 최소 길이

- maxLineGap: 탐색된 직선들 사이에 최대 허용 간격 

 

# Basic Hough Transform -------------------
import cv2
import numpy as np

img = cv2.imread('image.tif')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150)

lines = cv2.HoughLines(edges, 1, np.pi / 180, 150) # 허프 변환

for line in lines:
    rho, theta = line[0]
    a = np.cos(theta)
    b = np.sin(theta)
    x0 = a * rho
    y0 = b * rho
    # 두 점(x1,y1), (x2,y2)로 직선을 그림
    x1 = int(x0 + 1000*(-b))
    y1 = int(y0 + 1000*(a))
    x2 = int(x0 - 1000*(-b))
    y2 = int(y0 - 1000*(a))
    cv2.line(img, (x1,y1), (x2,y2), (0,0,255), 2)

cv2.imshow("Hough Lines", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

# Probabilistic Hough Transform --------------------------
import cv2
import numpy as np

img = cv2.imread('image.tif')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150)

# 확률 허프 변환
lines = cv2.HoughLinesP(edges, 1, np.pi/180, 80, minLineLength=50, maxLineGap=10)

for line in lines:
    x1, y1, x2, y2 = line[0]
    cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 2)

cv2.imshow("Probabilistic Hough", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
728x90
728x90

K-means 알고리즘은 비지도 학습 Unsupervised Learning 방법 중 하나로 군집화 Clustering 문제를 풀기 위한 기술 중 하나 입니다. 명칭에서처럼 데이터를 평균값을 기반으로 K 개의 유사한 그룹으로 묶어주는 방법입니다. 계산 복잡도가 낮아 처리 속도에서 빠른 편에 속하며 원형 형태의 군집을 구분할 때 적합합니다. 다양하고 복잡한 형태의 군집에 적용은 한계가 있으며, 군집 수 K는 자동 결정이 아닌 사전에 결정해야 할 변수 입니다.

 

단계별 작동 원리는 간단합니다. 

1) 초기값 K개 중심점 선택 또는 특정 방식으로 초기화

2) 각 데이터 포인트를 Euclidean Distance 기반하여 가장 가까운 중심점 할당

3) 각 군집(Cluster)에 속한 모든 데이터의 평균값 계산 및 중심점 업데이트

4) 더 이상 중심점이 변화하지 않거나 지정된 반복 횟수를 만족할 때까지 반복

 

그림에서처럼 초기점이 C1과 C2라면 각 포인트 별 거리 계산을 통해 평균값을 업데이트 하면 초기점들은 우측 그림에서처럼 각 군집에 중심으로 이동하게 됩니다.

 

 

 

K-means에서는 군집수 K가 제대로 설정되느냐에 따라 성능을 좌우합니다. 사전에 군집수를 알고 있으면 좋겠지만 대부분 알지 못하기 때문에 적절한 K를 찾아야 할 때가 많습니다. 아래는 적절한 K를 찾기 위한 몇 방법들입니다.

 

Elbow method (엘보우 방법)

클러스터 수에 따른 총 제곱거리 합(Sum of Squared Errors: SSE)을 그래프로 그려서 꺾이는 지점을 K로 선택, 시각적으로 직관적이어서 많이 활용.

Silhouette (실루엣 계수)

각 데이터가 해당 클러스터에 잘 속해 있는지를 평가, 범위 -1.0 ~ +1.0 사이로 값이 클수록 군집화가 잘 되었다는 의미. 다양한 K값에 대해 계산하고 가장 높은 값을 갖는 K를 선택하며 정량적으로 평가.

Gap Statistic (갭 통계량)

군집화 결과의 SSE와 랜덤한 군집화 SSE의 차이를 비교하여 최적 K를 결정, 구현 복잡성과 계산 비용이 큰 단점이 있음.

 

이 외에 Davies-Bouldin Index와 Calinski-Harabasz Index 등이 있습니다. 아래는 적절한 K를 찾기 위한 Elbow Method 예제와 K-means 군집화 파이썬 예제 입니다.

 

# Elbow Method Example ----------------------------------
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs

# 샘플 데이터 생성
X, _ = make_blobs(n_samples=300, centers=4, cluster_std=0.6, random_state=42)

# K 값에 따른 SSE 리스트
sse = []

# 다양한 K값(1~11)에 대해 K-Means
for k in range(1, 11):
    kmeans = KMeans(n_clusters=k, random_state=42)
    kmeans.fit(X)
    sse.append(kmeans.inertia_)  # inertia_가 바로 SSE 값

# Elbow 그래프 시각화
plt.plot(range(1, 11), sse, marker='o')
plt.xlabel('클러스터 수 (K)')
plt.ylabel('오차 제곱합 (SSE)')
plt.title('Optimal K')
plt.grid(True)
plt.show()

 

# K-means algorithm example ------------------------
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs

# 1.샘플 데이터 생성 
X, _ = make_blobs(n_samples=300, centers=4, cluster_std=0.60, random_state=0)

# 2.KMeans (K=4)
kmeans = KMeans(n_clusters=4)
kmeans.fit(X)

# 3.예측된 클러스터
y_kmeans = kmeans.predict(X)

# 4. 결과 시각화
plt.scatter(X[:, 0], X[:, 1], c=y_kmeans, s=50, cmap='viridis')  # 각 클러스터 색상표시

# 5. 중심점 표시
centers = kmeans.cluster_centers_
plt.scatter(centers[:, 0], centers[:, 1], c='red', s=200, alpha=0.75, marker='X')
plt.title("K-means Clustering Result")
plt.xlabel("X-axis")
plt.ylabel("Y-axis")
plt.grid(True)
plt.show()
728x90
728x90

시간 영역의 신호를 주파수 영역으로 변환하여 처리나 해석하는 이론이 푸리에 변환 Fourier Transform 입니다. 신호처리 분야에서 주파수 영역 분석을 위한 중요한 이론이기도 합니다. 영상처리에서는 이미지를 2차원 신호로 보고 푸리에 변환을 이용할 수 있으며, 이미지 공간 정보를 주파수 성분으로 바꾸어 분석할 수 있습니다. 분석을 통한 응용은 이미지 필터링, 압축, 복원등에 활용됩니다.

 

푸리에 변환은 이론적으로 4가지 형식으로 구분됩니다. 영상처리에 활용되는 기법은 이산 푸리에 변환 DFT (Discrete Fourier Transform) 입니다. 아래는 2차원 이산 푸리에 변환 수식이며 오일러 공식과 결합된 형태 입니다.

 

 

여기서 f(x,y)는 원본 이미지의 픽셀 값, F(u,v)는 주파수 평면에서의 값, M과 N은 이미지 크기인 Width와 Height 입니다. 추가로 푸리에 변환 요소에는 각 주파수 성분의 세기를 나타내는 크기 Magnitude와 성분의 위치 정보를 나타내는 위상 Phase 정보가 있으며, 위상 정보는 이미지의 구조를 유지하는데 중요한 요소입니다.

 

이미지를 푸리에 변환하면 최종 주파수 영역에 중심에는 저주파, 바깥쪽에는 고주파로 구성됩니다. 고주파는 이미지에서 물체의 윤곽선과 같은 에지 Edge 성분들을 의미하고 저주파는 물체의 전반적인 형태를 나타냅니다. 따라서 응용에서는 이러한 특징을 이용하여 저역통과 Low-pass 필터링 및 고역통과 High-pass 필터링을 통해 이미지의 경계 강조나 블러 효과를 낼 수 있습니다. 이미지 압축에서는 고주파 성분이 적으면 적을 수록 작은 정보만으로 이미지 재구성이 가능해지며, 대표적으로 JPEG 손실 압축 알고리즘에서 푸리에 이론의 일종인 이산 코사인 변환 DCT(Discrete Cosine Transform)을 사용합니다.

 

코드를 통해 푸리에 활용 방법을 확인해 보겠습니다. 실무에서는 C/C++, Python, Matlab 등 알고리즘 구현시 FFT (Fast Fourier Transform)을 이용합니다. DFT의 계산 복잡도 때문에 FFT를 활용하게 되며, 예를 들어 샘플 수가 N=1024일 때 DFT는 약 100만번의 연산이 필요한 반면 FFT는 10,000번의 연산이 필요합니다. 아래와 같이 파이썬 Numpy에서는 “np.fft.fft2” 함수를 사용하여 이미지 평면을 주파수 평면으로 변환하며, “np.fft.fftshift” 함수를 통해 저주파 성분을 중심으로 이동 시킵니다. 반대로 주파수 평면에서 이미지 평면으로 변환 시, “np.fft.ifftshift” > “np.fft.ifft2” 함수 순으로 적용할 수 있습니다. 

 

FFT의 이론 및 응용에 대해서는 추후 예와 함께 좀더 상세히 다루도록 하겠습니다.

 

import cv2
import numpy as np

# 이미지 불러오기 (그레이스케일)
img = cv2.imread('lena.jpg', cv2.IMREAD_GRAYSCALE)

f = np.fft.fft2(img) # 푸리에 FFT 변환
fshift = np.fft.fftshift(f)  # 중심 이동

magnitude_spectrum = 20 * np.log(np.abs(fshift)) # 주파수 스펙트럼 확인

f_ishift = np.fft.ifftshift(fshift_filtered) # 중심 복원
img_ifft = np.fft.ifft2(f_ishift) # 역 FFT 적용

# 실수 이미지 변환
img_re = np.abs(img_ifft)
728x90
728x90

이미지에서 우리가 원하는 영역을 찾거나 추출할 때 필요한 방법이 이진화 Binarization 입니다. 물체 탐색, 물체 추적, 이미지 분할 등 알고리즘을 구현할 때 필수적으로 사용해야하는 기법이 이진화 기술들입니다. 이진화 방법에는 다양한 기술들이 있으며, OpenCV 에서 제공하는 기술들 중 하나인 적응적 이진화 기술 Adaptive Thresholding Method에 대해 알아보겠습니다.

 

이진화 방법은 크게 전역적 처리 Global Processing과 지역적 처리 방법 Local Processing 방법으로 구분할 수 있습니다. OpenCV에서 제공하는 “adaptiveThreshold” 방법은 지역적 처리 방법과 적응적 방법이 합쳐진 이진화 방법입니다. Python에서는 아래와 같이 “cv2.adaptiveThreshold” 명으로 적용해 볼 수 있으며, 입력 변수는 아래와 같습니다.

 

[Python] dst = cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C)

(입력 변수 설명)

- src: 입력 이미지 (Gray Scale Image)

- maxValue: 임계값 Threshold을 초과한 픽셀에 적용할 값 (일반적으로 255)

- adaptiveMethod: 1) cv2.ADAPTIVE_THRESH_MEAN_C, 2) cv2.ADAPTIVE_THRESH_GAUSSIAN_C

- thresholdType: 1) cv2.THRESH_BINARY, 2) cv2.THRESH_BINARY_INV

- blockSize: 픽셀 임계값을 계산할 영역 크기 (홀수: 3, 5, 7 ...)

- C: 계산된 임계값에서 빼는 상수 (일반적으로 0 또는 2~10)

 

여기서 “cv2.ADAPTIVE_THRESH_MEAN_C”는 “blockSize”가 3이라면 3x3 크기 영역 안에 평균 값에서 “C”를 뺀 값을 임계값으로 사용하게 됩니다. “cv2.ADAPTIVE_THRESH_GAUSSIAN_C”는 3x3 크기 영역 안에 가우시안 가중 평균값에서 “C”를 뺀 값을 임계값으로 사용합니다. 앞에서 언급했듯이 “blockSize”는 3은 3x3, 5는 5x5의 블럭을 의미하며 블럭 크기가 작아질 수록 잡음 Noise에 민감해지고 너무 크면 지역적 처리의 이점이 줄어들 수 있으니 설정 시 고민이 필요할 수 있습니다.

 

C/C++ 코드를 사용한다면 아래와 같이 사용할 수 있습니다. 파이썬 함수와 다르게 결과 이미지는 입력 변수와 함께 설정해야 합니다. (dst = 결과 이미지)

[C/C++] adaptiveThreshold(src, dst, maxValue, adaptiveMethod, thresholdType, blockSize, C)

 

지역적이면서 적응적 이진화 방법은 이미지에 조명이 균일하지 않거나 배경이 복잡하여 명암이 불균일한 경우 유용할 수 있으며, 문서 이미지에서 문자 추출 시에도 잘 활용될 수 있습니다. 아래는 파이썬과 C/C++에서 “adaptiveThreshold”를 활용한 예이니 참고해서 보면 좋을 듯 합니다.

 

import cv2

# 그레이 이미지 읽기
img = cv2.imread('grayimage.jpg', cv2.IMREAD_GRAYSCALE)

# 가우시안 적응형 이진화
thresh = cv2.adaptiveThreshold(
    img,
    255,
    cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
    cv2.THRESH_BINARY,
    11,
    2
)

cv2.imshow("Adaptive Threshold", thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

#include <opencv2/opencv.hpp>
using namespace cv;

int main() {
    // 이미지 불러오기 (그레이스케일)
    Mat src = imread("grayimage.jpg", IMREAD_GRAYSCALE);
    Mat dst;
    // 적응형 이진화 적용
    adaptiveThreshold(
        src,              // 입력 이미지
        dst,              // 출력 이미지
        255,              // 최대값
        ADAPTIVE_THRESH_GAUSSIAN_C,  // 적응형 방법
        THRESH_BINARY,    // 임계값 타입
        7,               // 블록 크기 (홀수)
        2                 // C 상수
    );

    // 결과 출력
    imshow("Original", src);
    imshow("Adaptive Threshold", dst);
    waitKey(0);
    return 0;
}
728x90
728x90

영상처리 기술에서 보간법은 이미지 확대나 회전, 좌표 변환 기술 Affine Transform 등 알고리즘 구현 시 빈번히 사용됩니다. 머신 러닝 Machine Learning 및 딥러닝 Deep Learning 등 특히 CNN 계열에 Coarse Map을 Dense Map으로 Up-Sampling시 활용되는 기술이기도 합니다. 이미지를 두 배로 확대할 때 보간법은 새로운 픽셀 Pixel 값을 그럴듯하게 예측하는 기술로 볼 수 있습니다. 이론적인 부분은 영상처리 기술에서 다루도록 하겠습니다.

 

기본적인 보간법 세가지는 아래와 같습니다.

 

1. 최근접 이웃 보간법 Nearest Neighbor Interpolation

- 소수점 좌표에서 가장 가까운 정수 좌표 픽셀 사용, 수행속도가 가장 빠르지만 계단 현상 및 깨짐 현상 발생

 

2. 양선형 보간법 Bilinear Interpolation

- 주변 4개 픽셀을 사용해 선형 평균을 이용, 최근접 보다 자연스러운 결과를 보이고 일반적으로 많이 활용되는 기술

 

3. 양큐빅 보간법 Bicubic Interpolation

- 주변 16개 픽셀을 활용하며 앞에 두 기술보다 자연스러우며, Super Resolution 기술들에서 비교 결과로 많이 사용, 고화질 이미지 편집에 사용되는 기술이며 처리속도는 느림

 

이미지 회전 시 보간법이 사용되는 이유는 회전은 수학적으로 기존 픽셀 좌표를 회전 행렬로 이동시키는 과정입니다. 회전 시 생성되는 좌표는 보통 소수점 좌표가 되며, 새로운 평면에 매칭 시 좌표는 소수점이 아닌 정수이기때문에 빈공간이 생기게 됩니다. 그 빈공간을 채워주기 위해 보간법이 활용됩니다. 예를 들어 좌표 (5, 5)를 30도 회전하면 (5.3, 3.9) 되므로 실제 좌표 매칭을 위해서는 주변 픽셀 (5, 3), (6, 3), (5, 4), (6, 4)를 이용하여 추정해야 합니다.

 

OpenCV에서 이미지 회전 및 확대 시 보간법을 지정할 수 있습니다. 아래는 파이썬 활용시 CV 상수 입니다. 첫번째 예제는 회전시 파이썬 코드이며, 이전 블로그에서 설명한 “cv2.warpAffine” 함수를 사용합니다. 두번째 예제는 “cv2.resize” 함수를 활용한 보간법 사용 예입니다. 

 

1. 최근접 이웃 보간법 - cv2.INTER_NEAREST

2. 양선형 보간법 - cv2.INTER_LINEAR

3. 양큐빅 보간법 - cv2.INTER_CUBIC

4. Lanczos 보간법 - cv2.INTER_LANCZOS4

 

# 이미지 회전 예제
import cv2
import numpy as np

# 이미지 로드
img = cv2.imread('lena.jpg')

# 회전 중심, 각도, 스케일
center = (img.shape[1] // 2, img.shape[0] // 2)
angle = 50
scale = 1.0

# 회전 행렬 생성
M = cv2.getRotationMatrix2D(center, angle, scale)

# 양큐빅 보간법 사용예
rotated = cv2.warpAffine(img, M, (img.shape[1], img.shape[0]), flags=cv2.INTER_CUBIC)

# 결과 보기
cv2.imshow('Rotated Image', rotat_result)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

# 이미지 확대 예제
img = cv2.imread('lena.jpg')

# 2배 확대, 선형 보간법
resized = cv2.resize(img, None, fx=2, fy=2, interpolation=cv2.INTER_LINEAR)

# 결과 보기
cv2.imshow('Resized Image', resiz_result)
cv2.waitKey(0)
cv2.destroyAllWindows()
728x90

+ Recent posts