nudenet‐detection - veka-server/onnx-php GitHub Wiki

nudenet-detection

model sources : https://github.com/notAI-tech/NudeNet (v3.4 : 640m.onnx)

require_once(__DIR__.'/../vendor/autoload.php');

Onnx\Library::setFolder(__DIR__.'/../');
Onnx\Library::install();

$ia = new class extends \Onnx\Task\Vision {
    protected array $tags = [
        "FEMALE_GENITALIA_COVERED",
        "FACE_FEMALE",
        "BUTTOCKS_EXPOSED",
        "FEMALE_BREAST_EXPOSED",
        "FEMALE_GENITALIA_EXPOSED",
        "MALE_BREAST_EXPOSED",
        "ANUS_EXPOSED",
        "FEET_EXPOSED",
        "BELLY_COVERED",
        "FEET_COVERED",
        "ARMPITS_COVERED",
        "ARMPITS_EXPOSED",
        "FACE_MALE",
        "BELLY_EXPOSED",
        "MALE_GENITALIA_EXPOSED",
        "ANUS_COVERED",
        "FEMALE_BREAST_COVERED",
        "BUTTOCKS_COVERED",
    ];
    protected $rescale_factor = 1/255 ;
    protected $format = 'rgb';
    protected $height = 320;
    protected $width = 320;
    protected $shape = 'bchw'; /* batch channel height width */
    protected mixed $modelNameOrPath = __DIR__.'/../models/640m.onnx' ;

    protected function postprocess($result) {

        $resize_factor = 1;
        $pad_left = 0;
        $pad_top = 0;

        // Supposons que $output est une matrice multidimensionnelle.
        $output_squeezed = array_map('array_values', $result[$this->model->outputs()[0]['name']]->toArray()[0]);
        $output_transposed = array_map(null, ...$output_squeezed);

        $rows = count($output_transposed);
        $boxes = array();
        $scores = array();
        $class_ids = array();

        for ($i = 0; $i < $rows; $i++) {
            // Extraction des scores de classes à partir de l'indice 4
            $classes_scores = array_slice($output_transposed[$i], 4);
            $max_score = max($classes_scores);

            if ($max_score >= 0.2) {
                $class_id = array_search($max_score, $classes_scores);
                $x = $output_transposed[$i][0];
                $y = $output_transposed[$i][1];
                $w = $output_transposed[$i][2];
                $h = $output_transposed[$i][3];
                $left = intval(round(($x - $w * 0.5 - $pad_left) * $resize_factor));
                $top = intval(round(($y - $h * 0.5 - $pad_top) * $resize_factor));
                $width = intval(round($w * $resize_factor));
                $height = intval(round($h * $resize_factor));
                $class_ids[] = $class_id;
                $scores[] = $max_score;
                // $boxes[] = array($left, $top, $width, $height);

                /** hack test */
                $boxes[] = array($x - $w * 0.5, $y - $h * 0.5 , $w, $h);
            }
        }

        // Utilisation de Non-Maximum Suppression (NMS)
        $indices = $this->nmsBoxes($boxes, $scores, 0.25, 0.45);

        $detections = array();
        foreach ($indices as $i) {
            $box = $boxes[$i];
            $score = $scores[$i];
            $class_id = $class_ids[$i];
            $detections[] = array(
                "class" => $this->tags[$class_id],
                "score" => floatval($score),
                "box" => $box
            );
        }

        return $detections;
    }



    function nmsBoxes($boxes, $scores, $scoreThreshold, $nmsThreshold) {
        $selectedIndices = [];

        // Filtrer les boîtes et les scores en fonction du seuil de score
        $filteredIndices = array_filter(array_keys($scores), function($i) use ($scores, $scoreThreshold) {
            return $scores[$i] >= $scoreThreshold;
        });

        // Tri des indices filtrés en fonction des scores décroissants
        usort($filteredIndices, function($a, $b) use ($scores) {
            return $scores[$b] <=> $scores[$a];
        });

        while (!empty($filteredIndices)) {
            $current = array_shift($filteredIndices);
            $selectedIndices[] = $current;

            $filteredIndices = array_filter($filteredIndices, function($i) use ($boxes, $nmsThreshold, $current) {
                $iou = $this->calculateIOU($boxes[$current], $boxes[$i]);
                return $iou < $nmsThreshold;
            });
        }

        return $selectedIndices;
    }

    function calculateIOU($boxA, $boxB) {
        // Coordonnées de la boîte A : [x, y, largeur, hauteur]
        $xA = $boxA[0];
        $yA = $boxA[1];
        $wA = $boxA[2];
        $hA = $boxA[3];

        // Coordonnées de la boîte B : [x, y, largeur, hauteur]
        $xB = $boxB[0];
        $yB = $boxB[1];
        $wB = $boxB[2];
        $hB = $boxB[3];

        // Calcul des coordonnées du rectangle d'intersection
        $x1 = max($xA, $xB);
        $y1 = max($yA, $yB);
        $x2 = min($xA + $wA, $xB + $wB);
        $y2 = min($yA + $hA, $yB + $hB);

        // Calcul de l'aire de l'intersection
        $intersectionArea = max(0, $x2 - $x1 + 1) * max(0, $y2 - $y1 + 1);

        // Calcul de l'aire des boîtes A et B
        $boxAArea = ($wA + 1) * ($hA + 1);
        $boxBArea = ($wB + 1) * ($hB + 1);

        // Calcul de l'Union
        $unionArea = $boxAArea + $boxBArea - $intersectionArea;

        // Calcul de l'IOU
        if ($unionArea > 0) {
            $iou = $intersectionArea / $unionArea;
        } else {
            $iou = 0.0;
        }

        return $iou;
    }

};

$ia->loadModel();
$tags = $ia->getTags( __DIR__ . '\images\sexy.jpg');
var_dump($tags);