Filtragem de imagem em Python

Você já se deparou com uma imagem barulhenta? Quero dizer, uma imagem que não era tão clara ao visualizá-la? Acho que nos deparamos com essas imagens com muita frequência, especialmente quando muitas imagens hoje em dia são tiradas por nossas câmeras de celular ou câmeras digitais de baixa resolução.

Se você tivesse apenas aquela imagem barulhenta que significa algo para você, mas o problema é que ela não pode ser visualizada corretamente, haveria uma solução para se recuperar desse ruído?

É aqui que a filtragem de imagens entra em ação, e é isso que descreverei neste tutorial. Vamos começar!

Filtragem de Imagem

A filtragem de imagens é uma ferramenta popular usada no processamento de imagens. No final do dia, usamos filtragem de imagem para remover ruídos e quaisquer recursos indesejados de uma imagem, criando uma versão melhor e aprimorada dessa imagem. Existem dois tipos de filtros: linear e não linear. Exemplos de filtros lineares são os filtros médio e laplaciano. Os filtros não lineares constituem filtros como os filtros mediano, mínimo, máximo e Sobel.

Cada um desses filtros tem uma finalidade específica e é projetado para remover ruídos ou melhorar alguns aspectos da imagem. Mas como é feita a filtragem? É o que veremos na próxima seção.

Entendendo a matemática básica por trás da filtragem de imagens

Para realizar um processo de filtragem de imagem, precisamos de um filtrotambém chamado de mascarar. Esse filtro geralmente é uma janela quadrada bidimensional, ou seja, uma janela com dimensões iguais (largura e altura).

O filtro incluirá números. Esses números são chamados coeficientese eles são o que realmente determina o efeito do filtro e a aparência da imagem de saída.

Para aplicar o filtro, o 3x3 janela é deslizada sobre a imagem. Este processo de deslizar uma janela de filtro sobre uma imagem é chamado convolução no domínio espacial. A janela será colocada em cada pixel (ou seja, pense nela como uma célula em uma matriz) na imagem, onde o centro do filtro deve se sobrepor a esse pixel.

Uma vez que essa sobreposição aconteça, os pixels na sub-imagem que o filtro está em cima serão multiplicados pelos coeficientes correspondentes do filtro. Neste caso, teremos uma nova matriz com novos valores semelhantes ao tamanho do filtro (ex. 3x3). Finalmente, o valor do pixel central será substituído por um novo valor usando uma equação matemática específica dependendo do tipo de filtro utilizado (ou seja, filtro mediano).

Eu sei que o parágrafo acima é um pouco prolixo. Vamos dar um exemplo para mostrar como um filtro de imagem é aplicado em ação. Suponha que tenhamos a seguinte sub-imagem onde nosso filtro se sobrepôs (i e j referem-se à localização do pixel na sub-imagem, e I refere-se à imagem):

Filtrar MatemáticaFiltrar MatemáticaFiltrar Matemática

A convolução do nosso filtro mostrado na primeira figura com a sub-imagem acima ficará como mostrado abaixo, onde I_new(i,j) representa o resultado no local (i,j).

O processo é repetido para cada pixel na imagem, incluindo os pixels no limite da imagem. Mas, como você pode imaginar, parte do filtro ficará fora da imagem ao colocar o filtro nos pixels de limite. Neste caso, realizamos preenchimento.

Este processo significa simplesmente que inserimos novos valores de pixel na sub-imagem sob a parte do filtro que vem fora da imagem antes do processo de convolução, já que essa parte aparentemente não contém nenhum valor de pixel. Está fora da imagem! Esses pixels preenchidos podem ser zeros ou um valor constante. Existem outros métodos para definir os valores de preenchimento, mas eles estão fora do escopo deste tutorial.

Acho que é teoria suficiente por enquanto, então vamos em frente e sujar as mãos com a codificação! Neste tutorial, explicarei o filtro mediano (não linear) e o filtro médio (linear) e como podemos implementá-los em Python.

No filtro mediano, escolhemos uma janela deslizante que se moverá por todos os pixels da imagem. O que fazemos aqui é coletar os valores de pixel que estão sob o filtro e tomar a mediana desses valores. O resultado será atribuído ao pixel central.

