Як отримати висоту та ширину зображення за допомогою Java?


106

Чи є інший спосіб, крім використання ImageIO.read для отримання висоти та ширини зображення?

Тому що я стикаюся з проблемою, яка замикає нитку.

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)

Ця помилка виникає лише на сервері додатків Sun, і тому я підозрюю, що це помилка Sun.


Яка помилка? Ви показуєте лише частину сліду стека (це, здається, походить jstack).
Йоахім Зауер

Ви знайшли причину чи вирішили цю проблему? У мене виникає те саме питання, де він фіксує нитку тим самим методом.
Тимофій Чен

Існує помилка, яка може бути пов’язана: bugs.sun.com/bugdatabase/view_bug.do?bug_id=6791502
Адам Шмідег

Відповіді:


288

Ось щось дуже просте і зручне.

BufferedImage bimg = ImageIO.read(new File(filename));
int width          = bimg.getWidth();
int height         = bimg.getHeight();

7
Це найкраща відповідь дуже довгим шляхом, і вас обдурили голоси тим самим відповіддю, який опублікував хтось інший через 17 днів після вашого повідомлення. Це має бути головна відповідь, а не нижня.
Oversteer

34
З усього, що я читаю, це зчитує все зображення в пам'яті. Що надзвичайно просто для отримання ширини та висоти.
Марк

7
поганий спосіб: вам потрібно буде завантажити весь растровий образ у пам’ять, що викликає OOM з дуже великими зображеннями
yetagethercoder

4
Питання конкретно запитує спосіб інший , ніж при використанні ImageIO.read. Ваша відповідь - "використовувати ImageIO.read". Чому це вважається хорошою відповіддю?
ssimm

5
Це найгірший спосіб зробити це, і я не розумію, чому це наголошується настільки сильно. Це завантажує все зображення в купу, яка повільно, і споживає багато зайвої пам'яті.
Патрік Фавре

72

Це перепис чудового допису @Kay, який кидає IOException і забезпечує швидкий вихід:

/**
 * 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());
}

Я думаю, моя репутація недостатньо висока, щоб мій внесок вважався гідним як відповідь.


Дякую за це! і враховуючи порівняння продуктивності, зроблене користувачем194715, я прийму вашу пропозицію щодо продуктивності та png розгляду! Дякую!
ах-шианг хан

Не могли б ви також використовувати probeContentType з пакету nio.Files у поєднанні з, javax.imageio.ImageIO.getImageReadersByMIMEType(mimeType)якщо ви хочете бути визначеним типу файлу?
EdgeCaseBerg

3
Також не варто дзвонити .close()в ефір, перш ніж повернутися? інакше ви залишаєте потік відкритим
EdgeCaseBerg

1
@ php_coder_3809625 журнал знаходиться в ітераторі, тому може статися так, що один ImageReader виходить з ладу, але подальший успіх. Якщо всі вони виходять з ладу, тоді підвищується IOException.
Ендрю Тейлор

1
Ви можете розглянути можливість використання org.apache.commons.io.FilenameUtils#getExtensionдля виявлення розширення імені файлу.
Патрік Бергнер

52

Я знайшов інший спосіб прочитати розмір зображення (більш загальний). Ви можете використовувати клас ImageIO у співпраці з ImageReaders. Ось зразок коду:

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;
}

Зауважте, що getFileSuffix - це метод, який повертає розширення шляху без ". наприклад, png, jpg тощо. Приклад реалізації:

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;
}

Це рішення дуже швидко, оскільки з файлу читається лише розмір зображення, а не все зображення. Я перевірив це, і порівняння з продуктивністю ImageIO.read немає. Сподіваюся, хтось знайде це корисним.


getFileSuffix()містить непотрібні символи ifs та ініціалізація nullв цьому випадку не є хорошою ідеєю.
Джиммі Т.

2
Чорт так, це "в основному дуже швидко"! Думаю, ви отримали нагороду за "заниження року" з цією. Видуває ImageIO.read()повністю з води, як з точки зору часу процесора, так і з використанням пам'яті.
aroth

1
відкритий статичний рядок getFileSuffix (остаточний шлях рядка) {if (path! = null && path.lastIndexOf ('.')! = -1) {return path.substring (path.lastIndexOf ('.')). substring (1) ; } повернути нуль; }
Ніланшал

47

Я спробував перевірити продуктивність, використовуючи деякі перелічені підходи. Важко зробити суворий тест, оскільки на результат впливає багато факторів. Я підготував дві папки, одну з 330 jpg-файлами та іншу з 330-png-файлами. Середній розмір файлу в обох випадках становив 4 Мб. Тоді я викликав getDimension для кожного файлу. Кожна реалізація методу getDimension та кожен тип зображення тестувались окремо (окремий запуск). Ось час виконання, який я отримав (перший номер для jpg, другий номер для png):

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

Очевидно, що деякі методи завантажують весь файл для отримання розмірів, а інші отримують, лише читаючи інформацію із заголовка із зображення. Я думаю, що ці цифри можуть бути корисними, коли продуктивність програми важлива.

Дякую всім за внесок у цю тему - дуже корисно.


3
Тепер це відповідь. Хороша робота!
ssimm

1
Чи аналізували ви також використання простору купи під час завантаження зображень? Крім того, чи виникали ви помилки OOM під час виконання цих тестів будь-яким із методів?
Сайбхарат

Дякую, що ваша відповідь мені дуже допомогла. У мене (50k HD pic)
SüniÚr

17

Ви можете завантажити двійкові дані jpeg у вигляді файлу та самостійно проаналізувати заголовки jpeg. Ви шукаєте заголовок 0xFFC0 або 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.

Щоб отримати додаткові відомості про заголовки, ознайомтеся із записами jpeg у wikipedia або я отримав вищевказану інформацію тут .

Я використовував метод, подібний до коду, нижче якого я отримав з цієї публікації на форумах НД:

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

}


Добре. Але я думаю, що замість ==192нього слід перевірити числа 192-207, крім 196, 200 та 204.
vortexwolf

Або ви можете скористатися com.drewnoakes.metadata-extractorбібліотекою, щоб легко витягти ці заголовки
Віктор Петіт

9

Простий спосіб:

BufferedImage readImage = null;

try {
    readImage = ImageIO.read(new File(your path);
    int h = readImage.getHeight();
    int w = readImage.getWidth();
} catch (Exception e) {
    readImage = null;
}

3
для цього потрібно прочитати все зображення в пам'яті лише для того, щоб знати ширину та висоту. Так, це просто, але буде погано працювати як для багатьох зображень, так і для величезних ...
Клінт Іствуд

4

Проблема з ImageIO.read полягає в тому, що це дуже повільно. Все, що вам потрібно зробити - це прочитати заголовок зображення, щоб отримати розмір. ImageIO.getImageReaderє ідеальним кандидатом.

Ось приклад Groovy, але те саме стосується і 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)"

Вистава була такою ж, як використання Java-бібліотеки SimpleImageInfo.

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


Що таке getReaderByFormat?
Корай Тугай

3

Ви можете використовувати Інструментарій, не потрібно для ImageIO

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

Якщо ви не хочете виконувати завантаження зображення, зробіть

ImageIcon imageIcon = new ImageIcon(file.getAbsolutePath());
int height = imageIcon.getIconHeight();
int width = imageIcon.getIconWidth();

1
Не потрібно ImageIO, але потрібен інструментарій. Яка різниця?
дітер

ImageIO - це зовнішня залежність. Інструментарій не
Каруссел

ImageIO є частиною Java з 1.4 docs.oracle.com/javase/7/docs/api/javax/imageio/…
dieter

Так, можливо, це спрацює, вибачте, якщо це тоді було заплутано. Коли я спробував це мені було потрібно для деяких частин на JAI річ (може бути , щоб читати інші формати?): Stackoverflow.com/questions/1209583 / ...
Karussell

3

Ви можете отримати ширину та висоту зображення за допомогою об'єкта BufferedImage, використовуючи 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();
    }

це завантажить все зображення в пам'ять, дуже марно і дуже повільно.
argoden

1

Отримати завантажене зображення за допомогою ImageIO.read - це дуже важкий метод, оскільки це створює повну нестиснуту копію зображення в пам'яті. Для png-кодів ви також можете використовувати pngj та код:

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

0

ImageIOПротягом останніх років, маючи багато зусиль, я думаю , що рішення Ендрю Тейлора - це найкращий компроміс (швидкий: не використовуючий та універсальний). Спасибі людина !!ImageIO#read

Але мене трохи неприємно змусити використовувати локальний файл (File / String), особливо у випадках, коли ви хочете перевірити розміри зображень, що надходять, скажімо, із запиту багатосторонньої форми / даних, де ви зазвичай отримуєте InputPart/ InputStreams. Тому я швидко зробив варіант, який приймає File, InputStreamі RandomAccessFile, виходячи з вмінняImageIO#createImageInputStream робити це.

Звичайно, такий метод з Object input, можливо, залишається лише приватним, і ви повинні створити стільки поліморфних методів, скільки потрібно, називаючи цей. Ви також можете прийняти Pathз Path#toFile()і URLз URL#openStream()до переходу до цього методу:

  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");
      }
    }
  }

-1

Тож, на жаль, спробувавши всі відповіді зверху, я не змусив їх працювати після невтомних спроб. Тому я вирішив зробити справжній хак сам, і я йду, щоб це працювало для мене. Я вірю, що це буде чудово працювати і для вас.

Я використовую цей простий метод, щоб отримати ширину зображення, згенерованого програмою, і ще потрібно завантажити його для підтвердження:

Pls. Візьміть до уваги: ​​вам доведеться включити дозволи в маніфесті для зберігання доступу.

/ Я зробив його статичним і помістив у свій глобальний клас, щоб я міг посилатись на нього або отримувати доступ до нього лише з одного джерела, і якщо є якісь модифікації, все це повинно бути зроблено лише в одному місці. Просто підтримка DRY концепції в Java. (все одно) :) /

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;

}


Привіт @gpasch як так? ви пробували? Це прекрасно працює для мене. Жоден з інших прикладів не працював для мене. Деякі вимагали від мене дещо дивного імпорту класів, а інші викликали проблеми. Я дуже хочу дізнатися більше про те, якими були ваші виклики. Стоячи. . .
AppEmmanuel

-2

Щоб отримати розмір файлу ЕМП без ЕМП Reader зображень, ви можете використовувати код:

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);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.