Jak sprawdzić, czy plik jest prawidłowym plikiem obrazu?


106

Obecnie używam PIL.

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

Jednakże, chociaż wystarcza to w większości przypadków, niektóre pliki graficzne, takie jak xcf, svg i psd, nie są wykrywane. Pliki psd zgłaszają wyjątek OverflowError.

Czy jest jakiś sposób, że mógłbym je również uwzględnić?


21
Zamykanie duplikatów w różnych językach nie jest szczególnie powszechną praktyką. Jeśli nie możesz znaleźć żadnych innych pytań Pythona z tym, pozostaw to otwarte, ponieważ mogą istnieć rozwiązania specyficzne dla Pythona, które ludzie chcą opublikować, a które nie dotarły do ​​opublikowanego przez Ciebie pytania.
Paolo Bergantino

tak, przede wszystkim liczyłem na bibliotekę Pythona, o której nie wiedziałem: P, a potem, jak zauważył Ben, tylko magiczne liczby nie potwierdzają całego obrazu.
Sujoy

@Sujoy, walidacja całego obrazu jest prawie niemożliwa, chyba że masz już jego kopię, ponieważ komputer nie jest w stanie odróżnić prawidłowego kolorowego piksela, a zniekształconym zestawem jedynek i zer, o ile cała kontrola (magiczne liczby) są poprawne.
DevinB

@devinb, zgodziłam się, po prostu zdobędę magiczne liczby i skończę z tym, chyba że ktoś inny wymyśli coś lepszego do wezwania refaktora :)
Sujoy

xcf i psd nie są tak naprawdę obrazami, są plikami projektów, które zawierają (często wiele) obrazów ... chociaż prawdopodobnie możesz zrobić argument dla svg.
mgalgs

Odpowiedzi:


11

W wielu przypadkach pierwsze kilka znaków będzie magiczną liczbą dla różnych formatów plików. Możesz to sprawdzić oprócz sprawdzania wyjątków powyżej.


11
To nie wystarczy, jeśli on naprawdę testuje „prawidłowe” obrazy; obecność magicznej liczby nie gwarantuje na przykład, że plik nie został obcięty.
Ben Blank

1
świetna rada, teraz muszę tylko dowiedzieć się, jakie to liczby. dzięki :)
Sujoy

@ben, ouch jeszcze o tym nie pomyślałem. to rzeczywiście dobra uwaga
Sujoy

@Ben, jak można oczekiwać, że biblioteka wywnioskuje, że plik został obcięty?
DevinB

6
@Ben Blank: To prawda, ale rozwiązanie problemu w 99% przypadków jest często lepsze niż całkowity brak rozwiązania.
Brian R. Bondy

208

Właśnie znalazłem wbudowany moduł imghdr . Z dokumentacji Pythona:

Moduł imghdr określa typ obrazu zawartego w pliku lub strumieniu bajtów.

Tak to działa:

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

Korzystanie z modułu jest znacznie lepsze niż ponowne wdrażanie podobnej funkcjonalności


2
tak imghdr działa dla większości formatów graficznych, ale nie dla wszystkich. zgodnie z moim pierwotnym problemem z plikami svg, xcf i psd, no cóż, te są również niewykryte w imghdr
Sujoy

2
Twoja odpowiedź jest właściwie lepsza, dzięki. Jak ktoś powiedział powyżej ... ale rozwiązanie problemu w 99% jest często lepsze niż jego całkowite
zaniechanie

2
Warto zwrócić uwagę: imghdr.what(path)zwraca, Nonejeśli podany pathnie jest rozpoznany typ pliku obrazu. Lista obecnie rozpoznawanych typów obrazów: RGB , gif , PBM , PGM , ppm , tiff , Rast , XBM , JPEG , BMP , PNG , WebP , EXR .
patryk.beza

1
Bądź ostrożny! Prawidłowy hdr nie oznacza prawidłowego obrazu (np. Bajty obrazu mogły zostać zaszyfrowane!)
Filippo Mazza

1
Zgodnie z komentarzem @FilippoMazza, mogę potwierdzić, że zły obraz, który został odcięty podczas przesyłania, może przejść ten test, ale zepsuje się, gdy PIL spróbuje go odczytać.
kevinmicke

47

Oprócz tego, co sugeruje Brian, możesz skorzystać z PIL metody weryfikacji aby sprawdzić, czy plik nie jest uszkodzony.

im.verify ()

Próbuje określić, czy plik jest uszkodzony, bez faktycznego dekodowania danych obrazu. Jeśli ta metoda napotka jakieś problemy, generuje odpowiednie wyjątki. Ta metoda działa tylko na nowo otwartym obrazie; jeśli obraz został już załadowany, wynik jest niezdefiniowany. Ponadto, jeśli chcesz załadować obraz po użyciu tej metody, musisz ponownie otworzyć plik obrazu. Atrybuty


Cóż, głównym problemem jest to, że plików svg, xcf i psd nie można otworzyć za pomocą Image.open (), stąd nie ma szans na weryfikację za pomocą im.verify ()
Sujoy

16
Mój Boże, dokumentacja PIL jest okropna. Czym dokładnie jest „odpowiedni wyjątek”?
Timmmm

Oto link do dokumentacji Pillow dla Image.verify () . Niestety nie jest lepiej i wygląda na to, że właśnie usunęli powyższy akapit bez dodawania czegokolwiek.
Two-Bit Alchemist

Widziałem weryfikację podniesienia SyntaxError dla uszkodzonych plików png
Carl

czy istnieje sposób, aby zweryfikować „Z faktycznym dekodowaniem danych obrazu”?
Trevor Boyd Smith

7

Oprócz PILsprawdzania obrazu możesz również dodać sprawdzenie rozszerzenia nazwy pliku w następujący sposób:

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

Zwróć uwagę, że sprawdza to tylko, czy nazwa pliku ma prawidłowe rozszerzenie obrazu, w rzeczywistości nie otwiera obrazu, aby sprawdzić, czy jest to prawidłowy obraz, dlatego musisz użyć dodatkowo PILlub jednej z bibliotek sugerowanych w innych odpowiedziach.


Co się stanie, jeśli rozszerzenia są nieprawidłowe w plikach? Np. Plik tekstowy jest zapisywany z rozszerzeniem .jpg lub odwrotnie.
hafiz031

1
@ hafiz031 Aby uzyskać rzeczywisty format, możesz to zrobić, from PIL import Image img = Image.open(filename) print(img.format)a następnie sprawdzić to w ten sposób:img.format.lower() in ['png', 'jpg', 'jpeg', 'tiff', 'bmp', 'gif']
tsveti_iko

Niestety to nie zadziałało dla mnie. Nadal identyfikuje uszkodzony obraz jako obraz JPEG. W końcu udało mi się w ten sposób obsłużyć ten przypadek (korzystam z OpenCv): stackoverflow.com/a/63421847/6907424
hafiz031

6

Aktualizacja

Zaimplementowałem również następujące rozwiązanie w moim skrypcie Python tutaj na GitHub .

Sprawdziłem również, że uszkodzone pliki (jpg) często nie są „zepsutymi” obrazami, tj. Uszkodzony plik obrazu czasami pozostaje prawidłowym plikiem obrazu, oryginalny obraz zostaje utracony lub zmieniony, ale nadal można go załadować bez błędów. Ale obcięcie pliku zawsze powoduje błędy.

Zakończ aktualizację

Możesz użyć Python Pillow modułu (PIL) z większością formatów obrazu, aby sprawdzić, czy plik jest prawidłowym i nienaruszonym plikiem obrazu.

W przypadku, gdy celem jest wykrycie również uszkodzonych obrazów, @Nadia Alramli poprawnie sugeruje im.verify()metodę, ale to nie wykrywa wszystkich możliwych defektów obrazu , np.im.verify Nie wykrywa przyciętych obrazów (które większość widzów często ładuje z szarym obszarem).

Pillow jest również w stanie wykryć tego typu defekty, ale musisz zastosować manipulację obrazem lub dekodować / przekodować obraz lub uruchomić kontrolę. Na koniec proponuję użyć tego kodu:

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

