@halwarsing
Я python и Web программист.

Как можно ускорить код php?

Как можно ускорить это код php:
<?php
class HalFaceRec {
    protected $detection_data;
    protected $img;
    protected $face;
    private $reduced_img;
    
    public function __construct() {
        $this->detection_data = unserialize(file_get_contents('detection.dat'));
    }
    
    public function getImgStats($img) {
        $imgw = imagesx($img);
        $imgh = imagesy($img);
        $iis = $this->computeII($img,$imgw,$imgh);
        return [
            'width'=>$imgw,
            'height'=>$imgh,
            'ii'=>$iis['ii'],
            'ii2'=>$iis['ii2']
        ];
    }
    
    public function computeII($img,$imgw,$imgh) {
        $ii_w = $imgw+1;
        $ii_h = $imgh+1;
        $ii = array();
        $ii2 = array();

        for ($i=0; $i<$ii_w; $i++) {
            $ii[$i] = 0;
            $ii2[$i] = 0;
        }

        for ($i=1; $i<$ii_h-1; $i++) {
            $ii[$i*$ii_w] = 0;
            $ii2[$i*$ii_w] = 0;
            $rowsum = 0;
            $rowsum2 = 0;
            for ($j=1; $j<$ii_w-1; $j++) {
                $rgb = ImageColorAt($img, $j, $i);
                $red = ($rgb >> 16) & 0xFF;
                $green = ($rgb >> 8) & 0xFF;
                $blue = $rgb & 0xFF;
                $grey = (0.2989*$red + 0.587*$green + 0.114*$blue)>>0;  // this is what matlab uses
                $rowsum += $grey;
                $rowsum2 += $grey*$grey;

                $ii_above = ($i-1)*$ii_w + $j;
                $ii_this = $i*$ii_w + $j;

                $ii[$ii_this] = $ii[$ii_above] + $rowsum;
                $ii2[$ii_this] = $ii2[$ii_above] + $rowsum2;
            }
        }
        return ['ii'=>$ii, 'ii2' => $ii2];
    }
    
    public function detectOnSubImage($x,$y,$scale,$ii,$ii2,$w,$iiw,$inv_area,$count_data) {
        $mean  = ($ii[($y+$w)*$iiw + $x + $w] + $ii[$y*$iiw+$x] - $ii[($y+$w)*$iiw+$x] - $ii[$y*$iiw+$x+$w])*$inv_area;

        $vnorm = ($ii2[($y+$w)*$iiw + $x + $w]
                  + $ii2[$y*$iiw+$x]
                  - $ii2[($y+$w)*$iiw+$x]
                  - $ii2[$y*$iiw+$x+$w])*$inv_area - ($mean*$mean);

        $vnorm = $vnorm > 1 ? sqrt($vnorm) : 1;

        for ($i_stage = 0; $i_stage < $count_data; $i_stage++) {
            $stage = $this->detection_data[$i_stage];
            $trees = $stage[0];

            $stage_thresh = $stage[1];
            $stage_sum = 0;

            $count_trees = count($trees);

            for ($i_tree = 0; $i_tree < $count_trees; $i_tree++) {
                $tree = $trees[$i_tree];
                $current_node = $tree[0];
                $tree_sum = 0;
                while ($current_node != null) {
                    $vals = $current_node[0];
                    $node_thresh = $vals[0];
                    $leftval = $vals[1];
                    $rightval = $vals[2];
                    $leftidx = $vals[3];
                    $rightidx = $vals[4];
                    $rects = $current_node[1];

                    $rect_sum = 0;
                    $count_rects = count($rects);

                    for ($i_rect = 0; $i_rect < $count_rects; $i_rect++) {
                        $s = $scale;
                        $rect = $rects[$i_rect];
                        $rx = ($rect[0]*$s+$x)>>0;
                        $ry = ($rect[1]*$s+$y)>>0;
                        $rw = ($rect[2]*$s)>>0;
                        $rh = ($rect[3]*$s)>>0;
                        $wt = $rect[4];

                        $r_sum = ($ii[($ry+$rh)*$iiw + $rx + $rw]
                                  + $ii[$ry*$iiw+$rx]
                                  - $ii[($ry+$rh)*$iiw+$rx]
                                  - $ii[$ry*$iiw+$rx+$rw])*$wt;

                        $rect_sum += $r_sum;
                    }

                    $rect_sum *= $inv_area;

                    $current_node = null;

                    if ($rect_sum >= $node_thresh*$vnorm) {

                        if ($rightidx == -1) {

                            $tree_sum = $rightval;

                        } else {

                            $current_node = $tree[$rightidx];

                        }

                    } else {

                        if ($leftidx == -1) {

                            $tree_sum = $leftval;

                        } else {

                            $current_node = $tree[$leftidx];
                        }
                    }
                }

                $stage_sum += $tree_sum;
            }
            if ($stage_sum < $stage_thresh) {
                return false;
            }
        }
        return true;
    }
    
    public function detectGreedyBigToSmall($ii,$ii2,$width,$height,$s_w,$s_h) {
        $start_scale = $s_h < $s_w ? $s_h : $s_w;
        $scale_update = 1 / 1.2;
        $count_data = count($this->detection_data);
        $out = [];
        for ($scale = $start_scale; $scale > 1; $scale *= $scale_update) {
            $w = (20*$scale) >> 0;
            $endx = $width - $w - 1;
            $endy = $height - $w - 1;
            $step = max($scale, 2) >> 0;
            $inv_area = 1 / ($w*$w);
            for ($y = 0; $y < $endy; $y += $step) {
                for ($x = 0; $x < $endx; $x += $step) {
                    $passed = $this->detectOnSubImage($x, $y, $scale, $ii, $ii2, $w, $width+1, $inv_area,$count_data);
                    if ($passed) {
                        $out[] = ['x'=>$x, 'y'=>$y, 'w'=>$w];
                    }
                } // end x
            } // end y
        }  // end scale
        return $out;
    }
    
