zoukankan      html  css  js  c++  java
  • Flash在线拍摄用户头象

    很多网站在上传用户头象时,除了传统方式上传外,都支持在线摄像头拍照并做简单编辑,完成之后再将图象数据提交到服务端(比如ASP.Net),这几天正好需要这个功能,研究了下,思路如下:

    1、先获取摄像头视频

    2、利用BitmapData.draw来对视频截图

    3、在截图上,放一个方块允许用户手动调整位置,同时允许截图做缩放

    4、用户调整完成后,对指定区域的BitmapData做copyPixes处理(即拷贝指定区域的像素)

    5、将上一步得到的新BitmapData进行Jpeg编码压缩,然后再转到base64字符串

    6、将字符串发送到ASP.Net,然后还原成图片保存即可(即base64的解码)

    flash端代码:(第6步省略了)

    import flash.media.Camera;
    import flash.text.TextField;
    import flash.media.Video;
    import flash.text.TextFormat;
    import fl.controls.Button;
    import flash.utils.Timer;
    import flash.events.MouseEvent;
    import flash.display.BitmapData;
    import flash.display.Bitmap;
    import flash.display.Sprite;
    import flash.geom.Rectangle;
    import flash.events.Event;
    
    var _cam:Camera;
    var _txtInfo:TextField = txtInfo;
    var _txtMsg:TextField = txtMsg;
    var _btnGet:Button = btnGet;
    var _btnSave:Button = btnSave;
    var _video:Video = myVideo;
    var _timer:Timer;
    var _videoIsWorked:Boolean = false;
    var _bmp:Bitmap;
    var _bmpTest:Bitmap;
    var _mask:Sprite;
    var _slider:SimpleSlider;
    
    init();
    
    function init():void
    {
    	this._txtMsg.visible = false;
    	this._btnGet.label = "拍 照";
    	this._btnSave.label = "保 存";
    	this._txtInfo.selectable = false;
    	var style:TextFormat = new TextFormat();
    	style.font = "宋体";
    	style.size = 12;//字体大小
    	style.color = 0x000000;//RGB色
    	style.leading = 7;//行间距
    	this._btnGet.setStyle("textFormat",style);
    	this._btnSave.setStyle("textFormat",style);
    	getCamera();
    }
    
    function getCamera():void
    {
    
    	this._btnGet.enabled = false;
    	this._btnSave.enabled = false;
    
    	this._cam = Camera.getCamera();
    	if (_cam == null)
    	{
    		this._txtInfo.text = "未检测到摄像头设备!";
    		return;
    	}
    
    
    
    	_cam.addEventListener(StatusEvent.STATUS, cameraStatusHandler);
    	_cam.addEventListener(ActivityEvent.ACTIVITY, cameraActivityHandler);
    
    	_cam.setQuality(0,0);
    	this._video.attachCamera(this._cam);
    
    }
    
    //用户选择是否摄像头时触发 ;
    function cameraStatusHandler(e:StatusEvent):void
    {
    
    	if (e.code == "Camera.Muted")
    	{
    		this._txtInfo.text = "您不允许使用摄像头!";
    	}
    	else if (e.code == "Camera.Unmuted")
    	{
    		this._txtInfo.text = "摄像头视频获取中...";
    		_timer = new Timer(100,20);//每隔100ms检测摄像头状态,一共检测20次   
    		cameraActivityHandler(null);
    	}
    }
    
    
    //摄像头有活动时被触发 
    function cameraActivityHandler(e:ActivityEvent):void
    {
    	if (! _videoIsWorked)
    	{
    		if (_timer != null)
    		{
    			_timer.addEventListener(TimerEvent.TIMER, checkCamera);
    			_timer.addEventListener(TimerEvent.TIMER_COMPLETE, checkCameraComplete);
    			_timer.start();
    		}
    	}
    }
    
    
    //timer回调函数,用于检测摄像头设备是否正确 
    function checkCamera(e:TimerEvent):void
    {
    	this._txtInfo.text = "摄像头视频获取中...";
    	if (this._cam.currentFPS > 0)
    	{
    		_timer.stop();
    		_videoIsWorked = true;
    		this._txtInfo.text = "";//摄像头工作正常
    
    		this._txtInfo.text = "摄像头正常,请点击拍照";
    
    		//trace("_cam.width=",_cam.width,",_cam.height=",_cam.height);
    
    		this._btnGet.addEventListener(MouseEvent.CLICK,btnGet_Click);
    		this._btnGet.enabled = true;
    
    		this._video.width = 240;
    		this._video.height = 180;
    
    		//trace(this._video.width,this._video.height);
    
    	}
    
    }
    
    
    function btnGet_Click(e:MouseEvent):void
    {
    	var bmd:BitmapData = new BitmapData(_cam.width,_cam.height);
    	bmd.draw(_video);
    	//trace(bmd.width,bmd.height);
    	if (this._bmp == null)
    	{
    		this._bmp = new Bitmap(bmd);
    		addChild(_bmp);
    		_bmp.x = 260;
    		_bmp.y = 10;
    		_bmp.width = _video.width;
    		_bmp.height = _video.height;
    
    		if (this._mask == null)
    		{
    			this._mask = new Sprite();
    			this._mask.graphics.beginFill(0xffffff,0.3);
    			this._mask.graphics.drawRect(0,0,125,125);
    			this._mask.graphics.endFill();
    			this._mask.graphics.lineStyle(1,0xff0000,1);
    			this._mask.graphics.lineTo(1,124);
    			this._mask.graphics.lineTo(124,124);
    			this._mask.graphics.lineTo(124,1);
    			this._mask.graphics.lineTo(1,1);
    			addChild(this._mask);
    			this._mask.x = 260 + 57;
    			this._mask.y = 10 + 27;
    			this._mask.buttonMode = true;
    			this._mask.addEventListener(MouseEvent.MOUSE_DOWN,mask_startDrag);
    			this.stage.addEventListener(MouseEvent.MOUSE_UP,stage_endDrag);
    
    			if (_slider == null)
    			{
    				_slider = new SimpleSlider(125,180,180);
    				_slider.backWidth = 120;
    				_slider.backHeight = 6;
    				_slider.handleHeight = 16;
    				_slider.addEventListener(Event.CHANGE,slider_Change);
    				addChild(_slider);
    				_slider.x = 320;
    				_slider.y = 205;
    				this._txtMsg.visible = true;
    			}
    		}
    	}
    	else
    	{
    		this._bmp.bitmapData = bmd;
    	}
    	this._btnSave.enabled = true;
    
    	this._txtInfo.text = "拖动左侧方块可调整位置"
    	;
    	this._btnSave.addEventListener(MouseEvent.CLICK,btnSave_Click);
    }
    
    
    function btnSave_Click(e:Event):void
    {
    	//因为视频的大小跟摄像头原始像素的尺寸并不一致(即视频可能缩放过了,所以要用比例调整回最初始的状态)
    	var ratio:Number = this._cam.width / this._bmp.width;
    
    	var imgWidth:int = this._mask.width * ratio;
    	var imgHeight:int = this._mask.height * ratio;
    	//trace(ratio);
    
    	var bmd:BitmapData = new BitmapData(imgWidth,imgHeight,false,0xffff99);
    	bmd.copyPixels(this._bmp.bitmapData,new Rectangle((this._mask.x-260)*ratio,(this._mask.y - 10)*ratio,imgWidth,imgHeight),new Point());
    
    	if (this._bmpTest == null)
    	{
    		this._bmpTest = new Bitmap(bmd);
    		addChild(_bmpTest);
    		_bmpTest.x = 375;
    		_bmpTest.y = 230;
    
    	}
    	else
    	{
    		this._bmpTest.bitmapData = bmd;
    	}
    
    	_bmpTest.width = this._mask.width;
    	_bmpTest.height = this._mask.height;
    	trace("_bmpTest.width=",this._mask.width,",_bmpTest.height=",this._mask.height);
    
    	var jpegEncoder:JPGEncoder = new JPGEncoder();
    	var jpegArr:ByteArray = jpegEncoder.encode(bmd);//压缩成jpeg  
    	
    	trace(jpegArr.length);
    	
    	var base64string:String = Base64.encode(jpegArr);
    	
    	txtBase64.text = base64string;
    	
    	trace(base64string.length);
    
    }
    
    function slider_Change(e:Event):void
    {
    	//trace(e);
    	this._bmp.height = this._slider.value;
    	this._bmp.width = this._bmp.height * 4 / 3;
    }
    
    function mask_startDrag(e:MouseEvent):void
    {
    	this._mask.startDrag(false,new Rectangle(260,10,240-125,180-125));
    
    
    }
    
    function stage_endDrag(e:MouseEvent):void
    {
    	this._mask.stopDrag();
    	//trace("x=",_mask.x);
    	//trace("y=",_mask.y);
    }
    
    function checkCameraComplete(e:TimerEvent):void
    {
    	this._txtInfo.text = "设备无法使用!(有可能被占用)";
    	_timer.removeEventListener(TimerEvent.TIMER, checkCamera);
    	_timer.removeEventListener(TimerEvent.TIMER_COMPLETE, checkCameraComplete);
    	_timer = null;
    }
    

    Flash在线演示(需要电脑上连接好摄像头)

    C#端的处理示例:

    using System;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.IO;
    using System.Web;
    
    namespace OnlinePhotoCapture
    {
        /// <summary>
        /// 将图片base64字符串原还原为图片并调整尺寸后保存(by 菩提树下的杨过http://yjmyzz.cnblogs.com/)   
        /// </summary>    
        public class SavePhoto : IHttpHandler
        {
    
            public void ProcessRequest(HttpContext context)
            {
                context.Response.ContentType = "text/plain";
    
                string base64String = "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";
    
                Image bmp = null;
                MemoryStream ms = null;
                try
                {
                    byte[] b = Convert.FromBase64String(base64String);
                    ms = new MemoryStream(b);
                    bmp = ImageResize(new Bitmap(ms),125,125);
                    bmp.Save(context.Request.MapPath("logo.jpg"), ImageFormat.Jpeg);
                    context.Response.Write("success");
                }
                catch (Exception ex)
                {
                    context.Response.Write("failure:" + ex.Message.ToString());
                }
                finally
                {
                    bmp.Dispose();
                    ms.Dispose();
                }
    
            }
    
           
    
            /// <summary>
            /// 修改图片尺寸
            /// </summary>
            /// <param name="imgSrc"></param>
            /// <returns></returns>
            private Image ImageResize(Image imgSrc,int width,int height)
            {
                Image newImage = imgSrc.GetThumbnailImage(125, 125, null, new IntPtr());
                Graphics g = Graphics.FromImage(newImage);
                g.DrawImage(newImage, 0, 0, newImage.Width, newImage.Height);
                g.Dispose();
                return newImage;
            }
    
    
            public bool IsReusable
            {
                get
                {
                    return false;
                }
            }
    
        }
    }
    
    作者:菩提树下的杨过
    出处:http://yjmyzz.cnblogs.com
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    学习算法必备数学
    Use NDepend to Measure How SOLID Your Code Is
    使用Docker 快速体验TDengine
    ASP.NET Core 修改开源协议为MIT,.NET全平台 MIT协议开源了
    DNS泛域名解析应用(nip.io/sslip.io)
    对象池在 .NET (Core)中的应用[3]: 扩展篇
    对象池在 .NET (Core)中的应用[2]: 设计篇
    对象池在 .NET (Core)中的应用[1]: 编程篇
    项目组织结构的3种类型:职能型、项目型和矩阵型
    [LeetCode] 1208. Get Equal Substrings Within Budget 尽可能使字符串相等
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/1855358.html
Copyright © 2011-2022 走看看