调用AForge.video.DirectShow免费实现摄像头采集和上下左右镜像功能
这次我们使用开源免费的AForge.video.DirectShow实现摄像头采集和上下左右镜像功能
前面我分享了一个收费的sharpCamera.dll程序集实现此功能, 它是个收费控件, 虽然费用也不高. 但是我们能先用免费的就不用收费的原则 , 下面来探讨下AForge的使用方法.
二楼有写好的AForge封装库下载
和sharpCamera那个类似, AForge也是异步使用picturebox来实现

import win.ui;
/*DSG{{*/
mainForm = win.form(text="aardio工程2";right=959;bottom=564)
mainForm.add(
button={cls="button";text="start";left=307;top=527;right=418;bottom=553;db=1;dl=1;z=2};
button2={cls="button";text="stop";left=430;top=527;right=542;bottom=553;db=1;dl=1;dr=1;z=3};
button3={cls="button";text="flip";left=553;top=527;right=669;bottom=553;db=1;dr=1;z=4};
combobox={cls="combobox";left=53;top=527;right=286;bottom=553;db=1;dl=1;edge=1;items={};mode="dropdownlist";z=5};
custom={cls="custom";text="自定义控件";left=0;top=0;right=960;bottom=520;bgcolor=12639424;db=1;dl=1;dr=1;dt=1;z=1}
)
/*}}*/
import dotNet
import System.Drawing;
import System.Windows.Forms;
var pictureBox1 = System.Windows.Forms.CreateEmbed("PictureBox",mainForm.custom);
pictureBox1.BackColor = System.Drawing.Color.Black;
pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
dotNet.reference({
"AForge" = "\res\AForge.dll";
})
var carema = dotNet.load("AForge");
var AForgeCamera = carema.import("AForge.Video");
var videoDevices = AForgeCamera.DirectShow.FilterInfoCollection(AForgeCamera.DirectShow.FilterCategory.VideoInputDevice);
//默认取第一个序号的摄像头
var videoDevice = AForgeCamera.DirectShow.VideoCaptureDevice(videoDevices.Item[0].MonikerString);
//摄像头包含的可用帧率和分辨率
var videoCapabilitiesEx = videoDevice.VideoCapabilities;
for(i=0;videoCapabilitiesEx.LongLength-1;1){
mainForm.combobox.add("分辨率: "++videoCapabilitiesEx.Item[i].FrameSize.Width++"*"++videoCapabilitiesEx.Item[i].FrameSize.Height ++ " 帧率: " ++ videoCapabilitiesEx.Item[i].FrameRate);
}
if(mainForm.combobox.count){
mainForm.combobox.selIndex = 1;
}
var flip1 = 0;
videoDevice.NewFrame = function(sender, NewFrameEventArgs){
var temp = NewFrameEventArgs.Frame.Clone();
select(flip1) {
case 0 {
}
case 1 {
temp.RotateFlip(System.Drawing.RotateFlipType.RotateNoneFlipX);
}
case 2 {
temp.RotateFlip(System.Drawing.RotateFlipType.RotateNoneFlipXY);
}
case 3 {
temp.RotateFlip(System.Drawing.RotateFlipType.RotateNoneFlipY);
}
else {
}
}
if (pictureBox1.Image != null)
{
pictureBox1.Image.Dispose();
}
pictureBox1.Image = temp;
}
mainForm.button.oncommand = function(id,event){
videoDevice.Stop();
if(mainForm.combobox.count){
//设定帧率和分辨率
videoDevice.DesiredFrameRate = videoCapabilitiesEx.Item[mainForm.combobox.selIndex-1].FrameRate;
videoDevice.DesiredFrameSize = videoCapabilitiesEx.Item[mainForm.combobox.selIndex-1].FrameSize;
videoDevice.DesiredSnapshotSize = videoCapabilitiesEx.Item[mainForm.combobox.selIndex-1].FrameSize;
videoDevice.Start();
}
}
mainForm.button2.oncommand = function(id,event){
videoDevice.Stop();
}
mainForm.button3.oncommand = function(id,event){
select(flip1) {
case 0 {
flip1=1;
}
case 1 {
flip1=2;
}
case 2 {
flip1=3;
}
case 3 {
flip1=0;
}
else {
}
}
}
mainForm.onClose = function(hwnd,message,wParam,lParam){
videoDevice.Stop();
}
mainForm.show();
return win.loopMessage();完整测试工程如下:
C#工程中使用Aforge摄像头
using AForge;
using AForge.Video;
using AForge.Video.DirectShow;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp2
{
public partial class Form1 : Form
{
VideoCaptureDevice VideoDevice;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
FilterInfoCollection VideoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
//默认取第一个序号的摄像头
VideoDevice = new VideoCaptureDevice(VideoDevices[0].MonikerString);
foreach (var VideoCapabilitie in VideoDevice.VideoCapabilities)
{
Console.WriteLine(VideoCapabilitie.AverageFrameRate);
Console.WriteLine(VideoCapabilitie.FrameSize);
}
VideoDevice.VideoResolution = VideoDevice.VideoCapabilities[4];
VideoDevice.NewFrame += OnNewFrame;
VideoDevice.Start();
}
void OnNewFrame(object sender, NewFrameEventArgs eventArgs)
{
//var temp = eventArgs.Frame.Clone();
if (pictureBox1.Image != null)
{
pictureBox1.Image.Dispose();
}
pictureBox1.Image = (Bitmap)eventArgs.Frame.Clone();
}
private void button1_Click(object sender, EventArgs e)
{
VideoDevice.Stop();
}
private void button2_Click(object sender, EventArgs e)
{
VideoDevice.Start();
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
VideoDevice.Stop();
}
}
}更新:
给摄像头增加可移动水平辅助线和双击全屏功能

import win.ui;
/*DSG{{*/
var winform = win.form(text="aardio form";right=739;bottom=499)
winform.add(
video_line={cls="plus";left=0;top=205;right=740;bottom=206;bgcolor=65280;dl=1;dr=1;z=1}
)
/*}}*/
import AForgeVideo;
var video = AForgeVideo(winform);
//禁用清除背景
winform.onEraseBkgnd = lambda() 0;
try{//防止意外报错
video.start(1/*摄像头序号*/,1/*分辨率序号*/);
video.flip = 1/*水平镜像*/;
}
catch(e)
{
winform.msgboxErr("摄像头打开失败!")
}
//使辅助线显示在摄像头上
winform.video_line.setParent(winform)
//双击全屏
video.pictureBox.MouseDoubleClick = function(sender, e){
if(winform.parent){
winform.parent.fullscreen();
}else {
winform.fullscreen();
}
}
//单击移动线
video.pictureBox.MouseClick = function(sender, e){
var x,y = win.getMessagePos();
x,y = win.toClient(winform.hwnd,x,y);
winform.video_line.setPos(0,y);
}
winform.onClose = function(hwnd,message,wParam,lParam){
video.stop();
}
winform.show();
win.loopMessage();
return winform;c# 的winform界面中给摄像头画面增加功能:
鼠标点击画面, 在点击点位置绘制一条斜线 , 双击鼠标隐藏
难点:
摄像头的分辨率和界面上的picturebox的大小并不是一致的, 摄像头图片会被拉伸填充满画面, 那么你在picturebox点击的位置就需要对应的进行缩放, 这样在真实摄像头画面图片里面绘制的线才是真正的位置, 要不然你点击的位置绘线会出现很大偏差.
解决办法就是: 对应点进行比例缩放即可. 这样绘制到图片内的线在picturebox里无论怎么拉伸都是实际位置了.
FilterInfoCollection VideoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
//默认取第一个序号的摄像头
VideoDevice1 = new VideoCaptureDevice(VideoDevices[端口1].MonikerString);
VideoDevice1.VideoResolution = VideoDevice1.VideoCapabilities[分辨率1];
VideoDevice1.NewFrame += OnNewFrame1;
VideoDevice1.Start();
bool video1_clickFlag = false;
int video1_clickX;
int video1_clickY;
private void video1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
video1_clickX = e.X;
video1_clickY = e.Y;
video1_clickFlag = true;
}
else if (e.Button == MouseButtons.Right)
{
Form fm1 = new jiaodu();
fm1.ShowDialog();
}
}
private void video1_MouseDoubleClick(object sender, MouseEventArgs e)
{
video1_clickFlag = false;
}
void OnNewFrame1(object sender, NewFrameEventArgs eventArgs)
{
var temp = (Bitmap)eventArgs.Frame.Clone();
switch (flip1)
{
case 0:
break;
case 1:
temp.RotateFlip(System.Drawing.RotateFlipType.RotateNoneFlipX);
break;
case 2:
temp.RotateFlip(System.Drawing.RotateFlipType.RotateNoneFlipY);
break;
case 3:
temp.RotateFlip(System.Drawing.RotateFlipType.RotateNoneFlipXY);
break;
default:
break;
}
if (video1_clickFlag)
{
using (var graphics = Graphics.FromImage(temp))
using (var pen = new Pen(Color.Lime, 1))
{
var bili_X = temp.Width * 1.0 / video1.Width;
var bili_Y = temp.Height * 1.0 / video1.Height;
int Mdian_X = (int)(video1_clickX * bili_X);
int Mdian_Y = (int)(video1_clickY * bili_Y);
var p1 = new Point(Mdian_X, Mdian_Y);
//计算直线上的端点P0
var x0 = (int)(Mdian_X - Mdian_Y / Math.Tan(GlobalVar.jiaodu * Math.PI / 180));
//计算直线上的端点P2
var x2 = (int)(Mdian_X + (temp.Height - Mdian_Y) / Math.Tan(GlobalVar.jiaodu * Math.PI / 180));
var p0 = new Point(x0, 0);
var p2 = new Point(x2, temp.Height);
//在Bitmap上绘制直线
graphics.DrawLine(pen, p0, p1);
graphics.DrawLine(pen, p1, p2);
var p3 = new Point(0, Mdian_Y);
var p4 = new Point(temp.Width, Mdian_Y);
//在Bitmap上绘制直线
graphics.DrawLine(pen, p3, p1);
graphics.DrawLine(pen, p1, p4);
}
}
if (video1.Image != null)
{
video1.Image.Dispose();
}
video1.Image = temp;
}如果有两个摄像头, 软件里需要交换图像, 那么可以:
VideoDevice1.NewFrame -= OnNewFrame1; VideoDevice2.NewFrame -= OnNewFrame2; VideoDevice1.NewFrame += OnNewFrame2; VideoDevice2.NewFrame += OnNewFrame1;
当窗口关闭的时候记得释放掉:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
try
{
VideoDevice1.Stop();
VideoDevice2.Stop();
VideoDevice1.NewFrame -= OnNewFrame1;
VideoDevice2.NewFrame -= OnNewFrame2;
}
catch (Exception)
{
// throw;
}
}用了第二种可以了。能不能在自定义控件中只显示摄像头的中间一部分图像?然后点击就把显示的部分保存下来,并清除自定义控件?
**************************************************************************
// 定义裁剪函数
function cropAndSaveImage() {
var pic2 = gdip.snap(win.getForeground(),10,50,270,280);
try {pic2.save("D:\\1.jpg");}// 保存裁剪后的图像
catch (e) {console.log("保存文件时出错: " + e.Message);}// 处理保存文件时的异常
mainForm.picturebox.background = "D:\\1.jpg";// 显示裁剪后的图像
}
// 为 pictureBox1 添加点击事件处理逻辑
pictureBox1.Click = function(sender, e) {cropAndSaveImage();}
登录后方可回帖