    public function faceDetect($img) {
        if (is_resource($img)) {
            $this->img = $img;
        } else if (is_file($img)) {
            $ex = explode('.',$img);
            $ex = $ex[count($ex)-1];
            if ($ex == 'png') {
                $this->img = imagecreatefrompng($img);
            } else if ($ex == 'bmp') {
                $this->img = imagecreatefrombmp($img);
            } else {
                $this->img = imagecreatefromjpeg($img);
            }
        } else if (is_string($img)) {
            $this->img = imagecreatefromstring($img);
        } else {
            throw new Exception("Can't load your image");
        }
        
        $imgw = imagesx($this->img);
        $imgh = imagesy($this->img);
        
        $diffw = 320 - $imgw;
        $diffh = 240 - $imgh;
        if ($diffw > $diffh) {
            $ratio = $imgw / 320;
        } else {
            $ratio = $imgh / 240;
        }
        
        if ($ratio != 0) {
            $this->reduced_img = imagecreatetruecolor($imgw/$ratio,$imgh/$ratio);
            imagecopyresampled(
                $this->reduced_img,
                $this->img,
                0,
                0,
                0,
                0,
                $imgw/$ratio,
                $imgh/$ratio,
                $imgw,
                $imgh
            );
            
            $stats = $this->getImgStats($this->reduced_img);
            $this->face = $this->detectGreedyBigToSmall(
                $stats['ii'],
                $stats['ii2'],
                $stats['width'],
                $stats['height'],
                $stats['width']/20,
                $stats['height']/20
            );
            
            if (count($this->face) > 0) {
                for ($i = 0; $i < count($this->face); $i++) {
                    $this->face[$i]['x'] *= $ratio;
                    $this->face[$i]['y'] *= $ratio;
                    $this->face[$i]['w'] *= $ratio;
                }
            }
            return $this->face;
        } else {
            $stats = $this->getImgStats($this->img);
            $this->face = $this->detectGreedyBigToSmall(
                $stats['ii'],
                $stats['ii2'],
                $stats['width'],
                $stats['height'],
                $stats['width']/20,
                $stats['height']/20
            );
            return $this->face;
        }
    }
    
    public function draw($img,$result) {
        $color = imagecolorallocate($img,255,0,0);
        foreach ($result as $res) {
            imagerectangle(
                $img,
                $res['x'],
                $res['y'],
                $res['x']+$res['w'],
                $res['y']+$res['w'],
                $color
            );
        }
        return $img;
    }
    
    public function cropFace($img,$result) {
        $res = $result[0];
        $thumb = imagecreatetruecolor($res['w'],$res['w']);
        imagecopy($thumb,$img,0,0,$res['x'],$res['y'],$res['w'],$res['w']);
        return $thumb;
    }
}

$facerec = new HalFaceRec();
if (!empty($_GET['req']) and !empty($_FILES['img'])) {
    $req = $_GET['req'];
    if ($req == 'detectAndDraw') {
        $img = imagecreatefromjpeg($_FILES['img']['tmp_name']);
        header('Content-Type: image/jpeg');
        imagejpeg($facerec->draw($img,$facerec->faceDetect($img)));
    } else if ($req == 'detectAndCrop') {
        $img = imagecreatefromjpeg($_FILES['img']['tmp_name']);
        header('Content-Type: image/jpeg');
        imagejpeg($facerec->cropFace($img,$facerec->faceDetect($img)));
    }
}
?>
  • Вопрос задан
  • 326 просмотров
Решения вопроса 2
myks92
@myks92 Куратор тега PHP
Нашёл решение — пометь вопрос ответом!
Для начала данный код нужно отрефакторить и сделать его хотя бы читабельным. Почитайте про рефакторинг, про ООП...

Так как по коду нечего сказать скажу методы для ускорения работы уже написанного кода.

1. Первый вариант это перейти на более последнюю версию php. Например, 7.х, 8.х. Особенно почувствуете разницу при переходе с версии 5.6 на 7.х. Однако какой-то код может сломать. Подходите этому внимательнее.

2. Использовать кэширование. Кэшировать можно как внедрением в вашем коде кэширования, так и расширениями php вроде opcache.
Ответ написан
XXXXPro
@XXXXPro
Fullstack Web developer
Насколько я понимаю, это алгоритм распознавания лица. Можно использовать готовый из PHP OpenCV (https://github.com/php-opencv/php-opencv). Ну или если в вашем варианте есть какие-то отличия, то реализовать этот же код заново, используя функции PHP OpenCV. Там большая часть вычислений реализована на C, поэтому выполняться будет существенно быстрее. (Правда, для этого нужен VDS, где можно самому ставить PHP extensions, а не обычный хостинг.)
Ещё вариант — переписать код на C самостоятельно, без OpenCV, так, чтобы он висел в виде демона, а взаимодействие с ним происходило через Unix-сокет.
Также, как правильно отметили в комментариях, некоторое ускорение может дать переход с 5 на 7 версию PHP.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы