본문 바로가기
카테고리 없음

Emgu.CV.InputArray, Emgu.CV.IInputArray, Emgu.CV.Mat

by 공부봇 2025. 9. 8.
반응형

아래는 EmguCV에서 자주 만나게 되는 Emgu.CV.InputArray, Emgu.CV.IInputArray, Emgu.CV.Mat 세 타입을 초보자도 헷갈리지 않도록 정리한 설명입니다.
핵심은 “데이터 그릇(Mat)” vs “함수 인자 어댑터(IInputArray/InputArray)”의 역할 분리예요.

전체 그림 (왜 3가지?)

OpenCV(C++)에는 많은 함수들이 인자로 cv::_InputArray를 받습니다. 이는 cv::Mat, cv::UMat, cv::Matx, std::vector<Point> 등 여러 형태의 데이터를 한 번에 받기 위한 어댑터 타입입니다.
EmguCV는 이를 .NET에 옮겨오면서:

  • IInputArray: “이건 입력으로 읽을 수 있어요”를 나타내는 인터페이스
  • InputArray: 위 인터페이스 구현체를 네이티브로 넘길 때 쓰는 일시적 래퍼(클래스, IDisposable)
  • Mat: 실제 이미지 데이터 컨테이너(소유자)

로 나눕니다. 즉,

  • 보관/수정/저장의 주인공 = Mat
  • 함수 인자 형태 통일 = IInputArray(인터페이스) + InputArray(래퍼)

각 타입의 정체성과 쓰임새

1) Emgu.CV.Mat — “데이터 그릇(소유자)”

  • 무엇? OpenCV의 cv::Mat에 해당. 픽셀 버퍼를 직접 소유(참조카운팅)하고, 크기/채널/깊이/스트라이드 같은 메타데이터도 가짐.
  • 언제? 이미지를 오래 들고 여러 번 수정/처리/저장해야 할 때. 클래스의 필드로 보관할 타입.
  • 수명 관리: Dispose()가 매우 중요(네이티브 메모리 해제).
  • 복사: Clone()은 깊은 복사(픽셀 복제), 단순 대입은 얕은 복사(버퍼 공유).
  • 예시:
  • Mat img = CvInvoke.Imread("a.png", ImreadModes.Color); CvInvoke.CvtColor(img, img, ColorConversion.Bgr2Gray); CvInvoke.Imwrite("out.png", img); img.Dispose();

2) Emgu.CV.IInputArray — “읽기 전용 입력” 인터페이스

  • 무엇? “이 객체는 입력으로 읽을 수 있다”를 표현하는 인터페이스.
    Mat, UMat, Image<TColor,TDepth>, VectorOfPoint 등 여러 타입이 구현합니다.
  • 언제? 함수의 파라미터 타입으로 쓰면 좋아요. “Mat이든 UMat이든 Image<>든 다 받아요” 같은 범용 API 작성 가능.
  • 주의: 데이터를 소유하지 않음. 그저 “읽기”를 위한 형태 통일 장치.
  • 예시(시그니처):
  • // 어떤 입력이 와도( Mat, UMat, Image<> 등 ) 읽기만 할 함수 public static void ShowHistogram(IInputArray src) { /* ... */ }

3) Emgu.CV.InputArray — IInputArray를 감싸는 일시적 래퍼(클래스, IDisposable)

  • 무엇? OpenCV의 _InputArray를 .NET에서 안전하게 다루기 위한 래퍼 객체.
    IInputArray를 받아 내부에서 네이티브에 넘길 수 있는 핸들을 만들어 줍니다.
  • 언제? 내부 구현에서 잠깐 씁니다. 보통은같은 using 패턴으로 “잠깐 빌려 쓰고 바로 정리”합니다.
  • using (InputArray ia = someIInputArray.GetInputArray()) using (Mat mat = ia.GetMat()) { // mat 뷰로 잠깐 접근 }
  • 주의: 필드에 보관 금지(수명/원본 의존). 늘 using으로 짧게!

왜 이렇게 나눴나? (디자인 이유)

  1. 유연한 함수 인자 처리: 이미지/벡터/UMat 등 다양한 타입을 하나의 시그니처로 받으려면 어댑터가 필요 → IInputArray/InputArray.
  2. 성능/복사 최소화: 어댑터는 이므로 불필요한 복사 없이 네이티브로 넘길 수 있음.
  3. 소유권 명확화: 실제 데이터 소유는 Mat. 어댑터는 읽기 통로일 뿐 → 수명/해제 책임이 분리되어 **버그(특히 해제 후 접근)**를 줄임.

언제 무엇을 쓰면 유리한가?

상황 권장 타입 이유

이미지를 보관/수정/저장 Mat 데이터 소유, 참조카운팅, 대부분의 연산 기본 단위
여러 타입을 받는 유틸 함수 인자 IInputArray 다양성/범용성. 호출자는 Mat/UMat/Image<> 등 아무거나 전달
내부에서 네이티브에 잠깐 넘길 핸들 InputArray GetInputArray()로 얻어 using으로 짧게 사용
GPU/OpenCL(UMat)도 함께 지원하는 API 설계 인자에 IInputArray 같은 함수에 Mat/UMat 모두 전달 가능
내부 보관은 안 하고, 읽기만 필요 IInputArray 복사/제약 없이 입력만 받음

자주 하는 실수 vs 안전한 패턴

❌ 피해야 할 것

  • InputArray(또는 IInputArray에서 얻은 InputArray)를 필드에 저장해 오래 쓰기
    → 원본 수명에 의존하므로 원본이 Dispose되면 댕글링(무효 참조) 위험.
  • InputArray.GetMat()로 얻은 Mat의 DataPointer를 캐싱
    → 원본이 사라지면 포인터 무효. 필요하면 Clone()으로 내 소유 Mat 확보.

✅ 안전한 패턴

// (1) 내부에 보관: 반드시 Mat를 소유
private Mat _mat;

public void Load(string path)
{
    _mat?.Dispose();
    _mat = CvInvoke.Imread(path, ImreadModes.Color); // 소유권 확보
}

// (2) 인자 범용화: IInputArray로 받기
public static double MeanGray(IInputArray src)
{
    using (var ia = src.GetInputArray())  // 잠깐 핸들
    using (var m = ia.GetMat())           // 필요하면 Mat 뷰
    using (var gray = new Mat())
    {
        if (m.NumberOfChannels == 1) m.CopyTo(gray);
        else CvInvoke.CvtColor(m, gray, ColorConversion.Bgr2Gray);
        var mean = CvInvoke.Mean(gray);
        return mean.V0;
    }
}

// (3) 다른 모듈에 오래 넘길 필요가 있으면 Clone으로 깊은 복사
var deepCopy = _mat.Clone(); // 깊은 복사로 독립본 생성

요약 한 줄

  • Mat: 데이터를 소유하는 “이미지 그릇”. 오래 들고 수정/저장할 때 사용.
  • IInputArray: “읽기 가능한 입력”을 통일하는 인터페이스(함수 인자용).
  • InputArray: IInputArray를 네이티브로 넘기기 위한 일시적 래퍼—using으로 잠깐만.

이렇게 역할을 분리했기 때문에, EmguCV는 유연한 API(다양한 입력 지원)와 안전한 수명 관리(소유자=Mat)를 동시에 달성합니다.

반응형