¿Cómo comprobar si un archivo es un archivo de imagen válido?

105

Actualmente estoy usando PIL.

from PIL import Image
try:
    im=Image.open(filename)
    # do stuff
except IOError:
    # filename not an image file

Sin embargo, aunque esto cubre suficientemente la mayoría de los casos, algunos archivos de imagen como xcf, svg y psd no se detectan. Los archivos psd arrojan una excepción OverflowError.

¿Hay alguna forma en que pueda incluirlos también?

Sujoy
fuente
21
No es una práctica común cerrar duplicados en diferentes idiomas. Si no puede encontrar ninguna otra pregunta de Python con esto, déjela abierta, ya que podría haber soluciones específicas de Python que las personas quieran publicar y que no respondieron a la pregunta que publicó.
Paolo Bergantino
sí, en primer lugar, realmente esperaba una biblioteca de python que no conocía: P y luego, como señaló Ben, solo los números mágicos no validan la imagen completa.
Sujoy
@Sujoy, validar una imagen completa es casi imposible, a menos que ya tenga una copia de ella, porque la computadora no puede distinguir entre un píxel de color correcto y un conjunto confuso de 1 y 0, siempre que todo el control (números mágicos) son correctos.
DevinB
@devinb, de acuerdo, obtendré los números mágicos y terminaré con eso a menos que a alguien más se le ocurra algo mejor para llamar para un refactor :)
Sujoy
xcf y psd no son realmente imágenes, son archivos de proyecto que contienen (a menudo muchas) imágenes ... aunque probablemente podrías defender svg.
mgalgs

Respuestas:

11

Muchas veces los primeros dos caracteres serán un número mágico para varios formatos de archivo. Puede verificar esto además de su verificación de excepción anterior.

Brian R. Bondy
fuente
10
Eso no será suficiente si realmente está probando imágenes "válidas"; la presencia de un número mágico no garantiza que el archivo no se haya truncado, por ejemplo.
Ben Blank
1
Excelente consejo, ahora solo necesito averiguar cuáles son esos números. gracias :)
Sujoy
@ben, ouch no pensé en eso todavía. ese es un buen punto de hecho
Sujoy
@Ben, ¿cómo esperaría que una biblioteca infiera que un archivo ha sido truncado?
DevinB
6
@Ben Blank: Cierto, pero resolver un problema al 99% del camino es a menudo mejor que no resolverlo en absoluto.
Brian R. Bondy
206

Acabo de encontrar el módulo imghdr incorporado . De la documentación de Python:

El módulo imghdr determina el tipo de imagen contenida en un archivo o flujo de bytes.

Así es como funciona:

>>> import imghdr
>>> imghdr.what('/tmp/bass')
'gif'

Usar un módulo es mucho mejor que volver a implementar una funcionalidad similar

Nadia Alramli
fuente
2
sí, imghdr funciona para la mayoría de formatos de imagen, pero no para todos. según mi problema original con los archivos svg, xcf y psd, bueno, esos tampoco se detectan en imghdr
Sujoy
2
Tu respuesta es realmente mejor, gracias. Como dijo alguien arriba ... pero resolver un problema al 99% del camino es a menudo mejor que no resolverlo en absoluto ..
RinkyPinku
2
Vale la pena tener en cuenta: imghdr.what(path)devuelve Nonesi pathno se reconoce el tipo de archivo de imagen. Lista de tipos de imágenes reconocidos actualmente: rgb , gif , pbm , pgm , ppm , tiff , rast , xbm , jpeg , bmp , png , webp , exr .
patryk.beza
1
¡Ten cuidado! Un hdr válido no significa una imagen válida (por ejemplo, ¡los bytes de la imagen pueden haber sido codificados!)
Filippo Mazza
1
Según el comentario de @FilippoMazza, puedo confirmar que una mala imagen que se cortó durante la transferencia puede pasar esta prueba, pero se romperá cuando PIL intente leerla.
kevinmicke
47

Además de lo que sugiere Brian, puede utilizar el método de verificación de PIL para comprobar si el archivo está roto.

im.verify ()

Intenta determinar si el archivo está roto, sin realmente decodificar los datos de la imagen. Si este método encuentra algún problema, genera excepciones adecuadas. Este método solo funciona en una imagen recién abierta; si la imagen ya se ha cargado, el resultado no está definido. Además, si necesita cargar la imagen después de usar este método, debe volver a abrir el archivo de imagen. Atributos

Nadia Alramli
fuente
bueno, el problema principal es que los archivos svg, xcf y psd no se pueden abrir con Image.open (), por lo tanto, no hay posibilidad de verificar con im.verify ()
Sujoy
16
Dios mío, la documentación de PIL es terrible. ¿Qué es exactamente una "excepción adecuada"?
Timmmm
Aquí está el enlace a la documentación de Pillow para Image.verify () . Desafortunadamente, no es mejor, y parece que simplemente levantaron el párrafo anterior sin agregar nada.
Alquimista de Two-Bit
He visto verificar subir SyntaxError para archivos png corruptos
Carl
¿Hay alguna manera de verificar "CON realmente la decodificación de los datos de la imagen"?
Trevor Boyd Smith
7

Además de la PILverificación de imagen, también puede agregar una verificación de extensión de nombre de archivo como esta:

filename.lower().endswith(('.png', '.jpg', '.jpeg', '.tiff', '.bmp', '.gif'))

Tenga en cuenta que esto solo verifica si el nombre del archivo tiene una extensión de imagen válida, en realidad no abre la imagen para ver si es una imagen válida, es por eso que necesita usar adicionalmente PILo una de las bibliotecas sugeridas en las otras respuestas.

tsveti_iko
fuente
¿Qué pasa si las extensiones son incorrectas en los archivos? Por ejemplo, un archivo de texto se guarda con la extensión .jpg o viceversa.
hafiz031
1
@ hafiz031 Para obtener el formato real, puede hacerlo from PIL import Image img = Image.open(filename) print(img.format)y luego verificarlo así:img.format.lower() in ['png', 'jpg', 'jpeg', 'tiff', 'bmp', 'gif']
tsveti_iko
Desafortunadamente, esto no funcionó para mí. Todavía está identificando una imagen dañada como una imagen JPEG. Finalmente logré manejar este caso de esta manera (estoy usando OpenCv): stackoverflow.com/a/63421847/6907424
hafiz031
6

Actualizar

También implementé la siguiente solución en mi script de Python aquí en GitHub .

También verifiqué que los archivos dañados (jpg) con frecuencia no son imágenes 'rotas', es decir, un archivo de imagen dañado a veces sigue siendo un archivo de imagen legítimo, la imagen original se pierde o se altera, pero aún puede cargarla sin errores. Pero, el truncamiento de archivos siempre causa errores.

Finalizar actualización

Puede usar el módulo Python Pillow (PIL), con la mayoría de los formatos de imagen, para verificar si un archivo es un archivo de imagen válido e intacto.

En el caso de que pretenda detectar también imágenes rotas, @Nadia Alramli sugiere correctamente el im.verify()método, pero este no detecta todos los posibles defectos de imagen , por ejemplo, im.verifyno detecta imágenes truncadas (que la mayoría de los espectadores suelen cargar con un área en gris).

Pillow también puede detectar este tipo de defectos, pero debe aplicar la manipulación de imágenes o decodificar / recodificar la imagen o activar la verificación. Finalmente sugiero usar este código:

try:
  im = Image.load(filename)
  im.verify() #I perform also verify, don't know if he sees other types o defects
  im.close() #reload is necessary in my case
  im = Image.load(filename) 
  im.transpose(PIL.Image.FLIP_LEFT_RIGHT)
  im.close()
except: 
  #manage excetions here

En caso de defectos de imagen, este código generará una excepción. Tenga en cuenta que im.verify es aproximadamente 100 veces más rápido que realizar la manipulación de imágenes (y creo que voltear es una de las transformaciones más baratas). Con este código vas a verificar un conjunto de imágenes a unos 10 MBytes / seg con Pillow estándar o 40 MBytes / seg con módulo Pillow-SIMD (CPU moderna x86_64 de 2.5Ghz).

