AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 786767
Accepted
Binarus
Binarus
Asked: 2024-11-17 19:19:45 +0800 CST2024-11-17 19:19:45 +0800 CST 2024-11-17 19:19:45 +0800 CST

Como girar automaticamente uma área de imagem retangular em alguns graus para que sua borda esquerda fique vertical (usando ferramentas de linha de comando como o imagemagick)

  • 772

Tenho milhares de imagens cujas principais características são as da imagem anexa: todas as imagens estão em uma moldura (quase) preta, enquanto o conteúdo real da imagem quase sempre está em um fundo branco.

Agora eu gostaria de girar o conteúdo real da imagem para que a borda esquerda desse conteúdo fique vertical. Então eu gostaria de cortar (aparar) a imagem para que a borda preta seja jogada fora, mas mantendo o conteúdo real completamente. Ou seja, mesmo que a área que contém o conteúdo real não seja perfeitamente retangular, tudo dessa área deve ser mantido, o que significa que pequenos restos do quadro preto ainda podem ser visíveis depois.

Dado o número de imagens que eu gostaria de tratar dessa forma, acho que terei que fazer isso usando ferramentas de linha de comando. No passado, usei o ImageMagick para esse tipo de trabalho (para transformações que são muito mais fáceis), mas eu realmente não me importaria em combinar várias ferramentas diferentes para realizar a tarefa.

O que eu já tentei:

Eu pesquisei como desnivelar imagens, e isso funciona na maioria das vezes. No entanto, os métodos de desnivelamento que eu encontrei funcionam deixando as linhas de texto horizontais. Isso é legal porque facilita a leitura, mas é claro que na maioria dos casos as bordas da área que segura o texto não ficam verticais ou horizontais depois, respectivamente. Não é isso que eu estou procurando.

Para explicar com mais detalhes, gostaria de:

  1. Detecte a borda esquerda entre o quadro preto e a área que contém o conteúdo real da imagem.
  2. Ajuste uma linha reta (invisível) ao longo dessa borda.
  3. Determine o ângulo entre essa linha e uma linha vertical.
  4. Gire a imagem inteira pelo (negativo) ângulo para que a borda da etapa 1 fique vertical.
  5. Corte a imagem, descartando o máximo possível do quadro preto, mas mantendo assim a área completa que contém o conteúdo real (e, portanto, aceitando que o restante do quadro preto seja mantido se essa área não for perfeitamente retangular).

Alguém poderia explicar como fazer isso, de preferência usando ferramentas de linha de comando?

insira a descrição da imagem aqui

imagemagick
  • 3 3 respostas
  • 44 Views

3 respostas

  • Voted
  1. meuh
    2024-11-17T20:14:16+08:002024-11-17T20:14:16+08:00

    Para seu exemplo simples e limpo, você pode fazer o seguinte; usar o imagemagick trimpara remover a maior parte da borda externa, então tentar girar em 1 grau (adicionando um fundo preto correspondente aos pixels recém-adicionados da imagem agora maior), aparar novamente e ver se o tamanho da imagem foi reduzido. Pegue o melhor tamanho reduzido.

    file=in.png
    convert "$file" -trim out.png
    minwidth=$(identify -format '%w' out.png)
    minrot=0
    for rot in $(seq 1 30)
    do      convert out.png -background black -rotate -"$rot" out2.png
            convert out2.png -trim out3.png
            width=$(identify -format '%w' out3.png)
            echo "$rot degrees, width $width"
            if [ $width -lt $minwidth ]
            then    minwidth=$width minrot=$rot
            else    break
            fi
    done
    echo "choose rotate $minrot for min width $minwidth"
    convert out.png -background black -rotate -"$minrot" out2.png
    convert out2.png -trim result.png
    display result.png
    

    Para a imagem de teste obtive:

    1 degrees, width 381
    2 degrees, width 375
    3 degrees, width 369
    4 degrees, width 363
    5 degrees, width 355
    6 degrees, width 359
    choose 5 width=355
    

    imagem girada com texto em seta

    Se o quadro real for um pouco menos monocromático que o do exemplo, você pode tentar prefixar a -trimopção com, digamos, -fuzz 15%para obter uma comparação de cores de pixel mais aproximada.

    • 3
  2. Best Answer
    meuh
    2024-11-18T04:09:41+08:002024-11-18T04:09:41+08:00

    Esta resposta tenta ser rápida usando um recurso -trimpelo qual deixa o deslocamento da nova imagem dentro da tela antiga dentro das informações da nova imagem. Primeiro, fazemos um corte global para obter uma imagem onde: o canto superior do espaço em branco desejado deve encostar no topo da imagem, e o canto mais à esquerda do espaço em branco desejado deve encostar no lado esquerdo da imagem. Este é o retângulo amarelo pontilhado na imagem abaixo.

    Repaginamos esta imagem para que a tela tenha o mesmo tamanho da nova imagem.

    Uma fatia horizontal fina do topo é tirada (de altura $stripwidthno script abaixo) no arquivo line1.png, que é o retângulo vermelho. Isso é aparado (no arquivo out4.png), como mostrado pelo retângulo azul. O deslocamento com seta %Xda imagem resultante deve ser onde o canto superior foi encontrado. Para obter uma estimativa melhor, metade da largura da imagem aparada ( %w/2) é adicionada a isso (exagerada na imagem abaixo).

    Similarmente, uma fatia vertical fina é tomada e aparada. O deslocamento %Yé onde o canto mais à esquerda foi encontrado, com metade da altura %h/2adicionada.

    Por exemplo, se o resultado aparado da faixa vermelha line1.pngfor azul out4.png, então identify out4.pngpode ser gerado

    out4.png PNG 14x2 387x432+36+0 ...
    

    que diz que a imagem tem 14x2 pixels, de um line1.pngtamanho de tela ( ) de 387x432, com um deslocamento de 36 pixels em x e 0 em y. anotado

    Agora temos os comprimentos dos lados opostos e adjacentes do triângulo superior esquerdo. Se calcularmos os graus usando arctan, essa é a rotação necessária para fazer esse triângulo desaparecer, e assim alinhar o canto superior com a borda esquerda.

    file=in.png
    # fuzz='-fuzz 15%'
    stripwidth=2
    convert "$file" $fuzz -trim out.png
    minwidth=$(identify -format '%w' out.png)
    
    convert out.png +repage -crop ${minwidth}x${stripwidth}+0+0 line1.png
    convert line1.png $fuzz -trim out4.png
    opp=$(identify -format '0 %X +%w/2' out4.png)
    
    ht=$(identify -format '%h' out.png)
    convert out.png +repage -crop ${stripwidth}x${ht}+0+0 line2.png
    convert line2.png $fuzz -trim out5.png
    adj=$(identify -format '0 %Y +%h/2' out5.png)
    
    minrot=$(echo "scale=2; pi=4*a(1); 
        opp=$opp; adj=$adj; 
        a(opp/adj)*180/pi" |
        bc -l)
    
    convert out.png -background black -rotate -"$minrot" out2.png
    convert out2.png $fuzz -trim result.png
    display result.png
    

    Note que a()é arctan (em radianos) em bc. O identify -formatusa %w %h %X %Ypara obter a largura, altura, deslocamento x, deslocamento y da imagem aparada na tela original. O extra 0é porque o imagemagick adiciona um +sinal de liderança e bcnão lida com isso.

    O resultado foi 5,76 graus, como visto nesta sh -xsaída:

    + convert out.png +repage -crop 387x2+0+0 line1.png
    + convert line1.png -trim out4.png
    ++ identify -format '0 %X +%w/2' out4.png
    + opp='0 +36 +15/2'
    ++ identify -format %h out.png
    + ht=432
    + convert out.png +repage -crop 2x432+0+0 line2.png
    + convert line2.png -trim out5.png
    ++ identify -format '0 %Y +%h/2' out5.png
    + adj='0 +381 +19/2'
    ++ echo 'scale=2; pi=4*a(1); 
            opp=0 +36 +15/2; adj=0 +381 +19/2; 
            a(opp/adj)*180/pi'
    ++ bc -l
    + minrot=5.76
    + convert out.png -background black -rotate -5.76 out2.png
    + convert out2.png -trim result.png
    
    • 2
  3. Binarus
    2024-11-20T05:20:03+08:002024-11-20T05:20:03+08:00

    A solução a seguir é baseada nas ideias de @meuh. Por favor, vote positivamente nas respostas dele se achar minha solução útil. Primeiro, postarei meu código e, depois, explicarei o que alterei, mencionando apenas as coisas não óbvias.

    O código é baseado em algumas suposições que são seguras para as imagens que tenho que processar:

    1. A borda esquerda da área branca nas imagens originais não é girada em mais de 5 graus em relação a uma linha vertical.
    2. A área branca nas imagens originais tem pelo menos 2000 pixels de altura e 1000 pixels de largura.
    #!/usr/bin/bash
    
    file=ScanImage7123.tif
    stripwidth=4
    
    convert "$file" -fuzz 20% -trim out.png
    width=$(identify -format '%w' out.png)
    height=$(identify -format '%h' out.png)
    
    (( wd = (width / 2) ))
    (( l1yoff = (height / 10) ))
    (( l2yoff = (height - l1yoff) ))
    
    convert out.png +repage -crop ${wd}x${stripwidth}+0+${l1yoff} line1.png
    IFS=" x+" read a b x1 d < <(convert -fuzz 50% line1.png -format "%@" info:)
    
    convert out.png +repage -crop ${wd}x${stripwidth}+0+${l2yoff} line2.png
    IFS=" x+" read a b x2 d < <(convert -fuzz 50% line2.png -format "%@" info:)
    
    (( a = (x1 - x2) ))
    (( b = (l2yoff - l1yoff) ))
    rad2deg="57.2957795130823208768"
    
    angle=$(perl -E "say atan2(${a}, ${b}) * ${rad2deg}")
    
    convert out.png -background black -rotate -${angle} out2.png
    convert out2.png -fuzz 20% -trim result.png
    

    A ideia aqui é que na verdade não precisamos encontrar os cantos da área branca. Isso é importante, porque encontrar os cantos de forma confiável era impossível para mim, mesmo quando brincava com valores diferentes para stripwidth.

    Minhas imagens do mundo real vêm de um scanner, o que significa que as bordas da área branca não são muito nítidas. Isso torna bem difícil encontrar o canto superior. Normalmente, poderíamos encontrar a borda superior da seguinte maneira: após a primeira convertoperação (aparar), vá para a primeira linha de pixels (no topo), caminhe ao longo dela da esquerda para a direita e pare no primeiro pixel branco; então você encontrou o canto.

    Isso é praticamente impossível se a área branca já for horizontal, ou quase horizontal, e fica mais difícil quanto menos "nítida" for a borda superior.

    Portanto, movi a área (a "linha") que é cortada pela segunda convertoperação para longe da borda superior em um décimo da altura da imagem. Isso garante que temos uma transição horizontal clara e relativamente nítida do preto para o branco naquela linha (nome do arquivo line1.png).

    Eu queria evitar ter duas transições horizontais na linha 1 (por exemplo, preto -> branco no lado esquerdo e branco -> preto no lado direito). Isso é simplesmente porque eu não tenho ideia imagemagicke nenhuma pista sobre o que aconteceria na próxima etapa se houvesse mais de uma transição na linha. Portanto, ao cortar a linha, eu uso apenas metade da largura da imagem. [Nota lateral: ainda pode haver várias transições na linha, dependendo do conteúdo da área branca (por exemplo, texto preto), mas isso não impôs problemas em meus primeiros testes.]

    A terceira convertoperação gera algo como 0x10+45+0. A parte 0x10denota as dimensões da transição mencionada acima, +45é a Xcoordenada onde a transição começa. Este é o valor que buscamos; a readconstrução o salva em x1.

    O mesmo método é aplicado para a segunda "linha" (nome do arquivo line2.png), que é cortada um décimo da altura da imagem a partir da parte inferior da imagem (quarta convertoperação). Na próxima linha (quinta convertoperação), a Xcoordenada da transição do preto para o branco na linha 2 é salva em x2.

    Agora podemos calcular a distância horizontal e vertical entre as duas transições na linha 1 e na linha 2 e, a partir dessas distâncias, podemos calcular o ângulo de rotação.

    Usei Perl para executar esse cálculo porque bcele não fornece a atan2()função, que eu claramente prefiro nesses casos por sua capacidade de lidar com situações em que a distância horizontal ou vertical (ou mesmo ambas) é 0.

    O resto deve ser autoexplicativo.

    • 2

relate perguntas

  • Como passar muitas opções para o ImageMagick?

  • Depois de instalar o ImageMagick não tenho a ferramenta imgout

  • GNU paralelo com loop for?

  • Restaurando automaticamente a verticalidade das bordas de uma foto digitalizada

  • Crie uma imagem de paleta com várias linhas usando o ImageMagick

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Possível firmware ausente /lib/firmware/i915/* para o módulo i915

    • 3 respostas
  • Marko Smith

    Falha ao buscar o repositório de backports jessie

    • 4 respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    user12345 Falha ao buscar o repositório de backports jessie 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl Por que a maioria dos exemplos do systemd contém WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve