Bagaimana cara mendapatkan tinggi dan lebar gambar menggunakan java?

106

Apakah ada cara lain selain menggunakan ImageIO.read untuk mendapatkan tinggi dan lebar gambar?

Karena saya mengalami masalah yang mengunci utas.

at com.sun.medialib.codec.jpeg.Decoder.njpeg_decode(Native Method)      
at com.sun.medialib.codec.jpeg.Decoder.decode(Decoder.java:87)      
at com.sun.media.imageioimpl.plugins.jpeg.CLibJPEGImageReader.decode(CLibJPEGImageReader.java:73)     
 - locked <0xd96fb668> (a com.sun.media.imageioimpl.plugins.jpeg.CLibJPEGImageReader)      
at com.sun.media.imageioimpl.plugins.clib.CLibImageReader.getImage(CLibImageReader.java:320)    
 - locked <0xd96fb668> (a com.sun.media.imageioimpl.plugins.jpeg.CLibJPEGImageReader)     
 at com.sun.media.imageioimpl.plugins.clib.CLibImageReader.read(CLibImageReader.java:384)   
 - locked <0xd96fb668> (a com.sun.media.imageioimpl.plugins.jpeg.CLibJPEGImageReader)      
at javax.imageio.ImageIO.read(ImageIO.java:1400)      
at javax.imageio.ImageIO.read(ImageIO.java:1322)

Kesalahan ini hanya terjadi pada server aplikasi Sun dan oleh karena itu saya curiga bahwa ini adalah bug Sun.

est perak
sumber
Kesalahan apa? Anda hanya menampilkan sebagian dari jejak tumpukan (yang tampaknya berasal dari jstack).
Joachim Sauer
Sudahkah Anda menemukan penyebab atau perbaikan masalah ini? Saya mendapatkan masalah yang sama ketika mengunci utas pada metode yang sama.
Timothy Chen
Ada bug yang mungkin terkait: bugs.sun.com/bugdatabase/view_bug.do?bug_id=6791502
Adam Schmideg

Jawaban:

288

Ini sesuatu yang sangat sederhana dan praktis.

BufferedImage bimg = ImageIO.read(new File(filename));
int width          = bimg.getWidth();
int height         = bimg.getHeight();
Apurv
sumber
7
Ini adalah jawaban terbaik untuk waktu yang lama, dan Anda telah ditipu oleh jawaban yang sama yang diposting oleh orang lain 17 hari setelah posting Anda. Ini harus menjadi jawaban atas bukan yang paling bawah.
Oversteer
34
Dari semua yang saya baca, ini membaca seluruh gambar ke dalam memori. Yang ekstrim hanya untuk mendapatkan lebar dan tinggi.
Marc
7
cara buruk: Anda harus memuat seluruh gambar raster ke memori yang menyebabkan OOM dengan gambar yang sangat besar
yetanothercoder
4
Pertanyaannya khusus meminta cara lain selain menggunakan ImageIO.read. Jawaban Anda adalah "gunakan ImageIO.read". Mengapa ini dianggap sebagai jawaban yang bagus?
ssimm
5
Ini adalah cara terburuk untuk melakukannya dan saya tidak mengerti mengapa ini diberi suara positif begitu banyak. Ini memuat seluruh gambar ke dalam heap yang lambat dan menghabiskan banyak memori yang tidak diperlukan.
Patrick Favre
72

Ini adalah penulisan ulang postingan bagus dari @Kay, yang menampilkan IOException dan memberikan jalan keluar awal:

/**
 * Gets image dimensions for given file 
 * @param imgFile image file
 * @return dimensions of image
 * @throws IOException if the file is not a known image
 */
public static Dimension getImageDimension(File imgFile) throws IOException {
  int pos = imgFile.getName().lastIndexOf(".");
  if (pos == -1)
    throw new IOException("No extension for file: " + imgFile.getAbsolutePath());
  String suffix = imgFile.getName().substring(pos + 1);
  Iterator<ImageReader> iter = ImageIO.getImageReadersBySuffix(suffix);
  while(iter.hasNext()) {
    ImageReader reader = iter.next();
    try {
      ImageInputStream stream = new FileImageInputStream(imgFile);
      reader.setInput(stream);
      int width = reader.getWidth(reader.getMinIndex());
      int height = reader.getHeight(reader.getMinIndex());
      return new Dimension(width, height);
    } catch (IOException e) {
      log.warn("Error reading: " + imgFile.getAbsolutePath(), e);
    } finally {
      reader.dispose();
    }
  }

  throw new IOException("Not a known image file: " + imgFile.getAbsolutePath());
}

