Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일

(시리즈 글이 10개 있습니다.)
VC++: 121. DXGI를 이용한 윈도우 화면 캡처 소스 코드(Visual C++)
; https://www.sysnet.pe.kr/2/0/11385

.NET Framework: 705. C# - SharpDX + DXGI를 이용한 윈도우 화면 캡처 소스 코드
; https://www.sysnet.pe.kr/2/0/11400

.NET Framework: 706. C# - SharpDX + DXGI를 이용한 윈도우 화면 캡처 소스 코드 + Direct2D 출력
; https://www.sysnet.pe.kr/2/0/11401

.NET Framework: 712. C# - SharpDX + DXGI를 이용한 윈도우 화면 캡처 소스 코드 + Direct2D 출력 + OpenCV
; https://www.sysnet.pe.kr/2/0/11407

.NET Framework: 713. C# - SharpDX + DXGI를 이용한 윈도우 화면 캡처 소스 코드 + Direct2D 출력 + OpenCV (2)
; https://www.sysnet.pe.kr/2/0/11408

.NET Framework: 913. C# - SharpDX + DXGI를 이용한 윈도우 화면 캡처 라이브러리
; https://www.sysnet.pe.kr/2/0/12238

.NET Framework: 1123. C# - (SharpDX + DXGI) 화면 캡처한 이미지를 빠르게 JPG로 변환하는 방법
; https://www.sysnet.pe.kr/2/0/12889

.NET Framework: 1126. C# - snagit처럼 화면 캡처를 연속으로 수행해 동영상 제작
; https://www.sysnet.pe.kr/2/0/12895

.NET Framework: 1128. C# - 화면 캡처한 이미지를 ffmpeg(FFmpeg.AutoGen)로 동영상 처리
; https://www.sysnet.pe.kr/2/0/12897

.NET Framework: 1152. C# - 화면 캡처한 이미지를 ffmpeg(FFmpeg.AutoGen)로 동영상 처리 (저해상도 현상 해결)
; https://www.sysnet.pe.kr/2/0/12963




C# - SharpDX + DXGI를 이용한 윈도우 화면 캡처 소스 코드 + Direct2D 출력 + OpenCV (2)

지난번 글의 구현을,

C# - SharpDX + DXGI를 이용한 윈도우 화면 캡처 소스 코드 + Direct2D 출력 + OpenCV
; https://www.sysnet.pe.kr/2/0/11407

혹시 좀 더 부하를 적게 할 수는 없을까요?

가령, copyFrameBuffer에서 화면 캡처 데이터를 복사하지 말고 그 원본 데이터로부터 곧바로 OpenCV Mat과 연결해 데이터 조작을 하는 것도 가능합니다. 이를 위해 다음과 같은 변경을 하면 됩니다.

unsafe void copyFrameBuffer(IntPtr srcPtr, IntPtr dstPtr, int srcPitch, Rectangle offsetBounds)
{
    Mat mat = new Mat(new int[] { offsetBounds.Height, offsetBounds.Width }, MatType.CV_8UC4, srcPtr);

    using (Mat gray = mat.CvtColor(ColorConversionCodes.BGRA2GRAY))
    using (Mat blur = gray.GaussianBlur(new OpenCvSharp.Size(7, 7), 1.5, 1.5))
    using (Mat canny = blur.Canny(0, 30, 3))
    {
        IntPtr srcMatPtr = canny.Data;

        for (int y = 0; y < _renderTarget.Height; y++)
        {
            Utilities.CopyMemory(dstPtr + (offsetBounds.Left), srcMatPtr, _renderTarget.Width);

            srcMatPtr = IntPtr.Add(srcMatPtr, _renderTarget.Width);
            dstPtr = IntPtr.Add(dstPtr, _renderTarget.Width);
        }
    }
}

결과적으로 마지막에 복사되는 데이터도 32bit BGRA가 아닌 8bit GRAY이기 때문에 1920 * 1080인 1/4로 복사량이 줄어들게 됩니다. 따라서 SharpDX 측에서는 흑백 데이터를 다시 BGRA로 복원만 하고 화면에 출력하면 됩니다.

