source: spip-zone/_plugins_/centre_image/inc/FaceDetector.php @ 91673

Last change on this file since 91673 was 91673, checked in by arno@…, 6 years ago

Ajout d’une fonction *centre_image_visage* basée sur la détection du visage par Maurice Svay. Idéalement, serait fusionnée dans centre_image, mais c’est vraiment très lourd.

  • Property svn:executable set to *
File size: 9.5 KB
Line 
1<?php
2//
3// This program is free software; you can redistribute it and/or
4// modify it under the terms of the GNU General Public License
5// as published by the Free Software Foundation; either version 2
6// of the License, or (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11// GNU General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with this program; if not, write to the Free Software
15// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16//
17// @Author Karthik Tharavaad
18//         karthik_tharavaad@yahoo.com
19// @Contributor Maurice Svay
20//              maurice@svay.Com
21
22namespace svay;
23
24use Exception;
25
26class FaceDetector
27{
28
29    protected $detection_data;
30    protected $canvas;
31    protected $face;
32    private $reduced_canvas;
33
34    /**
35     * Creates a face-detector with the given configuration
36     *
37     * Configuration can be either passed as an array or as
38     * a filepath to a serialized array file-dump
39     *
40     * @param string|array $detection_data
41     */
42    public function __construct($detection_data = 'detection.dat')
43    {
44        if (is_array($detection_data)) {
45            $this->detection_data = $detection_data;
46            return;
47        }
48   
49        if (!is_file($detection_data)) {
50            // fallback to same file in this class's directory
51            $detection_data = dirname(__FILE__) . DIRECTORY_SEPARATOR . $detection_data;
52           
53            if (!is_file($detection_data)) {
54                throw new \Exception("Couldn't load detection data");
55            }
56        }
57       
58        $this->detection_data = unserialize(file_get_contents($detection_data));
59    }
60
61    public function faceDetect($file)
62    {
63        if (is_resource($file)) {
64
65            $this->canvas = $file;
66
67        } elseif (is_file($file)) {
68
69            $this->canvas = imagecreatefromjpeg($file);
70
71        } else {
72
73            throw new Exception("Can not load $file");
74        }
75
76        $im_width = imagesx($this->canvas);
77        $im_height = imagesy($this->canvas);
78
79        //Resample before detection?
80        $diff_width = 320 - $im_width;
81        $diff_height = 240 - $im_height;
82        if ($diff_width > $diff_height) {
83            $ratio = $im_width / 320;
84        } else {
85            $ratio = $im_height / 240;
86        }
87
88        if ($ratio != 0) {
89            $this->reduced_canvas = imagecreatetruecolor($im_width / $ratio, $im_height / $ratio);
90
91            imagecopyresampled(
92                $this->reduced_canvas,
93                $this->canvas,
94                0,
95                0,
96                0,
97                0,
98                $im_width / $ratio,
99                $im_height / $ratio,
100                $im_width,
101                $im_height
102            );
103
104            $stats = $this->getImgStats($this->reduced_canvas);
105
106            $this->face = $this->doDetectGreedyBigToSmall(
107                $stats['ii'],
108                $stats['ii2'],
109                $stats['width'],
110                $stats['height']
111            );
112
113            if ($this->face['w'] > 0) {
114                $this->face['x'] *= $ratio;
115                $this->face['y'] *= $ratio;
116                $this->face['w'] *= $ratio;
117            }
118        } else {
119            $stats = $this->getImgStats($this->canvas);
120
121            $this->face = $this->doDetectGreedyBigToSmall(
122                $stats['ii'],
123                $stats['ii2'],
124                $stats['width'],
125                $stats['height']
126            );
127        }
128        return ($this->face['w'] > 0);
129    }
130
131
132    public function toJpeg()
133    {
134        $color = imagecolorallocate($this->canvas, 255, 0, 0); //red
135
136        imagerectangle(
137            $this->canvas,
138            $this->face['x'],
139            $this->face['y'],
140            $this->face['x']+$this->face['w'],
141            $this->face['y']+ $this->face['w'],
142            $color
143        );
144
145        header('Content-type: image/jpeg');
146        imagejpeg($this->canvas);
147    }
148
149    public function toJson()
150    {
151        return json_encode($this->face);
152    }
153
154    public function getFace()
155    {
156        return $this->face;
157    }
158
159    protected function getImgStats($canvas)
160    {
161        $image_width = imagesx($canvas);
162        $image_height = imagesy($canvas);
163        $iis =  $this->computeII($canvas, $image_width, $image_height);
164        return array(
165            'width' => $image_width,
166            'height' => $image_height,
167            'ii' => $iis['ii'],
168            'ii2' => $iis['ii2']
169        );
170    }
171
172    protected function computeII($canvas, $image_width, $image_height)
173    {
174        $ii_w = $image_width+1;
175        $ii_h = $image_height+1;
176        $ii = array();
177        $ii2 = array();
178
179        for ($i=0; $i<$ii_w; $i++) {
180            $ii[$i] = 0;
181            $ii2[$i] = 0;
182        }
183
184        for ($i=1; $i<$ii_h-1; $i++) {
185            $ii[$i*$ii_w] = 0;
186            $ii2[$i*$ii_w] = 0;
187            $rowsum = 0;
188            $rowsum2 = 0;
189            for ($j=1; $j<$ii_w-1; $j++) {
190                $rgb = ImageColorAt($canvas, $j, $i);
191                $red = ($rgb >> 16) & 0xFF;
192                $green = ($rgb >> 8) & 0xFF;
193                $blue = $rgb & 0xFF;
194                $grey = (0.2989*$red + 0.587*$green + 0.114*$blue)>>0;  // this is what matlab uses
195                $rowsum += $grey;
196                $rowsum2 += $grey*$grey;
197
198                $ii_above = ($i-1)*$ii_w + $j;
199                $ii_this = $i*$ii_w + $j;
200
201                $ii[$ii_this] = $ii[$ii_above] + $rowsum;
202                $ii2[$ii_this] = $ii2[$ii_above] + $rowsum2;
203            }
204        }
205        return array('ii'=>$ii, 'ii2' => $ii2);
206    }
207
208    protected function doDetectGreedyBigToSmall($ii, $ii2, $width, $height)
209    {
210        $s_w = $width/20.0;
211        $s_h = $height/20.0;
212        $start_scale = $s_h < $s_w ? $s_h : $s_w;
213        $scale_update = 1 / 1.2;
214        for ($scale = $start_scale; $scale > 1; $scale *= $scale_update) {
215            $w = (20*$scale) >> 0;
216            $endx = $width - $w - 1;
217            $endy = $height - $w - 1;
218            $step = max($scale, 2) >> 0;
219            $inv_area = 1 / ($w*$w);
220            for ($y = 0; $y < $endy; $y += $step) {
221                for ($x = 0; $x < $endx; $x += $step) {
222                    $passed = $this->detectOnSubImage($x, $y, $scale, $ii, $ii2, $w, $width+1, $inv_area);
223                    if ($passed) {
224                        return array('x'=>$x, 'y'=>$y, 'w'=>$w);
225                    }
226                } // end x
227            } // end y
228        }  // end scale
229        return null;
230    }
231
232    protected function detectOnSubImage($x, $y, $scale, $ii, $ii2, $w, $iiw, $inv_area)
233    {
234        $mean  = ($ii[($y+$w)*$iiw + $x + $w] + $ii[$y*$iiw+$x] - $ii[($y+$w)*$iiw+$x] - $ii[$y*$iiw+$x+$w])*$inv_area;
235
236        $vnorm = ($ii2[($y+$w)*$iiw + $x + $w]
237                  + $ii2[$y*$iiw+$x]
238                  - $ii2[($y+$w)*$iiw+$x]
239                  - $ii2[$y*$iiw+$x+$w])*$inv_area - ($mean*$mean);
240
241        $vnorm = $vnorm > 1 ? sqrt($vnorm) : 1;
242
243        $count_data = count($this->detection_data);
244
245        for ($i_stage = 0; $i_stage < $count_data; $i_stage++) {
246            $stage = $this->detection_data[$i_stage];
247            $trees = $stage[0];
248
249            $stage_thresh = $stage[1];
250            $stage_sum = 0;
251
252            $count_trees = count($trees);
253
254            for ($i_tree = 0; $i_tree < $count_trees; $i_tree++) {
255                $tree = $trees[$i_tree];
256                $current_node = $tree[0];
257                $tree_sum = 0;
258                while ($current_node != null) {
259                    $vals = $current_node[0];
260                    $node_thresh = $vals[0];
261                    $leftval = $vals[1];
262                    $rightval = $vals[2];
263                    $leftidx = $vals[3];
264                    $rightidx = $vals[4];
265                    $rects = $current_node[1];
266
267                    $rect_sum = 0;
268                    $count_rects = count($rects);
269
270                    for ($i_rect = 0; $i_rect < $count_rects; $i_rect++) {
271                        $s = $scale;
272                        $rect = $rects[$i_rect];
273                        $rx = ($rect[0]*$s+$x)>>0;
274                        $ry = ($rect[1]*$s+$y)>>0;
275                        $rw = ($rect[2]*$s)>>0;
276                        $rh = ($rect[3]*$s)>>0;
277                        $wt = $rect[4];
278
279                        $r_sum = ($ii[($ry+$rh)*$iiw + $rx + $rw]
280                                  + $ii[$ry*$iiw+$rx]
281                                  - $ii[($ry+$rh)*$iiw+$rx]
282                                  - $ii[$ry*$iiw+$rx+$rw])*$wt;
283
284                        $rect_sum += $r_sum;
285                    }
286
287                    $rect_sum *= $inv_area;
288
289                    $current_node = null;
290
291                    if ($rect_sum >= $node_thresh*$vnorm) {
292
293                        if ($rightidx == -1) {
294
295                            $tree_sum = $rightval;
296
297                        } else {
298
299                            $current_node = $tree[$rightidx];
300
301                        }
302
303                    } else {
304
305                        if ($leftidx == -1) {
306
307                            $tree_sum = $leftval;
308
309                        } else {
310
311                            $current_node = $tree[$leftidx];
312                        }
313                    }
314                }
315
316                $stage_sum += $tree_sum;
317            }
318            if ($stage_sum < $stage_thresh) {
319                return false;
320            }
321        }
322        return true;
323    }
324}
Note: See TracBrowser for help on using the repository browser.