Limpador de PDF digitalizado (Scanned PDF cleaner)
Fotos de celular de um livro aberto ficam curvadas perto da lombada, inclinadas, acinzentadas, com um dedo na borda e um fragmento da página seguinte. Este skill transforma essas páginas em folhas que se leem como uma impressão limpa.
Os scripts fazem o trabalho nos pixels. Você faz o julgamento: você traça os cantos de cada página, observa o que foi removido e decide quais marcas fora do texto são impressas (manter) e quais não são (remover). Cada etapa escreve folhas de contato que podem ser lidas como imagens, então olhe para elas. Não presuma que uma etapa funcionou.
Configuração (uma vez por máquina)
SKILL=<pasta deste skill> # a pasta que contém este SKILL.md
python3 -m venv ~/.cache/scanned-pdf-cleaner-venv
~/.cache/scanned-pdf-cleaner-venv/bin/pip install -q -r "$SKILL/requirements.txt"
PY=~/.cache/scanned-pdf-cleaner-venv/bin/python
Todo comando abaixo é executado como cd "$SKILL/scripts" && $PY <script> .... Use uma pasta de trabalho nova e vazia por documento, fora do projeto do usuário (uma pasta temporária). Os arquivos do usuário são apenas lidos.
Fluxo de trabalho
1. Preparar
$PY prepare.py INPUT.pdf WORK # ou: uma pasta de imagens, ou arquivos de imagem (HEIC: converta primeiro)
Escreve WORK/src/p-NNN.jpg (fotos retas), WORK/grid/g-NNN.jpg (cada foto com uma grade vermelha a cada 100 px, numerada em centenas nas quatro bordas) e WORK/rough.json com um contorno provisório por foto.
Se uma foto estiver de lado ou de cabeça para baixo, vire-a: $PY prepare.py --turn WORK p-003.jpg 90|180|270 (sentido horário).
2. Traçar os cantos da página (você)
Leia cada imagem de grade. Para cada foto, encontre a página que pertence ao documento. Escreva seus quatro cantos em WORK/rough.json em pixels de origem, ordenados superior esquerdo, superior direito, inferior direito, inferior esquerdo da página conforme ela é lida. Para o rótulo da grade 7 na horizontal e 12 na vertical, escreva [700, 1200].
{"001": {"src": "p-000.jpg", "quad": [[96,72],[930,92],[945,1468],[70,1480]]}}
- As chaves são a ordem das páginas de saída (
001,002, ...). Mantenha essa ordem alinhada com a ordem de leitura. - Pegue a borda do papel da página, não o texto. Perto da lombada, pegue a linha onde a página se curva para dentro da dobra. Uma página que sai da foto recebe cantos na borda da foto.
- Uma foto de uma página dupla (abertura) dá duas entradas com o mesmo
src, uma por página, por exemplo"005"e"006". - Com cerca de 30 px de precisão é suficiente. A próxima etapa ajusta para a borda real.
3. Ajustar para a borda do papel
$PY refine.py WORK
Escreve WORK/quads.json e WORK/sheets/quads-NN.jpg: laranja = seu traçado, vermelho = ajustado, ponto verde = superior esquerdo. Leia as folhas. Se um contorno vermelho saltou para uma sombra, a mesa ou a página vizinha, corrija esse traçado e execute a etapa novamente.
4. Achatar, tonalizar e limpar as bordas
$PY clean.py WORK # ou: clean.py WORK 004 007 para refazer algumas páginas
Para cada página, isso:
- encontra suas linhas de texto e as bordas justificadas da coluna;
- quadra o bloco de texto e ajusta uma superfície de curvatura suave a cada linha, de modo que linhas curvas saiam retas;
- tonaliza o papel para branco e a tinta para preto;
- remove marcas escuras que tocam a borda da página: dedos, curvatura da lombada, vincos e fragmentos da página seguinte.
Páginas com menos de 5 linhas de texto, como páginas de título e figuras de página inteira, recebem uma distorção simples de quatro cantos. Saída: WORK/pages/, WORK/blocks.json e WORK/sheets/review-NN.jpg, onde vermelho = removido, azul = deixado para sua decisão e verde = bloco de texto.
Leia as folhas de revisão. Coisas para observar:
- Linhas ainda curvas, ou a página inclinada: o traçado está errado. Corrija
rough.json, depois execute novamente refine e clean para essa página. - Texto dentro da caixa verde mostrado em vermelho: informe o usuário. A limpeza de borda nunca remove texto dentro do bloco, a menos que esteja na faixa da borda da página.
5. Listar as marcas fora do texto
$PY flags.py WORK
Escreve WORK/flags/flags-N.jpg: blocos numerados de todas as marcas escuras fora do bloco de texto.
6. Decidir manter ou remover (você)
Leia as folhas de bandeiras e escreva WORK/decisions.json:
{"keep": [0, 3, 4, 7], "strokes_only": [23], "erase": [{"page": "014", "box": [1500, 300, 80, 900], "mode": "strokes"}]}
- keep: qualquer coisa impressa. Isso cobre números de página, cabeçalhos correntes, números de capítulo, legendas, rótulos laterais, réguas de tabela, figuras e desenhos. Texto impresso é nítido e regular. Desenhos no livro também são impressão, mesmo quando são rabiscos de criança.
- remove: deixe o número de fora. Isso cobre caligrafia, anotações a caneta e lápis, borrões, manchas, impressões digitais, barras cinzas e sombras de bordas rasgadas. Linhas de caneta são finas, irregulares e frequentemente inclinadas, e não combinam com a fonte.
- strokes_only: letras impressas com um traço de caneta passando por elas ou ao lado. Apenas traços mais altos que 1,5 linhas são removidos.
- erase: uma caixa em pixels da página em
pages/page-KEY.jpg, para marcas dentro do bloco de texto que você foi orientado a remover."mode": "all"clareia a caixa;"strokes"remove apenas traços altos.
Pergunte ao usuário antes de remover algo que ele possa querer. Isso cobre sublinhados a caneta e anotações entre as linhas dentro do texto, e marca-texto. Padrão: remova anotações na margem, mantenha sublinhados dentro do texto. Pergunte também quando não conseguir distinguir impresso de caneta. Mostre o bloco, não o descreva.
7. Finalizar
$PY finish.py WORK OUT.pdf # --no-frame mantém a moldura de cada página; --dpi 300 por padrão
O que faz:
- Clareia tudo fora do bloco de texto, exceto itens mantidos.
- Remove colchetes finos de caneta que abraçam as laterais do bloco.
- Enquadra cada página em um tamanho de página único:
- O texto sai em um tamanho: fotos tiradas de perto ou de longe se alinham. O dimensionamento usa a largura do bloco de texto, ou o passo da linha onde uma figura estreita a coluna.
- Margens laterais são iguais, então o conteúdo que estava encostado em uma borda ou deslocado pelo centro do livro é centralizado.
- Cada página começa na mesma margem superior.
Leia WORK/sheets/final-NN.jpg antes de entregar. Verifique cada página quanto a borrões restantes, texto cortado e qualquer coisa que você manteve por engano. Em seguida, forneça ao usuário o caminho do PDF. Salve o PDF próximo à entrada (por exemplo <nome>_clean.pdf) a menos que ele diga o contrário.
O que é considerado bom
- Linhas de texto retas e niveladas de ponta a ponta, incluindo a linha mais próxima da lombada.
- Papel branco puro e tipo sólido preto, com traços finos intactos. Desenhos grandes escuros mantêm seu preenchimento.
- Sem dedos, sombras, fragmentos de páginas vizinhas ou manchas. Margens laterais iguais, e o mesmo tamanho de texto em cada página.
Notas de uso real
- O retificador lê texto, portanto é melhor em páginas cheias de linhas justificadas. Funciona para qualquer script, incluindo da direita para a esquerda (hebraico, árabe), porque ele apenas olha as formas das linhas.
- Linhas curtas dentro de desenhos (rótulos) são ignoradas ao ajustar a curvatura, então figuras não dobram a página.
- Um desenho escuro que preenche grande parte de uma página seria desbotado para cinza com a tonalização ingênua.
tone()mantém a estimativa do papel baixa a partir do papel ao redor. - Quando a curvatura de uma página engole algumas letras na lombada, a foto não tem nada a recuperar. Diga isso e não invente texto.
- Artigos com duas colunas: o bloco abrange ambas as colunas. Verifique a folha de revisão para linhas ajustadas através da dobra.