Szybki sposób na uzyskanie wymiarów obrazu (nie rozmiaru pliku)

138

Szukam szybkiego sposobu na uzyskanie wysokości i szerokości obrazu w pikselach. Powinien obsługiwać co najmniej JPG, PNG i TIFF, ale im więcej, tym lepiej. Podkreślam szybko, ponieważ moje obrazy są dość duże (do 250 MB), a uzyskanie rozmiaru za pomocą ImageMagick zajmuje zbyt dużo czasu, identifyponieważ oczywiście najpierw odczytuje obrazy jako całość.

Najlepiej szukam sposobu, który działa dobrze w Rubim, a nawet w Railsach 3.

Znam teorię (różne formaty obrazów, ich nagłówki i różnice itd.). Rzeczywiście, proszę o jakąś bibliotekę, która może rozwiązać mój problem w dość ogólny sposób.

Właśnie znalazłem imagesize, który wygląda obiecująco, chociaż rozwój wydaje się być martwy.

dAnjou
źródło
8
Wydaje się, że nie jest to prawdą w przypadku nowych wersji programu ImageMagick. Używając ImageMagick 6.5.4-7 potwierdziłem, że identyfikacja (przynajmniej dla TIF i PNG) odczytuje tylko nagłówek (do 60KB) i działa bardzo szybko, nawet dla obrazów 335MB.
coderforlife

Odpowiedzi:

195
  • fileKomenda drukuje wymiary kilku formatów graficznych (np PNG, GIF, JPEG, najnowsze wersje również ppm, webp), a nie tylko czytać nagłówek.

  • identifyPoleceń (od ImageMagick) drukuje wiele informacji obrazu dla różnych obrazów. Wydaje się, że ogranicza się do czytania części nagłówka (patrz komentarze). Ma również ujednoliconą produkcję, której fileniestety brakuje.

  • exiv2podaje wymiary dla wielu formatów, w tym JPEG, TIFF, PNG, GIF, WEBP, nawet jeśli nie ma nagłówka EXIF. Nie jest jednak jasne, czy w tym celu odczytuje wszystkie dane. Zobacz stronę podręcznika exiv2 dla wszystkich obsługiwanych formatów obrazów.

  • head -n1 poda wymiary dla formatów PPM, PGM.

W przypadku formatów popularnych w sieci zarówno exiv2i identifybędą spełniały swoje zadanie. W zależności od przypadku użycia może być konieczne napisanie własnego skryptu, który łączy / analizuje dane wyjściowe kilku narzędzi.

ypnos
źródło
3
Wykonałem kilka testów za pomocą polecenia identyfikacji ImageMagick, używając strace do nagrywania wywołań open / read / mmap / close, aby zobaczyć, ile danych zostało odczytanych ze zidentyfikowanego obrazu. Zależy to nieznacznie od typu pliku i rozmiaru pliku, ale otrzymywałem 20-60 KB odczytywane przez „identyfikację” dla obrazów 5-335 MB (testowałem również pod kątem „konwersji”, która pokazywała wszystkie odczytywane bajty). Wygląda więc na to, że „identyfikacja” jest tutaj dobrym wyborem (ponieważ obsługuje wszystkie popularne formaty i czyta tylko nagłówek).
coderforlife
1
Myślę, że exiv2 również obsługuje PNG.
chx
Jakieś sposoby na łatwe analizowanie tych poleceń pliku? Identyfikacja jest świetna, ale niestety nie działa z plikami WebP
Brian Leishman
Zidentyfikować czyni pracę z WebP i ImageMagick posiada wsparcie dla WebP przez lata. Może mógłbyś dostać aktualizację?
ypnos
32

Nie jestem pewien, czy masz zainstalowany php, ale ta funkcja PHP jest całkiem przydatna

 php -r "print_r(getimagesize('http://www.google.com/images/logos/ps_logo2.png'));"
ajreal
źródło
1
Jest to znacznie szybsze niż „identyfikacja”. Dobre podejscie. Dzięki.
souravb
19

Możesz użyć funkcji identyfikacji ImageMagick . Oto jak to zrobić w bash (uwaga $ 0 to ścieżka obrazu):

width=$(identify -format "%w" "$0")> /dev/null
height=$(identify -format "%h" "$0")> /dev/null

A to również ukrywa potencjalne komunikaty o błędach. Nowoczesne implementacje identifyodczytują tylko nagłówek, a nie cały obraz, dzięki czemu jest szybki. Nie wiem jednak, jak wypada w porównaniu z innymi metodami.

James L.
źródło
2
Uważam, że w ten sposób jest znacznie bardziej wydajne:read width height < <(identify -format "%w %h" "${1}")
Cromax
5