Diga o nosso 3x3 filtro tinha os seguintes valores depois de colocá-lo em uma sub-imagem:

Filtro medianoFiltro medianoFiltro mediano

Vamos ver como calcular a mediana. A mediana, em sua essência, é a meio número de uma lista ordenada de números. Assim, para encontrar a mediana para o filtro acima, simplesmente ordenamos os números do menor para o maior, e o meio desses números será o nosso valor mediano. Classificando os valores em nosso 3x3 janela nos dará o seguinte:

Para encontrar o número do meio (mediana), simplesmente contamos o número de valores que temos, adicionamos 1 a esse número e dividimos por 2. Isso nos dará a localização do valor do meio na janela, que é o nosso valor mediano. Então o valor mediano estará no local 9+1/2 = 5qual é 59. Este valor será o novo valor do pixel sob o centro do nosso 3x3 janela.

Este tipo de filtro é usado para remover ruídos e funciona melhor com imagens que sofrem de sal e pimenta ruído. A imagem abaixo mostra um exemplo de uma imagem que sofre com esse ruído:

Imagem de gato com ruído de sal e pimentaImagem de gato com ruído de sal e pimentaImagem de gato com ruído de sal e pimenta

Agora, vamos escrever um script Python que aplicará o filtro mediano à imagem acima. Para este exemplo, usaremos a biblioteca OpenCV. Por favor, verifique este guia de instalação para ver como instalar o pacote OpenCV em Python.

Para aplicar o filtro de mediana, simplesmente usamos o OpenCV’s cv2.medianBlur() função. Nosso script pode ficar assim:

Observe que eu usei argparsepois é uma boa prática ser flexível aqui e usar a linha de comando para passar a imagem na qual queremos aplicar o filtro mediano como um argumento para nosso programa.

Depois de passar nossa imagem como um argumento de linha de comando, lemos essa imagem usando o cv2.imread() função. Em seguida, aplicamos o filtro da mediana usando o medianBlur() função, passando nossa imagem e tamanho do filtro como parâmetros. A imagem é exibida usando o cv2.imshow() função, e é salvo no disco usando cv2.imwrite().

O resultado do script acima é o seguinte:

Cat Image Redução de Ruído de Sal e Pimenta com Desfoque MedianoCat Image Redução de Ruído de Sal e Pimenta com Desfoque MedianoCat Image Redução de Ruído de Sal e Pimenta com Desfoque Mediano

Bem, o que você acha? Muito bonito – uma imagem bonita e limpa, sem ruído.

Filtro médio

O filtro médio é um exemplo de filtro linear. Basicamente, ele substitui cada pixel na imagem de saída pelo valor médio (médio) da vizinhança. Isso tem o efeito de suavizar a imagem (reduzindo a quantidade de variações de intensidade entre um pixel e o próximo), removendo o ruído da imagem e clareando a imagem.

Assim, na filtragem média, cada pixel da imagem será substituído pelo valor médio de seus vizinhos, incluindo o próprio pixel. o 3x3 kernel é geralmente usado para filtragem média, embora outros tamanhos de kernel possam ser usados ​​(ou seja, 5×5).

Um ponto importante a ser mencionado aqui é que todos os elementos do kernel médio devem:

Vamos dar um exemplo para tornar as coisas mais claras. Digamos que temos a seguinte sub-imagem:

Filtro médioFiltro médioFiltro médio

Ao aplicar o filtro de média, faríamos o seguinte:

O resultado exato é 44.3mas arredondei o resultado para 44. Portanto, o novo valor para o pixel central é 44 ao invés de 91.

Agora para a parte de codificação. Digamos que temos a seguinte imagem barulhenta:

Ruído de Poisson da Imagem do GatoRuído de Poisson da Imagem do GatoRuído de Poisson da Imagem do Gato

O que queremos fazer neste momento é aplicar o filtro médio na imagem acima e ver os efeitos da aplicação desse filtro.

O código para fazer esta operação é o seguinte:

Observe no código que usamos um 5x5 kernel para o nosso filtro médio. Também temos usado o blur() método para aplicar o filtro de média. O primeiro parâmetro desta função é nossa imagem de entrada, o segundo é o parâmetro que é nosso kernel.

