论坛风格切换切换到宽版
  • 26994阅读
  • 0回复

Flash AS3 的人脸识别技术 和 图片打标签 [复制链接]

上一主题 下一主题
 

发帖
1077
只看楼主 倒序阅读 使用道具 楼主  发表于: 2011-07-14
AS3的人脸识别技术
演示 http://activetuts.s3.amazonaws.com/tuts/317_faceRecognition/face_tagger.swf

源码下载 facerecognition.zip (571 K) 下载次数:493

原文如下

The Face Recognition Library can be an incredible library if usedcorrectly. It has the ability not only to detect faces in an image butalso to recognize who the face belongs to in an image. The library’sface detector object crops a single face within an image, but in thistutorial you will learn a quick workaround that will allow you to cropmultiple faces within an image. We’ll create editable tags that a usercan adjust after loading an image into the application.




Final Result Preview

Let’s take a look at the final result we will be working towards:
Click here to view the demo
Upload an image, and the demo will show you which areas the FaceRecognition Library recognises as faces. (It’s not always perfect!)
Since this is just a demo, there are a few restrictions: make sureyou pick a landscape-oriented photo, and don’t pick one with a largefilesize. Of course, when you use this library in your own projects, youcan work around these restrictions.
Need photos to test this on? Try Flickr!




Prerequisites

Download the FaceRecognitionLib.swc from the library’s project page. You will also need the face.zip file from the project’s supervision. Navigate to http://code.google.com/p/face-recognition-library-as3/source/browse/trunk/FaceRecognitionLib/src/face.zip. Click View raw file to download it.
You’ll need the Flex SDK (version 3.5 or later)and must have Flash Professional CS5 configured to target compilationfor Flash Player 10 or later. The last thing you will need is the latestversion of TweenLite, which we’ll be using to add simple animations to our application.




Step 1: Setting Up Flash

Open Flash CS5 Professional and create a new Actionscript 3.0 file.

Set the document class name to FaceTagger.

Click ActionScript Settings in the Properties panel. Click on the Library path tab in the ActionScript Settings window. Browse to the FaceRecognitionLib.swc to add it to the  project. Repeat this process for the greensock.swc file.






Step 2: Add Color to the Stage

Click on the Stage. Change the Stage’s color to a color of your choice.






Step 3: Create the Main Buttons

Open the Components panel. Drag a button onto the top right corner of the stage.

Set the instance name of the button to browseBtn in the Properties panel. Within the Component Parameters panel, change the button’s label to Browse.

Add another button to the stage directly to the left of browseBtn. Change the label of the button to Add Tag and set its instance name to addBtn.





Step 4: Create the Remove Button

When our application detects a face, a tag that will appear over thecropped face will be generated. It will contain a button that willremove the tag when it is clicked. The tag itself will be drawn entirelywith code but we need to draw the button that will remove the tagwithin manually.
Using the Oval tool (press the O key to switch to the oval tool),add an oval to the stage that is 36 pixels tall and 36 pixels wide (youcan alt-click the stage to bring up a dialog for this). The oval shouldhave a stroke of 2.0 and have a White line color and Dark Grey fillcolor as seen below.

Note: In this image, the layers containing our two buttons are locked and hidden. Do not remove them from the stage.

Using the Line tool (press the N key to switch to the line tool),draw a diagonal line on the stage while holding the shift key to givethe line a fixed angle. Paste a copy of the line to the stage. In thetop menu of Flash, select Modify, scroll down to Transfrom and select Flip Horizontal. Align the new line on top of the original line to form an X shape.


Drag both lines onto the oval shape. Select the oval shape along with each line. Press F8 to open the Convert to Symbol window. Set the name of the symbol to RemoveButton and the type to Movie Clip. Make sure the registration is set to the top left and that the folder is set to library root. Under the Advanced options, check Export for ActionScript. The Class field should read RemoveButton and the base class should flash.display.MovieClip. Click OK to create the symbol. An instance of the RemoveButton class doesn’t need to exist at runtime. We will instantiate it when we need to, using code, so you can remove it from the Stage.





Step 5: Our First Code

If you remember from Step 1, the FaceTagger class is our document class. Most of the magic will happen in this class.
Create a new class and name it FaceTagger. The class should extend the flash.display.Sprite class.