Saya kira perwakilan saya tidak cukup tinggi untuk masukan saya dianggap layak sebagai balasan.

Andrew Taylor
sumber
Terima kasih untuk ini! dan mempertimbangkan perbandingan kinerja yang dilakukan oleh user194715, saya akan mengambil saran Anda untuk kinerja dan pertimbangan png! Terima kasih!
ah-shiang han
Tidak bisakah Anda juga menggunakan probeContentType dari paket nio.Files yang digabungkan dengan javax.imageio.ImageIO.getImageReadersByMIMEType(mimeType)jika Anda ingin memastikan jenis filenya?
EdgeCaseBerg
3
Selain itu, bukankah sebaiknya Anda memanggil .close()streaming sebelum kembali? jika tidak, Anda membiarkan aliran terbuka
EdgeCaseBerg
1
@ php_coder_3809625 log berada di iterator, jadi bisa jadi ini adalah kasus di mana satu ImageReader gagal, tetapi berikutnya berhasil. Jika semuanya gagal, IOException akan dimunculkan.
Andrew Taylor
1
Anda mungkin mempertimbangkan org.apache.commons.io.FilenameUtils#getExtensionuntuk menggunakan untuk mendeteksi ekstensi nama file.
Patrick Bergner
52

Saya telah menemukan cara lain untuk membaca ukuran gambar (lebih umum). Anda dapat menggunakan kelas ImageIO bekerjasama dengan ImageReaders. Berikut ini contoh kode:

private Dimension getImageDim(final String path) {
    Dimension result = null;
    String suffix = this.getFileSuffix(path);
    Iterator<ImageReader> iter = ImageIO.getImageReadersBySuffix(suffix);
    if (iter.hasNext()) {
        ImageReader reader = iter.next();
        try {
            ImageInputStream stream = new FileImageInputStream(new File(path));
            reader.setInput(stream);
            int width = reader.getWidth(reader.getMinIndex());
            int height = reader.getHeight(reader.getMinIndex());
            result = new Dimension(width, height);
        } catch (IOException e) {
            log(e.getMessage());
        } finally {
            reader.dispose();
        }
    } else {
        log("No reader found for given format: " + suffix));
    }
    return result;
}

Perhatikan bahwa getFileSuffix adalah metode yang mengembalikan ekstensi jalur tanpa "." jadi misal: png, jpg dll. Contoh implementasinya adalah:

private String getFileSuffix(final String path) {
    String result = null;
    if (path != null) {
        result = "";
        if (path.lastIndexOf('.') != -1) {
            result = path.substring(path.lastIndexOf('.'));
            if (result.startsWith(".")) {
                result = result.substring(1);
            }
        }
    }
    return result;
}

Solusi ini sangat cepat karena hanya ukuran gambar yang dibaca dari file dan bukan keseluruhan gambar. Saya mengujinya dan tidak ada perbandingan dengan kinerja ImageIO.read. Saya berharap seseorang akan merasakan manfaatnya.

pengguna350756
sumber
getFileSuffix()mengandung jika yang tidak perlu dan inisialisasi dengan nullbukanlah ide yang baik dalam kasus ini.
Jimmy T.
2
Sial ya ini "pada dasarnya sangat cepat"! Saya pikir Anda memenuhi syarat untuk penghargaan 'meremehkan tahun ini' dengan yang satu itu. Hembusan ImageIO.read()sepenuhnya keluar dari air, baik dalam hal waktu CPU dan penggunaan memori.
Aroth
1
public String getFileSuffix statis (jalur String akhir) {if (path! = null && path.lastIndexOf ('.')! = -1) {return path.substring (path.lastIndexOf ('.')). substring (1) ; } mengembalikan null; }
Nilanchal
47

Saya mencoba menguji kinerja menggunakan beberapa dari berbagai pendekatan yang terdaftar. Sulit untuk melakukan pengujian yang ketat karena banyak faktor yang memengaruhi hasilnya. Saya menyiapkan dua folder, satu dengan 330 file jpg dan satu lagi dengan 330 file png. Ukuran file rata-rata adalah 4Mb dalam kedua kasus. Lalu saya menelepon getDimension untuk setiap file. Setiap implementasi metode getDimension dan setiap jenis gambar diuji secara terpisah (proses terpisah). Berikut waktu eksekusi yang saya dapat (angka pertama untuk jpg, angka kedua untuk png):

1(Apurv) - 101454ms, 84611ms
2(joinJpegs) - 471ms, N/A
3(Andrew Taylor) - 707ms, 68ms
4(Karussell, ImageIcon) - 106655ms, 100898ms
5(user350756) - 2649ms, 68ms

Jelas bahwa beberapa metode memuat seluruh file untuk mendapatkan dimensi sementara yang lain mendapatkan hanya dengan membaca beberapa informasi header dari gambar. Saya rasa angka-angka ini mungkin berguna ketika kinerja aplikasi sangat penting.

Terima kasih semuanya atas kontribusinya pada utas ini - sangat membantu.

mp31415
sumber
3
Nah, itu jawabannya. Kerja bagus!
ssimm
1
Apakah Anda juga menganalisis penggunaan ruang heap saat mengupload gambar? Juga, apakah Anda mendapatkan kesalahan OOM saat menjalankan tes ini pada salah satu metode?
saibharath
Terima kasih jawaban Anda banyak membantu saya. Saya punya (gambar HD 50k)
SüniÚr
17

Anda dapat memuat data biner jpeg sebagai file dan mengurai sendiri header jpeg. Yang Anda cari adalah header 0xFFC0 atau Start of Frame:

Start of frame marker (FFC0)

* the first two bytes, the length, after the marker indicate the number of bytes, including the two length bytes, that this header contains
* P -- one byte: sample precision in bits (usually 8, for baseline JPEG)
* Y -- two bytes
* X -- two bytes
* Nf -- one byte: the number of components in the image
      o 3 for color baseline JPEG images
      o 1 for grayscale baseline JPEG images

* Nf times:
      o Component ID -- one byte
      o H and V sampling factors -- one byte: H is first four bits and V is second four bits
      o Quantization table number-- one byte

The H and V sampling factors dictate the final size of the component they are associated with. For instance, the color space defaults to YCbCr and the H and V sampling factors for each component, Y, Cb, and Cr, default to 2, 1, and 1, respectively (2 for both H and V of the Y component, etc.) in the Jpeg-6a library by the Independent Jpeg Group. While this does mean that the Y component will be twice the size of the other two components--giving it a higher resolution, the lower resolution components are quartered in size during compression in order to achieve this difference. Thus, the Cb and Cr components must be quadrupled in size during decompression.

Untuk info lebih lanjut tentang tajuk, lihat entri jpeg wikipedia atau saya mendapat info di atas di sini .

Saya menggunakan metode yang mirip dengan kode di bawah ini yang saya dapatkan dari posting ini di forum matahari:

import java.awt.Dimension;
import java.io.*;

public class JPEGDim {

public static Dimension getJPEGDimension(File f) throws IOException {
    FileInputStream fis = new FileInputStream(f);

    // check for SOI marker
    if (fis.read() != 255 || fis.read() != 216)
        throw new RuntimeException("SOI (Start Of Image) marker 0xff 0xd8 missing");

    Dimension d = null;

    while (fis.read() == 255) {
        int marker = fis.read();
        int len = fis.read() << 8 | fis.read();

        if (marker == 192) {
            fis.skip(1);

            int height = fis.read() << 8 | fis.read();
            int width = fis.read() << 8 | fis.read();

            d = new Dimension(width, height);
            break;
        }

        fis.skip(len - 2);
    }

    fis.close();

    return d;
}

public static void main(String[] args) throws IOException {
    System.out.println(getJPEGDimension(new File(args[0])));
}

}

joinJpegs
sumber
Baik. Tapi saya pikir alih-alih ==192itu harus memeriksa nomor 192-207, kecuali 196, 200 dan 204.
vortexwolf
Atau Anda dapat menggunakan com.drewnoakes.metadata-extractorperpustakaan untuk mengekstrak header ini dengan mudah
Victor Petit
9

Cara sederhana:

BufferedImage readImage = null;

