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

비밀번호

댓글 작성자
 




... 166  167  168  169  170  171  172  [173]  174  175  176  177  178  179  180  ...
NoWriterDateCnt.TitleFile(s)
997정성태2/26/201120955    답변글 개발 환경 구성: 36.1. 개발 환경 구성: 34 - 1. RAID 1 구성 이후...
676정성태3/15/200922322오류 유형: 71. IE 8 RC1 - 멈춤 현상
675정성태3/14/200925209오류 유형: 70. Virtual Machine Additions 제거
673정성태3/11/200923829웹: 10. 금일 진행되었던 마이크로소프트 IE 8 행사의 ActiveX 세션 PPT [6]파일 다운로드1
672정성태3/1/200936766Windows: 43. Loopback network adapter 설치하기 [2]
670정성태2/22/200929950.NET Framework: 124. IIS 7에서 SVC 호스팅 [1]
669정성태2/21/200924443오류 유형: 69. The SQL Server Network Interface library could not register the Service Principal Name (SPN) for the SQL Server service.
668정성태2/21/200929461개발 환경 구성: 35. SQL Express 버전과 User Instance 옵션
667정성태2/14/200941338웹: 9. IE 7. IWebBrowser2 인터페이스 메서드의 Navigate / Navigate2 차이점 [3]파일 다운로드1
666정성태2/12/200927185웹: 8. IE 8: 프로세스와 윈도우의 관계 - LCIE [1]파일 다운로드1
665정성태2/7/200924093웹: 7. Internet Explorer 8 - XHR, XDR, XST, XSRF [1]
664정성태2/5/200920146Windows: 42. Concurrency Runtime
663정성태2/5/200924835.NET Framework: 123. WPF - Arial Narrow 폰트 문제
662정성태2/3/200920563VS.NET IDE: 59. HyperAddin 도구 사용 설명 [1]
661정성태2/3/200924235오류 유형: 68. msxml6r.dll 설치 오류
660정성태2/3/200922810Windows: 41. UAC 보안 취약 [2]
659정성태2/2/200934166오류 유형: 67. ClickOnce 응용 프로그램이 실행되지 않을 때.
658정성태2/1/200926202Team Foundation Server: 30. 소스 서버 보안
657정성태2/1/200929116Windows: 40. Q1 Ultra + Windows 7 베타
656정성태2/1/200930623디버깅 기술: 24. .NET JIT 최적화 코드 생성 제어
655정성태1/31/200926911Windows: 39. IE8 표준 모드 [4]
653정성태1/29/200921851.NET Framework: 122. XML Serializer를 이용한 값 복사: 성능은 어떨까!파일 다운로드1
652정성태1/22/200922650.NET Framework: 121. WPF - PrintTicket provider failed to bind to printer.
651정성태1/20/200919864.NET Framework: 120. 타입이 다른 배열끼리의 변환
650정성태1/19/200931649COM 개체 관련: 21. C/C++ 프로젝트에 /clr 옵션 적용으로 인한 COM 개체 사용 오류
649정성태1/18/200929225Windows: 38. Q1U UMPC에 Windows 7 베타 설치하기
... 166  167  168  169  170  171  172  [173]  174  175  176  177  178  179  180  ...