Eu estava fazendo um projeto divertido: resolvendo um Sudoku a partir de uma imagem de entrada usando o OpenCV (como no Google Goggles etc.). E concluí a tarefa, mas no final encontrei um pequeno problema para o qual vim aqui.
Fiz a programação usando a API Python do OpenCV 2.3.1.
Abaixo está o que eu fiz:
- Leia a imagem
- Encontre os contornos
- Selecione aquele com a área máxima (e também um pouco equivalente ao quadrado).
Encontre os pontos de canto.
por exemplo, dado abaixo:
( Observe aqui que a linha verde coincide corretamente com o verdadeiro limite do Sudoku, para que o Sudoku possa ser corretamente distorcido . Verifique a próxima imagem)
distorça a imagem para um quadrado perfeito
por exemplo, imagem:
Executar OCR (pelo qual usei o método que forneci no OCR de reconhecimento de dígito simples no OpenCV-Python )
E o método funcionou bem.
Problema:
Confira esta imagem.
A execução do passo 4 nesta imagem fornece o resultado abaixo:
A linha vermelha desenhada é o contorno original, que é o verdadeiro contorno do limite do sudoku.
A linha verde desenhada é um contorno aproximado, que será o contorno da imagem distorcida.
O que, é claro, existe diferença entre a linha verde e a linha vermelha na borda superior do sudoku. Portanto, enquanto estou empenado, não estou conseguindo o limite original do Sudoku.
Minha pergunta :
Como posso distorcer a imagem no limite correto do Sudoku, ou seja, a linha vermelha OU como posso remover a diferença entre a linha vermelha e a linha verde? Existe algum método para isso no OpenCV?
fonte
Respostas:
Eu tenho uma solução que funciona, mas você terá que traduzi-la para o OpenCV. Está escrito no Mathematica.
O primeiro passo é ajustar o brilho da imagem, dividindo cada pixel com o resultado de uma operação de fechamento:
O próximo passo é encontrar a área de sudoku, para que eu possa ignorar (mascarar) o fundo. Para isso, uso a análise de componentes conectados e seleciono o componente que possui a maior área convexa:
Ao preencher esta imagem, recebo uma máscara para a grade do sudoku:
Agora, posso usar um filtro derivativo de segunda ordem para encontrar as linhas verticais e horizontais em duas imagens separadas:
Uso a análise de componentes conectados novamente para extrair as linhas de grade dessas imagens. As linhas de grade são muito maiores que os dígitos, portanto, posso usar o comprimento do cursor para selecionar apenas os componentes conectados às linhas de grade. Classificando-os por posição, recebo imagens de máscara 2x10 para cada uma das linhas de grade verticais / horizontais da imagem:
Em seguida, pego cada par de linhas de grade verticais / horizontais, as dilato, calculo a interseção pixel por pixel e calculo o centro do resultado. Estes pontos são as interseções da linha de grade:
O último passo é definir duas funções de interpolação para mapeamento X / Y através desses pontos e transformar a imagem usando estas funções:
Todas as operações são funções básicas de processamento de imagem, portanto, isso também deve ser possível no OpenCV. A transformação de imagem baseada em splines pode ser mais difícil, mas não acho que você realmente precise. Provavelmente, o uso da transformação de perspectiva que você usa agora em cada célula individual dará bons resultados o suficiente.
fonte
A resposta de Nikie resolveu meu problema, mas sua resposta estava no Mathematica. Então, pensei em dar sua adaptação ao OpenCV aqui. Mas, após a implementação, pude ver que o código OpenCV é muito maior que o código mathematica da nikie. E também, não consegui encontrar o método de interpolação feito pelo nikie no OpenCV (embora possa ser feito usando o scipy, vou dizer quando chegar a hora).
1. Pré-processamento de imagem (operação de fechamento)
Resultado:
2. Localizando o Sudoku Square e criando a imagem da máscara
Resultado:
3. Encontrar linhas verticais
Resultado:
4. Localizando linhas horizontais
Resultado:
Claro, este não é tão bom.
5. Encontrar pontos de grade
Resultado:
6. Corrigindo os defeitos
Aqui, o nikie faz algum tipo de interpolação, sobre a qual não tenho muito conhecimento. E eu não consegui encontrar nenhuma função correspondente para este OpenCV. (pode estar lá, eu não sei).
Confira este SOF, que explica como fazer isso usando o SciPy, que eu não quero usar: Transformação de imagem no OpenCV
Então, aqui eu peguei 4 cantos de cada sub-quadrado e apliquei a Perspectiva de distorção a cada um.
Para isso, primeiro encontramos os centróides.
Mas os centróides resultantes não serão classificados. Confira abaixo a imagem para ver sua ordem:
Então, nós as classificamos da esquerda para a direita, de cima para baixo.
Agora veja abaixo a ordem deles:
Por fim, aplicamos a transformação e criamos uma nova imagem de tamanho 450x450.
Resultado:
O resultado é quase o mesmo que o da nikie, mas o tamanho do código é grande. Pode ser que métodos melhores estejam disponíveis por aí, mas até então, isso funcionará bem.
Atenciosamente ARK.
fonte
Você pode tentar usar algum tipo de modelagem baseada em grade de sua distorção arbitrária. E como o sudoku já é uma grade, isso não deve ser muito difícil.
Portanto, você pode tentar detectar os limites de cada sub-região 3x3 e distorcer cada região individualmente. Se a detecção for bem-sucedida, você obterá uma melhor aproximação.
fonte
Quero acrescentar que o método acima funciona somente quando a placa do sudoku permanece reta, caso contrário, o teste de relação altura / largura (ou vice-versa) provavelmente falhará e você não poderá detectar as bordas do sudoku. (Também quero acrescentar que, se as linhas que não são perpendiculares às bordas da imagem, as operações sobel (dx e dy) continuarão funcionando, pois as linhas ainda terão arestas em relação aos dois eixos.
Para poder detectar linhas retas, você deve trabalhar na análise de contorno ou pixel, como contourArea / boundingRectArea, pontos superior esquerdo e inferior direito ...
Edit: Consegui verificar se um conjunto de contornos forma uma linha ou não, aplicando regressão linear e verificando o erro. No entanto, a regressão linear teve um desempenho ruim quando a inclinação da linha é muito grande (ou seja,> 1000) ou muito próxima de 0. Portanto, a aplicação do teste de razão acima (na resposta mais votada) antes da regressão linear é lógica e funcionou para mim.
fonte
Para remover cantos não detectados, apliquei a correção gama com um valor gama de 0,8.
O círculo vermelho é desenhado para mostrar o canto que falta.
O código é:
Isso é um complemento à resposta de Abid Rahman, se faltam alguns pontos de canto.
fonte
Eu pensei que este era um ótimo post e uma ótima solução do ARK; muito bem definido e explicado.
Eu estava trabalhando em um problema semelhante e construí a coisa toda. Houve algumas mudanças (ou seja, intervalo x para intervalo, argumentos em cv2.findContours), mas isso deve funcionar imediatamente (Python 3.5, Anaconda).
Esta é uma compilação dos elementos acima, com parte do código ausente adicionado (ou seja, rotulagem de pontos).
fonte