try {
    readImage = ImageIO.read(new File(your path);
    int h = readImage.getHeight();
    int w = readImage.getWidth();
} catch (Exception e) {
    readImage = null;
}
pengguna1215499
sumber
3
ini perlu membaca seluruh gambar di memori hanya untuk mengetahui lebar dan tingginya. Ya, itu sederhana, tetapi akan berkinerja buruk untuk banyak gambar atau gambar besar ...
Clint Eastwood
4

Masalah dengan ImageIO.read adalah sangat lambat. Yang perlu Anda lakukan adalah membaca header gambar untuk mendapatkan ukurannya. ImageIO.getImageReaderadalah kandidat yang sempurna.

Ini adalah contoh Groovy, tetapi hal yang sama berlaku untuk Java

def stream = ImageIO.createImageInputStream(newByteArrayInputStream(inputStream))
def formatReader = ImageIO.getImageWritersByFormatName(format).next() 
def reader = ImageIO.getImageReader(formatReader)
reader.setInput(stream, true)

println "width:reader.getWidth(0) -> height: reader.getHeight(0)"

Performanya sama dengan menggunakan pustaka java SimpleImageInfo.

https://github.com/cbeust/personal/blob/master/src/main/java/com/beust/SimpleImageInfo.java

argoden
sumber
Apa getReaderByFormat?
Koray Tugay
3

Anda dapat menggunakan Toolkit, tidak perlu ImageIO

Image image = Toolkit.getDefaultToolkit().getImage(file.getAbsolutePath());
int width = image.getWidth(null);
int height = image.getHeight(null);

Jika Anda tidak ingin menangani pemuatan gambar lakukan

ImageIcon imageIcon = new ImageIcon(file.getAbsolutePath());
int height = imageIcon.getIconHeight();
int width = imageIcon.getIconWidth();
Karussell
sumber
1
Tidak perlu ImageIO tetapi perlu Toolkit. Apa bedanya?
pelaku diet
ImageIO adalah ketergantungan eksternal. Toolkit tidak
Karussell
ImageIO adalah bagian dari Java sejak 1.4 docs.oracle.com/javase/7/docs/api/javax/imageio/…
dieter
Ya, mungkin ini akan berhasil, maaf jika ini membingungkan. Ketika saya mencoba saya butuhkan untuk beberapa bagian hal JAI (mungkin untuk membaca format lain?): Stackoverflow.com/questions/1209583/...
Karussell
3

Anda bisa mendapatkan lebar dan tinggi gambar dengan objek BufferedImage menggunakan java.

public void setWidthAndHeightImage(FileUploadEvent event){
    byte[] imageTest = event.getFile().getContents();
                baiStream = new ByteArrayInputStream(imageTest );
                BufferedImage bi = ImageIO.read(baiStream);
                //get width and height of image
                int imageWidth = bi.getWidth();
                int imageHeight = bi.getHeight();
    }
KIBOU Hassan
sumber
ini akan memuat seluruh gambar di memori, sangat boros dan sangat lambat.
argoden
1

Untuk mendapatkan Gambar Buffered dengan ImageIO.read adalah metode yang sangat berat, karena ini membuat salinan lengkap gambar yang tidak terkompresi di memori. Untuk png Anda juga dapat menggunakan pngj dan kode:

if (png)
    PngReader pngr = new PngReader(file);
    width = pngr.imgInfo.cols;
    height = pngr.imgInfo.rows;
    pngr.close();
}
Jessi
sumber
0

Setelah berjuang dengan ImageIObanyak di tahun-tahun terakhir, saya pikir Andrew Taylor 's solusi adalah jauh kompromi terbaik (cepat: tidak menggunakan ImageIO#read, dan serbaguna). Terima kasih sobat!!

Tetapi saya sedikit frustasi karena dipaksa untuk menggunakan file lokal (File / String), terutama dalam kasus di mana Anda ingin memeriksa ukuran gambar yang berasal dari, katakanlah, permintaan multipart / form-data di mana Anda biasanya mengambil InputPart/ InputStream's. Jadi saya segera membuat varian yang menerima File, InputStreamdan RandomAccessFile, berdasarkan kemampuanImageIO#createImageInputStream untuk melakukannya.

