C# - OpenCvSharp.VideoWriter에 BMP 파일을 1초씩 출력하는 예제
마침 질문도 있으니,
[WPF/OpenCV] 이미지->영상 저장에 대해서 질문 드립니다!!
; https://www.sysnet.pe.kr/3/0/5447
오랜만에 OpenCvSharp을 다뤄보겠습니다. ^^ 간략하게 개발 환경 구성하고,
Visual Studio Code에서 OpenCvSharp 개발 환경 구성
; https://www.sysnet.pe.kr/2/0/11971
Bitmap 파일로부터 Mat을 반환하는 간단한 도우미 타입을 만든 후,
using OpenCvSharp;
using System;
using System.Drawing;
using System.Drawing.Imaging;
namespace WindowsFormsApp1
{
public class MatBitmap : IDisposable
{
string _filePath;
Image _img;
Bitmap _bitmap;
BitmapData _data;
Mat _mat;
public MatBitmap(string filePath)
{
_filePath = filePath;
}
public Mat Mat
{
get
{
if (_mat == null)
{
_mat = LoadBitmap(_filePath);
}
return _mat;
}
}
public Mat LoadBitmap(string filePath)
{
_img = Bitmap.FromFile(filePath);
Rectangle imgSize = new Rectangle(0, 0, _img.Width, _img.Height);
_bitmap = new Bitmap(_img);
_data = _bitmap.LockBits(
imgSize, System.Drawing.Imaging.ImageLockMode.ReadOnly, _img.PixelFormat);
MatType type = MatType.CV_8UC3;
switch (img.PixelFormat)
{
case PixelFormat.Format32bppArgb:
type = MatType.CV_8UC4;
break;
}
return new Mat(img.Height, img.Width, type, data.Scan0);
}
public void Dispose()
{
if (_data != null)
{
if (_bitmap != null)
{
_bitmap.UnlockBits(_data);
_bitmap.Dispose();
_bitmap = null;
}
_data = null;
}
if (_img != null)
{
_img.Dispose();
_img = null;
}
}
}
}
이렇게 사용하면,
using OpenCvSharp;
using System;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
// Install-Package OpenCvSharp4
// Install-Package OpenCvSharp4.Windows
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
MatBitmap[] bitmap = new MatBitmap[] {
new MatBitmap("640x480_1.png"),
new MatBitmap("640x480_2.png"),
new MatBitmap("640x480_3.png") };
int fps = 10;
using (VideoWriter writer = new VideoWriter("test.avi", FourCC.XVID, fps, bitmap[0].Mat.Size(), true))
{
for (int i = 0; i < bitmap.Length; i++)
{
for (int j = 0; j < fps; j++)
{
writer.Write(bitmap[i].Mat);
}
bitmap[i].Dispose();
}
}
}
}
}
3장의 "640x480_?.png" 파일을 읽어 각각 1초에 해당하는 분량으로 VideoWriter에 프레임을 쓰니 3초짜리 동영상이 만들어집니다. 굳이 더 이상 자세하게 설명할 것이 없군요. ^^
(
첨부 파일은 이 글의 예제 코드를 포함합니다.)
위의 예제에 기반을 둬 질문에 대한 답변을 하자면, 그러니까 결국 UDP 통신으로 전송하는 데이터는 _bitmap.LockBits로 얻은 IntPtr이 가리키는 버퍼이기만 하면 됩니다. 그것을 받아서 VideoWriter로 쓰면 되는데, 단지 UDP의 특성상 전송에 대한 신뢰성을 확보할 수 없으므로 (10 fps인 경우) 1초에 해당하는 10 frame 데이터를 온전히 받는 것을 기대해서는 안 됩니다.
따라서, 이미지 데이터와 함께 부가 헤더를 붙여 통신 프로토콜을 맞춰야 합니다. 가령, 일련번호도 함께 전송하고 수신 측에서는 일련번호가 순서대로 오지 않는 것을 감안해 몇 초 정도 버퍼링을 하다가 그래도 맞춰지지 않는 프레임은 그냥 이전 번호에 해당하는 프레임을 연이어 쓰는 식으로 보완을 해야 합니다.
게다가 위의 소스 코드에서는 단순히 raw 이미지 데이터를 다루고 있지만, 통신이 관여한다면 다양한 압축 방식을 고려하는 것이 좀 더 현실적일 수 있습니다.
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]