Para los otros formatos psd , xcf , .. puedes usar Imagemagick wrapper Wand , el código es el siguiente:

im = wand.image.Image(filename=filename)
temp = im.flip;
im.close()

Pero, a partir de mis experimentos, Wand no detecta imágenes truncadas, creo que carga las partes que faltan como un área en gris sin preguntar.

Red que Imagemagick tiene una identificación de comando externa que podría hacer el trabajo, pero no he encontrado una manera de invocar esa función programáticamente y no he probado esta ruta.

Sugiero que siempre haga una comprobación preliminar, compruebe el tamaño del archivo a no ser cero (o muy pequeño), es una muy barato idea:

statfile = os.stat(filename)
filesize = statfile.st_size
if filesize == 0:
  #manage here the 'faulty image' case
Fabiano Tarlao
fuente
5

En Linux, puede usar python-magic ( http://pypi.python.org/pypi/python-magic/0.1 ) que usa libmagic para identificar formatos de archivo.

AFAIK, libmagic busca en el archivo e intenta decirle más sobre él que solo el formato, como las dimensiones del mapa de bits, la versión del formato, etc. Así que puede ver esto como una prueba superficial de "validez".

Para otras definiciones de "válido", es posible que deba escribir sus propias pruebas.

fmarc
fuente
5

Puede usar los enlaces de Python a libmagic, python-magic y luego verificar los tipos de mime. Esto no le dirá si los archivos están dañados o intactos, pero debería poder determinar qué tipo de imagen es.

Kamil Kisiel
fuente
3

Bueno, no conozco el interior de psd, pero sí, sé que, de hecho, svg no es un archivo de imagen en sí mismo, sino que está basado en xml, por lo que es, esencialmente, un archivo de texto sin formato.

shylent
fuente
aha, tienes razón. es xml. sin embargo, contiene algunos datos de imagen incrustados.
Sujoy
2

Una opción es utilizar el filetypepaquete.

Instalación

python -m pip install filetype

Ventajas

  1. Rápido: hace su trabajo cargando los primeros bytes de su imagen ( verifique el número mágico )
  2. Admite diferentes tipos de mímica: imágenes, videos, fuentes, audio, archivos.

Ejemplo de solución

import filetype

filename = "/path/to/file.jpg"

if filetype.image(filename):
    print(f"{filename} is a valid image...")
elif filetype.video(filename):
    print(f"{filename} is a valid video...")

Información adicional sobre el repositorio oficial: https://github.com/h2non/filetype.py

Alex Fortin
fuente
1

¿Sería aceptable comprobar las extensiones de archivo o está tratando de confirmar que los datos en sí representan un archivo de imagen?

Si puede comprobar la extensión del archivo, una expresión regular o una simple comparación podrían satisfacer el requisito.

cerdo maldito
fuente
simplemente comprobar la extensión no será suficiente, ya que se puede cambiar el nombre de un archivo txt como jpg o algo así. Supongo que si no puedo encontrar una solución, solo entonces usaré la verificación de extensiones para xcf y svg
Sujoy
Es comprensible, solo esperaba alguna aclaración antes de proceder a idear una solución que pudiera adaptarse mejor a sus necesidades. ¡Gracias!
Doomspork
-1
format = [".jpg",".png",".jpeg"]
 for (path,dirs,files) in os.walk(path):
     for file in files:
         if file.endswith(tuple(format)):
             print(path)
             print ("Valid",file)
         else:
             print(path)
             print("InValid",file)
rObinradOO
fuente
Su código tiene algunos problemas de sangría y no se ejecutará correctamente. Además, considere agregar algunas explicaciones sobre por qué y cómo su código resuelve el problema. Las respuestas de solo código no serán tan útiles para los futuros lectores que vengan aquí.
Tomerikoo
Aquí hemos utilizado el método Agrparser.
rObinradOO