root/as3/Marilena/trunk/src/jp/maaash/ObjectDetection/ObjectDetector.as

リビジョン 935, 8.1 kB (コミッタ: gyuque, コミット時期: 4 年 前)

optimized

Line 
1 //
2 // Project Marilena
3 // Object Detection in Actionscript3
4 // based on OpenCV (Open Computer Vision Library) Object Detection
5 //
6 // Copyright (C) 2008, Masakazu OHTSUKA (mash), all rights reserved.
7 // contact o.masakazu(at)gmail.com
8 //
9 // Redistribution and use in source and binary forms, with or without modification,
10 // are permitted provided that the following conditions are met:
11 //
12 //   * Redistribution's of source code must retain the above copyright notice,
13 //     this list of conditions and the following disclaimer.
14 //
15 //   * Redistribution's in binary form must reproduce the above copyright notice,
16 //     this list of conditions and the following disclaimer in the documentation
17 //     and/or other materials provided with the distribution.
18 //
19 // This software is provided by the copyright holders and contributors "as is" and
20 // any express or implied warranties, including, but not limited to, the implied
21 // warranties of merchantability and fitness for a particular purpose are disclaimed.
22 // In no event shall the Intel Corporation or contributors be liable for any direct,
23 // indirect, incidental, special, exemplary, or consequential damages
24 // (including, but not limited to, procurement of substitute goods or services;
25 // loss of use, data, or profits; or business interruption) however caused
26 // and on any theory of liability, whether in contract, strict liability,
27 // or tort (including negligence or otherwise) arising in any way out of
28 // the use of this software, even if advised of the possibility of such damage.
29 //
30 package jp.maaash.ObjectDetection
31 {
32         import flash.events.Event;
33         import flash.events.EventDispatcher;
34         import flash.display.Bitmap;
35         import flash.geom.Rectangle;
36         import flash.utils.setTimeout;
37         public class ObjectDetector extends EventDispatcher{
38                 private var debug     :Boolean = false;
39                 private var tgt       :TargetImage;
40                 public  var detected  :Array;   // of Rectangles
41                 public  var cascade   :HaarCascade;
42                 private var _options  :ObjectDetectorOptions;
43                 private var xmlloader :HaarCascadeLoader;
44
45                 private var waiting   :Boolean = false;
46                 private var loaded    :Boolean = false;
47
48                 public function ObjectDetector() {
49                         tgt = new TargetImage;
50                 }
51
52                 public function detect( bmp :Bitmap = null ) :void{
53                         logger("[detect]");
54                         if ( bmp && bmp.bitmapData ) {
55                                 tgt.bitmapData = bmp.bitmapData;
56                         }
57
58                         if ( !loaded ) {
59                                 waiting = true;
60                                 return;
61                         }
62                         dispatchEvent( new ObjectDetectorEvent(ObjectDetectorEvent.DETECTION_START) );
63                         _detect();
64                 }
65
66                 private function _detect() :void {
67
68                         detected = new Array;
69                         var imgw :int = tgt.width, imgh :int = tgt.height;
70                         var scaledw :int, scaledh :int, limitx  :int, limity  :int, stepx :int, stepy :int, result :int, factor:Number = 1;
71                         for( factor = 1;
72                                 factor*cascade.base_window_w < imgw && factor*cascade.base_window_h < imgh;
73                                 factor *= _options.scale_factor )
74                         {
75                                 scaledw = int( cascade.base_window_w * factor );
76                                 scaledh = int( cascade.base_window_h * factor );
77                                 if( scaledw < _options.min_size || scaledh < _options.min_size ){
78                                         continue;
79                                 }
80                                 limitx = tgt.width  - scaledw;
81                                 limity = tgt.height - scaledh;
82                                 if( _options.endx != ObjectDetectorOptions.INVALID_POS && _options.endy != ObjectDetectorOptions.INVALID_POS ){
83                                         limitx = Math.min( _options.endx, limitx );
84                                         limity = Math.min( _options.endy, limity );
85                                 }
86                                 logger("[detect]limitx,y: "+limitx+","+limity);
87
88                                 //stepx  = Math.max(_options.MIN_MARGIN_SEARCH,factor);
89                                 stepx  = scaledw>>3;
90                                 stepy  = stepx;
91                                 logger("[detect] w,h,step: "+scaledw+","+scaledh+","+stepx);
92
93                                 var ix:int=0, iy:int=0, startx:int=0, starty:int=0;
94                                 if( _options.startx != ObjectDetectorOptions.INVALID_POS && _options.starty != ObjectDetectorOptions.INVALID_POS ){
95                                         startx = Math.max( ix, _options.startx );
96                                         starty = Math.max( iy, _options.starty );
97                                 }
98                                 logger("[detect]startx,y: "+startx+","+starty);
99
100                                 for(     iy = starty; iy < limity; iy += stepy ){
101                                         for( ix = startx; ix < limitx; ix += stepx ){
102                                                 if( _options.search_mode & ObjectDetectorOptions.SEARCH_MODE_NO_OVERLAP &&
103                                                         overlaps(ix,iy,scaledw,scaledh) ){
104                                                         // do nothing
105                                                 }else{
106                                                         //logger("[checkAndRun]ix,iy,scaledw,scaledh: "+ix+","+iy+","+scaledw+","+scaledh);
107                                                         cascade.scale = factor;
108                                                         result = runHaarClassifierCascade(cascade,ix,iy,scaledw,scaledh);
109                                                         if ( result > 0 ) {
110                                                                 var faceArea :Rectangle = new Rectangle(ix,iy,scaledw,scaledh);
111                                                                 detected.push( faceArea );
112                                                                 logger("[createCheckAndRun]found!: "+ix+","+iy+","+scaledw+","+scaledh);
113
114                                                                 // doesnt mean anything cause detection is not time-divided (now)
115                                                                 var ev1 :ObjectDetectorEvent = new ObjectDetectorEvent( ObjectDetectorEvent.FACE_FOUND );
116                                                                 ev1.rect = faceArea;
117                                                                 dispatchEvent( ev1 );
118                                                         }
119                                                 }
120                                         }
121                                 }
122                         }
123
124                         // integrate redundant candidates ...
125
126                         var ev2 :ObjectDetectorEvent = new ObjectDetectorEvent( ObjectDetectorEvent.DETECTION_COMPLETE );
127                         ev2.rects = detected;
128                         dispatchEvent( ev2 );
129                 }
130
131                 private function runHaarClassifierCascade(c:HaarCascade,x:int,y:int,w:int,h:int):int{
132                         //logger("[runHaarClassifierCascade] c:",c,x,y,w,h);
133                         var mean :Number                 = tgt.getSum(x,y,w,h) * c.inv_window_area;
134                         var variance_norm_factor :Number = tgt.getSum2(x,y,w,h)* c.inv_window_area - mean*mean;
135                         if( variance_norm_factor >= 0 ){
136                                 variance_norm_factor = Math.sqrt(variance_norm_factor);
137                         }else{
138                                 variance_norm_factor = 1;
139                         }
140
141                         var trees :Array = c.trees, treenums :int = trees.length, tree: FeatureTree, features :Array, featurenums :int, val :Number = 0, sum :Number = 0, feature :FeatureBase, i :int=0, j :int=0, st_th:Number = 0;
142                         for( i=0; i<treenums; i++ ){
143                                 tree        = trees[i];
144                                 features    = tree.features;
145                                 featurenums = features.length;
146                                 val         = 0;
147                                 st_th       = tree.stage_threshold;
148                                 for( j=0; j<featurenums; j++ ){
149                                         feature = features[j];
150                                         sum  = feature.getSum( tgt, x, y );
151
152 //                                      val += (sum < feature.threshold * variance_norm_factor) ?
153 //                                              feature.left_val : feature.right_val;
154 //
155 //                                      * Ternary operation causes coersion and makes slower.
156
157                                         if (sum < feature.threshold * variance_norm_factor)
158                                                 val += feature.left_val;
159                                         else
160                                                 val += feature.right_val;
161
162                                         if( val > st_th ){
163                                                 // left_val, right_val are always plus
164                                                 break;
165                                         }
166                                 }
167                                 if( val < st_th ){
168                                         return 0;
169                                 }
170                         }
171                         return 1;
172                 }
173
174                 private function overlaps(_x:int,_y:int,_w:int,_h:int):Boolean{
175                         // if the area we're going to check contains, or overlaps the square which is already picked up, ignore it
176                         var i:int=0;
177                         var l:int=detected.length;
178                         var tg: Rectangle;
179                         var x:int = _x, y:int = _y, w:int = _w, h:int = _h, tx1:int, tx2:int, ty1:int, ty2:int;
180                         for( i=0; i<l; i++ ){
181                                 tg = detected[i];
182                                 tx1 = tg.x;
183                                 tx2 = tg.x + tg.width;
184                                 ty1 = tg.y;
185                                 ty2 = tg.y + tg.height;
186                                 if(  ( ( x <= tx1 && tx1 < x+w )
187                                      ||( x <= tx2 && tx2 < x+w ) )
188                                   && ( ( y <= ty1 && ty1 < y+h )
189                                      ||( y <= ty2 && ty2 < y+h ) )  )
190                                 {
191                                         return true;
192                                 }
193                         }
194                         return false;
195                 }
196
197                 public function loadHaarCascades( url :String ) :void {
198                         xmlloader = new HaarCascadeLoader( url );
199                         xmlloader.addEventListener(Event.COMPLETE,function(e:Event):void{
200                                 xmlloader.removeEventListener(Event.COMPLETE,arguments.callee);
201                                 dispatchEvent( new ObjectDetectorEvent(ObjectDetectorEvent.HAARCASCADES_LOAD_COMPLETE) );
202                                 cascade = xmlloader.cascade;
203
204                                 loaded = true;
205                                 if( waiting ){
206                                         waiting = false;
207                                         detect();
208                                 }
209                         });
210                         loaded = false;
211                         dispatchEvent( new ObjectDetectorEvent(ObjectDetectorEvent.HAARCASCADES_LOADING) );
212                         xmlloader.load();       // kick it!
213                 }
214
215                 public function set bitmap( bmp :Bitmap ) :void {
216                         tgt.bitmapData = bmp.bitmapData;
217                 }
218                 public function set options( opt :ObjectDetectorOptions ) :void {
219                         _options = opt;
220                 }
221
222                 private function logger(... args):void{
223                         if(!debug){ return; }
224                         log(["[ObjectDetector]"+args.shift()].concat(args));
225                 }
226         }
227 }
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。