2015-12-08 19:00:02 waeceo 阅读数 14894
  • C#急速入门

    Unity开发,C#绝对是基础中的基础,课程针对纯小白而设计,各种语言细节在课程里均有涉猎,从原理到实战,从底层到算法,你想了解的C#,这里应有尽有,除了讲解,还有练习,你说棒不棒,哈哈,当然如果你是有其他语言基础的同学,课程依然会让你收货满满。来吧,我们进入正题。

    16985 人正在学习 去看看 张建飞

硕士研三找完工作后就开始投入紧张忙碌的论文生涯,现在我在忙大论文的工程。前期好多的实验和理论,头都大了,好在通过搞这些东西,收获了不少心得。

写一下在用C#读取摄像头并做处理的Demo心得吧。

如果我们需要读取摄像头,并且还要对其中的某帧图像进行处理,比如说取出其中的某个像素点坐标的对应的像素色彩值,RGB或者灰度值。有以下几种方法:

(1)我们第一个想到的就是使用大名鼎鼎的Opencv。确实Opencv非常好用,但是对于一些对于C++一知半解的人来说,要有界面程序的话还得学Qt,哇,Qt配置Opencv可麻烦了,以前做的时候用过,也配置过,可以说非常麻烦,网上众说纷纭,各种版本的配置,基本上没有一个版本能够不出问题的配置好,还是我参考了各个版本后,自己想出的配置方法,也写在我以前的博客里了,如果需要的话,可以上下捣一捣翻一翻,准能找到。我这一次做为啥不用以前的程序了呢?就是因为我刚把环境卸掉了,不想再装了,所以问题来了,想偷懒,就得有其他办法做。如下的方法(2)

(2)好多人说,opencv也可以配置在C#上啊,对,没错,大牛应该能用的了,搞得动。为什么这么说?我问了一下度娘,她说,你必须学习EmguCV才行啊,说这是microsoft吸取了opencv的精华,而后又加入了很多高级功能之后的版本。高大上啊,然后我又开始搜EmguCV,大概的资料在下面两个链接上:

http://www.opencv.org.cn/forum.php?mod=viewthread&tid=32710
http://wenku.baidu.com/link?url=DIvhesSEe7CjxWPtX2YH-HOVciLereVfNVfd-OSZRlcpCGENlYyRO7wdSlbcqkueLRGlqT-UkswmKOKeMbAN6SrJJJb1sE2ic4ukwrcWvoK

资料很少,用的人也不多,所以,给那些刚涉入程序员行列不久的人一条靠谱的建议:我们在做一个工程的时候,网上一搜,会有很多种办法解决,但是,我们不要随便找一个就开始干,这是蛮干,开始的时候可能还没啥难度,网上的资料也够用,但是越往后做,难度越大,网上的资料可以用的资料就越少了,那就要看你的功底了;所以对于内功修为不够的人,千万不要随便学一门武功,容易走火入魔惊恐!这样做是非常浪费时间的,如果你在公司这么干的话,是会有致命后果的。所以公司的人在做一个工程前,首先都会论证网上的各种技术,做法,选一个比较成熟的技术来做,这样的话,节省时间,而且也不容易出现大的错误。我算是记住了!所以这个方案对于我的工程来说也就pass掉了。
换下一话题,大笑,方法(3)
(3)起初为了完成某个短期的目标就慌不择路的选择了该方法,结果越陷越深啊,幸好,我还有点内功,能够控制住自己,没有走火入魔。很心酸。该方法就是本文主要讲的方法
AVICap;avicap32.dll;
百度一搜avicap的话会有不少的资料,不过有很多资料都是重复的,你copy我的,我copy你的,真正的有几篇写的还可以的,链接如下:

http://blog.chinaunix.net/uid-20594503-id-1619859.html
http://blog.csdn.net/laolei1986/article/details/5730245
还有一些百度文档和博客上的程序,基本上都是一个样的,他们的程序我就不贴了,网上可以搜到的,基本功能就是打开视频,保存bmp格式的图片,录制avi格式的视频并保存;
而我的功能需求是这样的,(1)可以打开视频;(2)能够在视频现实中抓取一帧图片并且把图片转换成bmp格式;(3)分析这帧图片,找出某像素点的RGB值;(4)最后用Graphics绘图把RGB值根据大小画到picbox上,二维图像这样的,横轴是像素数(图片中某一行的第多少个像素点),纵轴是R值;
现把各个功能模块的程序帖上来:
先附上一张图吧:

做完后基本是这个效果,左侧的图片是640*480的,右侧是点击图片中的某一列之后的480个像素点对应的R值,0-255;
程序如下:包括功能(1)和功能(2)的程序:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Threading;
using System.IO;
using System.Drawing;

namespace Test
{
    class VideoWork
    {
        private const int WM_USER = 0x400;
        private const int WS_CHILD = 0x40000000;
        private const int WS_VISIBLE = 0x10000000;
        private const int WM_CAP_START = WM_USER;
        private const int WM_CAP_STOP = WM_CAP_START + 68;
        private const int WM_CAP_DRIVER_CONNECT = WM_CAP_START + 10;
        private const int WM_CAP_DRIVER_DISCONNECT = WM_CAP_START + 11;
        private const int WM_CAP_SAVEDIB = WM_CAP_START + 25;//保存文件
        private const int WM_CAP_GRAB_FRAME = WM_CAP_START + 60;
        private const int WM_CAP_SEQUENCE = WM_CAP_START + 62;
        private const int WM_CAP_FILE_SET_CAPTURE_FILEA = WM_CAP_START + 20;//设置捕获文件
        public const int WM_CAP_FILE_GET_CAPTURE_FILE = WM_CAP_START + 21;//获得捕获文件
        private const int WM_CAP_SEQUENCE_NOFILE = WM_CAP_START + 63;
        private const int WM_CAP_SET_OVERLAY = WM_CAP_START + 51;
        private const int WM_CAP_SET_PREVIEW = WM_CAP_START + 50;//50
        private const int WM_CAP_SET_CALLBACK_VIDEOSTREAM = WM_CAP_START + 6;
        private const int WM_CAP_SET_CALLBACK_ERROR = WM_CAP_START + 2;
        private const int WM_CAP_SET_CALLBACK_STATUSA = WM_CAP_START + 3;
        private const int WM_CAP_SET_CALLBACK_FRAME = WM_CAP_START + 5;
        private const int WM_CAP_SET_SCALE = WM_CAP_START + 53;
        private const int WM_CAP_SET_PREVIEWRATE = WM_CAP_START + 52;
        //private IntPtr hWndC;
        public static IntPtr hWndC;

        private bool bWorkStart = false;
        private IntPtr mControlPtr;
        private int mWidth;
        private int mHeight;
        private int mLeft;
        private int mTop;
                   /// <summary>
                /// 初始化显示图像
                /// </summary>
                /// <param name= “handle “> 控件的句柄 </param>
                /// <param name= “left “> 开始显示的左边距 </param>
                /// <param name= “top “> 开始显示的上边距 </param>
                /// <param name= “width “> 要显示的宽度 </param>
                /// <param name= “height “> 要显示的长度 </param>
                public VideoWork(IntPtr handle, int left, int top, int width, int height)
                {
                mControlPtr = handle;
                mWidth = width;
                mHeight = height;
                mLeft = left;
                mTop = top;
                }
                [DllImport("avicap32.dll ")]
private static extern IntPtr capCreateCaptureWindowA(byte[] lpszWindowName, int dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, int nID);

                [DllImport("avicap32.dll ")]
                private static extern int capGetVideoFormat(IntPtr hWnd, IntPtr psVideoFormat, int wSize);

                //
                //这里特别注意,因为WinAPI中的long为32位,而C#中的long为64wei,所以需要将lParam该为int
                //
                [DllImport("User32.dll ")]
                private static extern bool SendMessage(IntPtr hWnd, int wMsg, int wParam, int lParam);

                /// <summary>
                /// 开始显示图像
                /// </summary>
                public void Start()
                {
                    if (bWorkStart)
                        return;

                    bWorkStart = true;
                    byte[] lpszName = new byte[100];

                    hWndC = capCreateCaptureWindowA(lpszName, WS_CHILD | WS_VISIBLE, mLeft, mTop, mWidth, mHeight, mControlPtr, 0);

                    if (hWndC.ToInt32() != 0)
                    {
                        SendMessage(hWndC, WM_CAP_SET_CALLBACK_VIDEOSTREAM, 0, 0);
                        SendMessage(hWndC, WM_CAP_SET_CALLBACK_ERROR, 0, 0);
                        SendMessage(hWndC, WM_CAP_SET_CALLBACK_STATUSA, 0, 0);
                        SendMessage(hWndC, WM_CAP_DRIVER_CONNECT, 0, 0);
                        SendMessage(hWndC, WM_CAP_SET_SCALE, 1, 0);//打开预览视频的缩放比例
                        SendMessage(hWndC, WM_CAP_SET_PREVIEWRATE, 66, 0);//66ms,视频刷新频率
                        SendMessage(hWndC, WM_CAP_SET_OVERLAY, 1, 0);//启用叠加 注:据说启用此项可以加快渲染速度    
                        SendMessage(hWndC, WM_CAP_SET_PREVIEW, 1, 0);//设置显示图像启动预览模式 PREVIEW
                    }
                    return;

                }