W przypadku wad obrazu ten kod zgłosi wyjątek. Proszę wziąć pod uwagę, że im.verify jest około 100 razy szybsze niż wykonanie manipulacji obrazem (i myślę, że flip jest jedną z tańszych transformacji). Za pomocą tego kodu zweryfikujesz zestaw obrazów z prędkością około 10 MB / s ze standardową poduszką lub 40 MB / s z modułem Pillow-SIMD (nowoczesny procesor 2,5 GHz x86_64).

Dla innych formatów PSD , XCF , .. można użyć ImageMagick wrapper Wand , kod jest w następujący sposób:

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

Ale z moich eksperymentów Wand nie wykrywa obciętych obrazów, myślę, że wczytuje brakujące części jako szare obszary bez monitowania.

Napisałem, że Imagemagick ma zewnętrzne polecenie identyfikujące, które może wykonać zadanie, ale nie znalazłem sposobu na programowe wywołanie tej funkcji i nie testowałem tej trasy.

Proponuję zawsze przeprowadzić wstępną kontrolę, sprawdzić, czy rozmiar pliku nie jest zerowy (lub bardzo mały), to bardzo tani pomysł:

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

5

W Linuksie możesz użyć Python-Magic ( http://pypi.python.org/pypi/python-magic/0.1 ), który używa libmagic do identyfikacji formatów plików.

AFAIK, libmagic zagląda do pliku i próbuje powiedzieć więcej niż tylko o formacie, jak wymiary bitmapy, wersja formatu itp. Więc możesz to potraktować jako powierzchowny test "poprawności".

W przypadku innych definicji „prawidłowych” może być konieczne napisanie własnych testów.


5

Możesz użyć powiązań Pythona do libmagic, python-magic, a następnie sprawdzić typy MIME. To nie powie ci, czy pliki są uszkodzone lub nienaruszone, ale powinno być w stanie określić, jaki to typ obrazu.


3

Cóż, nie wiem o wnętrzu psd, ale na pewno wiem, że w rzeczywistości svg nie jest plikiem obrazu jako takim - jest oparty na xml, więc zasadniczo jest zwykły plik tekstowy.


aha, masz rację. to jest xml. zawiera jednak osadzone w niej dane obrazu.
Sujoy

3

Jedną z opcji jest skorzystanie z filetypepakietu.

Instalacja

python -m pip install filetype

Zalety

  1. Szybko: działa, ładując kilka pierwszych bajtów obrazu ( sprawdź magiczną liczbę )
  2. Obsługuje różne typy MIME: obrazy, filmy, czcionki, audio, archiwa.

Przykład rozwiązania

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...")

Dodatkowe informacje o oficjalnym repozytorium: https://github.com/h2non/filetype.py


1

Czy sprawdzenie rozszerzeń plików byłoby dopuszczalne, czy też próbujesz potwierdzić, że same dane reprezentują plik obrazu?

Jeśli możesz sprawdzić rozszerzenie pliku, to wymaganie może spełnić wyrażenie regularne lub proste porównanie.


po prostu sprawdzenie rozszerzenia nie wystarczy, ponieważ można zmienić nazwę pliku txt na jpg lub coś podobnego. myślę, że jeśli nie znajdę rozwiązania, tylko wtedy użyję sprawdzania rozszerzeń dla xcf i svg
Sujoy

Zrozumiałe, liczyłem tylko na wyjaśnienie, zanim przystąpiłem do opracowywania rozwiązania, które mogłoby lepiej odpowiadać Twoim potrzebom. Dzięki!
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)

Twój kod ma pewne problemy z wcięciami i nie będzie działał poprawnie. Zastanów się również nad dodaniem wyjaśnień, dlaczego i jak Twój kod rozwiązuje problem. Odpowiedzi zawierające tylko kod nie będą tak pomocne dla przyszłych czytelników, którzy tu przyjdą.
Tomerikoo

Tutaj użyliśmy metody Agrparser.
rObinradOO
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.