O Nest Tem Potencial pra Ser um Archiver de Cartas de Magic
Por Brenner Cruvinel • 6 minutos de leitura
o felipe navarro, amigo meu, tá construindo o pasta.cards, um app opensource pra gerenciar coleção e deck de commander. roda local, guarda tudo em sqlite, e pra funcionar offline ele baixa a arte de cada carta de magic que já foi impressa, em duas versões: a carta inteira e só a ilustração. no meu mac isso deu 83 mil jpg e 7 GB. cada carta que existe, duas vezes, no meu disco, e o finder demorando pra abrir a pasta.
o que me chamou atenção nesse número não foi o tamanho, disco é barato. foi a estrutura. as cartas dividem moldura, caixa de texto, tipografia, símbolo de mana. e o banco ao lado mostra que quase metade das impressões reusa uma arte que já saiu em outra carta. jpg solto não sabe nada disso. um catálogo bom aponta em vez de copiar, e um codec bom não gasta bit no pixel que dá pra prever do vizinho. eu gosto das duas coisas pelo mesmo motivo.
eu tinha o nest do lado, o banco de vetores de arquivo único que eu venho construindo este ano, e desde agosto ele guarda imagem também. a pergunta que eu fiz naquela manhã foi bem mais ingênua do que tudo que veio depois: como funciona, ele transforma em um vídeo? eu tenho 38 mil imagens e um database, o que ele faz com isso?
ele transforma em um vídeo?
ele transforma em vídeo mesmo. cada imagem vira um frame de um stream av1 dentro do arquivo, e a busca roda sobre vetores tirados dos frames decodificados, não do jpeg de origem. isso é de propósito: o índice tem que descrever o que o arquivo serve, e o que o arquivo serve é o frame depois do codec. codec de vídeo é a tecnologia de compressão de imagem mais otimizada que existe, então o que parece gambiarra é só aproveitar décadas de trabalho dos outros.
eu já tinha medido isso em pele. dermatoscopia, lâmina de biópsia, página de livro antigo, os datasets que eu tinha na mão pelo trabalho na hoff. o que aqueles números me ensinaram é que o quanto comprime é uma propriedade do domínio, não do codec. mesmo encoder, mesmo ajuste, a dermatoscopia comprimiu 151 vezes e a página de livro comprimiu 4. pele lisa comprime bem porque tem pouco detalhe pra guardar. letra pequena é o contrário, cada borda custa bit.
carta de magic tem letra pequena. tem moldura igual em todas, que o codec pode ou não aproveitar. tem reprint da mesma arte, coisa que a dermatoscopia não tem. e tem uma coisa de produto que nenhuma métrica de busca mede: o tile de deck precisa ficar bonito na tela do app. eu achava que a pergunta era quanto comprime. era, mas por pouco tempo.
antes de rodar qualquer coisa, olhei a pasta bruta com mais calma, e ela tinha as próprias surpresas. metade do peso era a versão recortada da arte, e minha primeira ideia foi jogar ela fora e recortar da carta inteira quando precisasse. um agente leu o código do app e voltou com má notícia: o banco não guarda coordenada de recorte nenhuma, e o app usa o recorte direto nos tiles de deck. a arte recortada é uma classe própria, não umo driftda. e do lado da pasta de imagens tinha um diretório chamado _redundant, com mais sete giga de cópia de coisa que já estava lá. esse eu deletei sem dó.
o primeiro build
o piloto com algumas centenas de cartas morreu com segfault antes de fazer qualquer coisa. o binding python do nest tinha sido compilado sem a flag que diz "usa o python do processo, não traz o seu", e o python que eu uso é estático, então eram dois interpretadores brigando pela mesma memória. uma flag e pronto. bug pequeno, e do tipo que me deixa desconfiado: quantos outros pressupostos de ambiente o build carrega sem declarar?
com a flag, o build completo rodou em background e eu abri um dos vídeos no player. carta passando, uma por segundo, quarenta minutos de carta. minha primeira hipótese foi que o encoder estava repetindo frames pra segurar cada carta na tela, e que dava pra cortar isso. uma linha de ffprobe matou a hipótese: um frame por carta, e a duração é só anotação de tempo no container, não custa byte.
7 GB viraram 1,9 GB, com busca por imagem funcionando em cima. rodou de primeira, o que num projeto meu é raro o bastante pra eu desconfiar. aí eu abri a pasta de saída.
um archiver com 38 mp4 do lado não é um archiver
38 arquivos de vídeo, um .nest com os índices, um manifest em json e um diretório de cache. quatro tipos de arquivo pra servir uma coisa só. o nest existe pra ser o banco num arquivo, e o que saiu foi um banco com quarenta coisas ao lado. funcionava, do jeito que funciona um móvel montado com parafuso sobrando. o propósito do nest estava sendo perdido ali, e a compressão nem era o problema. o nest tem potencial pra ser um archiver de carta de magic, de mapa, de qualquer dataset de imagem com database do lado. mas archiver é um arquivo, não uma pasta.
a segunda pergunta veio do modelo. o script usava clip porque era o que o forge tinha, e eu nem sabia direito o que o clip fazia com uma carta. fui atrás: ele olha a arte e ignora o texto impresso. pra uma carta de magic isso é olhar a metade menos útil, porque a coisa que identifica a carta é o nome escrito em cima. pedi uma pesquisa no hugging face e no github do que existia de modelo multimodal aberto naquele mês, e a resposta era uma família da tencent chamada wemm, que lê texto dentro de imagem, mais o siglip e o jina. eu tinha o wemm de dois bilhões de parâmetros baixado por outro motivo.
numa comparação com 1500 cartas e sessenta buscas do tipo "a arte da carta tal", o clip acertou a carta certa uma vez em quatro. o wemm acertou nove em dez, o siglip ficou perto dele, o jina no meio. o preço do wemm é ser umas trinta vezes mais lento que o clip, e rodar ele no corpus inteiro levaria quase um dia na minha máquina. então a comparação ficou na amostra, e o corpus completo continuou com o clip por enquanto.
o que saiu disso não foi "troca pro wemm". foi que o modelo não pode ser uma constante no topo de um script. tem que ser uma lista de presets dentro da engine, e quem builda escolhe qual, ou quais, na hora do build. e o vídeo tem que caber dentro do arquivo, não do lado. antes de qualquer uma dessas duas coisas, eu queria saber quanto dava pra comprimir sem perder nada. nem que a gente tivesse que inventar.