https://joseluisbz.wordpress.com/2013/08/06/obtaining-size-or-dimension-of-images/ (BMP, PNG, GIF, JPG, TIF lub WMF)

Tutaj dla dwóch formatów PNG i JPG.

Mój kod pochodzi z klasy zaprojektowanej do mojego użytku, możesz go edytować zgodnie z własnymi potrzebami.

Sprawdź te funkcje / metody za pomocą PHP :

  public function ByteStreamImageString($ByteStream,&$Formato,&$Alto,&$Ancho) {
    $Alto = 0;
    $Ancho = 0;
    $Formato = -1;
    $this->HexImageString = "Error";
    if (ord($ByteStream[0])==137 && ord($ByteStream[1])==80 && ord($ByteStream[2])==78){
      $Formato = 1; //PNG
      $Alto = $this->Byte2PosInt($ByteStream[22],$ByteStream[23]);
      $Ancho = $this->Byte2PosInt($ByteStream[18],$ByteStream[19]);
    }
    if (ord($ByteStream[0])==255 && ord($ByteStream[1])==216
        && ord($ByteStream[2])==255 && ord($ByteStream[3])==224){
      $Formato = 2; //JPG
      $PosJPG = 2;
      while ($PosJPG<strlen($ByteStream)){
        if (sprintf("%02X%02X", ord($ByteStream[$PosJPG+0]),ord($ByteStream[$PosJPG+1]))=="FFC0"){
          $Alto = $this->Byte2PosInt($ByteStream[$PosJPG+5],$ByteStream[$PosJPG+6]);
          $Ancho = $this->Byte2PosInt($ByteStream[$PosJPG+7],$ByteStream[$PosJPG+8]);
        }
        $PosJPG = $PosJPG+2+$this->Byte2PosInt($ByteStream[$PosJPG+2],$ByteStream[$PosJPG+3]);
      }
    }
    if ($Formato > 0){
      $this->HexImageString = "";
      $Salto = 0;
      for ($i=0;$i < strlen($ByteStream); $i++){
        $Salto++;
        $this->HexImageString .= sprintf("%02x", ord($ByteStream[$i]));
        if ($Salto==64){
          $this->HexImageString .= "\n";
          $Salto = 0;
        }
      }
    }
  }


  private function Byte2PosInt($Byte08,$Byte00) {
    return ((ord($Byte08) & 0xFF) << 8)|((ord($Byte00) & 0xFF) << 0);
  }

Korzystanie z kodu PHP:

      $iFormato = NULL;//Format PNG or JPG
      $iAlto = NULL; //High
      $iAncho = NULL;//Wide
      ByteStreamImageString($ImageJPG,$iFormato,$iAlto,$iAncho);//The Dimensions will stored in  iFormato,iAlto,iAncho

Teraz te funkcje / metody używające JAVA :

  private void ByteStreamImageString(byte[] ByteStream,int[] Frmt,int[] High,int[] Wide) {
    High[0] = 0;
    Wide[0] = 0;
    Frmt[0] = -1;
    this.HexImageString = "Error";
    if ((int)(ByteStream[0]&0xFF)==137 && (int)(ByteStream[1]&0xFF)==80 &&(int)(ByteStream[2]&0xFF)==78){
      Frmt[0] = 1; //PNG
      High[0] = this.Byte2PosInt(ByteStream[22],ByteStream[23]);
      Wide[0] = this.Byte2PosInt(ByteStream[18],ByteStream[19]);
    }
    if ((int)(ByteStream[0]&0xFF)==255 && (int)(ByteStream[1]&0xFF)==216
        &&(int)(ByteStream[2]&0xFF)==255 && (int)(ByteStream[3]&0xFF)==224){
      Frmt[0] = 2; //JPG
      int PosJPG = 2;
      while (PosJPG<ByteStream.length){
        if (String.format("%02X%02X", ByteStream[PosJPG+0],ByteStream[PosJPG+1]).equals("FFC0")){
          High[0] = this.Byte2PosInt(ByteStream[PosJPG+5],ByteStream[PosJPG+6]);
          Wide[0] = this.Byte2PosInt(ByteStream[PosJPG+7],ByteStream[PosJPG+8]);
        }
        PosJPG = PosJPG+2+this.Byte2PosInt(ByteStream[PosJPG+2],ByteStream[PosJPG+3]);
      }
    }
    if (Frmt[0] > 0){
      this.HexImageString = "";
      int Salto = 0;
      for (int i=0;i < ByteStream.length; i++){
        Salto++;
        this.HexImageString += String.format("%02x", ByteStream[i]);
        if (Salto==64){
          this.HexImageString += "\n";
          Salto = 0;
        }
      }
    }
  }


  private Integer Byte2PosInt(byte Byte08, byte Byte00) {
    return new Integer (((Byte08 & 0xFF) << 8)|((Byte00 & 0xFF) << 0));
  }

