성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] VT sequences to "CONOUT$" vs. STD_O...
[정성태] NetCoreDbg is a managed code debugg...
[정성태] Evaluating tail call elimination in...
[정성태] What’s new in System.Text.Json in ....
[정성태] What's new in .NET 9: Cryptography ...
[정성태] 아... 제시해 주신 "https://akrzemi1.wordp...
[정성태] 다시 질문을 정리할 필요가 있을 것 같습니다. 제가 본문에...
[이승준] 완전히 잘못 짚었습니다. 댓글 지우고 싶네요. 검색을 해보...
[정성태] 우선 답글 감사합니다. ^^ 그런데, 사실 저 예제는 (g...
[이승준] 수정이 안되어서... byteArray는 BYTE* 타입입니다...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# - SharpDX + DXGI를 이용한 윈도우 화면 캡처 소스 코드 + Direct2D 출력 + OpenCV (2)</h1> <p> 지난번 글의 구현을,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - SharpDX + DXGI를 이용한 윈도우 화면 캡처 소스 코드 + Direct2D 출력 + OpenCV ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11407'>http://www.sysnet.pe.kr/2/0/11407</a> </pre> <br /> 혹시 좀 더 부하를 적게 할 수는 없을까요?<br /> <br /> 가령, copyFrameBuffer에서 화면 캡처 데이터를 복사하지 말고 그 원본 데이터로부터 곧바로 OpenCV Mat과 연결해 데이터 조작을 하는 것도 가능합니다. 이를 위해 다음과 같은 변경을 하면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > unsafe void copyFrameBuffer(<span style='color: blue; font-weight: bold'>IntPtr srcPtr</span>, IntPtr dstPtr, int srcPitch, Rectangle offsetBounds) { Mat mat = new Mat(new int[] { offsetBounds.Height, offsetBounds.Width }, MatType.CV_8UC4, <span style='color: blue; font-weight: bold'>srcPtr</span>); 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++) { <span style='color: blue; font-weight: bold'>Utilities.CopyMemory(dstPtr + (offsetBounds.Left), srcMatPtr, _renderTarget.Width);</span> srcMatPtr = IntPtr.Add(srcMatPtr, _renderTarget.Width); dstPtr = IntPtr.Add(dstPtr, _renderTarget.Width); } } } </pre> <br /> 결과적으로 마지막에 복사되는 데이터도 32bit BGRA가 아닌 8bit GRAY이기 때문에 1920 * 1080인 1/4로 복사량이 줄어들게 됩니다. 따라서 SharpDX 측에서는 흑백 데이터를 다시 BGRA로 복원만 하고 화면에 출력하면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private void CaptureLoop(DXGIManager manager) { using (Mat mat = new Mat(new OpenCvSharp.Size(manager.Width, manager.Height), MatType.<span style='color: blue; font-weight: bold'>CV_8UC1</span>)) { 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())); } } } } } } </pre> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1203&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 부가적으로 하나 더 설명을 드리면, OpenCvSharp은 C/C++ 컴파일된 OpenCvSharpExtern.dll에서 export된 함수들을 래퍼한 Managed DLL인 OpenCvSharp.*.dll에 의해 접근하는 형식입니다.<br /> <br /> 따라서, OpenCvSharp.*.dll 들에서 제공하지 않으면 OpenCV의 원래 기능들을 사용할 수 없게 되는데요. 이럴 때 별도로 C/C++ DLL을 만들어 원하는 기능을 export 시켜야만 합니다. 하지만, 그보다 더 쉬운 방법이 있는데 바로 DllImport를 이용해 직접 OpenCvSharpExtern.dll에서 export한 함수들을 사용하는 것입니다.<br /> <br /> 예를 들어 볼까요? OpenCV의 Math 타입은 다음과 같이 포인터 변수를 받는 생성자도 제공합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // <a target='tab' href='https://docs.opencv.org/trunk/d3/d63/classcv_1_1Mat.html#a51615ebf17a64c968df0bf49b4de6a3a'>https://docs.opencv.org/trunk/d3/d63/classcv_1_1Mat.html#a51615ebf17a64c968df0bf49b4de6a3a</a> cv::Mat::Mat(int rows, int cols, int type, void * data, size_t step = AUTO_STEP) </pre> <br /> 그런데 OpenCvSharp의 Mat에는 void* 타입을 데이터로 받는 생성자를 제공하지 않습니다. 이런 경우 아래의 글에서 설명한 데로,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# 개발자를 위한 Win32 DLL export 함수의 호출 규약 (1) - x86 환경에서의 __cdecl, __stdcall에 대한 Name mangling ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11132'>http://www.sysnet.pe.kr/2/0/11132</a> </pre> <br /> 원하는 함수의 signature를 (depends.exe 등의 도구로) 알아낸 다음,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > ??0Mat@cv@@QEAA@HHHPEAX_K@Z ==> (undecorated name) cv::Mat::Mat(int,int,int,void *,unsigned __int64) </pre> <br /> 다음과 같은 extern 메서드로 연결할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [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 포인터 </pre> <br /> 이를 이용해 이번 글에서 작성한 copyFrameBuffer의 Mat 사용을 다음과 같이 변경할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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); // ...[생략]... } </pre> <br /> 이 정도면, .NET Managed 래퍼로 인한 제약을 어느 정도는 C/C++ DLL을 별도로 제작하는 수고를 들이지 않고 극복할 수 있을 것입니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
2030
(왼쪽의 숫자를 입력해야 합니다.)