【问题标题】:Cannot load JPEG with java created by Samsung phone无法使用三星手机创建的 java 加载 JPEG
【发布时间】:2018-02-15 05:24:00
【问题描述】:

我无法使用 javafx 加载由三星 Galaxy S7 edge 拍摄的 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 图像,例如微软照片或画图。他们似乎容忍这些无效图像,将那些虚假标记视为内容数据。
有没有办法用java加载这些无效的图像,而不用自己处理单个字节?

【问题讨论】:

    标签: java javafx jpeg


    【解决方案1】:

    你的问题太棒了,真的让我思考和搜索了几个小时)

    这是我最终得到的一个有点 hacky 的解决方案(没有 3rd 方库):

    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

    结果:

    注意! 我仍然可以在日志中观察到一些异常,但它们不会阻止此代码成功执行。

    【讨论】:

    • 哇,有人有很好的awt技能! :D 为一张图像复制和冗余分配图像数据的内存是很多的。我仍然需要弄清楚如何提高性能,但它可以工作并且对我来说似乎是一个非常好的解决方案。特别是因为它不依赖于任何第三方库。非常感谢您的努力!
    • @David,是的,我相信该解决方案可以优化 :) 很高兴它有所帮助!
    • 你不能只使用new ImageView(SwingFXUtils.toFXImage(bi, null)) 作为你的getFinalImageView() 方法吗?还是您当时仍在处理“损坏”的图像?
    • @James_D,是的,你是对的,这部分可能会被简化。现在,我们拥有全部三个 - Swing、AWT 和 JavaFX)
    • 只是一个小提示:您的问题将来可能会引起其他 Stackoverflor 用户的兴趣,而您可能会更改或删除 Dropbox 文件夹中的图像。所以也许你想upload your image 到 Stackoverflow。 但是上传到 Stackoverlow 可能会“修复”有问题的图像,所以如果是这种情况,请随时忽略我的评论。
    【解决方案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 下的可执行文件名 - 在不同的操作系统上可能会有所不同。

    这给了我一个错误:

    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
    

    并提醒我去新西兰 - 那是一幅美丽的画面。

    【讨论】:

    • 感谢您的回答,但是通过使用它,我的程序将依赖 ImageMagick 安装在它运行的系统上。即使我使用 JMagic,它也只是一个界面,而不是 ImageMagick 软件本身。 - 是的,新西兰太棒了!谢谢你:)
    猜你喜欢
    • 1970-01-01
    • 2015-03-19
    • 1970-01-01
    • 1970-01-01
    • 2014-02-05
    • 2015-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多