private void CaptureLoop(DXGIManager manager)
{
    using (Mat mat = new Mat(new OpenCvSharp.Size(manager.Width, manager.Height), MatType.CV_8UC1))
    {
        while (true)
        {
            int signalId = EventWaitHandle.WaitAny(_waitSignals, Timeout.Infinite);

            if (signalId == 0)
            {
                break;
            }

            IntPtr dstPtr = mat.Data;

            if (manager.Capture(copyFrameBuffer, dstPtr, 1000) == true)
            {
                using (Mat last = mat.CvtColor(ColorConversionCodes.GRAY2BGRA))
                {
                    DataPointer dataPointer = new DataPointer(last.Data, (int)last.Total() * last.Channels());
                    SharpDX.Direct2D1.Bitmap bitmap = _renderTarget.CreateBitmap(dataPointer);

                    if (bitmap != null)
                    {
                        _queue.Add(bitmap);
                        _count++;

                        this.Invoke((Action)(() => this.Invalidate()));
                    }
                }
            }
        }
    }
}

(첨부 파일은 이 글의 예제 코드를 포함합니다.)




부가적으로 하나 더 설명을 드리면, OpenCvSharp은 C/C++ 컴파일된 OpenCvSharpExtern.dll에서 export된 함수들을 래퍼한 Managed DLL인 OpenCvSharp.*.dll에 의해 접근하는 형식입니다.

따라서, OpenCvSharp.*.dll 들에서 제공하지 않으면 OpenCV의 원래 기능들을 사용할 수 없게 되는데요. 이럴 때 별도로 C/C++ DLL을 만들어 원하는 기능을 export 시켜야만 합니다. 하지만, 그보다 더 쉬운 방법이 있는데 바로 DllImport를 이용해 직접 OpenCvSharpExtern.dll에서 export한 함수들을 사용하는 것입니다.

예를 들어 볼까요? OpenCV의 Math 타입은 다음과 같이 포인터 변수를 받는 생성자도 제공합니다.

// https://docs.opencv.org/trunk/d3/d63/classcv_1_1Mat.html#a51615ebf17a64c968df0bf49b4de6a3a
cv::Mat::Mat(int rows, int cols, int type, void * data, size_t step = AUTO_STEP)

그런데 OpenCvSharp의 Mat에는 void* 타입을 데이터로 받는 생성자를 제공하지 않습니다. 이런 경우 아래의 글에서 설명한 데로,

C# 개발자를 위한 Win32 DLL export 함수의 호출 규약 (1) - x86 환경에서의 __cdecl, __stdcall에 대한 Name mangling
; https://www.sysnet.pe.kr/2/0/11132

원하는 함수의 signature를 (depends.exe 등의 도구로) 알아낸 다음,

??0Mat@cv@@QEAA@HHHPEAX_K@Z 
==> (undecorated name) cv::Mat::Mat(int,int,int,void *,unsigned __int64)

다음과 같은 extern 메서드로 연결할 수 있습니다.

[DllImport("OpenCvSharpExtern.dll", EntryPoint = "??0Mat@cv@@QEAA@HHHPEAX_K@Z")]
internal static unsafe extern void CreateMatWithPtr(IntPtr thisPtr, int rows, int cols, int type, void* data, int step = 0);

// x64 호출 규약
// 클래스의 생성자이기 때문에 첫 번째 매개변수는 반드시 this 포인터

이를 이용해 이번 글에서 작성한 copyFrameBuffer의 Mat 사용을 다음과 같이 변경할 수 있습니다.

unsafe void copyFrameBuffer(IntPtr srcPtr, IntPtr dstPtr, int srcPitch, Rectangle offsetBounds)
{
    // 아래의 코드 대신,
    // Mat mat = new Mat(new int[] { offsetBounds.Height, offsetBounds.Width }, MatType.CV_8UC4, srcPtr);

    // 이렇게 void* 타입을 받는 생성자를 직접 호출
    Mat mat = new Mat();
    byte* pBuf = (byte *)srcPtr.ToPointer();
    CreateMatWithPtr(mat.CvPtr, offsetBounds.Height, offsetBounds.Width, MatType.CV_8UC4, pBuf, 0);

    // ...[생략]...
}

