OpenCvSharp 를 Linux 에서 써보신분 계실까요?

OpenCV 를 이용하여 컴퓨터 비전을 활용하려고 하는데,

예전에는 python 을 기반으로 했던 기억이 있는데

지금 진행하는 프로젝트가 C# .net 8.0 기반 Avalonia UI 메인 프로그램이 하나 있어서

C# 을 이용한 OpenCVSharp 를 기웃거려보고 있습니다.

Windows 상에서는 nuget package 를 이용하여

OpenCvSharp4, OpenCvSharp4.Extensions, Tesseract, OpenCvSharp4.runtime.Win

을 설치하였고

문제없이 동작하였습니다.

Linux (amd64) 로 와서

OpenCvSharp4.runtime.Win 을 OpenCvSharp4.runtime.ubuntu.22.04-x64 로 변경하고

build 하니 build는 잘 됩니다만…

실제 동작하려고 보면

library dependency 오류들이 주르륵 나와버립니다.

대표적으로, libOpenCvSharpExtern.so 와 libtesseract.so.4, libtiff.so.5 등

nuget 을 wrap 하는 특정 이름의 library 로만 인식하는 것 같은데

어찌저찌 symbolic 처리하거나 alternatives 하면 실행할 수 있을 것 같긴한데

nuget 을 이용한 간단한 배포를 기대했던 것과는 거리가 좀 머네요.

다른 방법이 있을까요?

(AI의 도움을 받아 작성한 답변입니다. 해당 주제에 대해 더 많은 의견을 남겨주시는 것을 환영합니다!)

Linux에서 OpenCvSharp 네이티브 라이브러리 의존성 문제에 대하여

겪고 계신 문제의 핵심은, OpenCvSharp4.runtime.ubuntu.22.04-x64 NuGet 패키지가 포함하는 네이티브 바이너리(libOpenCvSharpExtern.so)가 특정 버전의 시스템 라이브러리(libtesseract, libtiff, libpng 등)에 링크되어 빌드되어 있다는 점입니다. 배포 대상 Linux 환경의 라이브러리 버전이 패키지 빌드 시점과 다르면 DllNotFoundException이나 링커 오류가 발생합니다.

몇 가지 접근 방법을 정리해 드립니다.

1. Docker 컨테이너로 런타임 환경 고정

가장 확실한 방법입니다. ubuntu:22.04 기반 이미지 위에서 필요한 네이티브 패키지를 설치하고 앱을 실행하면, NuGet 런타임 패키지가 기대하는 라이브러리 버전과 정확히 일치시킬 수 있습니다.

FROM mcr.microsoft.com/dotnet/runtime:8.0-jammy

RUN apt-get update && apt-get install -y --no-install-recommends \
    libgdiplus \
    libtesseract-dev \
    libtiff-dev \
    libpng-dev \
    libjpeg-dev \
    libopencv-dev \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY publish/ .
ENTRYPOINT ["dotnet", "YourApp.dll"]

Avalonia UI 앱이라 GUI가 필요하신 상황이겠지만, X11 포워딩이나 Wayland 소켓 마운트로 컨테이너 안에서도 GUI 실행이 가능합니다. 다만 이 방식이 배포 환경에 맞지 않는다면 아래 방법들을 고려해 보십시오.

2. 시스템 OpenCV를 직접 빌드하고 P/Invoke 경로 지정

NuGet 런타임 패키지에 의존하지 않고, 대상 Linux 환경에서 OpenCV와 OpenCvSharp의 네이티브 래퍼를 직접 빌드하는 방법입니다.

  • OpenCvSharp4OpenCvSharp4.Extensions NuGet 패키지만 설치하고, runtime 패키지는 제거합니다.

  • 대상 환경에서 libopencv-dev를 설치하거나 OpenCV를 소스 빌드합니다.

  • OpenCvSharp GitHub 저장소의 네이티브 소스(OpenCvSharpExtern)를 CMake로 빌드하여 libOpenCvSharpExtern.so를 직접 생성합니다.

  • 빌드된 .so 파일을 앱의 출력 디렉터리에 배치하거나 LD_LIBRARY_PATH로 경로를 지정합니다.

이렇게 하면 시스템에 설치된 라이브러리 버전과 네이티브 래퍼가 동일한 환경에서 빌드되므로 버전 불일치가 원천적으로 해소됩니다. 빌드 과정이 추가되지만 가장 깔끔한 해결책입니다.

3. 조건부 RuntimeIdentifier로 패키지 분기

Windows와 Linux 모두 지원해야 한다면, 프로젝트 파일에서 RID 기반 조건부 참조를 설정할 수 있습니다.

<ItemGroup Condition="'$(RuntimeIdentifier)' == 'win-x64'">
  <PackageReference Include="OpenCvSharp4.runtime.win" Version="4.*" />
</ItemGroup>

<ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64'">
  <!-- runtime 패키지 제거, 직접 빌드한 .so를 번들 -->
  <None Include="native/linux-x64/libOpenCvSharpExtern.so"
        CopyToOutputDirectory="PreserveNewest" Link="libOpenCvSharpExtern.so" />
</ItemGroup>

이렇게 하면 dotnet publish -r linux-x64시에는 직접 빌드한 네이티브 바이너리가, Windows에서는 NuGet 런타임 패키지가 사용됩니다.

4. Emgu CV 고려

OpenCvSharp 외에 Emgu CV라는 대안도 있습니다. Emgu CV는 자체적으로 네이티브 바이너리를 NuGet 패키지에 포함하는 방식이 좀 더 성숙되어 있고, Linux 런타임 지원도 비교적 안정적입니다. 다만 상용 라이선스(듀얼 라이선스)인 점은 확인이 필요합니다. 프로젝트 초기 단계라면 검토해 볼 만합니다.


요약하면, NuGet 런타임 패키지만으로 Linux에서 "just works"를 기대하기 어려운 것이 현재 OpenCvSharp 생태계의 현실입니다. 실무적으로는 Docker로 런타임 환경을 고정하거나, 네이티브 래퍼를 대상 환경에서 직접 빌드하는 두 가지가 가장 많이 쓰이는 방법입니다.

추가로 궁금하신 점이 있으시면 이어서 질문해 주세요.

1개의 좋아요

OpenCvSharp에서 동적으로 라이브러리 불러서 쓸때, 불러올떄 타겟 라이브러리가 버전이 정적 코딩되어 있거나 하는 문제가 있을 수 있거나, 시스템 라이브러리 버전 호환성 문제가 있거나 할 수 있습니다.

nuget이 편하긴 한데, 가끔 네이티브 라이브러리 사용하는 경우, OS 내부 라이브러리를 이용할 수 있어서 OS 버전을 타더라구요. 그래서 소스를 가져와서 버전 맞추고 테스트해서 올립니다.ㅡ.ㅜ

명쾌한 답변 저도 줄서서 기다려봅니다.ㅋㅋㅋ

1개의 좋아요

답변 감사합니다.
일단 2번방향으로 해결했습니다.
OpenCvSharpExtern 을 제 펌웨어 환경에서 자체 build 하고,
만들어진 .so 파일을 실행시키는 .sh 스크립트내에서

LD_LIBRARY_PATH 로 강제 지정시켜서 dependency 를 해결했습니다.

범용적인 해결법은 아니지만 일단 제 펌웨어에서 돌아가게는 되었네요…

4개의 좋아요