Os rótulos são pré-segmentados e implantados pela rede neural descrita no artigo anterior.
Como a colagem geralmente funciona? Você precisa tirar duas fotos sobrepostas, calcular o deslocamento mútuo e sobrepor uma sobre a outra. Parece muito simples, mas vamos repassar cada uma das etapas.
Para calcular o deslocamento mútuo, você precisa encontrar alguns objetos que estão presentes em ambas as imagens e, de alguma forma, calcular a transformação dos pontos de uma imagem para outra. Essa mudança pode ser representada por uma matriz de transformação, onde os elementos da matriz codificam várias transformações ao mesmo tempo - escala, translação e rotação.
Existe uma excelente tabela na wikipedia que mostra como e quais elementos afetam a transformação.
Como você pode ver na imagem abaixo, existem objetos comuns suficientes:
Mas há um problema com os objetos selecionados - eles são difíceis de detectar algoritmicamente. Em vez disso, costuma-se procurar objetos mais simples - os chamados "cantos", eles também são descritores ("descritores", "recursos").
Há um ótimo artigo na documentação do OpenCV sobre por que os cantos - em resumo, definir uma linha é fácil, mas fornece apenas uma coordenada. Portanto, também é necessário detectar a segunda linha (não paralela). Se convergirem em um ponto, então este lugar é ideal para encontrar um descritor, é também um canto (embora os descritores reais não sejam cantos no sentido geométrico da palavra).
Um dos algoritmos para encontrar descritores é SIFT (Scale-Invariant Feature Transform). Apesar de ter sido inventado em 1999, é bastante popular devido à sua simplicidade e confiabilidade. Este algoritmo foi patenteado, mas a patente expirou nesta primavera (2020). No entanto, eles não conseguiram transferi-lo para a compilação principal do OpenCV, então você precisa usar uma compilação especial não livre.
Então, vamos encontrar cantos semelhantes em ambas as imagens:
sift = cv2.xfeatures2d.SIFT_create()
features_left = sift.detectAndCompute(left_image, None)
features_right = sift.detectAndCompute(left_image, None)
Vamos usar o matcher Flann - ele tem bom desempenho mesmo se o número de descritores for grande.
KNN = 2
LOWE = 0.7
TREES = 5
CHECKS = 50
matcher = cv2.FlannBasedMatcher({'algorithm': 0, 'trees': TREES}, {'checks': CHECKS})
matches = matcher.knnMatch(left_descriptors, right_descriptors, k=KNN)
logging.debug("filtering matches with lowe test")
positive = []
for left_match, right_match in matches:
if left_match.distance < LOWE * right_match.distance:
positive.append(left_match)
As linhas amarelas mostram como o combinador encontrou correspondências.
Como você pode ver claramente, existem apenas cerca de metade das correspondências corretas. No entanto, se as correspondências corretas sempre fornecem a mesma transformação, as incorretas mostram uma direção caoticamente nova. Essa. teoricamente, eles podem ser separados um do outro:
um dos algoritmos para encontrar a transformação correta é RANSAC. Este algoritmo funciona muito bem quando você deseja separar bons valores de ruído - este é exatamente o caso.
Felizmente, o OpenCV já possui funções que encontrarão a matriz de transformação por correspondências usando RANSAC, ou seja, na verdade, você não precisa escrever nada.
Vamos usar a função estimativaAffinePartial2D que procura as seguintes transformações: rotação, escala e translação (4 graus de liberdade).
H, _ = cv2.estimateAffinePartial2D(right_matches, left_matches, False)
Assim que a matriz de transformação for encontrada, podemos transformar a imagem certa para colagem.
Fragmento esquerdo: Fragmento
direito:
Primeiro, vamos usar a maneira mais simples de colar fragmentos, quando cada pixel de sua interseção é calculado como uma média. Infelizmente, o resultado é razoável - a imagem duplica visivelmente, especialmente perto da linha de colagem.
Na animação, a diferença entre os dois quadros é mais claramente visível:
Isso não é surpreendente - as fotos foram tiradas de ângulos diferentes, a rede neural também as expandiu de forma ligeiramente diferente e, como resultado, havia pequenas discrepâncias.
Para colagem perfeita, é necessário compensar a distorção não linear. A distorção pode ser representada como um campo vetorial com a mesma resolução da imagem original, mas em vez da cor, um deslocamento será codificado em cada pixel. Este campo vetorial é denominado “fluxo óptico”.
Em geral, existem diferentes métodos para calcular o fluxo óptico - alguns deles são integrados diretamente no OpenCV, e também existem redes neurais especiais.
No nosso caso, omitirei a técnica específica, mas publicarei o resultado:
Mas a compensação deve ser realizada na proporção de ambos os fragmentos. Para fazer isso, vamos dividi-lo em duas matrizes: O
fragmento esquerdo será compensado da esquerda para a direita de forma crescente, enquanto o da direita - vice-versa.
Agora, os dois fragmentos se sobrepõem quase perfeitamente:
Agora a sobreposição está geometricamente correta, mas observamos um salto muito perceptível no brilho nas costuras:
Este problema pode ser facilmente corrigido se em vez dos valores médios, eles forem sobrepostos com um gradiente:
Com esta abordagem, a costura não é visível de todo:
Em princípio, existem também outras técnicas de colagem, por exemplo , mesclagem multibanda, que são usados para costurar panoramas, mas não funcionam bem com texto - somente a compensação de fluxo ótico pode remover completamente o fantasma do texto.
Agora vamos colar a imagem completa:
Versão final:
Outras melhorias poderiam ser a compensação do efeito de sombra (lado direito da imagem), ou ainda mais pós-processamento de cor e contraste. Você também pode ver que a geometria global sofreu um pouco - as linhas à direita subiram ligeiramente. Teoricamente, este problema também pode ser corrigido adicionando uma correção de escala global, mas esta também não é uma tarefa completamente trivial.
Examinamos como a colagem funciona, uma solução pronta está disponível aqui na forma de uma API REST. Também recomendo olhar os seguintes links: