Quel filtre d'image peut être appliqué pour supprimer le motif quadrillé des fichiers JPEG corrompus?

8

J'ai environ 1 400 fichiers JPEG qui ont été corrompus d'une manière ou d'une autre et ont perdu les images de sauvegarde. Ils semblent tous avoir le même motif quadrillé de lignes sur chacun (c'est-à-dire que le quadrillage ne se déplace pas d'une image à l'autre.

Voici à quoi ressemble l'une de ces images:

entrez la description de l'image ici

Existe-t-il des techniques de filtrage d'image dans Matlab, en particulier ou autrement, qui supprimeront ou lisseront ce motif de quadrillage?

Stephen E
la source
2
pouvez-vous nous donner plus d'informations sur la façon dont ces images ont été corrompues? C'est un motif étrange, car il est très local en pixels, ce qui nécessite une haute résolution dans le domaine fréquentiel, ce qui impliquerait un encodeur JPEG horriblement cassé, peut-être?
Marcus Müller
Je ne suis pas tout à fait sûr, désolé. Les images font partie d'une base de données qui a changé de mains plusieurs fois et les gens n'ont pas été aussi prudents qu'ils auraient dû (... étudiants de premier cycle). En ce qui concerne le problème des pixels locaux, je suis d'accord. Sur certaines photos qui comportent des surfaces d'eau plus sombres, le motif de quadrillage est très clair.
Stephen E
@ MarcusMüller: Un décodeur JPEG horriblement cassé me semble plus probable, bien que je suppose que les deux sens sont possibles. Quoi qu'il en soit, sur la base de l'espacement inégal et sans puissance de 2 des lignes, il me semble que les images ont probablement été agrandies et recodées après la corruption, alors, hélas, en essayant de les corriger dans le Le domaine DCT est probablement futile. La solution de peinture par Maximilian Matthé ci-dessous est probablement le meilleur pari du PO.
Ilmari Karonen
1
Oh, et l'OP devrait certainement enregistrer une sauvegarde des images avant d' essayer de les réparer de quelque manière que ce soit, au cas où quelqu'un voudrait les réanalyser. La peinture, aussi bien faite soit-elle, est toujours une opération avec perte et a le potentiel d'introduire un biais (car cela revient essentiellement à créer de fausses données pour remplacer les pixels corrompus). Et il en va de même pour le filtrage médian ou la suppression de fréquence, ou tout autre élément susceptible de masquer ce type de dommage.
Ilmari Karonen
@IlmariKaronen Merci pour le conseil. J'essaierai certainement d'être plus prudent avec ces images.
Stephen E

Réponses:

16

Vous pouvez utiliser un algorithme de peinture standard. Ces algorithmes remplacent les pixels marqués d'une image par les valeurs de pixels qui entourent ces pixels marqués. Le défi ici est de détecter la grille (mes tests semblent montrer que ce n'est pas une grille complètement régulière). J'ai donc trouvé cette solution:

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)

La sortie du programme est la suivante:

entrez la description de l'image ici

entrez la description de l'image ici

Pour détecter la grille, j'ai fait une solution rapide et sale. Il peut être beaucoup amélioré, mais il montre l'idée initiale. Le flux général est le suivant:

  1. détecter la grille
  2. créer un masque qui décrit quels pixels sont corrompus par la grille
  3. inpaint les pixels corrompus.