                /// <summary>
                /// 停止显示
                /// </summary>
                public void Stop()
                {
                    SendMessage(hWndC, WM_CAP_DRIVER_DISCONNECT, 0, 0);
                    bWorkStart = false;
                }

                /// <summary>
                /// 抓图
                /// </summary>
                /// <param name= “path “> 要保存bmp文件的路径 </param>
                public void GrabImage()//string path
                {
                    IntPtr hBmp = Marshal.StringToHGlobalAnsi(@"G:\c.bmp");//正确@"G:\c.bmp"   正确Application.StartupPath+".bmp"
                    SendMessage(hWndC, WM_CAP_SAVEDIB, 0, hBmp.ToInt32());
                }
    }
}
比较简单,因为网上都有,但关键点比较难,就是我没有办法从正在播放的视频中抓取一帧直接保存成bmp格式的图片直接分析,取而代之的方法是,先把其中抓获的一帧图片保存到本地磁盘,然后,另外一个picbox在一个定时器中不停的读取本地磁盘保存的bmp格式的图片,然后再绘图类中,我们在从该picbox中读取bitmap格式的图片,总之绕了一大圈,很费劲,不过总算功能实现了,但是,说实话,我不满意!以上功能再绘图类中,如下:
class Draw
    {
        public static double startPixel = 0;
        public static double endPixel = 480;
        public static double startGray = 0;
        public static double endGray = 255;

        private Graphics glight;//灰度值
        public void draw(PaintEventArgs e, PictureBox pictureBox3)
        {
            glight = e.Graphics;
            Pen p = new Pen(Color.Black, 2);

            //画标题
            Font fii = new Font("Tahoma",15, FontStyle.Regular);
            //以下两个for循环是画网格
            for (int i = 0; i < 6; i++)
            {
                glight.DrawLine(p, 70, i * 102, 550, i * 102);//横轴
            }
            for (int i = 0; i < 6; i++)
            {
                glight.DrawLine(p, 70 + i * 96, 0, 70 + i * 96, 512);
            }
            //画纵坐标
            for (int i = 0; i < 6; i++)
            {
                glight.DrawString((startGray + i * ((endGray - startGray) / 5)).ToString(), fii, new SolidBrush(Color.Red), 20, 505 - i * 102);
            }
            //画横坐标
            for (int i = 0; i < 6; i++)
            {
                glight.DrawString((startPixel + i * ((endPixel - startPixel) / 5)).ToString(), fii, new SolidBrush(Color.Red), 50+ i * 96, 520);
            }

            //画波形图
            double perPixel = 480 / (endPixel - startPixel);//每个点占多少像素
            double perGray = 512 / (endGray - startGray);//每个灰度值占多少像素

            //画点
            Pen point = new Pen(Color.Red, 3);
            Bitmap bmp;
            bmp = (Bitmap)pictureBox3.Image;
            if (bmp == null)
                bmp = new Bitmap(@"G:\c.bmp");

            int begin=0;
            for (double i = 0; i < endPixel - startPixel; i=i+1)
            {
                //bmp.GetPixel(Form1.clickpixel, (int)i);
                begin=Convert.ToInt32(bmp.GetPixel(Form1.clickpixel,(int)(startPixel + i)).R);
                if (begin>= startGray &&begin<= endGray)
                    glight.DrawEllipse(point, 70 + (int)(perPixel * (i + 1)), 512 - (int)((begin - startGray) * perGray), 1, 1);//画椭圆 420 - x * 100, 610 - y * 100, 1, 1
            }
        }
    }
