Qual filtro de imagem pode ser aplicado para remover o padrão de grade de jpegs corrompidos?

8

Tenho cerca de 1.400 jpegs que foram corrompidos de alguma forma e perderam as imagens de backup. Todos eles parecem ter o mesmo padrão de linhas em grade sobre cada um (ou seja, a grade não muda de imagem para imagem.

Aqui está a aparência de uma dessas imagens:

insira a descrição da imagem aqui

Existem técnicas de filtragem de imagem no Matlab, particularmente ou não, que removerão ou suavizarão esse padrão de grade?

Stephen E
fonte
2
você pode nos dar mais informações sobre como essas imagens foram corrompidas? Esse é um padrão estranho, pois é muito local em pixels, que requer alta resolução no domínio da frequência, o que implicaria em um codificador JPEG terrivelmente quebrado, talvez?
Marcus Müller
Não tenho muita certeza, desculpe. As imagens fazem parte de um banco de dados que mudou de mãos algumas vezes e as pessoas não foram tão cuidadosas quanto deveriam (... estudantes de graduação). Quanto à questão local dos pixels, eu concordo. Em algumas fotos que incluem superfícies de água mais escuras, o padrão de grade é muito claro.
Stephen E
@ MarcusMüller: Um decodificador JPEG horrivelmente quebrado parece mais provável para mim, embora eu suponha que seja possível de qualquer maneira. De qualquer forma, com base no espaçamento desigual e sem potência de 2 das linhas, parece-me que as imagens provavelmente foram ampliadas e recodificadas após a ocorrência da corrupção; portanto, tentando corrigi-las no O domínio DCT é provavelmente inútil. A solução de pintura de Maximilian Matthé abaixo é provavelmente a melhor aposta do OP.
Ilmari Karonen 4/17/17
1
Ah, e o OP definitivamente deve salvar um backup das imagens antes de tentar corrigi-las de qualquer forma, caso alguém queira analisá-las novamente. A pintura, por mais bem feita, sempre é uma operação com perdas e tem o potencial de apresentar viés (uma vez que basicamente consiste em criar dados falsos para substituir os pixels corrompidos). E o mesmo vale para filtragem mediana ou remoção de frequência também, ou qualquer outra coisa que possa ocultar esse tipo de dano.
Ilmari Karonen 4/17/17
@IlmariKaronen Obrigado pela dica. Definitivamente tentará ter mais cuidado com essas imagens.
Stephen E

Respostas:

16

Você pode usar um algoritmo de pintura padrão. Esses algoritmos substituem os pixels marcados em uma imagem pelos valores de pixel que cercam esses pixels marcados. O desafio aqui é detectar a grade (meus testes parecem mostrar que não é uma grade completamente regular). Então, eu vim com esta solução:

from PIL import Image
import requests
from io import BytesIO
import cv2

url = "http://i.stack.imgur.com/Ahrnl.jpg"
response = requests.get(url)
img = Image.open(BytesIO(response.content))

plt.imshow(img)
A = np.array(img)
A2 = A.copy()
A_gray = cv2.cvtColor(A, cv2.COLOR_RGB2GRAY)


# Do some rough edge detection to find the grid
sX = cv2.Sobel(A_gray, cv2.CV_64F, 1, 0, ksize=3)
sY = cv2.Sobel(A_gray, cv2.CV_64F, 0, 1, ksize=3)
sX[sX<0] = 0
sY[sY<0] = 0

plt.subplot(221)
plt.imshow(sX)

plt.subplot(222)
plt.imshow(sY)

plt.subplot(223)
# the sum operation projects the edges to the X or Y-axis. 
# The 0.2 damps the high peaks a little
eX = (sX**.2).sum(axis=0)  
eX = np.roll(eX, -1) # correct for the 1-pixel offset due to Sobel filtering
plt.plot(eX)

plt.subplot(224)
eY = (sY**.2).sum(axis=1)
eY = np.roll(eY, -1)
plt.plot(eY)

mask = np.zeros(A2.shape[:2], dtype=np.uint8)
mask[eY>480,:] = 1
mask[:, eX>390] = 1


A2[mask.astype(bool),:] = 255
plt.figure()
plt.subplot(221)
plt.imshow(A)

plt.subplot(222)
plt.imshow((A2))

restored = cv2.inpaint(A, mask, 1, cv2.INPAINT_NS)
plt.subplot(223)
plt.imshow(restored)

A saída do programa é a seguinte:

insira a descrição da imagem aqui

insira a descrição da imagem aqui

Para detectar a grade, fiz uma solução rápida e suja. Pode ser melhorado bastante, mas mostra a ideia inicial. O fluxo geral é:

  1. detectar a grade
  2. crie uma máscara que descreva quais pixels estão corrompidos pela grade
  3. pintar os pixels corrompidos.

Para a pintura, usei a operação de pintura OpenCV . Para detectar a grade, realizei a detecção de arestas nas direções X e Y usando um filtro Sobel. Em seguida, adiciono todos os valores das arestas nas direções X e Y para encontrar picos, onde estão as linhas de grade. Depois, escolho os picos mais altos como as coordenadas nas quais as linhas da grade são estimadas. Não está funcionando perfeitamente (por exemplo, bordas fortes na imagem são falsamente detectadas como linhas de grade), mas mostra a ideia. Pode ser melhorada, por exemplo, na transformação de Hough para encontrar linhas, expulsar arestas muito fortes, etc.

Como alternativa, se a grade é realmente a mesma para todas as imagens, você pode executar a detecção da grade em conjunto para todas as imagens, o que produziria uma precisão muito melhor (basta fazer a técnica acima, mas antes de escolher os picos, resuma os resultados de todas as imagens). Mais detalhadamente, você calcularia o eX para todas as imagens e adicionaria todos esses eX em um único vetor. Esse vetor terá uma estrutura de pico muito mais clara e o limiar pode ser feito mais facilmente.

Maximilian Matthé
fonte
Obrigdo por sua contribuição! Seu resultado aqui é muito bom! Vou tentar. Exoneração de responsabilidade: Sou muito novato no processamento de imagens, por isso levarei algum tempo para trabalhar sozinho, mas marcará como resolvido, pois funciona tão bem no seu objetivo. Em que ambiente você está executando esse algoritmo? Acho que a detecção indesejável de bordas na instrumentação e na infraestrutura da imagem não será um problema, pois a maioria das imagens não inclui nada disso e é apenas tundra e / ou água. Como você adiciona os picos de todas as fotos?
Stephen E
Obrigado! O ambiente é Python e acabei de adicionar uma observação sobre o que quero dizer com resumir os picos de todas as imagens.
Maximilian Matthé 4/17/17
3

Tentei um algoritmo realmente simples de executar um filtro mediano 3x3 nos canais R e G dessa imagem e funciona muito bem. insira a descrição da imagem aqui O código python é realmente simples:

import scipy.signal as sp
from scipy import ndimage

image = ndimage.imread('Ahrnl.jpg', flatten=False)
image_filtered = np.array(image)
for i in range(2) :
  image_filtered[:,:,i] = sp.medfilt2d(image[:,:,i])

Como alternativa, você pode usar a filtragem de domínio de frequência conforme discutido nesta pergunta: /programming/34027840/removing-periodic-noise-from-an-image-using-the-fourier-transform

A transformação de Fourier da sua imagem mostra claramente alguns "pontos" repetidos no espectro correspondentes a esse ruído periódico. insira a descrição da imagem aqui

Como Maximilian apontou, este último método funciona bem apenas se o ruído for perfeitamente periódico, o que não parece ser o caso aqui.

Tentei executar um filtro realmente estúpido que zera quadrados de caixas de frequência 5x5 centrados em múltiplos de 9 nas direções x e y e (meio que) suprime o ruído, mas introduz artefatos em locais que não contêm ruído (por exemplo, o céu).

Talvez se possa fazer melhor com o design cuidadoso do filtro de entalhe, em vez de zerar diretamente as caixas de FFT ( nunca faça isso na prática! ) E aplicar o filtro apenas nas regiões da imagem em que o ruído está presente (por exemplo, não filtre o céu).

insira a descrição da imagem aqui

Atul Ingle
fonte
em sua última linha, eu acredito que você só filtrar o canal vermelho duas vezes (último índice deve ser i, não 0)
Maximilian Matthé
@ MaximilianMatthé boa captura! (Felizmente o meu código real que eu corri foi ok: P)
Atul Ingle
Em relação ao método de transformada de Fourier: Isso só funciona de maneira confiável, se a grade é realmente regular (ou seja, a mesma distância entre todas as linhas). Não consegui (pelo menos não rapidamente) encontrar parâmetros para desenhar uma grade regular em cima das linhas corrompidas. Então, o método de Fourier também não seria capaz de remover esse ruído não exatamente periódico.
Maximilian Matthé
@ MaximilianMatthé, você está certo - o método FFT é complicado, pois o ruído não é um padrão perfeitamente periódico. Mas pode funcionar com um design cuidadoso de filtro de entalhe. Talvez.
Atul Ingle
Obrigdo por sua contribuição! Tentei um filtro mediano 3x3 no Matlab e, embora ele remova a grade (principalmente), não gostei da diminuição nos detalhes da imagem (eles precisam aparecer em um pop-up em um aplicativo de mapeamento da Web.
Stephen E