PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Desempenho do Delta E (CIE Lab) calculando e ordenando em SQL


Duas coisas:1) você não está usando o banco de dados em toda a sua extensão e 2) seu problema é um ótimo exemplo para uma extensão personalizada do PostgreSQL. Aqui está o porquê.

Você está usando apenas o banco de dados como armazenamento, armazenando cores como floats. Na sua configuração atual, independente do tipo de consulta, o banco de dados sempre terá que verificar todos os valores (fazer uma varredura sequencial). Isso significa muito IO e muito cálculo para poucas correspondências retornadas. Você está tentando encontrar as N cores mais próximas, portanto, existem algumas possibilidades de como evitar a realização de cálculos em todos os dados.

Melhoria simples


O mais simples é limitar seus cálculos a um subconjunto menor de dados. Você pode assumir que a diferença será maior se os componentes diferirem mais. Se você puder encontrar uma diferença segura entre os componentes, onde os resultados são sempre inapropriados, você pode excluir essas cores completamente usando WHERE variado com índices btree. No entanto, devido à natureza do espaço de cores L*a*b, isso provavelmente piorará seus resultados.

Primeiro crie os índices:
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);

Então adaptei sua consulta para incluir uma cláusula WHERE para filtrar apenas cores, onde qualquer um dos componentes difere por no máximo 20.

Atualização: Depois de outra olhada, adicionar um limite de 20 provavelmente piorará os resultados, pois encontrei pelo menos um ponto no espaço, para o qual isso é verdade.:
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 ;

Preenchi a tabela com 100.000 de cores aleatórias com seu script e testei:

Tempo sem índices:44.006.851 ms

Tempo com índices e consulta de intervalo:1293.092 ms

Você pode adicionar esta cláusula WHERE a delta_e_cie1976_query também, nos meus dados aleatórios, ele diminui o tempo de consulta de ~ 110 ms para ~ 22 ms.

BTW:Consegui o número 20 empiricamente:tentei com 10, mas obtive apenas 380 registros, o que parece um pouco baixo e pode excluir algumas opções melhores, pois o limite é 100. Com 20, o conjunto completo era de 2900 linhas e pode ser bastante certeza de que as correspondências mais próximas estarão lá. Eu não estudei o espaço de cores DELTA_E_CIE2000 ou L*a*b* em detalhes, então o limite pode precisar de ajuste ao longo de diferentes componentes para que isso seja realmente verdade, mas o princípio de excluir dados não interessantes se mantém.

Reescreva Delta E CIE 2000 em C


Como você já disse, o Delta E CIE 2000 é complexo e bastante inadequado para implementação em SQL. Atualmente, ele usa cerca de 0,4 ms por chamada no meu laptop. Implementá-lo em C deve acelerar consideravelmente isso. PostgreSQL atribui o custo padrão para funções SQL como 100 e funções C como 1. Acho que isso é baseado em experiência real.

Atualização: Como isso também me irrita, reimplementei as funções Delta E do módulo colormath em C como uma extensão do PostgreSQL, disponível em PGXN . Com isso consigo ver uma aceleração de cerca de 150x para o CIE2000 ao consultar todos os registros da tabela com 100k registros.

Com esta função C, recebo tempos de consulta entre 147 ms e 160 ms para 100k cores. Com WHERE extra, o tempo de consulta é de cerca de 20 ms, o que parece bastante aceitável para mim.

Melhor, mas solução avançada


No entanto, como seu problema é a pesquisa de N vizinho mais próximo em espaço tridimensional, você pode usar a indexação K-Nearest-Neighbor que está no PostgreSQL desde a versão 9.1 .

Para que isso funcione, você colocaria componentes L*a*b* em um cubo . Esta extensão ainda não é compatível com o operador de distância ( está em andamento ), mas mesmo que fosse, não suportaria distâncias Delta E e você precisaria reimplementá-lo como uma extensão C.

Isso significa implementar a classe de operadores de índice GiST (btree_gist extensão PostgreSQL in contrib faz isso) para suportar a indexação de acordo com as distâncias Delta E. A parte boa é que você pode usar diferentes operadores para diferentes versões do Delta E, por exemplo. <-> para Delta E CIE 2000 e <#> para Delta E CIE 1976 e as consultas seriam muito muito rápidas para LIMIT pequeno mesmo com Delta E CIE 2000.

No final, pode depender de quais são os seus requisitos (negócios) e restrições.