이 정도면, .NET Managed 래퍼로 인한 제약을 어느 정도는 C/C++ DLL을 별도로 제작하는 수고를 들이지 않고 극복할 수 있을 것입니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 12/18/2017]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 76  77  78  79  80  81  82  83  84  85  86  [87]  88  89  90  ...
NoWriterDateCnt.TitleFile(s)
11470정성태3/20/201816380오류 유형: 458. Visual Studio - CUDA 프로젝트 빌드 시 오류 C1189, expression must have a constant value
11469정성태3/19/201810247오류 유형: 457. error MSB3103: Invalid Resx file. Could not load file or assembly 'System.Windows.Forms, ...' or one of its dependencies.
11468정성태3/19/201810129오류 유형: 456. 닷넷 응용 프로그램 실행 시 0x80131401 예외 발생
11467정성태3/19/20189442오류 유형: 455. Visual Studio Installer - 업데이트 실패
11466정성태3/18/201810916개발 환경 구성: 355. 한 대의 PC에서 2개 이상의 DirectX 게임을 실행하는 방법
11463정성태3/15/201812605.NET Framework: 733. 스레드 간의 read/write 시에도 lock이 필요 없는 경우파일 다운로드1
11462정성태3/14/201814458개발 환경 구성: 354. HTTPS 호출에 대한 TLS 설정 확인하는 방법 [1]
11461정성태3/13/201817657오류 유형: 454. 윈도우 업데이트 설치 오류 - 0x800705b4 [1]
11460정성태3/13/201811230디버깅 기술: 112. windbg - 닷넷 메모리 덤프에서 전역 객체의 내용을 조사하는 방법
11459정성태3/13/201810987오류 유형: 453. Debug Diagnostic Tool에서 mscordacwks.dll을 찾지 못하는 문제
11458정성태2/21/201812522오류 유형: 452. This share requires the obsolete SMB1 protocol, which is unsafe and could expose your system to attack. [1]
11457정성태2/17/201817323.NET Framework: 732. C# - Task.ContinueWith 설명 [1]파일 다운로드1
11456정성태2/17/201822048.NET Framework: 731. C# - await을 Task 타입이 아닌 사용자 정의 타입에 적용하는 방법 [7]파일 다운로드1
11455정성태2/17/201812491오류 유형: 451. ASP.NET Core - An error occurred during the compilation of a resource required to process this request.
11454정성태2/12/201820763기타: 71. 만료된 Office 제품 키를 변경하는 방법
11453정성태1/31/201812307오류 유형: 450. Azure Cloud Services(classic) 배포 시 "Certificate with thumbprint ... doesn't exist." 오류 발생
11452정성태1/31/201817616기타: 70. 재현 가능한 최소한의 예제 프로젝트란? [3]파일 다운로드1
11451정성태1/24/201812542디버깅 기술: 111. x86 메모리 덤프 분석 시 닷넷 메서드의 호출 인자 값 확인
11450정성태1/24/201825948Windows: 146. PowerShell로 원격 프로세스(EXE, BAT) 실행하는 방법 [1]
11449정성태1/23/201814942오류 유형: 449. 단위 테스트 - Could not load file or assembly 'Microsoft.VisualStudio.QualityTools.VideoRecorderEngine' or one of its dependencies. [1]
11448정성태1/20/201812110오류 유형: 448. Fakes를 포함한 단위 테스트 프로젝트를 빌드 시 CS0619 관련 오류 발생
11447정성태1/20/201813619.NET Framework: 730. dotnet user-secrets 명령어 [2]파일 다운로드1
11446정성태1/20/201814899.NET Framework: 729. windbg로 살펴보는 GC heap의 Segment 구조 [2]파일 다운로드1
11445정성태1/20/201812171.NET Framework: 728. windbg - 눈으로 확인하는 Workstation GC / Server GC
11444정성태1/19/201813196VS.NET IDE: 125. Visual Studio에서 Selenium WebDriver를 이용한 웹 브라우저 단위 테스트 구성파일 다운로드1
11443정성태1/18/201813075VC++: 124. libuv 모듈 살펴 보기
... 76  77  78  79  80  81  82  83  84  85  86  [87]  88  89  90  ...