我在使用javafx加载由三星Galaxy S7边缘拍摄的JPEG图像时遇到问题(图片可在https://www.dropbox.com/s/w6lvdnqwcgw321s/20171122_140732.jpg?dl=0处获得)。我正在使用Image类加载图像。
import java.io.FileInputStream;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class JPEGProblem extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage window) throws Exception {
Image img = new Image(new FileInputStream("/path/to/image.jpg"));
if (img.getException() != null)
throw img.getException();
ImageView imgView = new ImageView(img);
window.setScene(new Scene(new Pane(imgView)));
window.show();
}
}
尝试加载图像的构造函数调用在错误流上打印以下错误消息:
2018年2月4日晚上11:48:23 com.sun.javafx.tk.quantum.PrismImageLoader2 $ PrismLoadListener imageLoadWarning 警告:顺序JPEG的SOS参数无效
我从图像对象获得的异常是一个IOException,其中包含消息:
不支持的标记类型0x65
我做了一些研究,事实证明,这是三星手机拍摄的全景图像的一个已知问题。正如在这个线程中所指出的那样:https://forums.adobe.com/thread/2131432,一些0xFF字节表示后面的字节是元信息而不是实际数据,不会通过在0xFF之后添加一个跟随的0x00字节来转义。
但是我尝试编写操作图像数据的代码,以便添加丢失的0x00字节,但结果比预期的要复杂得多,我不想编写自己的JPEG解析器/加载器。
有一些程序可以显示那些无效的JPEG图像,例如Microsoft Fotos或Paint。看起来他们容忍这些无效图像,将这些虚假标记视为内容数据
有没有办法用java加载这些无效图像,而不用自己处理单个字节?
答案 0 :(得分:11)
你的问题太棒了,真让我思考并搜索了几个小时)
这是一个有点hacky解决方案(没有第三方库),我最终得到了:
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;
import java.awt.image.DirectColorModel;
import java.awt.image.PixelGrabber;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
public class JPEGProblem extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage window) throws Exception {
BufferedImage bi = getBufferedImage();
ImageView imgView = getFinalImageView(bi);
window.setScene(new Scene(new Pane(imgView)));
window.show();
}
private BufferedImage getBufferedImage() throws InterruptedException {
final java.awt.Image image = Toolkit.getDefaultToolkit().createImage("path\to\file");
final int[] RGB_MASKS = {0xFF0000, 0xFF00, 0xFF};
final ColorModel RGB_OPAQUE =
new DirectColorModel(32, RGB_MASKS[0], RGB_MASKS[1], RGB_MASKS[2]);
PixelGrabber pg = new PixelGrabber(image, 0, 0, -1, -1, true);
pg.grabPixels();
int width = pg.getWidth(), height = pg.getHeight();
DataBuffer buffer = new DataBufferInt((int[]) pg.getPixels(), pg.getWidth() * pg.getHeight());
WritableRaster raster = Raster.createPackedRaster(buffer, width, height, width, RGB_MASKS, null);
return new BufferedImage(RGB_OPAQUE, raster, false, null);
}
private ImageView getFinalImageView(BufferedImage bi) throws Exception {
ImageView imgView = new ImageView(SwingFXUtils.toFXImage(bi, null));
imgView.setFitWidth(1024);
imgView.setFitHeight(756);
imgView.setRotate(180);
return imgView;
}
}
这里的问题是标准Image
api无法读取"破坏"图像,所以我们需要以不同的方式阅读它。可以使用此Toolkit.getDefaultToolkit().createImage()方法。实际上,getBufferedImage
部分取自this answer,因此,此处的所有学分都将从那里开始)
在getFinalImageView
方法中,我们只需将此BufferedImage
转换为javafx Image
,然后使用ImageIO类转换为ImageView
。
注意! 我仍然可以在日志中观察到一些例外,但它们不会阻止此代码成功执行。
答案 1 :(得分:2)
根据您的环境,我能够ImageMagick阅读并重新编写图像。我使用以下代码进行测试:
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class ReadImage {
public static void main(String[] argv) {
try {
BufferedImage img = ImageIO.read(new File("/path/to/20171122_140732.jpg"));
System.out.println( "image is " + img.getHeight() + " pixels in height and " + img.getWidth() + " pixels wide");
} catch (IOException e) {
e.printStackTrace();
}
}
}
如果我在原始图像上运行它,我得到了:
javax.imageio.IIOException: Bogus DQT index 14
at com.sun.imageio.plugins.jpeg.JPEGImageReader.readImage(Native Method)
at com.sun.imageio.plugins.jpeg.JPEGImageReader.readInternal(JPEGImageReader.java:1247)
at com.sun.imageio.plugins.jpeg.JPEGImageReader.read(JPEGImageReader.java:1050)
at javax.imageio.ImageIO.read(ImageIO.java:1448)
at javax.imageio.ImageIO.read(ImageIO.java:1308)
at com.hotjoe.so.imagereader.ReadImage.main(ReadImage.java:15)
然后我跑了(在Ubuntu上,但ImageMagick是跨平台的)
convert-im6 -rotate 360 20171122_140732.jpg blah.jpg
convert-im6
是Ubuntu下的可执行文件名 - 在不同的O / S上可能有所不同。
这给了我一个错误:
convert: Invalid SOS parameters for sequential JPEG `20171122_140732.jpg' @ warning/jpeg.c/JPEGWarningHandler/352.
convert: Corrupt JPEG data: 61 extraneous bytes before marker 0x65 `20171122_140732.jpg' @ warning/jpeg.c/JPEGWarningHandler/352.
convert: Unsupported marker type 0x65 `20171122_140732.jpg' @ warning/jpeg.c/JPEGErrorHandler/319.
但它仍然奏效:
image is 3760 pixels in height and 11888 pixels wide
提醒我去新西兰 - 这是一幅美丽的画面。