如何获得图像的高度和宽度使用 Java?

除了使用 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)

This error only occurs on a Sun app server and therefore I suspect that it is a Sun bug.

240024 次浏览

您可以将 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 条目,或者我得到了上面的信息 给你

我使用了一个类似于我从太阳论坛上的 这篇文章得到的代码的方法:

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

}

我找到了另一种读取图像大小的方法(更通用的方法)。 您可以与 ImageReader 合作使用 ImageIO 类。 下面是示例代码:

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

注意,getFileAffix 是返回不带“ .”的路径扩展的方法,例如: 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 相比。我希望有人会发现这个有用。

这里有一些非常简单方便的东西。

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

简单的方法:

BufferedImage readImage = null;


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

这是@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());
}

我想我的声望还不够高,我的投入还不足以被认为是有价值的答复。

您可以使用工具箱,不需要 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();

我尝试使用列出的一些不同方法来测试性能。由于影响测试结果的因素很多,因此很难进行严格的测试。我准备了两个文件夹,一个有330个 jpg 文件,另一个有330个 png 文件。在这两种情况下,文件的平均大小都是4Mb。然后,我为每个文件调用 get維度。方法的每个实现和每个图像类型都分别进行了测试(单独运行)。下面是我得到的执行时间(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

很明显,有些方法加载整个文件是为了获得维度,而其他方法只是从图像中读取一些头信息。我认为当应用程序性能非常关键时,这些数字可能会很有用。

感谢大家对这个帖子的贡献——非常有帮助。

To get a Buffered Image with ImageIO.read is a very heavy method, as it's creating a complete uncompressed copy of the image in memory. For png's you may also use pngj and the code:

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

要在没有 EMF 图像阅读器的情况下获得 EMF 文件的大小,你可以使用代码:

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

可以使用 java 通过 BufferedImage 对象获取图像的宽度和高度。

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

Read 的问题在于它非常慢。所有您需要做的是读取图像标题获得大小。ImageIO.getImageReader是最佳人选。

Here is the Groovy example, but the same thing applies to 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)"

性能与使用 SimpleImageInfo java 库相同。

Https://github.com/cbeust/personal/blob/master/src/main/java/com/beust/simpleimageinfo.java

所以不幸的是,在尝试了上面的所有答案之后,我没有让他们在不知疲倦的尝试之后工作。所以我决定自己做一个真正的黑客,我要为自己工作。我相信这对你来说也是完美的。

我使用这个简单的方法来获取应用程序生成的图像的宽度,然后再上传验证:

请注意: 您必须在清单中启用访问存储的权限。

/我把它变成了静态的,放在我的 Global 类中,这样我就可以只从一个源引用或访问它,如果有任何修改,所有这些都必须在一个地方完成。只是在 Java 中保持 DRY 的概念。(无论如何) :)/

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;

}

在过去的几年里,我一直在与 ImageIO作斗争,我认为 安德鲁 · 泰勒解决方案是目前为止最好的折衷方案(快: 不使用 ImageIO#read,而且通用)。谢谢,伙计! !

但是我有点沮丧,不得不使用本地文件(File/String) ,特别是当您想检查来自多部分/表单数据请求的图像大小时,通常检索 InputPart/InputStream。因此,我迅速制作了一个变体,接受 FileInputStreamRandomAccessFile,基于能力的 ImageIO#createImageInputStream这样做。

当然,这样一个带有 Object input的方法,可能只能保持私有,您应该根据需要创建尽可能多的多态方法,调用这个方法。在传递给这个方法之前,你也可以接受 PathPath#toFile(),以及 URLURL#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");
}
}
}
byte[] bytes = file.getBytes();
InputStream inputStream = new ByteArrayInputStream(bytes);
BufferedImage image = ImageIO.read(inputStream);
imageWidth = image.getWidth();
imageHeight = image.getHeight();