Let’s begin by importing the following classes:


  1. package {
  2.     import flash.display.Sprite;
  3.     import flash.events.Event;
  4.     import flash.events.MouseEvent;
  5.     import flash.text.TextField;
  6.     import flash.text.TextFieldType;
  7.     import flash.text.TextFieldAutoSize;
  8.     import flash.display.StageAlign;
  9.     import flash.display.StageScaleMode;
  10.     import flash.net.FileReference;
  11.     import flash.net.FileFilter;
  12.     import flash.geom.Rectangle;
  13.     import flash.display.Loader;
  14.     import flash.display.Bitmap;
  15.     import flash.display.BitmapData;
  16.     import flash.display.MovieClip
  17.     import flash.text.TextFormat;
  18.     import flash.filters.DropShadowFilter;
  19.     public class FaceTagger extends Sprite {
Create the following variables and constants after the class declaration.


  1. private var statusTxt:TextField;
  2. private var fileRef:FileReference;
  3. private var fileFilter:FileFilter;
  4. private var loader:Loader;
  5. private var darkBox:Sprite;
  6. private var bitmap:Bitmap;
  7. private var image:MovieClip;
  8. public static const MIN_WIDTH:Number = 50;
  9. public static const MIN_HEIGHT:Number = 50;
  10. public static const MAX_WIDTH:Number = 1000;
  11. public static const MAX_HEIGHT:Number = 1000;
  12. public static const FILE_TYPES:String = "*.jpg; *.jpeg; *.png";


We need a TextField object to display messages to the user. The FileReference object allows the user to upload an image from the computer and the FileFilter object limits the file types the user can upload. The Loader object is needed to actually parse the image’s data from the FileReference object. The darkBox variable is a Spritethat will be used to darken the screen when a tag is being edited.We’ll do this to add contrast to our application. Doing so will let theuser know that they are in some kind of Edit mode when the screen is dark and the current tag is in focus. Finally we’ll need a Bitmap object to represent the actual image and a MovieClip to contain the bitmap image and all of the image’s tags.
MIN_WIDTH and MIN_HEIGHT represent the minimum demensions that the users image should be in order for the face detection process to occur. MAX_WIDTH and MAX_HEIGHT represent the maximum demensions. FILE_TYPES holds the file types that we will allow the user to choose from when they’re loading an image into our application.
Let’s add some code to the class constructor.

  1. public function FaceTagger() {
  2.     statusTxt = new TextField();
  3.     fileRef = new FileReference();
  4.     fileFilter = new FileFilter( "Image (" + FILE_TYPES + ")", FILE_TYPES );
  5.     loader = new Loader();
  6.     darkBox = new Sprite();
  7.     init();
  8. }

We pass two parameters into the FileFilter contructor. Thefirst parameter is string that contains the description of the filterwhich in this case is an image. We pass the FILE_TYPESconstant within parentheses to display the allowed file types to theuser when they are browsing for an image to load. The second parameteris a String that contains the allowed file types. We pass our FILE_TYPES constant into this parameter as will.  Lasly, we call the init method which we will create later, to initialize the application.




Step 6: Initializing the Display

Let’s create the init method.

  1. private function init():void {
  2.     stage.align = StageAlign.TOP_LEFT;
  3.     stage.scaleMode = StageScaleMode.NO_SCALE;
  4.     stage.addEventListener( Event.RESIZE, onStageResize );
  5.     browseBtn.addEventListener( MouseEvent.CLICK, browse );
  6.     addBtn.addEventListener( MouseEvent.CLICK, addTag );
  7.     fileRef.addEventListener( Event.SELECT, onFileSelected );
  8.     fileRef.addEventListener( Event.COMPLETE, onFileComplete );
  9.     loader.contentLoaderInfo.addEventListener( Event.COMPLETE, detectFaces );
  10.     statusTxt.type = TextFieldType.DYNAMIC;
  11.     statusTxt.selectable = false;
  12.     statusTxt.autoSize = TextFieldAutoSize.CENTER;
  13.     statusTxt.defaultTextFormat = new TextFormat( null, 22, 0xFFFFFF, true );
  14.     statusTxt.text = "Choose an image to upload";
  15.     statusTxt.filters = [ new DropShadowFilter( 5, 45, 0, 1, 5, 5, 1, 3 ) ];
  16.     darkBox.graphics.beginFill( 0, .5 );
  17.     darkBox.graphics.drawRect( 0, 0, stage.stageWidth, stage.stageHeight );
  18.     darkBox.visible = false;
  19.     darkBox.addEventListener( MouseEvent.CLICK, exitEditMode );
  20.     addChild( statusTxt );
  21.     addChild( darkBox );
  22.     positionContents();
  23. }



We initialize the stage by setting its align property to TOP_LEFT and setting its scaleMode property to NO_SCALE. We call the onStageResize method when the stage is resized. We call the browse method, which will allow the user to browse images to load, when browseBtn has been clicked.
The addTag method is called when a user clicks addBtn.We need to listen for when the user selects a file so that we can loadthe file into memory. We also listen for when the file has been loadedand when the Loader object has finished parsing the loaded data into an image.
We initialize our textfield and apply a nice drop shadow filter to add depth to our text. Finally we draw graphics to the darkBox object, display the textfield, and we call the positionContents method which we will now create.

  1. private function positionContents():void {
  2.     browseBtn.x = stage.stageWidth - browseBtn.width - 10;
  3.     addBtn.x = browseBtn.x - addBtn.width - 10;
  4.     statusTxt.x = ( stage.stageWidth - statusTxt.width ) / 2;
  5.     statusTxt.y = stage.stageHeight - statusTxt.height - 10;
  6.     darkBox.width = stage.stageWidth;
  7.     darkBox.height = stage.stageHeight;
  8.     if ( image ) {
  9.         image.x = ( stage.stageWidth - image.width ) / 2;
  10.         image.y = ( stage.stageHeight - image.height ) / 2;
  11.     }
  12. }

The positionContents method is self-explanatory. It positions each element based on the size of the stage. We resize the darkBox object to the size of the stage and only position the image object if it exists.
We want to call the positionContents method every time the stage is resized. Create the method onStageResize to handle this:

  1. private function onStageResize( e:Event ):void {
  2.     positionContents();
  3. }





Step 7: Loading an Image

In order to look for faces within an image, we need an image in our possession. Let’s create the browse method.

  1. private function browse( e:MouseEvent ):void {
  2.     fileRef.browse( [ fileFilter ] );
  3. }


We simply call the FileReference object’s browse method and this opens a native window which allows the user to browse for an image file. We need to apply our FileFilter object to limit the file types that the user can browse. We pass the filter into the first parameter of the browse method within an Array object to accomplish this.
Now the user can browse for an image to load. We’ll have to load thefile into Flash when the user selects a file. For this, let’s create theonFileSelected method. This method is an event handler function that is called when the user selects a file.

  1. private function onFileSelected( e:Event ):void {
  2.     browseBtn.enabled = false;
  3.     statusTxt.text = "loading";
  4.     fileRef.load();
  5. }

We listened for the SELECTED event in the init method. We also listened for the COMPLETE event on the which is dispatched by the contentLoaderInfo object when the image data has been parsed. The onFileComplete method is called when this event occurs. Let’s create this method also.

  1. private function onFileComplete( e:Event ):void {
  2.     loader.loadBytes( fileRef.data );
  3. }

First thing we want to do once a file has been selected is disable the Browse button so that the user can’t load a second file. We display the status to the user and finally we call the load method from the FileReference object. This loads the selected file into memory.
The onFileComplete method simply tells the Loader object to parse the bytes of the loaded file. We previously listened for loader.contentLoaderInfo to dispatch a COMPLETE event. When this event is dispatched the detectFaces method is called. Let’s start coding this method.

  1. private function detectFaces( e:Event ):void {
  2.     bitmap = Bitmap( loader.content );
  3.     addChild( bitmap);
  4. }

For now we will only assign a reference to loader.content to our Bitmap object. We use type casting to tell Flash to treat loader.content as a Bitmap object, and then finally we display the image.




Step 8: Handling the Loaded Image

Before we can start the face detection process, we need to make surethat the demensions of the uploaded image are within a range foraccurate face detection. If an image is to large it can cause ourapplication too crash when we scan the image for faces. If the image istoo tiny, the chances of the FaceDetector object not being able to detect a face within an image that actually has a face increases. We need to set limits here.
Let’s create a static method called inRange.

  1. private static function inRange( width:Number, height:Number ):Boolean {
  2.    if ( width < MIN_WIDTH || width > MAX_WIDTH ) {
  3.         return false;
  4.     }
  5.     else if (  height < MIN_HEIGHT || height > MAX_HEIGHT ) {
  6.         return false;
  7.     }
  8.     else {
  9.         return true;
  10.     }
  11. }

The inRange method returns true if the specified width and height are not out of range. It returns false otherwise. We are going to call this method in the detectFaces method. Remove the line "addChild( bitmap );" from the detectFaces method. Then add the following lines of code.

  1. if ( !inRange( bitmap.width, bitmap.height ) ) {
  2.     if ( !image ) image = new MovieClip();
  3.     image.addChild( bitmap );
  4.     addChildAt( image, 0 );
  5.     image.alpha = 0;
  6.     TweenLite.to( image, 1, { alpha:1 } );
  7.     statusTxt.text = "image too large for face detection";
  8.     return;
  9. }

Also we need to add com.greensock.TweenLite to the class path.

  1. import com.greensock.TweenLite;


We pass the width and height of the Bitmap object into the parameters of the inRange method. If the inRange method returns false, we instantiate the image object, add bitmap to the imageobject and display the image underneath the user interface.  The imagefades in using TweenLite and we display an error message to the userthat the image is out of range. Finally, we exit out of the functionusing a return statement since we don’t want the code that will follow the if statement to execute.
Now that we have checked to see if the image is in a safe range for face detection we can now start detecting faces.




Step 9: Using the FaceDetector Class

Include the following classes in the FaceTagger classpath.

  • com.oskarwicha.images.FaceDetection.FaceDetector
  • com.oskarwicha.images.FaceDetection.Events.FaceDetectorEvent
  1. import com.oskarwicha.images.FaceDetection.FaceDetector;
  2. import com.oskarwicha.images.FaceDetection.Events.FaceDetectorEvent;


Create a new variable underneath the class declaration named detector. The detector object should be of type FaceDetector. The beginning of the FaceTagger class should now look as follows.

  1. package {
  2.     import flash.display.Sprite;
  3.     import flash.events.Event;
  4.     import flash.events.MouseEvent;
  5.     import flash.text.TextField;
  6.     import flash.text.TextFieldType;
  7.     import flash.text.TextFieldAutoSize;
  8.     import flash.display.StageAlign;
  9.     import flash.display.StageScaleMode;
  10.     import flash.net.FileReference;
  11.     import flash.net.FileFilter;
  12.     import flash.geom.Rectangle;
  13.     import flash.display.Loader;
  14.     import flash.display.Bitmap;
  15.     import flash.display.BitmapData;
  16.     import flash.display.MovieClip;
  17.     import flash.text.TextFormat;
  18.     import flash.filters.DropShadowFilter;
  19.     import com.greensock.TweenLite;
  20.     import com.oskarwicha.images.FaceDetection.FaceDetector;
  21.     import com.oskarwicha.images.FaceDetection.Events.FaceDetectorEvent;
  22.     public class FaceTagger extends Sprite {
  23.         private var statusTxt:TextField;
  24.         private var fileRef:FileReference;
  25.         private var fileFilter:FileFilter;
  26.         private var loader:Loader;
  27.         private var bitmap:Bitmap;
  28.         private var image:MovieClip;
  29.         private var darkBox:Sprite;
  30.         private var detector:FaceDetector;
  31.         public static const MIN_WIDTH:Number = 50;
  32.         public static const MIN_HEIGHT:Number = 50;
  33.         public static const MAX_WIDTH:Number = 1000;
  34.         public static const MAX_HEIGHT:Number = 1000;
  35.         public static const FILE_TYPES:String = "*.jpg; *.jpeg; *.bmp; *.png";
  36.         public function FaceTagger() {


Add the following lines of code to the detectFaces method.
  1. statusTxt.text = "detecting faces... please wait";
  2. detector.addEventListener( FaceDetectorEvent.FACE_CROPED, onFacesDetected );
  3. detector.addEventListener( FaceDetectorEvent.NO_FACES_DETECTED, onNoFaces );
  4. detector.loadFaceImageFromBitmap( bitmap );


First we add event listeners to the FaceDetector. There are two events that we need to listen for. The FACE_CROPED event and the NO_FACES_DETECTED event.
(Note: FACE_CROPED is not a typo. It is likely a spelling mistake by the author of the library.)
We want to call the onFacesDetected method whenever a face has been detected and the onNoFaces method whenever no faces were detected on the image. The last line of code loads the face image from our bitmap object containing the loaded image. We call the FaceDetector object’s loadFaceImageFromBitmap method using the bitmap object as the parameter Bitmap that the method calls for.




Step 10: Detecting Multiple Faces

When the FaceDetector object’s objectDetector detects faces in an image, the FaceDetector itself only crops one of those faces. If we want access to all of the detected faces we’ll have to directly access the objectDetector. Create the onFacesDetected method and the onNoFaces method containing the following lines of code.
  1. private function onFacesDetected( e:Event ):void {
  2.     var faces:Array = detector.objectDetector.detected;
  3.     if ( !image ) image = new MovieClip()
  4.     else return;
  5.     image.addChild( bitmap );
  6.     addChildAt( image, 0 );
  7.     positionContents();
  8.     image.alpha = 0;
  9.     TweenLite.to( image, 1, { alpha:1 } );
  10.     statusTxt.text = "";
  11.     for each( var face:Rectangle in faces ) {
  12.         trace( face );
  13.         var rect:Rectangle = face;
  14.         newTag( rect );
  15.     }
  16.     positionContents();
  17. }
  18. private function onNoFaces( e:Event ):void {
  19.     statusTxt.text = "no faces were detected";
  20.     if ( !image ) image = new MovieClip();
  21.     image.addChild( bitmap );
  22.     addChildAt( image, 0 );
  23.     positionContents();
  24.     image.alpha = 0;
  25.     TweenLite.to( image, 1, { alpha:1 } );
  26. }


Let’s take a look at the onFacesDetected method. The FaceDetector object’s objectDetector contains an array of detected objects. These objects are Rectangle objects that each represent the location and size of each cropped face that was successfully found in the image. We assign it to the faces variable which is an Array. If the imageobject doesn’t exist yet we need to instantiate it. If it exists, weneed to exit the function because the code has already executed once. Weexecute the same code the we did in the detectFaces method to display the image using a fade in animation. Then we clear the textfield.
The for each loop is used to iterate through each Rectangle in the faces array. When traced to the output panel, a detected face will look something like this:
  1. (x=30, y=30, w=123, h=123)


The newTag method generates a new PhotoTag object to display on the image based on the parameter Rectangleobject supplied. Before we can add a tag to the display, we’ll need tofirst create our tag class. We’ll create this class in the next step.In the onNoFaces method we repeat the process but thistime after we display the image, we notify the user that no faces weredetected in the loaded image.




Step 11: Begin the PhotoTag Class

Create a new class named PhotoTag that extends Sprite.

Import the following classes to the PhotoTag class.
  1. import flash.display.Sprite;
  2. import flash.geom.Rectangle;
  3. import flash.events.Event;
  4. import flash.display.MovieClip;
  5. import flash.filters.DropShadowFilter;
  6. import flash.events.MouseEvent;
  7. import flash.events.KeyboardEvent;
  8. import flash.ui.Keyboard;
  9. import com.greensock.TweenLite;

Create the following variables and constants.
  1. private var _rectangle:Rectangle;
  2. private var _image:MovieClip;
  3. private var _editMode:Boolean;
  4. private var _removed:Boolean;
  5. private var removeBtn:RemoveButton;
  6. public static const LINE_THICKNESS:Number = 6;
  7. public static const LINE_COLOR:uint = 0x00CCFF;
  8. public static const FILL_COLOR:uint = 0xFFFFFF;
  9. public static const ELLIPSE_WIDTH:Number = 10;
  10. public static const ELLIPSE_HEIGHT:Number = 10;
  11. public static const INIT_ALPHA:Number = .6;

Add the following code to the class contructor.
  1. public function PhotoTag( rectangle:Rectangle, image:MovieClip ) {
  2.     _rectangle = rectangle;
  3.     _image = image;
  4.     removeBtn = new RemoveButton();
  5.     graphics.lineStyle( LINE_THICKNESS, LINE_COLOR );
  6.     graphics.beginFill( FILL_COLOR, .5 );
  7.     graphics.drawRoundRect( 0, 0, rectangle.width, rectangle.height, ELLIPSE_WIDTH, ELLIPSE_HEIGHT );
  8.     filters = [ new DropShadowFilter( 5, 45, 0, 1, 5, 5, 1, 3 ) ];
  9.     alpha = INIT_ALPHA;
  10.     _editMode = false;
  11.     addEventListener( Event.ADDED, onAdded );
  12. }

The class contructor accepts two parameters:

  • The first parameter is the rectangle object that contains data about the cropped face.
  • The second parameter is the MovieClip that contains the uploaded bitmap image.
We assign these to variables so that we can reference them throughout our code.
On the next line a new instance of the RemoveButton class is created. Use the inherited graphics object to draw a rounded rectangle that is the size of the Rectangle object’s width and height.The rectangle’s ellipse width and height come from the class constantsas well as  the rectangle’s line attributes. We apply a drop shadowfilter to add depth to the object and set the alpha to the constant INIT_ALPHA (the initial alpha). _editMode is a property that tells us whether or not the tag is in an editable state.
The last line in the contructor listens for the ADDED event then calls the onAdded method once the tag has been added to the display list. We do this because we’ll need to access the stage object which is currently null. Once the object has been added we can safely access the stage.
  1. private function onAdded( e:Event ):void {
  2.     removeEventListener( Event.ADDED, onAdded );
  3.     init();
  4. }
The onAdded method is called when the object is added to the display list. The event listener is no longer needed so it is removed and the init method is called.




Step 12: Initializing the Photo Tag

Create the init method in the PhotoTag class.
  1. private function init():void {
  2.     if ( this.parent != image ) _image.addChild( this );
  3.     x = _rectangle.x;
  4.     y = _rectangle.y;
  5.     addChild( removeBtn );
  6.     removeBtn.alpha = 0;
  7.     doubleClickEnabled = true;
  8.     addEventListener( MouseEvent.ROLL_OVER, overState );
  9.     addEventListener( MouseEvent.MOUSE_DOWN, onMouseDown );
  10.     stage.addEventListener( MouseEvent.MOUSE_WHEEL, onMouseWheel );
  11.     stage.addEventListener( KeyboardEvent.KEY_DOWN, onKeyDownE );
  12.     removeBtn.addEventListener( MouseEvent.CLICK, remove );
  13. }


The first line of code in the init method checks to see whether the tag was actually added to the image object. If it wasn’t, the tag will add itself to the image.
Next the tag’s x and y are set to the rectangle‘s x and y. This simply positions the tag over the cropped face. By default, the removeBtn‘s alpha property is set to 0 because we don’t want it to be visible just yet. Later we will need to listen for a DOUBLE_CLICK event and the tag’s doubleClickEnabled must be set to true in order for this event to be dispatched.
The last five lines of code add event listeners to the tag, to the stage, and to the removeBtn.
When the removeBtn is clicked we want to remove the tag from the display list. Create the remove method.
  1. private function remove( e:MouseEvent ):void {
  2.     if ( this.parent ) {
  3.         _removed = true;
  4.         this.parent.removeChild( this );
  5.     }
  6. }

The method removes the tag from its parent object, if it has one. We set the _removed property to true since the object is being removed. The _removed property is a private property butit needs to be accessed outside of the scope of this class. We onlywant to grant code, outside of the class, access to read the properties data. We also want to grant the same limited access to the _rectangle object, the _image object, and the _editMode property. Create the following getter methods.
  1. public function get rectangle():Rectangle {
  2.     return _rectangle;
  3. }
  4. public function get image():MovieClip {
  5.     return _image;
  6. }
  7. public function get editMode():Boolean {
  8.     return _editMode;
  9. }
  10. public function get removed():Boolean {
  11.     return _removed;
  12. }
Now code outside of this class can access these objects through thegetter methods. The methods can be treated as properties, so eachproperty is effectively read only.




Step 13: Adding Life to the PhotoTag Object

If we want our PhotoTag objects to have a little life in them we’ll need to add some interaction and animation. Add the following methods to the PhotoTag class.
  1. private function resize( percent:Number ):void {
  2.     var newWidth:Number = ( percent * _rectangle.width ) / 100 + _rectangle.width;
  3.     var newHeight:Number = ( percent * _rectangle.height ) / 100 + _rectangle.height;
  4.     if ( newWidth < 60 ) {
  5.         newWidth = 60;
  6.     }
  7.     if ( newHeight < 60 ) {
  8.         newHeight = 60;
  9.     }
  10.     var newX:Number = ( x - image.x ) - ( newWidth - _rectangle.width ) / 2;
  11.     var newY:Number = ( y - image.y ) - ( newHeight - _rectangle.height ) / 2;
  12.     _rectangle.x = newX;
  13.     _rectangle.y = newY;
  14.     _rectangle.width = newWidth;
  15.     _rectangle.height = newHeight;
  16.     draw( newWidth, newHeight );
  17.     x = newX + image.x;
  18.     y = newY + image.y;
  19. }
  20. private function overState( e:MouseEvent ):void {
  21.     if ( _editMode ) return;
  22.     removeEventListener( MouseEvent.ROLL_OVER, outState );
  23.     removeBtn.visible = true;
  24.     removeBtn.alpha = 0;
  25.     var percent:Number = 40;
  26.     var newWidth:Number = ( percent * _rectangle.width ) / 100 + _rectangle.width;
  27.     var newHeight:Number = ( percent * _rectangle.height ) / 100 + _rectangle.height;
  28.     var newX:Number = _rectangle.x - ( newWidth - _rectangle.width ) / 2;
  29.     var newY:Number = _rectangle.y - ( newHeight - _rectangle.height ) / 2;
  30.     TweenLite.to( this, .1, { alpha:1, x:newX, y:newY, width:newWidth, height:newHeight } );
  31.     TweenLite.to( removeBtn, .1, { alpha:1 } );
  32.     addEventListener( MouseEvent.ROLL_OUT, outState );
  33. }
  34. private function outState( e:MouseEvent ):void {
  35.     if ( _editMode ) return;
  36.     removeEventListener( MouseEvent.ROLL_OUT, outState );
  37.     TweenLite.to( this, .1, { alpha:INIT_ALPHA, x:_rectangle.x, y:_rectangle.y, width:_rectangle.width, height:_rectangle.height } );
  38.     TweenLite.to( removeBtn, .1, { alpha:1 } );
  39.     addEventListener( MouseEvent.ROLL_OVER, overState );
  40. }
  41. private function onMouseWheel( e:MouseEvent ):void {
  42.     if ( !_editMode ) return;
  43.     var percent:Number = e.delta * 10;
  44.     resize( percent );
  45. }
  46. private function onKeyDownE( e:KeyboardEvent ):void {
  47.     if ( !_editMode ) return;
  48.     var percent:Number;
  49.     if ( e.keyCode == Keyboard.UP ) {
  50.         percent = 10;
  51.     }
  52.     else if ( e.keyCode == Keyboard.DOWN ) {
  53.         percent = -10;
  54.     }
  55.     else {
  56.         return;
  57.     }
  58.     resize( percent );
  59. }

It may look like a lot of code but what it actually accomplishes isquite simple; all of the methods basically execute the same code. Earlier we added an event listener for the ROLL_OVER event. We passed an event handler function to the eventHandler parameter called overState.This method adds a simple scale effect when the mouse hovers over thetag. The new width and height of the object is based on how much we wantto increase the object’s size by. In this case we want to increase theobject’s size by 40 percent.
We could have stopped right here and simply used TweenLite to animatethe object being resized but this wouldn’t look very appealing becausethe object’s registration is in the top left corner. What we really wantto do is transform the object from its center instead. If you are aClub Greensock member then you could just use the transformAroundCenterplugin to accomplish this with just one line of code and without havingto make the calculations yourself, but for the rest of us we’ll need aquick workaround.
We calculate the new x position based on the new width and wecalculate the new y position based on the new height. Then we tween the x, y, width, and height properties to their new values. We also fade in the removeBtn and make the tag fully opaque.
We do the complete opposite in the outState method which is called everytime the mouse hovers off of the tag. The tag is tweened back to its original state.
The resize method accomplishes what the overState method accomplishes without affecting the alpha property and without any tweening. The resizemethod is called every time we want to resize the tag dynamically. Themethod takes one parameter which is the percentage to increase ordecrease the tag’s size by. The method adds a minimum size limit to thetag so that it does not become to small.
The onMouseWheel method is called whenever a user interacts with the wheel on their mouse. The onKeyDownE method is called everytime a key is pressed. Each method only executes code if the tag is currently in edit mode. And each method resizes the tag based on specific properties. The onMouseWheel method resizes the tag based on the MouseEvent‘s delta property’s value. The onKeyDownE increases the tag’s size when the Up key is pressed and decreases the tags size when the Down key is pressed.
Their is one last thing we need to fix in order to make our tag animate correctly. TweenLite changes the value of the width and heightproperties on each frame until the animation is complete. When youresize objects in Flash, the can become distorted. We don’t want ourtags looking distorted. To fix this problem we’ll need to override the set width and set height methods from the parent class
  1. public override function set width( value:Number ):void {
  2.     draw( value, height );
  3. }
  4. public override function set height( value:Number ):void {
  5.     draw( width, value );
  6. }


Whenever the width or height of the tag is set, we want to redraw the tag’s graphics. Create the draw method.
  1. private function draw( newWidth:Number, newHeight:Number ):void {
  2.     graphics.clear();
  3.     graphics.lineStyle( LINE_THICKNESS, LINE_COLOR );
  4.     graphics.beginFill( FILL_COLOR, .5 );
  5.     graphics.drawRoundRect( 0, 0, newWidth, newHeight, ELLIPSE_WIDTH, ELLIPSE_HEIGHT );
  6. }


The draw method should accept two parameters: the newwidth of the tag and the new height of the tag. The graphics are clearedthen redrawn again based on the newWidth and newHeight parameters. Now our tag will animate beautifully.




Step 14: Entering and Exiting Edit Mode

We want our users to be able to edit each tag individually. When atag is in edit mode, the user can manipulate the tag’s size andreposition the tag on the image. Several things need be done toaccomplish this.
First, let’s create the enterEditMode and exitEditMode methods. Both of these methods will be public methods as we will need to call them from the document class.
  1. public function enterEditMode():void {
  2.     if ( !_editMode ) {
  3.         _editMode = true;
  4.         removeEventListener( MouseEvent.ROLL_OUT, outState );
  5.         draw( _rectangle.width, _rectangle.height );
  6.     }
  7. }
  8. public function exitEditMode():void {
  9.     if ( _editMode && !removed ) {
  10.         _editMode = false;
  11.         image.addChild( this );
  12.         x = _rectangle.x;
  13.         y = _rectangle.y;
  14.         stopDrag();
  15.         alpha = INIT_ALPHA;
  16.         TweenLite.to( removeBtn, .1, { alpha:0 } );
  17.         addEventListener( MouseEvent.ROLL_OVER, overState );
  18.     }
  19. }

The enterEditMode method executes the block of code within the if statement if the tag is currently not in edit mode. If the tag is not in edit mode the tag will enter edit mode, setting _editMode to true to allow the user to change the tag’s properties. The ROLL_OUT event listener is removed as we don’t wan’t the method to accidently execute if the mouse hovers off of the tag. Finally, the draw method is called to display the original size of the tag.The exitEditMode method sets the tag back to its initial state while positioning the tag at the _rectangle‘s new x and y position. We call the stopDrag method just in case the tag was being dragged by the user when edit mode has been exited.




Step 15: Creating a New Tag

Now let’s go back to our Document class, FaceTagger. Finally we can create the newTag method now that we have a PhotoTag class. Create the newTag method.
  1. private function newTag( rectangle:Rectangle ):void {
  2.     var tag:PhotoTag = new PhotoTag( rectangle, image );
  3.     image.addChild( tag );
  4.     tag.addEventListener( MouseEvent.ROLL_OVER, onTagRollOver );
  5.     tag.addEventListener( MouseEvent.DOUBLE_CLICK, enterEditMode );
  6.     tag.addEventListener( Event.REMOVED, onTagRemoved );
  7. }

The method creates a new PhotoTag object passing the rectangle parameter and the current image into the PhotoTagcontructor method. The tag is added to the current image. We listen forseveral events that the tag will dispatch. The first event is the ROLL_OVER event. A method called onTagRollOver is called when the event is dispatched. We listen for the DOUBLE_CLICK event so that we can call the enterEditMode method, which will be different from the enterEditMode method from the PhotoTag class, and finally we listen for the REMOVED event that calls the onTagRemoved method whenever the tag is removed from its parent object.Create an event handler method named onTagRollOver.
[code]private function onTagRollOver( e:MouseEvent ):void {

    if ( !e.target.editMode ) statusTxt.text = "double-click to edit";
}[/code]
The method is very simple, as you can see. The method displays amessage to the user if the tag that the mouse is hovering over is not inedit mode. The message notifies the user that if the double-clicks on the current tag, then the tag will enter edit mode.




Step 16: Granting the User Access to Edit

We now need to allow the user access to edit each tag. Create a new variable underneath the class delclaration of the FaceTagger class and name it currentTag. It should be a PhotoTag object. The beginning of the document class should now look like so:
[code]public class FaceTagger extends Sprite {

        private var statusTxt:TextField;
        private var fileRef:FileReference;
        private var fileFilter:FileFilter;
        private var loader:Loader;
        private var bitmap:Bitmap;
        private var image:MovieClip;
        private var darkBox:Sprite;
        private var detector:FaceDetector;
        private var currentTag:PhotoTag;

        public static const MIN_WIDTH:Number = 50;
        public static const MIN_HEIGHT:Number = 50;
        public static const MAX_WIDTH:Number = 1000;
        public static const MAX_HEIGHT:Number = 1000;
        public static const FILE_TYPES:String = "*.jpg; *.jpeg; *.bmp; *.png";

        public function FaceTagger() {[/code]
Create the enterEditMode method within the FaceTagger class. This method is the event handler method that we called upon in the newTag method.
[code]private function enterEditMode( e:MouseEvent ):void {

    statusTxt.text = "click the background to exit 'edit' mode\n";
    statusTxt.appendText( "use the mouse wheel or the up and down\n" );
    statusTxt.appendText( "arrow keys to resize the tag" );

    var tag:PhotoTag = e.target as PhotoTag;
    darkBox.visible = true;
    addChild( darkBox );
    addChild( tag );
    addChild( statusTxt );
    tag.x = image.x + tag.rectangle.x;
    tag.y = image.y + tag.rectangle.y;
    tag.enterEditMode();
    currentTag = tag;
    positionContents();
}[/code]
The method above notifies the user that they are currently in edit mode. The screen is darkened and the tag is removed from the image object and placed on the stage. The tag’s x and yproperties have to be set to values that are relevant to the image’sposition on the stage. If we didn’t do this the tag would just sit inthe top left hand corner of the screen. When this method is called, itwon’t even seem as if the tags position has changed at all but inreality it has changed quite a bit.Finally we call the tag objects’s enterEditMode method, assign a reference to the tag object to the currentTag variable and position the contents of the screen (particularly the statusTxt textfield).




Step 17: Saving the State of the Current Tag

After the user has finished editing the currentTag, the tag needs to be added back to the image object and correctly positioned. Create the exitEditMode method within the FaceTagger class.
[code]private function exitEditMode( e:MouseEvent = null ):void {

    darkBox.visible = false;

    if ( currentTag ) {

        statusTxt.text = " ";
        currentTag.rectangle.x = currentTag.x - image.x;
        currentTag.rectangle.y = currentTag.y - image.y;
        currentTag.exitEditMode();
        currentTag = null;
        positionContents();
    }
}[/code]
Notice how the MouseEvent parameter is set to null by default. We are going to call this method from the onTagRemoved event handler method later and we won’t have a MouseEvent object to pass as the method’s parameter so we give the parameter a null default value. This makes the method more accessible.The first task the method performs is hiding the darkBox object to give the user a sense of exiting edit mode. The block of code within the if statement executes if the currentTag exists. The textfield is set to a single space (" "). Remember that the textfield has its autoSizeproperty set to resize from its center. To get the textfield toposition itself correctly, we need to make sure that there is some kindof text present so that the textfield doesn’t resize itself down tonothing. This can cause a potential problem if the user exits edit mode then hovers over a tag. The effect caused is that the  textfield may appear off screen.
Next, the currentTag‘s rectangle object is given new x and y property values. These values are based on the tag’s current position on the stage relative to the image‘s position on the stage. When the currentTag‘s exitEditMode method is called the currentTag is added back to the image object and positioned to the rectangle‘s new x and y position.
Last, we set the reference of the currentTag to null and position the contents on the screen.
Create the onTagRemoved method.
[code]private function onTagRemoved( e:Event ):void {

    if ( e.target.removed ) {  

        exitEditMode();
    }
}[/code]
This method exits edit mode if the remove method of the targeted tag (e.target) has been called. Thus if the user has clicked the remove button on the targeted tag, we exit editMode. Without this method, if the user removed a tag while in edit mode, the screen would remain dark.




Step 18: Repositioning a Tag

Return to the PhotoTag class and add the following methods to the class
[code]private function onMouseDown( e:MouseEvent ):void {

    if ( !_editMode ) return;
    removeEventListener( MouseEvent.MOUSE_DOWN, onMouseDown );
    var thisRect:Rectangle = image.getRect( this );
    startDrag( false, new Rectangle( image.x, image.y, thisRect.width - _rectangle.width, thisRect.height - _rectangle.height ) );
    addEventListener( MouseEvent.MOUSE_UP, onMouseUp );
}

private function onMouseUp( e:MouseEvent ):void {

    if ( !_editMode ) return;
    removeEventListener( MouseEvent.MOUSE_UP, onMouseUp );
    stopDrag();
    addEventListener( MouseEvent.MOUSE_DOWN, onMouseDown );
}[/code]Both methods require that the tag be in edit mode to execute completely. The onMouseUp method starts dragging the tag and limits the tag to only drag within the _image object. The onMouseUp method stops the dragging process.




Step 19: Adding Additional Tags

By now you’re probably wondering what happened to addBtn. When this button is clicked a new PhotoTag object is added to the current image. Let’s create the addTag method in the document class.
Note: You should remember this method from Step 6. It is themethod that was passed as the eventhandler parameter when we added anevent listener that listens for a CLICK event to be dispatched from the addBtn object.
[code]private function addTag( e:MouseEvent ):void {

    if ( image ) {

        var rect:Rectangle = new Rectangle( 0, 0, 75, 75 );
        newTag( rect );
    }
}[/code]
If image exists, we call the newTag method as before. We create a new made up Rectangle object so that the user can customize the tag on their own. The tag is simply added to the image in the top left corner.




Step 20: Improving the Appearence of the RemoveButton Object

As we have seen, you can dramatically improve an object’s appearance with a few lines of code. Let’s improve the look of the RemoveButton object. Create a new class and name it RemoveButton. The class will have to extend the MovieClip class since it is linked to a library symbol of that type.

Save the class to the root folder. Import the following within RemoveButton:
[code]import flash.display.MovieClip;
import flash.filters.DropShadowFilter;
import flash.filters.BevelFilter;[/code]
All we’re going to do now is add a simple line of code within the RemoveButton object’s class constructor method.
[code]filters = [ new DropShadowFilter( 5, 45, 0, 1, 5, 5, 1, 3 ), new BevelFilter( 10, 90, 0xF5E1AF, .6, 0, 1, 15, 15, 1, 3 ) ];[/code]
We have just added a DropShadowFilter and a BevelFilter to the RemoveButton object. This will add a great deal of depth to our button.




Step 21: Testing Our Application

At this point, if we test our application, everything should runsmoothly. But we will encounter a problem when we upload an image andthe face detection process begins. If you do not put the face.zip file in the same directory as the SWF, the face detection process can not occur. It is very important to remember to do this. Once you have placed face.zip into the same directory as the SWF, you may then run the application.





Conclusion

Please take into consideration that the Face Recognition Library isn’tperfect. There will be times where it won’t detect any faces even thoughthere are faces within the image that was uploaded. There will also betimes where only some faces in an image are detected and times wherepatterns that look like faces (e.g., A cloud shaped like a face) will bedetected as faces. This is why we allow for our users to edit tags.Generally, I found the library pretty amazing and I hope you will too.This concludes my tutorial. Thank you for reading.
快速回复
限100 字节
如果您提交过一次失败了,可以用”恢复数据”来恢复帖子内容
 
上一个 下一个