Após executar o código em nossa imagem ruidosa, este foi o resultado que obtive:

Imagem de gato com desfoque médioImagem de gato com desfoque médioImagem de gato com desfoque médio

Se você observar a imagem de saída, podemos ver que ela é mais suave que a imagem com ruído. Missão cumprida!

Desfoque Gaussiano

Outra técnica importante que podemos usar para reduzir o ruído da imagem é chamada de desfoque gaussiano. A matemática real usada para o desfoque gaussiano é complicada e está além do escopo deste tutorial. No entanto, você pode usar essa técnica simplesmente usando um método chamado GaussianBlur().

Esse método aceita a imagem de origem como seu primeiro parâmetro, uma tupla com largura e altura do kernel como segundo parâmetro e um valor de desvio padrão como terceiro parâmetro. Você pode especificar separadamente o sigmaX ou sigmaY valores para desvio padrão. No entanto, ambos são considerados iguais quando apenas um valor é especificado. Lembre-se também de que o tamanho do kernel deve ser positivo e ímpar.

o GaussianBlur() método é mais eficaz na remoção de ruído gaussiano. A imagem a seguir tem algum ruído gaussiano aplicado a ela.

Imagem de gato com ruído gaussianoImagem de gato com ruído gaussianoImagem de gato com ruído gaussiano

Agora, vamos ver o quão bem nosso GaussianBlur() método remove o ruído desta imagem. A imagem a seguir mostra o resultado da aplicação do nosso filtro Gaussian Blue na imagem do gato acima.

Filtro azul gaussiano de imagem de gatoFiltro azul gaussiano de imagem de gatoFiltro azul gaussiano de imagem de gato

Como você pode ver, há uma redução perceptível no ruído.

Filtragem Bilateral

O filtro gaussiano que usamos acima depende de todos os pixels próximos para filtrar o ruído. Isso também resulta em desfoque de quaisquer bordas nas imagens onde a intensidade do pixel varia consideravelmente. o bilateralFilter() O método supera essa limitação usando outro filtro gaussiano que é uma função da diferença de pixels. Esse filtro garante que apenas pixels de intensidade semelhante sejam considerados para desfoque.

O método aceita vários parâmetros. Seu primeiro parâmetro especifica a imagem de origem, o segundo parâmetro determina o tamanho da vizinhança do pixel usada para filtragem. O terceiro e quarto parâmetro especificam até que ponto as cores ou a distância dos pixels podem estar antes de deixarem de influenciar o valor do pixel central. Os valores sigma no terceiro e quarto parâmetro geralmente devem estar em torno de 70-80. No entanto, eles podem ser tão baixos quanto 10 e tão altos quanto 150.

O snippet de código a seguir aplica um filtro bilateral nas imagens:

Para observar claramente a diferença entre um desfoque gaussiano e um filtro bilateral, precisamos aplicá-lo em imagens com bastante textura e bordas nítidas. Imagens de pranchas são um candidato ideal para essa finalidade. Aqui está a imagem original da prancha:

Imagem original da pranchaImagem original da pranchaImagem original da prancha

Aqui está a mesma imagem com um desfoque gaussiano regular aplicado a ela:

Imagem de prancha com desfoque gaussianoImagem de prancha com desfoque gaussianoImagem de prancha com desfoque gaussiano

Agora, aqui está a imagem da prancha com o filtro bilateral aplicado a ela:

Filtro bilateral de imagem de pranchaFiltro bilateral de imagem de pranchaFiltro bilateral de imagem de prancha

A diferença é sutil, mas você poderá notar linhas mais nítidas nas segundas imagens enquanto a textura ainda estiver desfocada.

Conclusão

Como vimos neste tutorial, o Python nos permite realizar tarefas avançadas como filtragem de imagens, principalmente por meio de sua biblioteca OpenCV, de maneira simples.

Este post foi atualizado com contribuições de Nitish Kumar. Nitish é um desenvolvedor web com experiência na criação de sites de comércio eletrônico em várias plataformas. Ele passa seu tempo livre trabalhando em projetos pessoais que facilitam sua vida cotidiana ou fazendo longas caminhadas noturnas com os amigos.

Deixe uma resposta