성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
글쓰기
제목
이름
암호
전자우편
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# - OpenCvSharp을 이용한 동영상(avi, mp4, ...) 처리 + Direct2D</h1> <p> OpenCvSharp을 이용해 동영상 처리를 해봤는데,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - OpenCvSharp을 이용한 동영상(avi, mp4, ...) 처리 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11403'>http://www.sysnet.pe.kr/2/0/11403</a> </pre> <br /> 역시나 OpenCvSharp.Window을 이용해야 하는 점이 마음에 들지 않습니다. 결국 Graphics.DrawImage의 속도가 문제이니, 그렇다면 이것을 Direct2D의 힘을 빌려 처리하면 될 듯합니다.<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 출력 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11401'>http://www.sysnet.pe.kr/2/0/11401</a> </pre> <br /> 이에 기반을 둬 "<a target='tab' href='http://www.sysnet.pe.kr/2/0/11403'>C# - OpenCvSharp을 이용한 동영상(avi, mp4, ...) 처리</a>" 글의 예제를 Direct2D를 이용한 처리로 바꿔보겠습니다. 우선 SharpDX를 참조하고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Install-Package SharpDX.Direct2D1 -Version 4.0.1 Install-Package SharpDX.Direct3D11 -Version 4.0.1 </pre> <br /> 남은 작업은 OpenCV Mat 타입을 Direct2D가 렌더링할 SharpDX.Direct2D1.Bitmap 타입으로 변환하는 것입니다. 문제는 Direct2D1의 Bitmap이 PixelFormat으로 Alpha 값이 없는 24비트를 지원하지 않는다는 점에 있습니다. 이 때문에 (대부분의 동영상 파일의 형식인) Mat.8UC3 포맷인 경우 Direct2D1의 Bitmap으로 픽셀 단위로 읽어 복사를 해야 합니다. 예를 들어, 다음과 같은 식입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private SharpDX.Direct2D1.Bitmap ToSharpDXBitmap(Mat image) { IntPtr dstPtr = _dataStream.DataPointer; IntPtr srcPtr = image.Data; int srcPitch = image.Width * image.Channels(); // 동영상의 경우 대부분 Channels == 3 (Alpha 채널이 없음) for (int y = 0; y < _renderTarget.Height; y++) { for (int x = 0; x < _renderTarget.Width; x++) { <span style='color: blue; font-weight: bold'>IntPtr dstPixel = dstPtr + x * 4; IntPtr srcPixel = srcPtr + x * 3; Utilities.CopyMemory(dstPixel, srcPixel, 3);</span> } srcPtr = IntPtr.Add(srcPtr, srcPitch); dstPtr = IntPtr.Add(dstPtr, _renderTarget.Width * 4); } return _renderTarget.CreateBitmap(_dataStream); } </pre> <br /> 그래서 "<a target='tab' href='http://www.sysnet.pe.kr/2/0/11403'>C# - OpenCvSharp을 이용한 동영상(avi, mp4, ...) 처리</a>" 글의 예제에 위의 코드를 적용해 Direct2D로 출력하면 Graphics.DrawImage 호출에 있었던 20ms ~ 50ms 부하가 3ms 이하로 줄어듭니다. 따라서 동영상 재생에 끊김 현상도 발생하지 않습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그런데 문제는 저렇게 Pixel 단위의 for 루프를 돌면서 처리하는 것에 대한 부하가 심하다는 것입니다. 실제로 위의 코드를 돌려 보면 1280 * 720 해상도에서 25% 정도의 CPU 부하가 발생합니다. 4 코어이기 때문에 이 정도면 CPU 100% 현상에 가깝습니다.<br /> <br /> 혹시나 싶어, 코드를 다음과 같이 OpenCV를 사용해 컬러 공간을 바꾸는 코드로 교체해봤습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private SharpDX.Direct2D1.Bitmap ToSharpDXBitmap(Mat mat) { <span style='color: blue; font-weight: bold'>using (Mat image = mat.CvtColor(ColorConversionCodes.BGR2BGRA))</span> { DataPointer dataPointer = new DataPointer(image.Data, (int)image.Total() * image.ElemSize()); return _renderTarget.CreateBitmap(dataPointer); } } </pre> <br /> 그 결과 CPU 사용량이 5 ~ 10%로 뚝 떨어졌습니다. 빨라도 너무 빠릅니다. ^^ 처음엔 이것이 C# 언어의 부하라고 생각했는데 해당 코드를 C++ DLL로 교체해서 수행해도 마찬가지여서 적잖이 당황했습니다. 그러다 이에 대해 검색해 보니 다음과 같은 좋은 글이 나옵니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > OpenCV - 속도 분석 (1) ; <a target='tab' href='https://laonple.blog.me/220861902363'>https://laonple.blog.me/220861902363</a> </pre> <br /> Intel의 최적화 코드가 그만큼 대단하다는 것입니다. 따라서, 앵간한 반복 코드는 직접 루프를 돌면서 만드는 것보다 가능한 OpenCV에서 제공하는 기능이 있다면 그것을 쓰는 것이 더 낫습니다.<br /> <br /> <a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1199&boardid=331301885'>다음은 이에 대한 최종 소스 코드로 첨부 파일에 완전한 csproj 파일로 포함</a>되어 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using OpenCvSharp; using SharpDX; using SharpDX.Direct2D1; using System; using System.Collections.Concurrent; using System.Diagnostics; using System.Threading; using System.Windows.Forms; namespace WindowsFormsApp1 { public partial class Form1 : Form { RenderTarget2D _renderTarget; public Form1() { InitializeComponent(); _renderTarget = new RenderTarget2D(); } protected override void OnFormClosing(FormClosingEventArgs e) { _renderTarget.Dispose(); base.OnFormClosing(e); } protected override void OnPaintBackground(PaintEventArgs e) { } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); while (true) { if (_q.TryTake(out SharpDX.Direct2D1.Bitmap item) == true) { _renderTarget.Render( (renderer) => { renderer.DrawBitmap(item, 1.0f, BitmapInterpolationMode.Linear); }); item.Dispose(); } else { break; } } } BlockingCollection<SharpDX.Direct2D1.Bitmap> _q = new BlockingCollection<SharpDX.Direct2D1.Bitmap>(); private Thread camera; private void CaptureCameraCallback() { VideoCapture capture = new VideoCapture("c:\\temp\\test.avi"); this.Invoke((Action)(() => { this.Width = capture.FrameWidth; this.Height = capture.FrameHeight; _renderTarget.Initialize(this.Handle, capture.FrameWidth, capture.FrameHeight); } )); if (capture.IsOpened() == false) { return; } int fps = (int)capture.Fps; int expectedProcessTimePerFrame = 1000 / fps; Stopwatch st = new Stopwatch(); st.Start(); using (Mat image = new Mat()) { while (true) { long started = st.ElapsedMilliseconds; capture.Read(image); if (image.Empty() == true) { break; } SharpDX.Direct2D1.Bitmap bitmap = ToSharpDXBitmap(image); _q.Add(bitmap); try { this.Invoke((Action)(() => this.Invalidate())); } catch (ObjectDisposedException) { } catch (InvalidOperationException) { } int elapsed = (int)(st.ElapsedMilliseconds - started); int delay = expectedProcessTimePerFrame - elapsed; if (delay > 0) { Thread.Sleep(delay); } } } } private SharpDX.Direct2D1.Bitmap ToSharpDXBitmap(Mat mat) { using (Mat image = mat.CvtColor(ColorConversionCodes.BGR2BGRA)) { DataPointer dataPointer = new DataPointer(image.Data, (int)image.Total() * image.ElemSize()); return _renderTarget.CreateBitmap(dataPointer); } } private void Form1_Load(object sender, EventArgs e) { camera = new Thread(CaptureCameraCallback); camera.IsBackground = true; camera.Start(); } } } </pre> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1760
(왼쪽의 숫자를 입력해야 합니다.)