Библиотека распознавания лиц может быть невероятной библиотекой при правильном использовании. Он обладает способностью не только обнаруживать лица на изображении, но и распознавать, кому принадлежит лицо на изображении. Объект детектора лиц библиотеки обрезает одно лицо на изображении, но в этом уроке вы узнаете быстрый обходной путь, который позволит вам обрезать несколько лиц на изображении. Мы создадим редактируемые теги, которые пользователь может настроить после загрузки изображения в приложение.
Окончательный результат предварительного просмотра
Давайте посмотрим на конечный результат, к которому мы будем стремиться:
Нажмите здесь, чтобы посмотреть демо
Загрузите изображение, и демоверсия покажет вам, какие области Библиотека распознавания лиц распознает как лица. (Это не всегда идеально!)
Поскольку это всего лишь демонстрационная версия, есть несколько ограничений: убедитесь, что вы выбрали альбомную фотографию, а не одну с большим размером файла. Конечно, когда вы используете эту библиотеку в своих собственных проектах, вы можете обойти эти ограничения.
Нужны фотографии, чтобы проверить это? Попробуйте Flickr !
Предпосылки
Загрузите FaceRecognitionLib.swc со страницы проекта библиотеки. Вам также понадобится файл face.zip от руководства проекта. Перейдите на страницу http://code.google.com/p/face-recognition-library-as3/source/browse/trunk/FaceRecognitionLib/src/face.zip . Нажмите View raw file, чтобы загрузить его.
Вам понадобится Flex SDK (версия 3.5 или более поздняя) и должен быть настроен Flash Professional CS5 для целевой компиляции для Flash Player 10 или более поздней версии. Последнее, что вам нужно, это последняя версия TweenLite , которую мы будем использовать для добавления простых анимаций в наше приложение.
Шаг 1: Настройка Flash
Откройте Flash CS5 Professional и создайте новый файл Actionscript 3.0.
Установите имя класса документа в FaceTagger .
Нажмите « Параметры ActionScript» на панели « Свойства» . Нажмите на вкладку « Путь к библиотеке » в окне « Параметры ActionScript» . Перейдите к FaceRecognitionLib.swc, чтобы добавить его в проект. Повторите этот процесс для файла greensock.swc .
Шаг 2. Добавьте цвет на сцену
Нажмите на сцену. Измените цвет сцены на цвет по вашему выбору.
Шаг 3: Создайте главные кнопки
Откройте панель « Компоненты» . Перетащите кнопку в верхний правый угол сцены.
Установите имя экземпляра кнопки browseBtn
на панели « Свойства» . На панели « Параметры компонента» измените метку кнопки на « Обзор» .
Добавьте еще одну кнопку на сцену прямо слева от browseBtn
. Измените метку кнопки на « Добавить тег» и установите имя ее экземпляра на « addBtn
.
Шаг 4. Создайте кнопку «Удалить»
Когда наше приложение обнаружит лицо, будет сгенерирован тег, который появится над обрезанным лицом. Он будет содержать кнопку, которая удалит тег при нажатии. Сам тег будет нарисован полностью с кодом, но нам нужно нарисовать кнопку, которая удалит тег внутри вручную.
Используя инструмент « Овал» ( нажмите клавишу «O», чтобы переключиться на инструмент «Овал» ), добавьте овал к сцене высотой 36 пикселей и шириной 36 пикселей (вы можете щелкнуть по сцене щелчком мыши, чтобы вызвать диалоговое окно для этого). У овала должен быть штрих 2,0, цвет белой линии и темно-серый цвет заливки, как показано ниже.
Примечание. На этом изображении слои, содержащие две наши кнопки, заблокированы и скрыты. Не удаляйте их со сцены.
Используя инструмент « Линия» (нажмите клавишу N, чтобы переключиться на инструмент «Линия») , нарисуйте диагональную линию на сцене, удерживая клавишу Shift, чтобы придать линии фиксированный угол. Вставьте копию линии на сцену. В верхнем меню Flash выберите « Modify» , прокрутите вниз до « Transfrom» и выберите « Flip Horizontal» . Выровняйте новую линию поверх исходной линии, чтобы сформировать форму X.
Перетащите обе линии на овальную форму. Выберите овальную форму вместе с каждой линией. Нажмите клавишу F8, чтобы открыть окно « Преобразовать в символ» . Установите имя символа на RemoveButton и тип на Movie Clip . Убедитесь, что регистрация установлена в верхнем левом углу и что папка установлена в корневой каталог библиотеки . В разделе « Дополнительные параметры» выберите « Экспорт для ActionScript» . Поле Class должно иметь значение RemoveButton, а базовый класс — flash.display.MovieClip
. Нажмите OK, чтобы создать символ. Экземпляр класса RemoveButton
не должен существовать во время выполнения. Мы создадим его, когда потребуется, используя код, чтобы вы могли удалить его из Stage
.
Шаг 5: наш первый код
Если вы помните из шага 1, класс FaceTagger — это наш класс документов. Большая часть магии случится в этом классе.
Создайте новый класс и назовите его FaceTagger . Класс должен расширять класс flash.display.Sprite
.
Начнем с импорта следующих классов:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.text.TextFieldType;
import flash.text.TextFieldAutoSize;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.net.FileReference;
import flash.net.FileFilter;
import flash.geom.Rectangle;
import flash.display.Loader;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.MovieClip
import flash.text.TextFormat;
import flash.filters.DropShadowFilter;
public class FaceTagger extends Sprite {
|
Создайте следующие переменные и константы после объявления класса.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
private var statusTxt:TextField;
private var fileRef:FileReference;
private var fileFilter:FileFilter;
private var loader:Loader;
private var darkBox:Sprite;
private var bitmap:Bitmap;
private var image:MovieClip;
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; *.png»;
|
Нам нужен объект TextField
для отображения сообщений пользователю. Объект FileReference
позволяет пользователю загружать изображение с компьютера, а объект FileFilter
ограничивает типы файлов, которые пользователь может загружать. Объект Loader
необходим для фактического анализа данных изображения из объекта FileReference
. Переменная darkBox
— это darkBox
который будет использоваться для затемнения экрана при редактировании тега. Мы сделаем это, чтобы добавить контраст к нашему приложению. Это позволит пользователю узнать, что он находится в каком-то режиме редактирования, когда экран темный и текущий тег находится в фокусе. Наконец, нам понадобится Bitmap
объект для представления фактического изображения и MovieClip
содержащий растровое изображение и все теги изображения.
MIN_WIDTH
и MIN_HEIGHT
представляют минимальные измерения, которыми должно быть изображение пользователя, чтобы процесс обнаружения лица происходил. MAX_WIDTH
и MAX_HEIGHT
представляют максимальные размеры. FILE_TYPES
содержит типы файлов, которые мы позволим пользователю выбирать при загрузке изображения в наше приложение.
Давайте добавим немного кода в конструктор класса.
1
2
3
4
5
6
7
8
9
|
public function FaceTagger() {
statusTxt = new TextField();
fileRef = new FileReference();
fileFilter = new FileFilter( «Image (» + FILE_TYPES + «)», FILE_TYPES );
loader = new Loader();
darkBox = new Sprite();
init();
}
|
Мы передаем два параметра в конструктор FileFilter
. Первый параметр — это строка, которая содержит описание фильтра, который в данном случае является изображением. Мы FILE_TYPES
константу FILE_TYPES
в скобках для отображения разрешенных типов файлов пользователю, когда он просматривает изображение для загрузки. Второй параметр — это String
, содержащая разрешенные типы файлов. Мы передаем нашу константу FILE_TYPES
в этот параметр как воля. Ласли, мы вызываем метод init
который создадим позже, для инициализации приложения.
Шаг 6: Инициализация дисплея
Давайте создадим метод init
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
private function init():void {
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.addEventListener( Event.RESIZE, onStageResize );
browseBtn.addEventListener( MouseEvent.CLICK, browse );
addBtn.addEventListener( MouseEvent.CLICK, addTag );
fileRef.addEventListener( Event.SELECT, onFileSelected );
fileRef.addEventListener( Event.COMPLETE, onFileComplete );
loader.contentLoaderInfo.addEventListener( Event.COMPLETE, detectFaces );
statusTxt.type = TextFieldType.DYNAMIC;
statusTxt.selectable = false;
statusTxt.autoSize = TextFieldAutoSize.CENTER;
statusTxt.defaultTextFormat = new TextFormat( null, 22, 0xFFFFFF, true );
statusTxt.text = «Choose an image to upload»;
statusTxt.filters = [ new DropShadowFilter( 5, 45, 0, 1, 5, 5, 1, 3 ) ];
darkBox.graphics.beginFill( 0, .5 );
darkBox.graphics.drawRect( 0, 0, stage.stageWidth, stage.stageHeight );
darkBox.visible = false;
darkBox.addEventListener( MouseEvent.CLICK, exitEditMode );
addChild( statusTxt );
addChild( darkBox );
positionContents();
}
|
Мы инициализируем stage
, устанавливая его свойство align
в TOP_LEFT и устанавливая его свойство scaleMode в NO_SCALE . Мы вызываем метод onStageResize
при изменении размера stage
. Мы вызываем метод browse
, который позволит пользователю просматривать изображения для загрузки, когда browseBtn
.
Метод addTag
вызывается, когда пользователь нажимает addBtn
. Нам нужно слушать, когда пользователь выбирает файл, чтобы мы могли загрузить файл в память. Мы также слушаем, когда файл был загружен и когда объект Loader
завершил анализ загруженных данных в изображение.
Мы инициализируем наше текстовое поле и применяем хороший фильтр тени, чтобы добавить глубину нашему тексту. Наконец, мы рисуем графику для объекта darkBox
, отображаем текстовое поле и вызываем метод positionContents
который мы сейчас создадим.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
private function positionContents():void {
browseBtn.x = stage.stageWidth — browseBtn.width — 10;
addBtn.x = browseBtn.x — addBtn.width — 10;
statusTxt.x = ( stage.stageWidth — statusTxt.width ) / 2;
statusTxt.y = stage.stageHeight — statusTxt.height — 10;
darkBox.width = stage.stageWidth;
darkBox.height = stage.stageHeight;
if ( image ) {
image.x = ( stage.stageWidth — image.width ) / 2;
image.y = ( stage.stageHeight — image.height ) / 2;
}
}
|
Метод positionContents
не требует пояснений. Он позиционирует каждый элемент в зависимости от размера сцены. Мы darkBox
размер объекта darkBox
до размера stage
и darkBox
объект image
только в том случае, если он существует.
Мы хотим вызывать метод positionContents
каждый раз, когда размер stage
изменяется. Создайте метод onStageResize
для обработки этого:
1
2
3
4
|
private function onStageResize( e:Event ):void {
positionContents();
}
|
Шаг 7: Загрузка изображения
Чтобы искать лица на изображении, нам нужно изображение, которым мы располагаем. Давайте создадим метод browse
.
1
2
3
4
|
private function browse( e:MouseEvent ):void {
fileRef.browse( [ fileFilter ] );
}
|
Мы просто вызываем метод browse
объекта FileReference
, и это открывает собственное окно, которое позволяет пользователю искать файл изображения. Нам нужно применить наш объект FileFilter
чтобы ограничить типы файлов, которые пользователь может просматривать. Для этого мы передаем фильтр в первый параметр метода browse
в объекте Array
.
Теперь пользователь может просматривать изображение для загрузки. Мы должны будем загрузить файл во Flash, когда пользователь выберет файл. Для этого давайте создадим метод onFileSelected
. Этот метод является функцией обработчика событий, которая вызывается, когда пользователь выбирает файл.
1
2
3
4
5
6
|
private function onFileSelected( e:Event ):void {
browseBtn.enabled = false;
statusTxt.text = «loading»;
fileRef.load();
}
|
Мы прослушали событие SELECTED
в методе init
. Мы также прослушали событие COMPLETE
события, которое отправляется объектом contentLoaderInfo
когда данные изображения были проанализированы. Метод onFileComplete
вызывается, когда происходит это событие. Давайте создадим этот метод также.
1
2
3
4
|
private function onFileComplete( e:Event ):void {
loader.loadBytes( fileRef.data );
}
|
Первое, что мы хотим сделать после выбора файла, — отключить кнопку « Обзор» , чтобы пользователь не мог загрузить второй файл. Мы отображаем статус для пользователя и, наконец, вызываем метод load
из объекта FileReference
. Это загружает выбранный файл в память.
Метод onFileComplete
просто сообщает объекту Loader
для анализа байтов загруженного файла. Ранее мы прослушивали loader.contentLoaderInfo
для отправки события COMPLETE
. Когда отправляется это событие, detectFaces
метод detectFaces
. Давайте начнем кодировать этот метод.
1
2
3
4
5
|
private function detectFaces( e:Event ):void {
bitmap = Bitmap( loader.content );
addChild( bitmap);
}
|
На данный момент мы только назначим ссылку на loader.content
для нашего объекта Bitmap
. Мы используем приведение типов, чтобы указать Flash обрабатывать loader.content
как объект Bitmap
, а затем, наконец, отображаем изображение.
Шаг 8: Обработка загруженного изображения
Прежде чем мы сможем начать процесс обнаружения лица, мы должны убедиться, что размеры загруженного изображения находятся в пределах диапазона для точного обнаружения лица. Если изображение слишком большое, это может привести к аварийному завершению работы нашего приложения при сканировании изображения на наличие лиц. Если изображение слишком маленькое, вероятность того, что объект FaceDetector
не сможет обнаружить лицо на изображении, на котором действительно есть лицо, увеличивается. Нам нужно установить ограничения здесь.
Давайте создадим статический метод с именем inRange
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
private static function inRange( width:Number, height:Number ):Boolean {
if ( width < MIN_WIDTH || width > MAX_WIDTH ) {
return false;
}
else if ( height < MIN_HEIGHT || height > MAX_HEIGHT ) {
return false;
}
else {
return true;
}
}
|
Метод inRange
возвращает true
если указанные ширина и высота не выходят за пределы диапазона. В противном случае возвращается false
. Мы собираемся вызвать этот метод в методе detectFaces
. Удалить строку "addChild( bitmap );"
из метода detectFaces
. Затем добавьте следующие строки кода.
01
02
03
04
05
06
07
08
09
10
|
if ( !inRange( bitmap.width, bitmap.height ) ) {
if ( !image ) image = new MovieClip();
image.addChild( bitmap );
addChildAt( image, 0 );
image.alpha = 0;
TweenLite.to( image, 1, { alpha:1 } );
statusTxt.text = «image too large for face detection»;
return;
}
|
Также нам нужно добавить com.greensock.TweenLite
в путь к классу.
1
|
import com.greensock.TweenLite;
|
Мы передаем width
и height
объекта Bitmap
в параметры метода inRange
. Если метод inRange
возвращает false
, мы создаем экземпляр объекта image
, добавляем bitmap
к объекту image
и отображаем изображение под пользовательским интерфейсом. Изображение исчезает при использовании TweenLite, и мы показываем пользователю сообщение об ошибке, что изображение выходит за пределы диапазона. Наконец, мы выходим из функции, используя оператор return
так как мы не хотим, чтобы выполнялся код, который будет следовать за оператором if
.
Теперь, когда мы проверили, находится ли изображение в безопасном диапазоне для обнаружения лица, мы можем начать обнаруживать лица.
Шаг 9: Использование класса FaceDetector
Включите следующие классы в путь к классу FaceTagger.
-
com.oskarwicha.images.FaceDetection.FaceDetector
-
com.oskarwicha.images.FaceDetection.Events.FaceDetectorEvent
1
2
|
import com.oskarwicha.images.FaceDetection.FaceDetector;
import com.oskarwicha.images.FaceDetection.Events.FaceDetectorEvent;
|
Создайте новую переменную под объявлением класса с именем detector
. Объект detector
должен иметь тип FaceDetector
. Начало класса FaceTagger
теперь должно выглядеть следующим образом.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.text.TextFieldType;
import flash.text.TextFieldAutoSize;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.net.FileReference;
import flash.net.FileFilter;
import flash.geom.Rectangle;
import flash.display.Loader;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.MovieClip;
import flash.text.TextFormat;
import flash.filters.DropShadowFilter;
import com.greensock.TweenLite;
import com.oskarwicha.images.FaceDetection.FaceDetector;
import com.oskarwicha.images.FaceDetection.Events.FaceDetectorEvent;
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;
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() {
|
Добавьте следующие строки кода в метод detectFaces
.
1
2
3
4
5
|
statusTxt.text = «detecting faces… please wait»;
detector.addEventListener( FaceDetectorEvent.FACE_CROPED, onFacesDetected );
detector.addEventListener( FaceDetectorEvent.NO_FACES_DETECTED, onNoFaces );
detector.loadFaceImageFromBitmap( bitmap );
|
Сначала мы добавляем прослушиватели событий в FaceDetector
. Есть два события, которые мы должны слушать. Событие NO_FACES_DETECTED
событие NO_FACES_DETECTED
.
( Примечание: FACE_CROPED
— не опечатка. Вероятно, это ошибка орфографии автора библиотеки. )
Мы хотим вызывать метод onFacesDetected
всякий раз, когда лицо было обнаружено, и метод onNoFaces
когда лица не были обнаружены на изображении. Последняя строка кода загружает изображение лица из нашего bitmap
объекта, содержащего загруженное изображение. Мы вызываем метод FaceDetector
объекта loadFaceImageFromBitmap
используя bitmap
объект в качестве параметра Bitmap
, для которого вызывается метод.
Шаг 10: Обнаружение нескольких лиц
Когда FaceDetector
объекта objectDetector
обнаруживает лица на изображении, сам FaceDetector
только одно из этих лиц. Если мы хотим получить доступ ко всем обнаруженным лицам, нам нужно получить прямой доступ к objectDetector
. Создайте метод onFacesDetected
метод onNoFaces
содержащий следующие строки кода.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
private function onFacesDetected( e:Event ):void {
var faces:Array = detector.objectDetector.detected;
if ( !image ) image = new MovieClip()
else return;
image.addChild( bitmap );
addChildAt( image, 0 );
positionContents();
image.alpha = 0;
TweenLite.to( image, 1, { alpha:1 } );
statusTxt.text = «»;
for each( var face:Rectangle in faces ) {
trace( face );
var rect:Rectangle = face;
newTag( rect );
}
positionContents();
}
private function onNoFaces( e:Event ):void {
statusTxt.text = «no faces were detected»;
if ( !image ) image = new MovieClip();
image.addChild( bitmap );
addChildAt( image, 0 );
positionContents();
image.alpha = 0;
TweenLite.to( image, 1, { alpha:1 } );
}
|
Давайте посмотрим на метод onFacesDetected
. FaceDetector
объекта objectDetector
содержит массив обнаруженных объектов. Эти объекты являются объектами Rectangle
, каждый из которых представляет местоположение и размер каждой обрезанной грани, которая была успешно найдена на изображении.
Мы назначаем его переменной faces
которая является Array
. Если объект image
еще не существует, нам нужно создать его экземпляр. Если она существует, нам нужно выйти из функции, потому что код уже был выполнен один раз. Мы выполняем тот же код, который мы делали в методе detectFaces
чтобы отобразить image
используя анимацию с постепенным исчезновением. Затем мы очищаем текстовое поле.
Цикл for each
используется для итерации каждого Rectangle
в массиве faces
. При отслеживании на панели вывода обнаруженное лицо будет выглядеть примерно так:
1
|
(x=30, y=30, w=123, h=123)
|
Метод newTag
генерирует новый объект PhotoTag для отображения на изображении на основе предоставленного параметра Rectangle
. Прежде чем мы сможем добавить тег на дисплей, нам нужно сначала создать наш класс тегов. Мы создадим этот класс на следующем шаге.
В методе onNoFaces
мы повторяем процесс, но на этот раз после отображения изображения мы уведомляем пользователя о том, что на загруженном изображении не было обнаружено лиц.
Шаг 11: Начните Класс PhotoTag
Создайте новый класс с именем PhotoTag
который расширяет Sprite
.
Импортируйте следующие классы в класс PhotoTag
.
1
2
3
4
5
6
7
8
9
|
import flash.display.Sprite;
import flash.geom.Rectangle;
import flash.events.Event;
import flash.display.MovieClip;
import flash.filters.DropShadowFilter;
import flash.events.MouseEvent;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;
import com.greensock.TweenLite;
|
Создайте следующие переменные и константы.
01
02
03
04
05
06
07
08
09
10
11
12
|
private var _rectangle:Rectangle;
private var _image:MovieClip;
private var _editMode:Boolean;
private var _removed:Boolean;
private var removeBtn:RemoveButton;
public static const LINE_THICKNESS:Number = 6;
public static const LINE_COLOR:uint = 0x00CCFF;
public static const FILL_COLOR:uint = 0xFFFFFF;
public static const ELLIPSE_WIDTH:Number = 10;
public static const ELLIPSE_HEIGHT:Number = 10;
public static const INIT_ALPHA:Number = .6;
|
Добавьте следующий код в конструктор класса.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
public function PhotoTag( rectangle:Rectangle, image:MovieClip ) {
_rectangle = rectangle;
_image = image;
removeBtn = new RemoveButton();
graphics.lineStyle( LINE_THICKNESS, LINE_COLOR );
graphics.beginFill( FILL_COLOR, .5 );
graphics.drawRoundRect( 0, 0, rectangle.width, rectangle.height, ELLIPSE_WIDTH, ELLIPSE_HEIGHT );
filters = [ new DropShadowFilter( 5, 45, 0, 1, 5, 5, 1, 3 ) ];
alpha = INIT_ALPHA;
_editMode = false;
addEventListener( Event.ADDED, onAdded );
}
|
Конструктор класса принимает два параметра:
- Первый параметр — это объект
rectangle
который содержит данные об обрезанной грани. - Вторым параметром является
MovieClip
который содержит загруженное растровое изображение.
Мы назначаем их переменным, чтобы мы могли ссылаться на них в нашем коде.
На следующей строке создается новый экземпляр класса RemoveButton
. Используйте унаследованный graphics
объект, чтобы нарисовать прямоугольник со скругленными углами, Rectangle
width
и height
объекта Rectangle
. Ширина и высота эллипса прямоугольника зависят от констант класса, а также от атрибутов линии прямоугольника. Мы применяем фильтр теней для добавления глубины к объекту и устанавливаем alpha
на константу INIT_ALPHA
(начальная альфа) . _editMode
— это свойство, которое сообщает нам, находится ли тег в редактируемом состоянии.
Последняя строка в конструкторе прослушивает событие ADDED
затем вызывает метод onAdded
после добавления тега в список отображения. Мы делаем это, потому что нам нужно получить доступ к объекту stage
который в настоящее время является null
. Как только объект был добавлен, мы можем безопасно получить доступ к stage
.
1
2
3
4
5
|
private function onAdded( e:Event ):void {
removeEventListener( Event.ADDED, onAdded );
init();
}
|
Метод onAdded
вызывается, когда объект добавляется в список отображения. Прослушиватель событий больше не нужен, поэтому он удаляется и вызывается метод init
.
Шаг 12: Инициализация фото тега
Создайте метод init
в классе PhotoTag
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
private function init():void {
if ( this.parent != image ) _image.addChild( this );
x = _rectangle.x;
y = _rectangle.y;
addChild( removeBtn );
removeBtn.alpha = 0;
doubleClickEnabled = true;
addEventListener( MouseEvent.ROLL_OVER, overState );
addEventListener( MouseEvent.MOUSE_DOWN, onMouseDown );
stage.addEventListener( MouseEvent.MOUSE_WHEEL, onMouseWheel );
stage.addEventListener( KeyboardEvent.KEY_DOWN, onKeyDownE );
removeBtn.addEventListener( MouseEvent.CLICK, remove );
}
|
Первая строка кода в методе init
проверяет, действительно ли тег был добавлен к объекту image
. Если это не так, тег добавит себя к изображению.
Затем теги x
и y
устанавливаются в rectangle
x
и y
. Это просто помещает тег на обрезанное лицо. По умолчанию alpha
свойство removeBtn
установлено в 0
потому что мы не хотим, чтобы оно было видимым. Позже нам нужно будет прослушивать событие DOUBLE_CLICK
и для тега doubleClickEnabled
должно быть установлено значение true
чтобы это событие было отправлено.
Последние пять строк кода добавляют прослушиватели событий в тег, на stage
и в removeBtn
.
При нажатии на removeBtn
мы хотим удалить тег из списка отображения. Создайте метод remove
.
1
2
3
4
5
6
7
8
|
private function remove( e:MouseEvent ):void {
if ( this.parent ) {
_removed = true;
this.parent.removeChild( this );
}
}
|
Метод удаляет тег из родительского объекта, если он есть. Мы устанавливаем для свойства _removed
значение true
поскольку объект удаляется.
Свойство _removed
является частным свойством, но к нему нужно обращаться за пределами области действия этого класса. Мы только хотим предоставить коду вне класса доступ для чтения данных свойств. Мы также хотим предоставить такой же ограниченный доступ к объекту _image
объекту _image
и свойству _editMode
. Создайте следующие методы получения.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
public function get rectangle():Rectangle {
return _rectangle;
}
public function get image():MovieClip {
return _image;
}
public function get editMode():Boolean {
return _editMode;
}
public function get removed():Boolean {
return _removed;
}
|
Теперь код вне этого класса может обращаться к этим объектам через методы получения. Методы могут рассматриваться как свойства, поэтому каждое свойство эффективно только для чтения .
Шаг 13: Добавление жизни к объекту PhotoTag
Если мы хотим, чтобы у наших объектов PhotoTag
было немного жизни, нам нужно добавить немного взаимодействия и анимации. Добавьте следующие методы в класс PhotoTag
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
private function resize( percent:Number ):void {
var newWidth:Number = ( percent * _rectangle.width ) / 100 + _rectangle.width;
var newHeight:Number = ( percent * _rectangle.height ) / 100 + _rectangle.height;
if ( newWidth < 60 ) {
newWidth = 60;
}
if ( newHeight < 60 ) {
newHeight = 60;
}
var newX:Number = ( x — image.x ) — ( newWidth — _rectangle.width ) / 2;
var newY:Number = ( y — image.y ) — ( newHeight — _rectangle.height ) / 2;
_rectangle.x = newX;
_rectangle.y = newY;
_rectangle.width = newWidth;
_rectangle.height = newHeight;
draw( newWidth, newHeight );
x = newX + image.x;
y = newY + image.y;
}
private function overState( e:MouseEvent ):void {
if ( _editMode ) return;
removeEventListener( MouseEvent.ROLL_OVER, outState );
removeBtn.visible = true;
removeBtn.alpha = 0;
var percent:Number = 40;
var newWidth:Number = ( percent * _rectangle.width ) / 100 + _rectangle.width;
var newHeight:Number = ( percent * _rectangle.height ) / 100 + _rectangle.height;
var newX:Number = _rectangle.x — ( newWidth — _rectangle.width ) / 2;
var newY:Number = _rectangle.y — ( newHeight — _rectangle.height ) / 2;
TweenLite.to( this, .1, { alpha:1, x:newX, y:newY, width:newWidth, height:newHeight } );
TweenLite.to( removeBtn, .1, { alpha:1 } );
addEventListener( MouseEvent.ROLL_OUT, outState );
}
private function outState( e:MouseEvent ):void {
if ( _editMode ) return;
removeEventListener( MouseEvent.ROLL_OUT, outState );
TweenLite.to( this, .1, { alpha:INIT_ALPHA, x:_rectangle.x, y:_rectangle.y, width:_rectangle.width, height:_rectangle.height } );
TweenLite.to( removeBtn, .1, { alpha:1 } );
addEventListener( MouseEvent.ROLL_OVER, overState );
}
private function onMouseWheel( e:MouseEvent ):void {
if ( !_editMode ) return;
var percent:Number = e.delta * 10;
resize( percent );
}
private function onKeyDownE( e:KeyboardEvent ):void {
if ( !_editMode ) return;
var percent:Number;
if ( e.keyCode == Keyboard.UP ) {
percent = 10;
}
else if ( e.keyCode == Keyboard.DOWN ) {
percent = -10;
}
else {
return;
}
resize( percent );
}
|
Это может выглядеть как много кода, но то, что он на самом деле выполняет, довольно просто; все методы в основном выполняют один и тот же код.
Ранее мы добавили прослушиватель событий для события ROLL_OVER
. Мы передали функцию обработчика eventHandler
параметру overState
называется overState
. Этот метод добавляет простой эффект масштабирования, когда мышь наводит курсор на тег. Новая ширина и высота объекта основана на том, насколько мы хотим увеличить размер объекта. В этом случае мы хотим увеличить размер объекта на 40 процентов.
Мы могли бы остановиться здесь и просто использовать TweenLite для анимации изменяемого размера объекта, но это не выглядело бы очень привлекательно, потому что регистрация объекта была в верхнем левом углу. Что мы действительно хотим сделать, так это преобразовать объект из его центра. Если вы являетесь членом клуба Greensock, то вы можете просто использовать плагин transformAroundCenter, чтобы выполнить это с помощью всего одной строки кода и без необходимости производить вычисления самостоятельно, но для остальных из нас нам понадобится быстрый обходной путь.
Мы вычисляем новую позицию x на основе новой ширины и вычисляем новую позицию y на основе новой высоты. Затем мы добавляем свойства x
, y
, width
и height
к их новым значениям. Мы также постепенно removeBtn
и делаем тег полностью непрозрачным.
Мы делаем полную противоположность в методе outState
который вызывается каждый раз, когда мышь перемещается от тега. Тег возвращается в исходное состояние.
Метод resize
выполняет то, что overState
метод overState
не влияя на свойство alpha
и без каких-либо анимаций. Метод resize
вызывается каждый раз, когда мы хотим динамически изменить размер тега. Метод принимает один параметр, который представляет собой процент, на который увеличивается или уменьшается размер тега. Метод добавляет минимальный предел размера для тега, чтобы он не стал маленьким.
Метод onMouseWheel
вызывается всякий раз, когда пользователь взаимодействует с колесом мыши. Метод onKeyDownE
вызывается при каждом нажатии клавиши. Каждый метод выполняет код, только если тег находится в режиме редактирования . И каждый метод изменяет размер тега на основе определенных свойств. Метод onMouseWheel
изменяет onMouseWheel
тега на основе значения свойства delta
MouseEvent
. onKeyDownE
увеличивает размер тега при нажатии клавиши «Вверх» и уменьшает размер тега при нажатии клавиши «Вниз».
Это одна из последних вещей, которую нам нужно исправить, чтобы правильно настроить анимацию нашего тега. TweenLite изменяет значение свойств width
и height
в каждом кадре, пока анимация не будет завершена. При изменении размеров объектов во Flash они могут искажаться. Мы не хотим, чтобы наши теги выглядели искаженными. Чтобы решить эту проблему, нам нужно переопределить методы set width
и set height
из родительского класса.
1
2
3
4
5
6
7
8
9
|
public override function set width( value:Number ):void {
draw( value, height );
}
public override function set height( value:Number ):void {
draw( width, value );
}
|
Всякий раз, когда ширина или высота тега установлена, мы хотим перерисовать графику тега. Создать метод draw
.
1
2
3
4
5
6
7
|
private function draw( newWidth:Number, newHeight:Number ):void {
graphics.clear();
graphics.lineStyle( LINE_THICKNESS, LINE_COLOR );
graphics.beginFill( FILL_COLOR, .5 );
graphics.drawRoundRect( 0, 0, newWidth, newHeight, ELLIPSE_WIDTH, ELLIPSE_HEIGHT );
}
|
Метод draw
должен принимать два параметра: новую ширину тега и новую высоту тега. Графика очищается, а затем перерисовывается снова на основе параметров newWidth
и newHeight
. Теперь наш тег оживит красиво.
Шаг 14: вход и выход из режима редактирования
Мы хотим, чтобы наши пользователи могли редактировать каждый тег отдельно. Когда тег находится в режиме редактирования, пользователь может манипулировать размером тега и изменять положение тега на изображении. Для этого нужно сделать несколько вещей.
Сначала давайте создадим методы enterEditMode
и exitEditMode
. Оба эти метода будут открытыми, так как нам нужно будет вызывать их из класса документа.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public function enterEditMode():void {
if ( !_editMode ) {
_editMode = true;
removeEventListener( MouseEvent.ROLL_OUT, outState );
draw( _rectangle.width, _rectangle.height );
}
}
public function exitEditMode():void {
if ( _editMode && !removed ) {
_editMode = false;
image.addChild( this );
x = _rectangle.x;
y = _rectangle.y;
stopDrag();
alpha = INIT_ALPHA;
TweenLite.to( removeBtn, .1, { alpha:0 } );
addEventListener( MouseEvent.ROLL_OVER, overState );
}
}
|
Метод enterEditMode
выполняет блок кода в операторе if, если тег в данный момент не находится в режиме редактирования . Если тег не находится в режиме редактирования, тег перейдет в режим редактирования , установив для _editMode
значение true
чтобы позволить пользователю изменять свойства тега. ROLL_OUT
события ROLL_OUT
удален, так как мы не хотим, чтобы метод случайно выполнялся, если мышь зависает над тегом. Наконец, вызывается метод draw
для отображения исходного размера тега.
Метод exitEditMode
устанавливает тэг обратно в его начальное состояние, в то время как позиционирует тэг в новых позициях x и y _rectangle
. Мы вызываем метод stopDrag
на тот случай, если тег перетаскивается пользователем при выходе из режима редактирования .
Шаг 15: Создание нового тега
Теперь вернемся к нашему классу Document
FaceTagger
. Наконец, мы можем создать метод newTag
теперь, когда у нас есть класс PhotoTag
. Создайте метод newTag
.
1
2
3
4
5
6
7
8
|
private function newTag( rectangle:Rectangle ):void {
var tag:PhotoTag = new PhotoTag( rectangle, image );
image.addChild( tag );
tag.addEventListener( MouseEvent.ROLL_OVER, onTagRollOver );
tag.addEventListener( MouseEvent.DOUBLE_CLICK, enterEditMode );
tag.addEventListener( Event.REMOVED, onTagRemoved );
}
|
Метод создает новый объект PhotoTag
передавая параметр rectangle
и текущее изображение в метод конструктора PhotoTag
. Тег добавляется к текущему изображению. Мы прослушиваем несколько событий, которые тег отправит. Первое событие — это событие ROLL_OVER
. Метод onTagRollOver
вызывается при отправке события. Мы прослушиваем событие DOUBLE_CLICK
чтобы мы могли вызвать метод enterEditMode
, который будет отличаться от метода enterEditMode
из класса PhotoTag
, и, наконец, мы прослушиваем событие onTagRemoved
которое вызывает метод onTagRemoved
всякий раз, когда тег удаляется из его родительского объекта. ,
Создайте метод обработчика событий с именем onTagRollOver
.
1
2
3
4
|
private function onTagRollOver( e:MouseEvent ):void {
if ( !e.target.editMode ) statusTxt.text = «double-click to edit»;
}
|
Метод очень прост, как видите. Метод отображает сообщение для пользователя, если тег, над которым находится указатель мыши, не находится в режиме редактирования . Сообщение уведомляет пользователя о том, что если дважды щелкнуть текущий тег, то тег перейдет в режим редактирования .
Шаг 16: Предоставление пользователю доступа для редактирования
Теперь нам нужно разрешить пользователю доступ для редактирования каждого тега. Создайте новую переменную под FaceTagger
класса класса FaceTagger
и назовите ее currentTag
. Это должен быть объект PhotoTag
. Начало класса документа теперь должно выглядеть так:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
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() {
|
Создайте метод FaceTagger
классе FaceTagger
. Этот метод является методом обработчика событий, который мы вызвали в методе newTag
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
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(); }
|
Приведенный выше метод уведомляет пользователя о том, что он в данный момент находится в режиме редактирования . Экран затемнен, а метка удалена с image
объекта и размещена на stage
. Теги x
и y
свойства должны быть установлены в значения, которые соответствуют положению изображения на сцене. Если бы мы этого не делали, тег бы просто находился в верхнем левом углу экрана. Когда вызывается этот метод, даже не кажется, что позиция тегов вообще изменилась, но на самом деле она изменилась совсем немного.
Наконец, мы вызываем метод tag
объектов enterEditMode
, назначаем ссылку на tag
объект currentTag
переменной и размещаем содержимое экрана (особенно statusTxt
текстовое поле).
Шаг 17: Сохранение состояния текущего тега
После того, как пользователь завершил редактирование currentTag
, тег необходимо добавить обратно к image
объекту и правильно расположить. Создайте exitEditMode
метод в FaceTagger
классе.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
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(); }
}
|
Обратите внимание, как MouseEvent
параметр установлен null
по умолчанию. Мы собираемся вызвать этот метод из метода onTagRemoved
обработчика событий позже, и у нас не будет MouseEvent
объекта для передачи в качестве параметра метода, поэтому мы присваиваем параметру null
значение по умолчанию. Это делает метод более доступным.
Первая задача, которую выполняет метод, — скрыть darkBox
объект, чтобы дать пользователю ощущение выхода из режима редактирования . Блок кода в if
инструкции выполняется, если currentTag
существует. Текстовое поле установлено в один пробел ( " "
). Помните, что текстовое поле имеет autoSize
свойство изменять размер из своего центра. Чтобы текстовое поле располагалось правильно, нам нужно убедиться, что присутствует какой-то текст, чтобы текстовое поле не уменьшало себя до нуля. Это может вызвать потенциальную проблему, если пользователь выходит из режима редактирования, а затем наводит курсор на тег. Эффект вызван тем, что текстовое поле может появиться за пределами экрана.
Далее, currentTag
«S rectangle
объекта задается новое x
и y
значение свойств. Эти значения основаны на текущей позиции тега stage
относительно image
позиции в stage
. Когда currentTag
«ы exitEditMode
метод называется currentTag
добавляется обратно в image
объект и позиционируется на rectangle
» нового x
и y
позиции.
Наконец, мы устанавливаем ссылку currentTag
на null
и размещаем содержимое на экране.
Создать onTagRemoved
метод.
1
2
3
4
5
6
7
|
private function onTagRemoved( e:Event ): void { if ( e.target.removed ) { exitEditMode(); }
}
|
Этот метод выходит из режима редактирования, если был вызван remove
метод целевого тега ( e.target
). Таким образом, если пользователь нажал кнопку удаления на целевом теге, мы выйдем из режима редактирования . Без этого метода, если пользователь удалит тег в режиме редактирования , экран останется темным.
Шаг 18: перестановка тега
Вернитесь в PhotoTag
класс и добавьте в него следующие методы.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
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 ); }
|
Оба метода требуют, чтобы тег находился в режиме редактирования для полного выполнения. onMouseUp
Метод начинает перетаскивание тега и ограничивает тег только перетащить в пределах _image
объекта. onMouseUp
Метод останавливает процесс перетаскивания.
Шаг 19: Добавление дополнительных тегов
К настоящему времени вы, вероятно, задаетесь вопросом, что случилось с addBtn
. При нажатии этой кнопки новый PhotoTag
объект добавляется к текущему изображению. Давайте создадим addTag
метод в классе документа.
Примечание. Вы должны помнить этот метод из шага 6. Это метод, который был передан в качестве параметра обработчика событий, когда мы добавили прослушиватель событий, который прослушивает CLICK
событие, которое будет отправлено из addBtn
объекта .
1
2
3
4
5
6
7
8
|
private function addTag( e:MouseEvent ): void { if ( image ) { var rect:Rectangle = new Rectangle( 0 , 0 , 75 , 75 ); newTag( rect ); }
}
|
Если image
существует, мы вызываем newTag
метод, как и раньше. Мы создаем новый составленный Rectangle
объект, чтобы пользователь мог настроить тег самостоятельно. Тег просто добавляется к изображению в левом верхнем углу.
Шаг 20. Улучшение внешнего вида объекта RemoveButton
Как мы уже видели, вы можете значительно улучшить внешний вид объекта с помощью нескольких строк кода. Давайте улучшим внешний вид RemoveButton
объекта. Создайте новый класс и назовите его RemoveButton
. Класс должен будет расширить MovieClip
класс, так как он связан с символом библиотеки этого типа.
Сохраните класс в корневую папку. Импортируйте следующее внутри RemoveButton
:
1
2
3
|
import flash.display.MovieClip;
import flash.filters.DropShadowFilter; import flash.filters.BevelFilter; |
Все, что мы собираемся сделать сейчас, это добавить простую строку кода в RemoveButton
метод конструктора класса объекта.
1
|
filters = [ new DropShadowFilter( 5 , 45 , 0 , 1 , 5 , 5 , 1 , 3 ), new BevelFilter( 10 , 90 , 0xF5E1AF , . 6 , 0 , 1 , 15 , 15 , 1 , 3 ) ]; |
Мы только что добавили a DropShadowFilter
и a BevelFilter
к RemoveButton
объекту. Это добавит большую глубину нашей кнопке.
Шаг 21: Тестирование нашего приложения
На этом этапе, если мы тестируем наше приложение, все должно работать без сбоев. Но мы столкнемся с проблемой, когда загрузим изображение и начнется процесс распознавания лиц. Если вы не поместите файл face.zip в тот же каталог, что и SWF-файл , процесс обнаружения лица может не произойти. Это очень важно помнить, чтобы сделать это. После того, как вы поместили face.zip в тот же каталог, что и SWF , вы можете запустить приложение.
Вывод
Пожалуйста, примите во внимание, что Библиотека распознавания лиц не идеальна. Будут времена, когда он не обнаружит никаких лиц, даже если на изображении, которое было загружено, есть лица. Также будут моменты, когда на изображении будут обнаруживаться только некоторые лица, и случаи, когда узоры, похожие на лица (например, облако в форме лица), будут обнаруживаться как лица. Вот почему мы разрешаем нашим пользователям редактировать теги. Вообще, я нашел библиотеку довольно удивительной, и я надеюсь, что вы тоже. На этом мой учебник завершен. Спасибо за чтение.