貌似都完成了,但是还有一个令我百思不得其解的问题,以前graphics绘图,刷新的时候就直接在定时器中用invalidate就可以搞定了,一般在定时器中首先调用抓图函数并保存到本地,然后另外一个picturebox在读取本地的图片就OK了,但是该实验这么做就就不行,在获取了图片之后,就直接刷新是不行的,在绘图中会报错,解决方法是在用一个定时器,在另外一个定时器中定时刷新就可以了,如下程序:
    private void timer1_Tick(object sender, EventArgs e)
                {
                    wv.GrabImage();
                    this.pictureBox3.ImageLocation = @"G:\c.bmp";
                }

我 2015/12/8 20:27:13

                private void timer2_Tick(object sender, EventArgs e)
                {
                    this.pictureBox2.Invalidate();//代表每隔多长时间就重绘一次
                }

至此,该工程基本完成了,但是效率很低,因为抓帧后保存本地---picbox从本地读取----bitmap从picbox读取,所以绕了很多弯子,动态显示的时候,鼠标都在抖动,说明很差啊,不过也算是用该方法完成了这个功能了吧,因为我实在不知道该如何从视频中直接抓取一帧bitmap的图像直接处理。有知道的大神,可以写评论啊,谢谢!

2015-06-23 10:34:28 xujie1991618 阅读数 30638
  • C#急速入门

    Unity开发,C#绝对是基础中的基础,课程针对纯小白而设计,各种语言细节在课程里均有涉猎,从原理到实战,从底层到算法,你想了解的C#,这里应有尽有,除了讲解,还有练习,你说棒不棒,哈哈,当然如果你是有其他语言基础的同学,课程依然会让你收货满满。来吧,我们进入正题。

    16985 人正在学习 去看看 张建飞

#C#调用摄像头的方式
本文将讲诉在C#的winform程序中如何调用摄像头:
1.调用USB摄像头
2.调用普通IP摄像头
3.通过摄像头SDK进行调用

##使用的DLL
首先给上我所使用的DLL
dll

##调用USB摄像头
调用USB摄像头其实比较简单,就是通过读取电脑自身所拥有的设备数,再执行预览。
videoSourcePlayer是AForge中的控件。

private FilterInfoCollection videoDevices;
this.videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
if (this.videoDevices.Count > 0)
   {
       VideoCaptureDevice source = new VideoCaptureDevice(this.videoDevices[0].MonikerString);
       this.videoSourcePlayer.SignalToStop();
       this.videoSourcePlayer.WaitForStop();
       this.videoSourcePlayer.VideoSource = source;
       this.videoSourcePlayer.Start();
   }

##调用普通IP摄像头
首先要确认HTTP方式传送的图片的地址,我用的SAMSUN地址是http://{0}/cgi-bin/video.cgi?msubmenu=jpg,还有其他的http://{0}/axis-cgi/jpg/image.cgi?camera=1
使用JPEGStream或者MJPEGStream,有用户名和密码的就加上。有了source其他的和上面USB一样。

JPEGStream source = new JPEGStream(URL);
//MJPEGStream source = new MJPEGStream(URL);
source.Login = username;
source.Password = password;
this.OpenVideoSource(source);

##通过摄像头SDK进行调用
这个调用还是很简单的,一般都是使用控件的Handle来进行预览的,我们可能会想取每一帧的图片,那么如何取得每一帧呢?因为是通过Handle来预览的所以控件中无法取得,我们需要调用摄像头SDK的获取每一帧的接口,但是这个接口是直接将图片保存到本地的,感觉巨坑有木有。不过我们还是有方法处理的。下面是我对三星摄像头的处理:

public override Bitmap CapturePicture()
{
    Bitmap bitmap = null;
    string path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Templates) + @"\SAMSUNG_CapturePicture.bmp";
    if (File.Exists(path))
    {
        File.Delete(path);
    }
    if (SSNetSDK.XNS_DEV_SaveSnapshot(playHandle, path,0))
    {
        using (MemoryStream stream = new MemoryStream(File.ReadAllBytes(path)))
        {
            bitmap = (Bitmap)Image.FromStream(stream);
        }
        File.Delete(path);
    }
    return bitmap;
}

DEMO http://download.csdn.net/detail/xujie1991618/8830347

我看到很多人问我AForge库,其实网上找一找很多的,为了方便大家我特地找了一份放云盘了
链接: https://pan.baidu.com/s/1Wkmykt00hdP1Jum2m_7k6Q 提取码: pr7m

2017-07-05 15:54:01 luo_5458 阅读数 7602
  • C#急速入门

    Unity开发,C#绝对是基础中的基础,课程针对纯小白而设计,各种语言细节在课程里均有涉猎,从原理到实战,从底层到算法,你想了解的C#,这里应有尽有,除了讲解,还有练习,你说棒不棒,哈哈,当然如果你是有其他语言基础的同学,课程依然会让你收货满满。来吧,我们进入正题。

    16985 人正在学习 去看看 张建飞

项目中用到 USB 摄像头,需要根据情况进行图像抓拍,查了半天资料,比较多的是使用 WPFMediaKit 和 AForge 。
但是由于项目要求不显示 USB 摄像头拍摄的画面,最终确定使用 AForge 解决。
下面用一个测试程序记录一下。

一、无预览拍照

首先建立一个 WPF 项目,我的就叫 AForgeTest,你们随意就好:

新建项目

然后在 NuGet 包管理器中安装 AForge 库:

这里写图片描述

我只安装了图中打勾的几个库,这个根据自己项目需要安装就好。
不过用 USB 摄像头拍照必须安装:
AForge.Video
AForge.Control
AForge.Video.DirectShow
这三个库文件。

不习惯使用 NuGet 也可以到 AForge 的 .NET lib 下载页面下载。

在 MainWindow.xaml 文件中添加两个按钮:

<Window x:Class="AForgeTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:AForgeTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="300" Width="300"
        Closing="Window_Closing">
    <StackPanel>
        <Button Name="btnCapture" Click="btnCapture_Click">拍照</Button>
        <Button Name="btnOpenCamera" Click="btnOpenCamera_Click">打开</Button>
    </StackPanel>
</Window>

后台交互逻辑如下:

using System;
using System.Windows;

namespace AForgeTest
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void btnOpenCamera_Click(object sender, EventArgs e)
        {
            CameraHelper.UpdateCameraDevices();
            if (CameraHelper.CameraDevices.Count > 0)
            {
                CameraHelper.SetCameraDevice(0);
            }
        }

        private void btnCapture_Click(object sender, EventArgs e)
        {
            CameraHelper.CaptureImage(@"E:\1");
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            CameraHelper.CloseDevice();
        }
    }
}

CameraHelper 类代码如下:

using System;
using AForge.Video.DirectShow;
using AForge.Controls;
using System.Windows;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;

namespace AForgeTest
{
    public static class CameraHelper
    {
        private static FilterInfoCollection _cameraDevices;
        private static VideoCaptureDevice div = null;
        private static VideoSourcePlayer sourcePlayer = new VideoSourcePlayer();
        private static bool _isDisplay = false;
        //指示_isDisplay设置为true后,是否设置了其他的sourcePlayer,若未设置则_isDisplay重设为false
        private static bool isSet = false;

        /// <summary>
        /// 获取或设置摄像头设备,无设备为null
        /// </summary>
        public static FilterInfoCollection CameraDevices
        {
            get
            {
                return _cameraDevices;
            }
            set
            {
                _cameraDevices = value;
            }
        }
        /// <summary>
        /// 指示是否显示摄像头视频画面
        /// 默认false
        /// </summary>
        public static bool IsDisplay
        {
            get { return _isDisplay; }
            set { _isDisplay = value; }
        }
        /// <summary>
        /// 获取或设置VideoSourcePlayer控件,
        /// 只有当IsDisplay设置为true时,该属性才可以设置成功
        /// </summary>
        public static VideoSourcePlayer SourcePlayer
        {
            get { return sourcePlayer; }
            set
            {
                if (_isDisplay)
                {
                    sourcePlayer = value;
                    isSet = true;
                }

            }
        }
        /// <summary>
        /// 更新摄像头设备信息
        /// </summary>
        public static void UpdateCameraDevices()
        {
            _cameraDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
        }
        /// <summary>
        /// 设置使用的摄像头设备
        /// </summary>
        /// <param name="index">设备在CameraDevices中的索引</param>
        /// <returns><see cref="bool"/></returns>
        public static bool SetCameraDevice(int index)
        {
            if (!isSet) _isDisplay = false;
            //无设备,返回false
            if (_cameraDevices.Count <= 0 || index < 0) return false;
            if (index > _cameraDevices.Count - 1) return false;
            // 设定初始视频设备
            div = new VideoCaptureDevice(_cameraDevices[index].MonikerString);
            sourcePlayer.VideoSource = div;
            div.Start();
            sourcePlayer.Start();
            return true;
        }
        /// <summary>
        /// 截取一帧图像并保存
        /// </summary>
        /// <param name="filePath">图像保存路径</param>
        /// <param name="fileName">保存的图像文件名</param>
        public static void CaptureImage(string filePath, string fileName = null)
        {
            if (sourcePlayer.VideoSource == null) return;
            if (!Directory.Exists(filePath))
            {
                Directory.CreateDirectory(filePath);
            }
            try
            {
                //sourcePlayer.Start();
                Image bitmap = sourcePlayer.GetCurrentVideoFrame();
                if(fileName == null) fileName = DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss");
                bitmap.Save(filePath + @"\" + fileName + "-cap.jpg", ImageFormat.Jpeg);
                bitmap.Dispose();
                //sourcePlayer.Stop();
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message.ToString());
            }
        }
        /// <summary>
        /// 关闭摄像头设备
        /// </summary>
        public static void CloseDevice()
        {
            if (div != null && div.IsRunning)
            {
                sourcePlayer.Stop();
                div.SignalToStop();
                div = null;
                _cameraDevices = null;
            }
        }
    }
}