Korzystanie z kodu Java:

        int[] iFormato = new int[1]; //Format PNG or JPG
        int[] iAlto = new int[1]; //High
        int[] iAncho = new int[1]; //Wide
        ByteStreamImageString(ImageJPG,iFormato,iAlto,iAncho); //The Dimensions will stored in  iFormato[0],iAlto[0],iAncho[0]
joseluisbz
źródło
Widzę, że używasz tablic do argumentów jako sztuczki do pobierania ref/ outparametrów w Javie - czy jest to uważane za najlepszą praktykę?
Dai
Ta odpowiedź jest bardzo stara, teraz nie chcę aktualizować (wiele rzeczy zapomniałem i nie mam czasu), ale możesz sprawdzić kod i edytować go.
joseluisbz
joseluisbz.wordpress.com/2013/07/26/… (wyjaśnienie dla WMF)
joseluisbz
W tym przykładzie polecam implementację nowej klasy z 3 polami, Format, High i Width, zwracając instancję tej klasy.
joseluisbz
1

Zakładam, że to żądane wymiary w pikselach (szerokość i wysokość)?

Wydaje mi się, że większość formatów plików ma pewne informacje w nagłówku określające wymiary, dzięki czemu oprogramowanie czytające plik może wiedzieć, ile miejsca musi zarezerwować, zanim zacznie czytać plik. Niektóre "surowe" formaty plików mogą być po prostu strumieniem bajtów z pewnymi bajtami "końca wiersza" na końcu każdego poziomego rzędu pikseli (w takim przypadku oprogramowanie musi odczytać pierwszą linię i podzielić rozmiar strumienia bajtów przez długość linii, aby uzyskać wysokość).

Nie sądzę, aby można było to zrobić w jakikolwiek „ogólny” sposób, ponieważ musisz zrozumieć format pliku (lub oczywiście skorzystać z biblioteki), aby wiedzieć, jak go czytać. Prawdopodobnie możesz znaleźć kod, który w większości przypadków da przybliżone oszacowanie wymiarów bez czytania całego pliku, ale myślę, że niektóre typy plików mogą wymagać przeczytania całego pliku, aby upewnić się, jakie naprawdę ma wymiary. Spodziewam się, że większość formatów obrazów zorientowanych na WWW ma nagłówek z takimi informacjami, aby przeglądarka mogła utworzyć wymiary pudełka przed załadowaniem całego obrazu.

Sądzę, że dobra biblioteka miałaby pewne metody uzyskiwania wymiarów plików, które obsługuje, i że metody te byłyby zaimplementowane tak wydajnie, jak to tylko możliwe.

Aktualizacja : wydaje się, że imageinfo robi to, co chcesz. (Nie testowałem tego)

Stein G. Strindhaug
źródło
To narzędzie działa tak szybko, jak tego potrzebuję;). Zobaczę, czy uda mi się go właściwie wykorzystać.
dAnjou
0

Jeśli masz informacje EXIF ​​na obrazach, możesz po prostu przeczytać nagłówek EXIF.

Georgi
źródło
Niestety nie wiem, jakie będą obrazy i czy mają dane EXIF.
dAnjou
3
Ile z twoich obrazów NIE mają te informacje? Może jeśli 90% z nich ma dane EXIF, to powolność korzystania z ImageMagick na pozostałych 10% będzie akceptowalna.
Andy Lester
Dlaczego ta odpowiedź ma głosy przeciw? To poprawna odpowiedź na pytanie i może być dokładnie tym, czego szuka OP lub ktoś inny.
Will Sheppard,
0

-ping jest opcją, która wydaje się być wprowadzona w tym celu.

Jednak od ImageMagick 6.7.7 nie obserwuję spowolnienia nawet dla każdego dużego pliku, np .:

head -c 100000000 /dev/urandom > f.gray
# I don't recommend that you run this command as it eats a lot of memory.
convert -depth 8 -size 20000x10000 f.gray f.png
identify f.png

Czy możesz stworzyć przykładowy obraz wejściowy, dla którego nadal jest wolny?

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
źródło
0

tldr: wystarczy plik "nazwaobrazu"

współpracuje z webp, wszystkimi formatami jpg (jpeg, jpg200, ..),

Przykładowy wynik wygląda tak

Dane obrazu JPEG, standard JFIF 1.02, współczynnik kształtu, gęstość 1x1, długość segmentu 16, linia bazowa, dokładność 8, 650x400, klatki 3

załaduj wyjście pliku na listę Pythona i użyj czwartego pola na liście.

FYI, zoptymalizował około 18000+ obrazów, aby zmniejszyć ruch w sieci.

mj-ek
źródło