Artigo Técnico

Reduzir PDFs mesclando imagens quase idênticas no Delphi

A PDF Library for Delphi pode mesclar imagens dentro de um PDF que parecem iguais, mas não são idênticas byte a byte, por meio de DeduplicateSimilarImages. A deduplicação exata só mescla imagens cujos dicionários e cargas úteis codificadas coincidem; esta compara o que as imagens realmente parecem, usando um hash perceptual mais dois limites de erro de pixel, e redireciona toda referência para um único objeto canônico

Os arquivos onde isso compensa são os que cresceram por acidente. Um lote digitalizado em que cada página carrega o mesmo logotipo recodificado por um scanner diferente. Um relatório montado a partir de fontes que cada uma incorporou o mesmo gráfico em uma qualidade JPEG ligeiramente diferente. Um catálogo cujas miniaturas foram regeneradas por duas ferramentas. Em cada caso o arquivo contém muitas cópias de uma imagem, e a deduplicação exata não consegue tocar em nenhuma delas

O que "parecido o suficiente" precisa significar

Três limites precisam ser satisfeitos antes que duas imagens sejam mescladas, e cada um captura um tipo diferente de diferença. A distância do hash perceptual delimita a diferença estrutural: um hash de diferença de 64 bits é calculado a partir de uma amostra de 64 por 64 da imagem decodificada, e a distância de Hamming permitida vai de 0 a 7. O erro médio de canal delimita o desvio geral, que é o que a recodificação produz. O erro máximo de canal único delimita a diferença localizada, que é o que uma pequena edição produz

Exigir os três é o que torna a mesclagem defensável. Uma imagem que difere por uma marca d'água em um canto tem um erro médio baixo e um erro máximo alto, então o portão de erro máximo a rejeita. Uma imagem que foi recomprimida tem um pequeno erro em toda parte, então ela passa. Essa é exatamente a distinção que um otimizador de tamanho precisa fazer

Três limites controlam a mesclagem de imagens quase idênticas: distância de hash, erro médio de canal e erro máximo de canal único
Os três portões usam pixels decodificados — um par com marca d'água é barrado no portão de erro máximo, enquanto um gêmeo recodificado passa tranquilamente
uses
  PDFlibrary;

var
  Lib: TPDFlib;
  Stats: TPDFlibImageSimilarityDedupStats;
  Merged: Integer;
begin
  Lib := TPDFlib.Create;
  try
    Lib.LoadFromFile('scanned-batch.pdf', '');

    // Distância de hash 2, erro médio 2, erro de canal único 4.
    // As opções podem pedir uma execução de teste sem modificar nada
    Merged := Lib.DeduplicateSimilarImages(2, 2, 4, 0, Stats);
    if Merged < 0 then
      raise Exception.Create('deduplication failed');

    Log(Format('%d scanned, %d merged, %d refs repointed, %d bytes duplicated',
      [Stats.ImagesScanned, Stats.ImagesDeduplicated,
       Stats.ReferencesRepointed, Stats.DuplicatePayloadBytes]));
    Log(Format('worst accepted: hash %d, mean %.2f, channel %d',
      [Stats.MaxAcceptedHashDistance, Stats.MaxAcceptedMeanError,
       Stats.MaxAcceptedChannelError]));
    Lib.SaveToFile('scanned-batch-optimised.pdf');
  finally
    Lib.Free;
  end;
end;

A identidade semântica vem antes da similaridade visual

Antes de qualquer pixel ser comparado, as imagens candidatas precisam concordar semanticamente. A biblioteca constrói uma assinatura estável a partir do dicionário de imagem completo, excluindo /Length, /Filter e /DecodeParms, de modo que dimensões, espaço de cor, profundidade de bits, máscaras, o array /Decode, interpolação, associação a conteúdo opcional e metadados, tudo precisa coincidir

Essa ordenação evita toda uma classe de mesclagens erradas. Duas imagens podem parecer idênticas em uma amostra de 64 por 64 e se comportar de forma diferente na página: uma com /Interpolate verdadeiro e outra sem serão suavizadas de forma diferente ao serem escaladas, e uma pertencente a um grupo de conteúdo opcional desaparecerá quando essa camada for ocultada. Mesclar através dessas fronteiras produz um documento que é menor e errado

Apenas Image XObjects indiretos são escaneados. Imagens inline são uma construção diferente com economia diferente, e imagens que não podem ser decodificadas são ignoradas de forma conservadora e contabilizadas em vez de mescladas por fé — sem evidência de pixel, sem mesclagem

Como a busca permanece barata