简单封装了个AForgeVideo摄像头库:
//AForge摄像头库 import dotNet import System.Drawing; import System.Windows.Forms; class AForgeVideo{ ctor( winform ){ this.pictureBox = ..System.Windows.Forms.CreateEmbed("PictureBox",winform); this.pictureBox.BackColor = ..System.Drawing.Color.Black; this.pictureBox.SizeMode = ..System.Windows.Forms.PictureBoxSizeMode.StretchImage; this.carema = ..dotNet.load("\res\AForge.dll"); this.AForgeCamera = this.carema.import("AForge.Video"); this.flip = 0; this.videoDevice = null; ..table.gc(this,"stop"); }; getNameList = function(){ var videoDevices = this.AForgeCamera.DirectShow.FilterInfoCollection(this.AForgeCamera.DirectShow.FilterCategory.VideoInputDevice); var retTab = {}; if(videoDevices.Count>0){ for(i=0;videoDevices.Count-1;1){ //压入摄像头设备号名称列表 ..table.push(retTab , videoDevices.Item[i].MonikerString); } return retTab; } return null; } getVideoCapabilitiesList = function(deviceName){ if(deviceName!=null){ var videoDevice = this.AForgeCamera.DirectShow.VideoCaptureDevice(deviceName); var videoCapabilitiesEx = videoDevice.VideoCapabilities; var retTab = {}; if(videoCapabilitiesEx.LongLength>0){ for(i=0;videoCapabilitiesEx.LongLength-1;1){ ..table.push(retTab,{ ["Width"] = videoCapabilitiesEx.Item[i].FrameSize.Width, ["Height"] = videoCapabilitiesEx.Item[i].FrameSize.Height, ["FrameRate"] = videoCapabilitiesEx.Item[i].FrameRate, }); } return retTab; } return null; } return null; } start = function(DeviceIndex=1,index=1){ if(this.videoDevice){ this.videoDevice.Stop(); } var namelist = this.getNameList(); if(DeviceIndex <= #namelist){ name = namelist[DeviceIndex]; }else { //超出摄像头数量,不开启 return false; } //设定帧率和分辨率 var VideoCapabilities = this.getVideoCapabilitiesList(name); this.videoDevice = this.AForgeCamera.DirectShow.VideoCaptureDevice(name); if(VideoCapabilities!=null){ if(index>#VideoCapabilities){ index = 1; } this.videoDevice.DesiredFrameRate = VideoCapabilities[index].FrameRate; this.videoDevice.DesiredFrameSize = ..System.Drawing.Size(VideoCapabilities[index].Width,VideoCapabilities[index].Height); this.videoDevice.DesiredSnapshotSize = ..System.Drawing.Size(VideoCapabilities[index].Width,VideoCapabilities[index].Height); } this.videoDevice.NewFrame = function(sender, NewFrameEventArgs){ var temp = NewFrameEventArgs.Frame.Clone(); select(this.flip) { case 0 { } case 1 { temp.RotateFlip(..System.Drawing.RotateFlipType.RotateNoneFlipX); } case 2 { temp.RotateFlip(..System.Drawing.RotateFlipType.RotateNoneFlipXY); } case 3 { temp.RotateFlip(..System.Drawing.RotateFlipType.RotateNoneFlipY); } else { } } if (this.pictureBox.Image != null) { this.pictureBox.Image.Dispose(); } this.pictureBox.Image = temp; } this.videoDevice.Start(); return true; } stop = function(){ if(this.videoDevice){ this.videoDevice.Stop(); this.videoDevice = null; } } flipLoop = function(){ select(this.flip) { case 0 { this.flip=1; } case 1 { this.flip=2; } case 2 { this.flip=3; } case 3 { this.flip=0; } else { } } } } /**intellisense() AForgeVideo = AForge摄像头库 AForgeVideo(.(winform) = 创建 摄像头 界面 AForgeVideo() = !AVideo. end intellisense**/ /**intellisense(!AVideo) getNameList() = 获取摄像头设备硬件DeviceName名称列表 getVideoCapabilitiesList(.(DeviceName) = 获取指定设备的 分辨率和帧率 列表,如果不存在那么返回 null start(.(DeviceIndex,CapabilitiesIndex) = 开启摄像头\n DeviceIndex:设备序号 , CapabilitiesIndex:分辨率和帧率表序号 stop() = 关闭摄像头 flipLoop() = 循环镜像图像,调用一次改变一次镜像方向 end intellisense**/使用的时候:
import win.ui; /*DSG{{*/ mainForm = win.form(text="aardio工程2";right=1209;bottom=556) mainForm.add( button={cls="button";text="start";left=16;top=519;right=127;bottom=545;db=1;dl=1;z=2}; button2={cls="button";text="flip";left=142;top=519;right=254;bottom=545;db=1;dl=1;z=3}; button3={cls="button";text="stop";left=265;top=519;right=381;bottom=545;db=1;dl=1;z=4}; button4={cls="button";text="start";left=833;top=518;right=944;bottom=544;db=1;dr=1;z=6}; button5={cls="button";text="flip";left=959;top=518;right=1071;bottom=544;db=1;dr=1;z=7}; button6={cls="button";text="stop";left=1082;top=518;right=1198;bottom=544;db=1;dr=1;z=8}; custom={cls="custom";text="自定义控件";left=0;top=0;right=601;bottom=496;bgcolor=12639424;db=1;dl=1;dr=0.5;dt=1;z=1}; custom2={cls="custom";text="自定义控件";left=609;top=0;right=1210;bottom=496;bgcolor=12639424;db=1;dl=0.5;dr=1;dt=1;z=5} ) /*}}*/ import AForgeVideo; var video = AForgeVideo(mainForm.custom); mainForm.button.oncommand = function(id,event){ //默认开启序号1摄像头,分辨率为序号1的分辨率和帧率 video.start(); } mainForm.button2.oncommand = function(id,event){ //摄像头1镜像 video.flipLoop(); } mainForm.button3.oncommand = function(id,event){ //摄像头1停止 video.stop(); } //定义2号摄像头 var video2 = AForgeVideo(mainForm.custom2); mainForm.button4.oncommand = function(id,event){ //开启序号2摄像头,分辨率为序号1的分辨率和帧率 video2.start(2); } mainForm.button5.oncommand = function(id,event){ //摄像头2镜像 video2.flipLoop(); } mainForm.button6.oncommand = function(id,event){ //摄像头2停止 video2.stop(); } mainForm.show(); return win.loopMessage();完整示例工程和库代码如下: