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

Performances de calcul et de tri de Delta E (CIE Lab) en SQL

Deux choses :1) vous n'utilisez pas la base de données dans son intégralité et 2) votre problème est un excellent exemple d'extension PostgreSQL personnalisée. Voici pourquoi.

Vous utilisez uniquement la base de données comme stockage, en stockant les couleurs sous forme de flotteurs. Dans votre configuration actuelle, quel que soit le type de requête, la base de données devra toujours vérifier toutes les valeurs (faire un parcours séquentiel). Cela signifie beaucoup d'IO et beaucoup de calculs pour peu de correspondances retournées. Vous essayez de trouver les N couleurs les plus proches, il existe donc quelques possibilités pour éviter d'effectuer des calculs sur toutes les données.

Amélioration simple

Le plus simple est de limiter vos calculs à un plus petit sous-ensemble de données. Vous pouvez supposer que la différence sera plus grande si les composants diffèrent davantage. Si vous pouvez trouver une différence sûre entre les composants, où les résultats sont toujours inappropriés, vous pouvez exclure complètement ces couleurs en utilisant WHERE avec des index btree. Cependant, en raison de la nature de l'espace colorimétrique L*a*b, cela aggravera probablement vos résultats.

Créez d'abord les index :

CREATE INDEX color_lab_l_btree ON color USING btree (lab_l);
CREATE INDEX color_lab_a_btree ON color USING btree (lab_a);
CREATE INDEX color_lab_b_btree ON color USING btree (lab_b);

Ensuite, j'ai adapté votre requête pour inclure une clause WHERE pour filtrer uniquement les couleurs, où l'un des composants diffère d'au plus 20.

Mise à jour : Après un autre coup d'œil, l'ajout d'une limite de 20 aggravera très probablement les résultats, car j'ai trouvé au moins un point dans l'espace, pour lequel cela est vrai. :

SELECT 
    c.rgb_r, c.rgb_g, c.rgb_b,
    DELTA_E_CIE2000(
        25.805780252087963, 53.33446637366859, -45.03961353720049, 
        c.lab_l, c.lab_a, c.lab_b,
        1.0, 1.0, 1.0) AS de2000
FROM color c 
WHERE 
    c.lab_l BETWEEN 25.805780252087963 - 20 AND 25.805780252087963 + 20 
    AND c.lab_a BETWEEN 53.33446637366859 - 20 AND 53.33446637366859 + 20 
    AND c.lab_b BETWEEN -45.03961353720049 - 20 AND -45.03961353720049 + 20 
ORDER BY de2000 ;

J'ai rempli le tableau avec 100000 couleurs aléatoires avec votre script et testé :

Temps sans index :44006,851 ms

Temps avec index et requête de plage :1 293 092 ms

Vous pouvez ajouter cette clause WHERE à delta_e_cie1976_query de plus, sur mes données aléatoires, le temps de requête passe d'environ 110 ms à environ 22 ms.

BTW :J'ai obtenu le nombre 20 de manière empirique :j'ai essayé avec 10, mais je n'ai obtenu que 380 enregistrements, ce qui semble un peu faible et pourrait exclure de meilleures options puisque la limite est de 100. Avec 20, l'ensemble complet était de 2900 lignes et on peut être assez sûr que les matchs les plus proches seront là. Je n'ai pas étudié en détail l'espace colorimétrique DELTA_E_CIE2000 ou L*a*b*, donc le seuil peut nécessiter un ajustement selon différentes composantes pour que cela soit réellement vrai, mais le principe d'exclusion des données non intéressantes est valable.

Réécrire Delta E CIE 2000 en C

Comme vous l'avez déjà dit, Delta E CIE 2000 est complexe et assez inadapté à une implémentation en SQL. Il utilise actuellement environ 0,4 ms par appel sur mon ordinateur portable. Son implémentation en C devrait considérablement accélérer cela. PostgreSQL attribue un coût par défaut aux fonctions SQL à 100 et aux fonctions C à 1. Je suppose que cela est basé sur une expérience réelle.

Mise à jour : Comme cela gratte aussi une de mes démangeaisons, j'ai réimplémenté les fonctions Delta E du module colormath en C en tant qu'extension PostgreSQL, disponible sur PGXN . Avec cela, je peux voir une accélération d'environ 150x pour CIE2000 lors de l'interrogation de tous les enregistrements de la table avec 100 000 enregistrements.

Avec cette fonction C, j'obtiens des temps de requête entre 147 ms et 160 ms pour 100k couleurs. Avec WHERE supplémentaire, le temps de requête est d'environ 20 ms, ce qui me semble tout à fait acceptable.

Meilleure solution, mais avancée

Cependant, puisque votre problème est la recherche de N voisins les plus proches dans un espace tridimensionnel, vous pouvez utiliser l'indexation K-Nearest-Neighbor qui se trouve dans PostgreSQL depuis la version 9.1 .

Pour que cela fonctionne, vous devez mettre les composants L*a*b* dans un cube . Cette extension ne prend pas encore en charge l'opérateur à distance ( c'est en cours ), mais même si c'était le cas, il ne prendrait pas en charge les distances Delta E et vous auriez besoin de le réimplémenter en tant qu'extension C.

Cela signifie implémenter la classe d'opérateur d'index GiST (extension PostgreSQL btree_gist in contrib le fait) pour prendre en charge l'indexation en fonction des distances Delta E. La bonne partie est que vous pouvez alors utiliser différents opérateurs pour différentes versions de Delta E, par exemple. <-> pour Delta E CIE 2000 et <#> pour Delta E CIE 1976 et les requêtes seraient très très rapides pour petite LIMIT même avec Delta E CIE 2000.

En fin de compte, cela peut dépendre de vos exigences et contraintes (professionnelles).