Comparar toda imagem contra toda outra imagem é quadrático, o que é inaceitável em um documento contendo milhares de imagens. O hash é, portanto, dividido em oito bandas de 8 bits, com um índice ordenado sobre cada uma

O princípio da casa dos pombos faz o resto: se dois hashes diferem em no máximo 7 bits, eles precisam concordar exatamente em pelo menos uma das oito bandas. Buscar candidatos banda por banda, portanto, encontra toda correspondência permissível sem uma varredura exaustiva de pares, e o limite publicado de 7 existe precisamente porque a garantia só vale até essa distância

O hash de diferença de 64 bits se divide em oito bandas de 8 bits, e o princípio da casa dos pombos garante que uma banda ainda corresponda depois de sete inversões de bit
Sete bits invertidos entre oito bandas precisam deixar uma banda intacta, o que transforma uma varredura quadrática em oito buscas ordenadas

Candidatos e acertos de banda são ordenados deterministicamente por número de objeto e geração, e o menor número de objeto se torna canônico. Isso torna a saída reproduzível: rodar o otimizador duas vezes na mesma entrada produz as mesmas escolhas canônicas, o que importa quando o resultado é comparado ou assinado

Execuções de teste e relatórios honestos

Como isso é uma otimização com perdas no sentido de que as imagens mescladas não eram idênticas, o fluxo de trabalho sensato é olhar antes de saltar. A opção de execução de teste relata o que seria mesclado e quanto de carga útil seria recuperada sem modificar o grafo de objetos, de modo que um trabalho em lote pode registrar o efeito e um humano pode aprovar os limites uma vez, em vez de por arquivo

Duas notas de honestidade no relatório merecem menção. O número recuperável estima apenas a carga útil codificada de streams duplicados; ele não conta o overhead de dicionário, objeto indireto e referência cruzada, então a redução real de tamanho de arquivo é ligeiramente maior do que a relatada, e não menor. E o relatório expõe o erro máximo aceito e a carga de trabalho de comparação, de modo que você pode ver o quão perto as mesclagens chegaram dos seus limites

A grade fixa de amostragem de 64 por 64 é uma aproximação intencional, e tem uma consequência que vale a pena declarar claramente: uma diferença menor do que uma célula de amostra pode passar despercebida mesmo com um limite zero. Uma única palavra alterada em uma página digitalizada pode não sobreviver a essa amostragem. Quando prova em nível de byte é exigida, use o caminho de deduplicação exata em vez disso — ele mescla apenas recursos genuinamente idênticos e não faz nenhum julgamento visual

Onde isso se encaixa em um pipeline de redução de tamanho

Ordene as passagens por risco. Rode primeiro a deduplicação exata de recursos, já que ela não tem perdas e é gratuita. Depois a subsetagem de fontes, que também não tem perdas e geralmente é o maior ganho isolado em documentos com muito texto, como abordado em otimização de tamanho de arquivo PDF e subsetagem de fontes. Depois recompressão ou subamostragem de imagem, se o meio de destino permitir. A deduplicação perceptual vem depois dessas, porque é a única etapa cujo resultado depende de um julgamento

Ordem recomendada do pipeline, da deduplicação exata sem perdas e subsetagem de fontes até a recompressão condicional de imagens, deduplicação perceptual e verificação
Cada etapa acrescenta risco, então os ganhos sem perdas vêm primeiro e a mesclagem por julgamento roda por último, seguida de uma verificação de renderizar e comparar

Escolha os limites por classe de documento, e não globalmente. Para lotes digitalizados em que toda página carrega o mesmo timbre, uma distância de hash de 2 a 3 com um erro médio em torno de 2 mescla o timbre de forma confiável. Para documentos em que as imagens carregam dados — gráficos, mapas, imagens médicas — use limites zero ou próximos de zero, ou pule a passagem inteiramente, porque dois gráficos que diferem em um ponto plotado são dois gráficos diferentes, não importa o quão parecidos pareçam em 64 por 64

Depois de otimizar, verifique. Renderize uma amostra de páginas antes e depois e compare-as, e confira se a contagem de páginas e a contagem de imagens mudaram do jeito que você esperava. O caminho de inspeção para o que um documento realmente contém é descrito em auditando o tamanho de arquivo PDF por categoria, que também é a forma mais rápida de descobrir se as imagens são seu problema, para começar

A deduplicação perceptual, a deduplicação exata de recursos e a auditoria de tamanho rodam sobre o mesmo grafo de objetos para Delphi, C++Builder e Free Pascal; a lista completa de recursos está na página da PDF Library for Delphi