Detectar imagens duplicadas por hash perceptual
Se você já calcula hashes perceptuais para as suas imagens, este endpoint transforma essa lista simples em grupos de duplicatas.
Executar grátis
Você envia uma entrada por imagem — um nome e o seu hash, como string hexadecimal ou como sequência literal de bits 0/1 — além de uma distância máxima de Hamming em bits. Todo par de hashes dentro dessa distância é conectado, e os pares conectados são fundidos em grupos ligados, de modo que uma cadeia de quase duplicatas colapsa em um único cluster em vez de pares sobrepostos. O resultado lista cada grupo com os seus membros, as distâncias internas entre pares e a maior distância dentro do grupo, junto com os totais: quantas imagens são duplicatas e quantas permanecem únicas. Nada é enviado para servidores: os hashes que você já possui são toda a entrada necessária.
De uma lista de hashes a grupos de duplicatas
Um hash perceptual, como um pHash de 64 bits, codifica a aparência de uma imagem, de modo que duas fotos que diferem apenas em compressão, tamanho ou marca d'água geram hashes que diferem em poucos bits. Comparar esses hashes dois a dois é fácil; a parte trabalhosa é o que vem depois. A imagem A corresponde a B, e B corresponde a C, mas A não corresponde exatamente a C — elas formam um conjunto de duplicatas ou dois? Esta capacidade resolve essa questão da forma padrão: trata cada imagem como um nó, cria uma aresta entre dois hashes cuja distância de Hamming seja no máximo o limite que você escolher e devolve os componentes conectados como os grupos de duplicatas. Você recebe uma entrada por grupo com os nomes dos membros, cada par dentro do limite e a sua distância exata, além da maior distância interna do grupo, para poder auditar por que duas imagens ficaram juntas. Como o agrupamento é por transitividade, uma cadeia de regravações que muda um bit por vez ainda cai em um único cluster, que é exatamente o comportamento esperado da detecção de quase duplicatas em bibliotecas de fotos reais.
Escolhendo o limite e o formato do hash
O limite é uma distância máxima de Hamming em bits e o valor padrão é 5, um ponto de partida comum para hashes de 64 bits: imagens idênticas resultam em 0, e recodificações visualmente indistinguíveis costumam ficar abaixo de 5. Reduza o valor na direção de 0 quando quiser apenas correspondências exatas ou quase exatas; aumente com cautela quando o seu pipeline redimensiona de forma agressiva ou corta bordas, pois cada bit extra de tolerância multiplica a chance de falsos positivos em coleções grandes. Os hashes são aceitos em dois formatos: strings hexadecimais, decodificadas a quatro bits por caractere, ou sequências literais de bits 0/1. A única regra rígida é a consistência — todos os hashes de uma requisição devem usar o mesmo formato e decodificar para o mesmo comprimento em bits; caso contrário, a distância entre eles não faria sentido e a requisição é rejeitada com um erro claro. Um limite negativo também é rejeitado como entrada inválida. Você pode calcular os próprios hashes com a capacidade irmã image.phash, que produz exatamente as strings hexadecimais de 16 caracteres que este endpoint espera.
Saída determinística que você pode comparar e armazenar
A resposta foi projetada para ser guardada ao lado do seu catálogo e comparada ao longo do tempo. Os grupos são ordenados pela posição do seu membro mais antigo na lista de entrada, os membros mantêm a ordem de entrada e os pares internos são listados com os dois nomes e a distância exata, de modo que duas execuções sobre a mesma lista produzem saída idêntica byte a byte e regressões aparecem como diffs limpos. Junto com os grupos, você recebe as contagens de que os painéis precisam: total de imagens processadas, o comprimento em bits dos hashes, o número de grupos de duplicatas, quantas imagens caem dentro de algum grupo e quantas permanecem únicas. Não há aleatoriedade, relógio nem rede envolvidos: a mesma entrada sempre gera a mesma resposta, na API e no executor gratuito do navegador nesta página, que executa exatamente o mesmo código. Isso torna seguro usar em pipelines de CI que reprovam um build quando novas duplicatas entram em um repositório de assets, e em rotinas agendadas de limpeza que enviam um relatório de deduplicação por e-mail.
Casos de uso
Deduplicar um catálogo de produtos
Forneça os pHashes de todas as fotos de produtos e una anúncios que reutilizam a mesma imagem com nomes de arquivo ou tamanhos diferentes.
Auditar uma biblioteca de fotos de banco de imagens
Encontre cliques quase idênticos da mesma sessão para que os revisores guardem um representante em vez de doze reexportações.
Proteger um pipeline de upload
Rejeite ou sinalize um novo envio quando o hash dele ficar dentro do limite de uma imagem que você já armazena.
Perguntas frequentes
Quanto custa?
US$ 0,002 por requisição. Também é gratuito executar no seu navegador nesta página com o mesmo código.
Preciso enviar as imagens em si?
Não. Você envia apenas os hashes perceptuais que já calculou, mais um nome por imagem. Os arquivos binários nunca saem do seu lado.
Quais formatos de hash são aceitos?
Strings hexadecimais (quatro bits por caractere, sem distinção entre maiúsculas e minúsculas) e sequências literais de bits 0/1. Todos os hashes de uma requisição devem ter o mesmo formato e o mesmo comprimento em bits.
Que limite devo usar?
O padrão é 5 bits, um ponto de partida sensato para pHashes de 64 bits. Use 0 apenas para correspondências exatas e aumente com cuidado: valores maiores capturam mais regravações, mas também mais falsos positivos.
Como os grupos se formam quando as correspondências se sobrepõem?
Por componentes conectados: se A corresponde a B e B corresponde a C dentro do limite, os três caem em um mesmo grupo, mesmo que A e C estejam mais distantes do que o limite. As distâncias exatas entre pares são listadas para que você possa auditar cada ligação.
O que acontece com um limite negativo?
A requisição é rejeitada como entrada inválida, pois uma distância de Hamming não pode ser negativa, e nada é cobrado de você.
Para desenvolvedores — acesso via API
Tudo nesta página está disponível via API. Esta seção é para equipes que querem integrar a ferramenta aos próprios sistemas; quem não precisa disso pode simplesmente usar a ferramenta acima.
Endpoint
Autenticação por token Bearer. Um único POST coloca a tarefa na fila; o resultado chega por webhook ou link assinado.
Chame do seu código
curl -X POST https://api.kit.forhosting.com/image/duplicate-detect-hash \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"images":[{"name":"hero-a.jpg","hash":"ff8f8383c3c3c3ff"},{"name":"hero-b.jpg","hash":"ff8f8383c3c3c3f7"},{"name":"hero-c.jpg","hash":"ff8f8383c3c3c3f3"},{"name":"logo.png","hash":"007c7c7c7c7c7c00"}]}'const res = await fetch("https://api.kit.forhosting.com/image/duplicate-detect-hash", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"images": [
{
"name": "hero-a.jpg",
"hash": "ff8f8383c3c3c3ff"
},
{
"name": "hero-b.jpg",
"hash": "ff8f8383c3c3c3f7"
},
{
"name": "hero-c.jpg",
"hash": "ff8f8383c3c3c3f3"
},
{
"name": "logo.png",
"hash": "007c7c7c7c7c7c00"
}
]
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/image/duplicate-detect-hash",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"images": [
{
"name": "hero-a.jpg",
"hash": "ff8f8383c3c3c3ff"
},
{
"name": "hero-b.jpg",
"hash": "ff8f8383c3c3c3f7"
},
{
"name": "hero-c.jpg",
"hash": "ff8f8383c3c3c3f3"
},
{
"name": "logo.png",
"hash": "007c7c7c7c7c7c00"
}
]
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/image/duplicate-detect-hash", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"images":[{"name":"hero-a.jpg","hash":"ff8f8383c3c3c3ff"},{"name":"hero-b.jpg","hash":"ff8f8383c3c3c3f7"},{"name":"hero-c.jpg","hash":"ff8f8383c3c3c3f3"},{"name":"logo.png","hash":"007c7c7c7c7c7c00"}]}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"images":[{"name":"hero-a.jpg","hash":"ff8f8383c3c3c3ff"},{"name":"hero-b.jpg","hash":"ff8f8383c3c3c3f7"},{"name":"hero-c.jpg","hash":"ff8f8383c3c3c3f3"},{"name":"logo.png","hash":"007c7c7c7c7c7c00"}]}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/image/duplicate-detect-hash", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Exemplo de requisição
{
"images": [
{
"name": "hero-a.jpg",
"hash": "ff8f8383c3c3c3ff"
},
{
"name": "hero-b.jpg",
"hash": "ff8f8383c3c3c3f7"
},
{
"name": "hero-c.jpg",
"hash": "ff8f8383c3c3c3f3"
},
{
"name": "logo.png",
"hash": "007c7c7c7c7c7c00"
}
]
}Exemplo de resposta
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "image.duplicate_detect_hash",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}A API é assíncrona: cada chamada devolve um task_id na hora. Se preferir polling, consulte o status a até 1 requisição por segundo.
Preço
Preço publicado, sem tokens nem créditos escondidos. Tarefa que falha não é cobrada.
Limites
max_items | 5000 |
Erros
| HTTP | Código | O que significa |
|---|---|---|
401 | unauthorized | Token ausente ou inválido. Confira o header Authorization. |
402 | insufficient_balance | Saldo insuficiente para esta tarefa. Faça uma recarga e tente de novo. |
404 | unknown_type | Esse tipo de tarefa não existe. Confira o campo type no catálogo. |
429 | rate_limited | Muitas requisições em pouco tempo. Espere um instante e tente de novo. |