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



@halwarsing

Как можно ускорить это код 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)));
    }
}
?>


Решения вопроса 0


Ответы на вопрос 2



@maksim92 Куратор тега PHP

Для начала данный код нужно отрефакторить и сделать его хотя бы читабельным. Почитайте про рефакторинг, про ООП…

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

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

2. Использовать кэширование. Кэшировать можно как внедрением в вашем коде кэширования, так и расширениями php вроде opcache.



@XXXXPro

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *