写在前面
C# Tips是博主开启的第一个项目,以C#为示例语言,旨在普及编程技巧、经典算法以及计算机视觉、图形学知识。
图像
虽然EmguCV的图像本质依然是矩阵,但是为了更加方便地使用,EmguCV将图像封装在Image<TColor, TDepth>类中,其中的TColor属于Emgu.CV.Structure,表示色彩类型,常用的有Bgr(蓝绿红三通道)和Gray(灰度);TDepth表示深度类型,即每一个像元的取值类型,常用的0-255对应的是byte。
读取图像
最常用的读取图像方式就是从本地文件中读取:
//读取彩色图像
Image<Bgr, byte> image=new Image<Bgr, byte>(图像路径);
//读取灰度图像
Image<Gray, byte> image=new Image<Gray, byte>(图像路径);
另外还可以从C#的位图转换过来:
Image<TColor, TDepth> image=new Image<TColor, TDepth>(Bitmap);
显示图像
EmguCV提供了和.NET自带的PictureBox类似的ImageBox控件,用于图像显示。现有一个名为ib的ImageBox,显示图像的方法如下:
ib.Image=image;
如果需要从ImageBox中获得显示的图像,需要进行强制转换,因为ImageBox的Image属性是IImage接口类型。
image=(Image<TColor, TDepth>)ib.Image;
保存和释放图像
Image<TColor, TDepth>提供了图像的保存方法,图像可以保存成jpg、png等常用类型,可用来进行图像格式的转换。
image.Save(图像保存路径);
由于读取的图像的内存并不会被自动回收,因此在确保使用完图像后一定要及时释放图像内存,否则很容易造成内存溢出。
image.Dispose();
视频
视频的本质是多张图像(帧)的连续播放,ImageBox控件可以显示图像,自然而然可以播放视频。EmguCV提供了VideoCapture类,用于从视频文件或摄像头抓取视频帧。
//从视频文件抓取
VideoCapture capture=new VideoCapture(视频路径);
//从摄像头抓取
VideoCapture capture=new VideoCapture();
capture一次只能按时间顺序抓取一帧:
//返回抓取结果
Mat frame=capture.QueryFrame();
//不返回抓取结果
capture.Grab();
由于播放视频属于耗时操作,为避免线程阻塞,保证视频播放流畅,需要将视频帧的抓取放在非主线程中。程序的空闲进程很适合用来处理视频播放问题,一方面其保留了子线程的性质,另一方面其寿命与程序等同。
首先,写一个实现视频抓取的方法ProcessFrame,由于必须要注册空闲进程的EventHandler委托,所以ProcessFrame的返回值和参数都只能与EventHandler保持一致。
private void ProcessFrame(object sender, EventArgs arg)
{
if(正在播放)
{
if(ib.Image!=null)
ib.Image.Dispose();
ib.Image=capture.QueryFrame();
if(ib.Image==null)
正在播放变为结束播放
else
System.Threading.Thread.Sleep((int)(1000.0 / 视频帧率));
}
}
代码中的正在播放和结束播放由视频播放键状态指示,视频帧率可以通过capture的GetCaptureProperty方法获取,该方法还能获取更多视频属性:
视频帧率=capture.GetCaptureProperty(Emgu.CV.CvEnum.CapProp.Fps);
然后,在窗体的构造方法或初始化事件中注册EventHandler委托:
Application.Idle += new EventHandler(ProcessFrame);
可惜的是,EmguCV只能提取视频文件中的图像信息,尽管利用Sleep方法进行适当的线程延时,视频依然不能按照原始帧率进行播放。