Pour l'inpainting, j'ai utilisé l' opération Inpaint d' OpenCV . Pour détecter la grille, j'ai effectué une détection de bord dans les directions X et Y en utilisant un filtre Sobel. Ensuite, j'ajoute toutes les valeurs de bord dans la direction X et la direction Y pour trouver des pics, où se trouvent les lignes de la grille. Ensuite, je choisis les pics les plus élevés comme coordonnées où les lignes de la grille sont estimées. Cela ne fonctionne pas parfaitement (par exemple, les bords forts de l'image sont faussement détectés comme des lignes de grille), mais cela montre l'idée. Il peut être amélioré par exemple par une transformation de Hough pour trouver des lignes, éliminer les bords très forts, etc.

Alternativement, si la grille est vraiment la même pour toutes les images, vous pouvez effectuer la détection de grille conjointement pour toutes les images, ce qui donnerait une bien meilleure précision (faites simplement la technique ci-dessus, mais avant de choisir les pics, résumez les résultats à partir de toutes les photos). Plus en détail, vous devez calculer l'eX pour toutes les images et ajouter tous ces eX ensemble dans un seul vecteur. Ce vecteur aura une structure de pic beaucoup plus claire et le seuillage peut être fait plus facilement.

Maximilian Matthé
la source
Merci pour votre participation! Votre résultat ici est vraiment sympa! Je vais essayer. Avis de non-responsabilité: Je suis très novice dans le traitement d'image, cela me prendra donc un certain temps pour travailler par moi-même, mais cela marquera la résolution car il fonctionne si bien de votre côté. Dans quel environnement utilisez-vous cet algorithme? Je pense que la détection indésirable des bords sur l'instrumentation et l'infrastructure dans l'image ne sera pas un gros problème car la majorité des images n'incluent rien de tout cela et ne sont que de la toundra et / ou de l'eau. Comment procédez-vous pour additionner les pics de toutes les images?
Stephen E
Merci! L'environnement est Python et je viens d'ajouter une note sur ce que je veux dire par résumer les pics pour toutes les images.
Maximilian Matthé
3

J'ai essayé un algorithme très simple pour exécuter un filtre médian 3x3 sur les canaux R et G de cette image et cela fonctionne assez bien. entrez la description de l'image ici Le code python est vraiment simple:

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])

Vous pouvez également utiliser le filtrage du domaine fréquentiel comme indiqué dans cette question: /programming/34027840/removing-periodic-noise-from-an-image-using-the-fourier-transform

La transformée de Fourier de votre image montre clairement quelques "points" répétés dans le spectre correspondant à ce bruit périodique. entrez la description de l'image ici

Comme l'a souligné Maximilien, cette dernière méthode ne fonctionne bien que si le bruit est parfaitement périodique, ce qui ne semble pas être le cas ici.

J'ai essayé d'exécuter un filtre vraiment stupide qui met à zéro des carrés de 5 x 5 cases de fréquence centrées sur des multiples de 9 dans les directions x et y et il (en quelque sorte) supprime le bruit mais introduit des artefacts dans des endroits qui ne contiennent pas de bruit (par exemple, le ciel).

On peut peut-être faire mieux avec une conception soignée du filtre coupe-bande au lieu de mettre à zéro directement les bacs FFT ( ne faites jamais cela en pratique! ) Et d'appliquer le filtre uniquement dans les régions de l'image où le bruit est présent (c'est-à-dire ne pas filtrer le ciel).

entrez la description de l'image ici

Atul Ingle
la source
dans votre dernière ligne, je crois que vous venez de filtrer le canal rouge deux fois (le dernier index devrait être i, pas 0)
Maximilian Matthé
@ MaximilianMatthé bonne prise! (Heureusement, mon code actuel que j'ai exécuté était correct: P)
Atul Ingle
Concernant la méthode de Fourier-Transform: Cela ne fonctionne de manière fiable que si la grille est vraiment régulière (c'est-à-dire la même distance entre toutes les lignes). Je n'ai pas pu (du moins pas rapidement) trouver des paramètres tels que je pouvais dessiner une grille régulière au-dessus des lignes corrompues. Ensuite, la méthode de Fourier ne serait pas non plus en mesure de supprimer ce bruit pas exactement périodique.
Maximilian Matthé
@ MaximilianMatthé vous avez raison - la méthode FFT est délicate car le bruit n'est pas parfaitement périodique. Mais cela pourrait fonctionner avec une conception soignée du filtre coupe-bande. Peut être.
Atul Ingle
Merci pour votre participation! J'ai essayé un filtre médian 3x3 dans Matlab et bien qu'il ait supprimé le maillage (principalement), je n'ai pas aimé la diminution des détails de l'image (ils doivent apparaître dans une fenêtre contextuelle sur une application de cartographie Web.
Stephen E