Twee dingen:1) u gebruikt de database niet volledig en 2) uw probleem is een goed voorbeeld voor een aangepaste PostgreSQL-extensie. Dit is waarom.
U gebruikt de database alleen als opslag en slaat kleuren op als floats. In uw huidige configuratie zal de database, ongeacht het type query, altijd alle waarden moeten controleren (een sequentiële scan maken). Dit betekent veel IO en veel rekenwerk voor weinig geretourneerde matches. U probeert de dichtstbijzijnde N-kleuren te vinden, dus er zijn een paar mogelijkheden om te voorkomen dat u berekeningen op alle gegevens uitvoert.
Eenvoudige verbetering
Het eenvoudigst is om uw berekeningen te beperken tot een kleinere subset van gegevens. Je kunt ervan uitgaan dat het verschil groter is als de onderdelen meer van elkaar verschillen. Als u een veilig verschil tussen de componenten kunt vinden, waarbij de resultaten altijd ongepast zijn, kunt u die kleuren helemaal uitsluiten met behulp van ranged WHERE met btree-indexen. Vanwege de aard van de L*a*b-kleurruimte zal dit uw resultaten echter waarschijnlijk verslechteren.
Maak eerst de indexen:
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);
Vervolgens heb ik uw zoekopdracht aangepast om een WHERE-clausule op te nemen om alleen kleuren te filteren, waarbij een van de componenten maximaal 20 verschilt.
Bijwerken: Na nog een keer kijken, zal het toevoegen van een limiet van 20 de resultaten zeer waarschijnlijk verslechteren, aangezien ik ten minste één punt in de ruimte heb gevonden, waarvoor dit geldt.:
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 ;
Ik heb de tabel gevuld met 100000 willekeurige kleuren met je script en getest:
Tijd zonder indexen:44006.851 ms
Tijd met indexen en bereikquery:1293.092 ms
U kunt deze WHERE-clausule toevoegen aan delta_e_cie1976_query
ook, op mijn willekeurige gegevens verlaagt het de querytijd van ~110 ms naar ~22 ms.
Tussen haakjes:ik heb empirisch nummer 20:ik heb het met 10 geprobeerd, maar kreeg slechts 380 records, wat een beetje laag lijkt en sommige betere opties zou kunnen uitsluiten, aangezien de limiet 100 is. Met 20 was de volledige set 2900 rijen en één kan redelijk zijn zeker dat de dichtstbijzijnde overeenkomsten er zullen zijn. Ik heb de DELTA_E_CIE2000- of L*a*b*-kleurruimte niet in detail bestudeerd, dus de drempelwaarde moet mogelijk langs verschillende componenten worden aangepast voordat dat echt waar is, maar het principe van het uitsluiten van niet-interessante gegevens geldt.
Herschrijf Delta E CIE 2000 in C
Zoals u al zei, is Delta E CIE 2000 complex en tamelijk ongeschikt om in SQL te implementeren. Het gebruikt momenteel ongeveer 0,4 ms per gesprek op mijn laptop. Implementatie in C zou dit aanzienlijk moeten versnellen. PostgreSQL kent standaardkosten toe aan SQL-functies als 100 en C-functies als 1. Ik vermoed dat dit gebaseerd is op echte ervaring.
Bijwerken: Aangezien dit ook een van mijn jeuken schaadt, heb ik de Delta E-functies van de colormath-module in C opnieuw geïmplementeerd als een PostgreSQL-extensie, beschikbaar op PGXN . Hiermee kan ik een snelheid van ongeveer 150x zien voor CIE2000 bij het opvragen van alle records uit de tabel met 100k records.
Met deze C-functie krijg ik querytijden tussen 147 ms en 160 ms voor 100k kleuren. Met extra WHERE is de querytijd ongeveer 20 ms, wat voor mij heel acceptabel lijkt.
Beste, maar geavanceerde oplossing
Aangezien uw probleem echter N is zoeken naar de dichtstbijzijnde buur in een driedimensionale ruimte, kunt u K-Nearest-Neighbor Indexing gebruiken, dat zich in PostgreSQL sinds versie 9.1 .
Om dat te laten werken, zou je L*a*b*-componenten in een kubus . Deze extensie ondersteunt nog geen afstandsoperator ( het is in de maak ), maar zelfs als dat zo zou zijn, zou het geen Delta E-afstanden ondersteunen en zou u het opnieuw moeten implementeren als een C-extensie.
Dit betekent het implementeren van de GiST-indexoperatorklasse (btree_gist PostgreSQL-extensie
in contrib doet dit) om indexering volgens Delta E-afstanden te ondersteunen. Het goede is dat je dan verschillende operators kunt gebruiken voor verschillende versies van Delta E, bijv. <->
voor Delta E CIE 2000 en <#>
voor Delta E CIE 1976 en vragen zouden heel erg snel
zijn voor kleine LIMIT, zelfs met Delta E CIE 2000.
Uiteindelijk kan het afhangen van wat uw (zakelijke) vereisten en beperkingen zijn.