最终效果如下:

这里写图片描述

首先单击打开按钮,然后单击拍照按钮,就会在指定路径下生成一个 jpg 文件。

单击打开按钮之后需要等待 2s 以上才能点击拍照(需要等待连接到摄像头),否则会报出异常,如下图:

这里写图片描述

二、显示摄像头拍摄画面和截取的图片

首先添加 System.Windows.Forms 和 WindowsFormsIntegration 的引用。
然后在 MainWindows.xmal 文件中加命名空间:

xmlns:wfi ="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
xmlns:aforge ="clr-namespace:AForge.Controls;assembly=AForge.Controls"

添加 VideoSourcePlayer 和 Image 控件:

<wfi:WindowsFormsHost Grid.Row="0">
    <aforge:VideoSourcePlayer x:Name="player" Height="480" Width="640"/>
</wfi:WindowsFormsHost>
<Image Grid.Row="0" Grid.Column="1" Name="imgCapture" Stretch="Fill" Height="480" Width="640"/>

这里有个小细节,注意对 VideoSourcePlayer 命名时,一定要使用 x:Name 不要省略 x: ,否则无法在后台代码中使用(不要问我是怎么知道的)。

对 CameraHelper.cs 中的 CaptureImage 函数做一点修改:

        /// <summary>
        /// 截取一帧图像并保存
        /// </summary>
        /// <param name="filePath">图像保存路径</param>
        /// <param name="fileName">保存的图像文件名</param>
        /// <returns>如果保存成功,则返回完整路径,否则为 null</returns>
        public static string CaptureImage(string filePath, string fileName = null)
        {
            if (sourcePlayer.VideoSource == null) return null;
            if (!Directory.Exists(filePath))
            {
                Directory.CreateDirectory(filePath);
            }
            try
            {
                Image bitmap = sourcePlayer.GetCurrentVideoFrame();
                if(fileName == null) fileName = DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss");
                string fullPath = Path.Combine(filePath, fileName + "-cap.jpg");
                bitmap.Save(fullPath, ImageFormat.Jpeg);
                bitmap.Dispose();
                return fullPath;
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message.ToString());
                return null;
            }
        }

修改后台代码如下:

using System;
using System.Windows;
using System.Windows.Media.Imaging;

namespace AForgeTest
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            CameraHelper.IsDisplay = true;
            CameraHelper.SourcePlayer = player;
            CameraHelper.UpdateCameraDevices();
        }

        private void btnOpenCamera_Click(object sender, EventArgs e)
        {
            if (CameraHelper.CameraDevices.Count > 0)
            {
                CameraHelper.SetCameraDevice(0);
            }
        }

        private void btnCapture_Click(object sender, EventArgs e)
        {
            string fullPath = CameraHelper.CaptureImage(AppDomain.CurrentDomain.BaseDirectory + @"\Capture");

            BitmapImage bit = new BitmapImage();
            bit.BeginInit();
            bit.UriSource = new Uri(fullPath);
            bit.EndInit();
            imgCapture.Source = bit;
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            CameraHelper.CloseDevice();
        }
    }
}

最终结果如下:

结果