Tentu saja, metode dengan Object input, mungkin hanya tetap privat dan Anda harus membuat metode polimorfik sebanyak yang diperlukan, memanggil yang ini. Anda juga dapat menerima Pathwith Path#toFile()dan URLwith URL#openStream()sebelum meneruskan ke metode ini:

  private static Dimension getImageDimensions(Object input) throws IOException {

    try (ImageInputStream stream = ImageIO.createImageInputStream(input)) { // accepts File, InputStream, RandomAccessFile
      if(stream != null) {
        IIORegistry iioRegistry = IIORegistry.getDefaultInstance();
        Iterator<ImageReaderSpi> iter = iioRegistry.getServiceProviders(ImageReaderSpi.class, true);
        while (iter.hasNext()) {
          ImageReaderSpi readerSpi = iter.next();
          if (readerSpi.canDecodeInput(stream)) {
            ImageReader reader = readerSpi.createReaderInstance();
            try {
              reader.setInput(stream);
              int width = reader.getWidth(reader.getMinIndex());
              int height = reader.getHeight(reader.getMinIndex());
              return new Dimension(width, height);
            } finally {
              reader.dispose();
            }
          }
        }
        throw new IllegalArgumentException("Can't find decoder for this image");
      } else {
        throw new IllegalArgumentException("Can't open stream for this image");
      }
    }
  }
Fabien M
sumber
-1

Jadi sayangnya, setelah mencoba semua jawaban dari atas, saya tidak berhasil membuat mereka bekerja setelah waktu yang tak kenal lelah mencoba. Jadi saya memutuskan untuk melakukan peretasan yang sebenarnya sendiri dan saya melakukannya untuk saya. Saya percaya ini akan bekerja dengan sempurna untuk Anda juga.

Saya menggunakan metode sederhana ini untuk mendapatkan lebar gambar yang dihasilkan oleh aplikasi dan belum diunggah nanti untuk verifikasi:

Pls. perhatikan: Anda harus mengaktifkan izin dalam manifes untuk penyimpanan akses.

/ Saya membuatnya statis dan memasukkan kelas Global saya sehingga saya dapat mereferensikan atau mengaksesnya dari satu sumber saja dan jika ada modifikasi, semuanya harus dilakukan di satu tempat. Hanya mempertahankan konsep KERING di java. (bagaimanapun) :) /

public static int getImageWidthOrHeight(String imgFilePath) {

            Log.d("img path : "+imgFilePath);

            // Decode image size
            BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(imgFilePath, o);

            int width_tmp = o.outWidth, height_tmp = o.outHeight;

            Log.d("Image width : ", Integer.toString(width_tmp) );

            //you can decide to rather return height_tmp to get the height.

            return width_tmp;

}

AppEmmanuel
sumber
Halo @gpasch bagaimana bisa? sudahkah kamu mencobanya Ini bekerja dengan sempurna untuk saya. Tidak ada contoh lain yang berhasil untuk saya. Beberapa meminta saya untuk membuat beberapa impor kelas yang aneh dan lainnya yang menyebabkan masalah. Saya benar-benar ingin belajar lebih banyak dari tantangan Anda. Berdiri. . .
AppEmmanuel
-2

Untuk mendapatkan ukuran file emf tanpa EMF Image Reader Anda dapat menggunakan kode:

Dimension getImageDimForEmf(final String path) throws IOException {

    ImageInputStream inputStream = new FileImageInputStream(new File(path));

    inputStream.setByteOrder(ByteOrder.LITTLE_ENDIAN);

    // Skip magic number and file size
    inputStream.skipBytes(6*4);

    int left   = inputStream.readInt();
    int top    = inputStream.readInt();
    int right  = inputStream.readInt();
    int bottom = inputStream.readInt();

    // Skip other headers
    inputStream.skipBytes(30);

    int deviceSizeInPixelX = inputStream.readInt();
    int deviceSizeInPixelY = inputStream.readInt();

    int deviceSizeInMlmX = inputStream.readInt();
    int deviceSizeInMlmY = inputStream.readInt();

    int widthInPixel = (int) Math.round(0.5 + ((right - left + 1.0) * deviceSizeInPixelX / deviceSizeInMlmX) / 100.0);
    int heightInPixel = (int) Math.round(0.5 + ((bottom-top + 1.0) * deviceSizeInPixelY / deviceSizeInMlmY) / 100.0);

    inputStream.close();

    return new Dimension(widthInPixel, heightInPixel);
}
Viktor Sirotin
sumber