PHP, AI

Using ApyHub for Image Moderation

In Auctibles, we use ApyHub to moderate images of items for auction. Auctibles is built using the Laravel PHP framework.

Uploading Image Files

Videos are effortlessly uploaded using a straightforward Livewire component and an HTML input element with type=file.

<input id="photos" name="photos" type="file" class="sr-only" wire:model.live="photos" accept="image/png,image/jpg,image/gif">

The component has a public property:

public $photos = [];

Upon clicking a submit button, we apply validation to the field,

'photos' => 'nullable|array|max:3', // array
'photos.*' => [
    'required',
    'image',
    'max:10240', // 10MB
    new ExplicitImage(),
],  

ExplicitImage is a validation rule.

Temporary Files

We upload temporary files to a Minio bucket which resides on the server. The bucket is defined as an uploads bucket for Laravel. This is done in config/filesystems.php.

ApyHub Results for Image Content

We use curl to call ApyHub. The response looks like this:

{
  "data": {
    "apyhub": {
      "adult": {
        "adultScore": 0.0025164163671433926,
        "goreScore": 0.0014069777680560946,
        "isAdultContent": false,
        "isGoryContent": false,
        "isRacyContent": false,
        "racyScore": 0.0032450903672724962
      },
      "metadata": {
        "format": "Png",
        "height": 1024,
        "width": 1024
      }
    }
  }
}

The Validation Rule

<?php

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use CURLFile;
use CURLStringFile;
use Illuminate\Support\Facades\Storage;

class ExplicitImage implements ValidationRule
{
    /**
     * Run the validation rule.
     *
     * @param  \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString  $fail
     */
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
            // path to temporary uploaded file in uploads disk
        $path = config('livewire')['temporary_file_upload']['directory'] . DIRECTORY_SEPARATOR . $value->getFilename();

        $ch = curl_init();

        curl_setopt(
          $ch,
          CURLOPT_URL,
          "https://api.apyhub.com/ai/image/detect/explicit-content/file"
        );
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
          'apy-token: ' . config('app.APYHUB_TOKEN'),
          'content-type: multipart/form-data'
        ]);
        curl_setopt($ch, CURLOPT_POSTFIELDS, [
            'file' => new CURLStringFile(Storage::disk('uploads')->get($path), $value->getFilename(), $value->getMimeType()),
            'requested_service' => 'apyhub',
        ]);

        $response = curl_exec($ch);

        curl_close($ch);

        if ($response === false) {
          $error = curl_error($ch);
          logger()->warning("ApyHub image moderation CURL Error: $error");
          return;
        } else {
          // Process the successful CURL call here.
          $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

          if ($statusCode == 200) {
            // The request was successful
            $data = json_decode($response, true); //

            $response = $data['data']['apyhub']['adult'];

            if ($response['isAdultContent'] || $response['isGoryContent'] || $response['isRacyContent']) {
                $fail('validation.' . 'explicit_image')->translate(); 
            }

          } else {
            // The server responded with an error
            logger()->warning("ApyHub image moderation HTTP Error: $statusCode");
            return;
          }
        }

    }
}

Yoram Kornatzky

Yoram Kornatzky