2013-05-02 11:27:14 lihui1886 阅读数 1003
  • C#急速入门

    Unity开发,C#绝对是基础中的基础,课程针对纯小白而设计,各种语言细节在课程里均有涉猎,从原理到实战,从底层到算法,你想了解的C#,这里应有尽有,除了讲解,还有练习,你说棒不棒,哈哈,当然如果你是有其他语言基础的同学,课程依然会让你收货满满。来吧,我们进入正题。

    16985 人正在学习 去看看 张建飞

最近,想用c#调用电脑摄像头,参考了“赤色火焰”的博客http://www.cnblogs.com/mgod/archive/2008/06/18/1224351.html,但对码流解码方面的介绍没有,上网搜也没有找到。于是,开始分析FrameCallBack中的VideoData的码流结构发现其分辨率是640*480,排列方式是YCbYCr,一行的排列是Y0CbY1CrY2CbY3CrY4CbY5Cr...,采用从左到右 从上到下的排列方式。其中YCbYCr表示左右相邻的两个像素,这两个像素的Y值不同,共用Cb和Cr,例如:Y0CbY1Cr解码是应为:Y0CbCr、Y1CbCr。

根据,YCbYCr转RGB公式:

                               r = 1.164 * (y - 16) + 1.596 * (cr - 128);
                               g = 1.164 * (y - 16) - 0.391 * (cb - 128) - 0.813 * (cr - 128);
                               b = 1.164 * (y - 16) + 2.018 * (cb - 128);

编写解码函数,再调用解码函数

	private double[] ycbcrToRGB(byte y, byte cb, byte cr)
        {
            double[] rgb = new double[3];
            rgb[0] = 1.164 * (y - 16) + 1.596 * (cr - 128);
            rgb[1] = 1.164 * (y - 16) - 0.391 * (cb - 128) - 0.813 * (cr - 128);
            rgb[2] = 1.164 * (y - 16) + 2.018 * (cb - 128);
            rgb[0] = Math.Min(Math.Max(0, rgb[0]), 255);
            rgb[1] = Math.Min(Math.Max(0, rgb[1]), 255);
            rgb[2] = Math.Min(Math.Max(0, rgb[2]), 255);
            return rgb;
        }


 

	protected unsafe void wc_RecievedFrame(byte[] data)
        {
            int w = 320, h = 480;
            Bitmap bi = new Bitmap(w * 2, h);
            BitmapData bData = bi.LockBits(new Rectangle(0, 0, w * 2, h), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
            for (int i = 0; i < w; i++)
            {
                for (int j = 0; j < h; j++)
                {
                    byte* pRef = (byte*)bData.Scan0 + bData.Stride * j + i * 2 * 3;
                    int index = i * 4 + j * w * 4;

                    byte Y0 = data[index];
                    byte cb = data[index + 1];
                    byte cr = data[index + 3];
                    byte Y1 = data[index + 2];

                    double[] rgb0 = ycbcrToRGB(Y0, cb, cr);
                    double[] rgb1 = ycbcrToRGB(Y1, cb, cr);
                    pRef[0] = (byte)rgb0[2];
                    pRef[1] = (byte)rgb0[1];
                    pRef[2] = (byte)rgb0[0];

                    pRef[3] = (byte)rgb1[2];
                    pRef[4] = (byte)rgb1[1];
                    pRef[5] = (byte)rgb1[0];
                }
            }
            bi.UnlockBits(bData);
            pictureBox1.Image = bi;
        }


 

2017-04-11 17:14:39 Fighting_Dreamer 阅读数 5261
  • C#急速入门

    Unity开发,C#绝对是基础中的基础,课程针对纯小白而设计,各种语言细节在课程里均有涉猎,从原理到实战,从底层到算法,你想了解的C#,这里应有尽有,除了讲解,还有练习,你说棒不棒,哈哈,当然如果你是有其他语言基础的同学,课程依然会让你收货满满。来吧,我们进入正题。

    16985 人正在学习 去看看 张建飞
买的是1080的摄像头,但是emgu调用只能是640*480的,那不是亏了,直接设置一下就ok了.
opencv的还好搞,emgu的确实资料不多.

直接上代码,使用.SetCaptureProperty属性设置就ok了

                _capture = new VideoCapture(1);
                _capture.SetCaptureProperty(CapProp.FrameHeight, 1080);
                _capture.SetCaptureProperty(CapProp.FrameWidth, 1920);
                _capture.ImageGrabbed += ProcessFrame;

http://www.cnblogs.com/zhongxia/p/4248835.html

博文 来自: qq_18616165

C#代码调用摄像头

阅读数 4121

没有更多推荐了,返回首页