Mysql
 sql >> Base de données >  >> RDS >> Mysql

Libpuzzle Indexer des millions d'images ?

Alors, regardons l'exemple qu'ils donnent et essayons de développer.

Supposons que vous ayez une table qui stocke les informations relatives à chaque image (chemin, nom, description, etc.). Dans cette table, vous incluez un champ pour la signature compressée, calculée et stockée lorsque vous remplissez initialement la base de données. Définissons cette table ainsi :

CREATE TABLE images (
    image_id INTEGER NOT NULL PRIMARY KEY,
    name TEXT,
    description TEXT,
    file_path TEXT NOT NULL,
    url_path TEXT NOT NULL,
    signature TEXT NOT NULL
);

Lorsque vous calculez initialement la signature, vous allez également calculer un certain nombre de mots à partir de la signature :

// this will be run once for each image:
$cvec = puzzle_fill_cvec_from_file('img1.jpg');
$words = array();
$wordlen = 10; // this is $k from the example
$wordcnt = 100; // this is $n from the example
for ($i=0; $i<min($wordcnt, strlen($cvec)-$wordlen+1); $i++) {
    $words[] = substr($cvec, $i, $wordlen);
}

Vous pouvez maintenant mettre ces mots dans un tableau, défini ainsi :

CREATE TABLE img_sig_words (
    image_id INTEGER NOT NULL,
    sig_word TEXT NOT NULL,
    FOREIGN KEY (image_id) REFERENCES images (image_id),
    INDEX (image_id, sig_word)
);

Maintenant, vous insérez dans ce tableau, en ajoutant l'index de position de l'endroit où le mot a été trouvé, de sorte que vous sachiez quand un mot correspond qu'il correspond au même endroit dans la signature :

// the signature, along with all other data, has already been inserted into the images
// table, and $image_id has been populated with the resulting primary key
foreach ($words as $index => $word) {
    $sig_word = $index.'__'.$word;
    $dbobj->query("INSERT INTO img_sig_words (image_id, sig_word) VALUES ($image_id,
        '$sig_word')"); // figure a suitably defined db abstraction layer...
}

Vos données ainsi initialisées, vous pouvez saisir des images avec des mots correspondants relativement facilement :

// $image_id is set to the base image that you are trying to find matches to
$dbobj->query("SELECT i.*, COUNT(isw.sig_word) as strength FROM images i JOIN img_sig_words
    isw ON i.image_id = isw.image_id JOIN img_sig_words isw_search ON isw.sig_word =
    isw_search.sig_word AND isw.image_id != isw_search.image_id WHERE
    isw_search.image_id = $image_id GROUP BY i.image_id, i.name, i.description,
    i.file_path, i.url_path, i.signature ORDER BY strength DESC");

Vous pouvez améliorer la requête en ajoutant un HAVING clause qui nécessite un minimum de strength , réduisant ainsi davantage votre ensemble correspondant.

Je ne garantis pas qu'il s'agisse de la configuration la plus efficace, mais elle devrait être à peu près fonctionnelle pour accomplir ce que vous recherchez.

Fondamentalement, diviser et stocker les mots de cette manière vous permet de faire une vérification approximative de la distance sans avoir à exécuter une fonction spécialisée sur les signatures.