使用ImageIO导出8位深度的JPEG图片。

huangapple go评论98阅读模式
英文:

Exporting 8 bit-dpeth JPEG with ImageIO

问题

  1. public void testJpegBitDepth() throws Exception {
  2. Path pIn = Paths.get("testing/jpg/box1.jpg"), pOut;
  3. BufferedImage bi;
  4. // *******************************************
  5. // Write 8 bit jpg
  6. // Init ImageWriter
  7. Iterator<ImageWriter> it = ImageIO.getImageWritersByFormatName("jpg");
  8. ImageWriter writer = null;
  9. while (it.hasNext()) {
  10. try {
  11. writer = it.next();
  12. // Read input
  13. bi = ImageIO.read(pIn.toFile());
  14. if (bi == null)
  15. throw new Exception("Failed to read input file: " + pIn);
  16. // Convert to gray
  17. bi = AWTImaging.convertToGray(bi);
  18. log.debug("Num bands from the image raster: " + bi.getRaster().getNumBands());
  19. pOut = test.outputDir.resolve("jpegBitDepth-8-"
  20. + pIn.getFileName().toString() + ".jpg");
  21. // Init ImageTypeSpecifier
  22. ImageTypeSpecifier imageType = ImageTypeSpecifier.createGrayscale(
  23. 8, // 8 bits per pixel
  24. DataBuffer.TYPE_BYTE, // stored in a byte
  25. false); // unsigned
  26. // Init WriteParam
  27. ImageWriteParam param = writer.getDefaultWriteParam();
  28. param.setDestinationType(imageType);
  29. // Not sure if this is required or not, but the same Exception occurs either way
  30. // param.setSourceBands(new int[] {0});
  31. // Init meta
  32. IIOMetadata meta = writer.getDefaultImageMetadata(imageType, param);
  33. String metadataFormat = "javax_imageio_jpeg_image_1.0";
  34. IIOMetadataNode root = new IIOMetadataNode(metadataFormat);
  35. IIOMetadataNode jpegVariety = new IIOMetadataNode("JPEGvariety");
  36. IIOMetadataNode markerSequence = new IIOMetadataNode("markerSequence");
  37. // I think we want app0JFIF metadata here, as it can specify a grayscale image https://docs.oracle.com/javase/10/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html
  38. IIOMetadataNode app0JFIF = new IIOMetadataNode("app0JFIF");
  39. root.appendChild(jpegVariety);
  40. root.appendChild(markerSequence);
  41. jpegVariety.appendChild(app0JFIF);
  42. meta.mergeTree(metadataFormat, root);
  43. // Export jpg
  44. Files.deleteIfExists(pOut);
  45. ImageOutputStream ios = ImageIO.createImageOutputStream(pOut.toFile());
  46. writer.setOutput(ios);
  47. writer.write(meta, new IIOImage(bi, null, meta), param);
  48. log.debug("Succeeded writing jpeg with writer: " + writer.getClass().toString());
  49. break;
  50. } catch (Exception e) {
  51. log.error("Failed writing jpeg with writer: " + (writer != null ? writer.getClass().toString() : "null"));
  52. log.error("Ex: " + e);
  53. }
  54. }
  55. }

I'm getting an Exception thrown from JpegImageWriter, here is the relevant stack trace:

  1. Ex: javax.imageio.IIOException: Metadata components != number of destination bands
  2. File=null,Class=com.sun.imageio.plugins.jpeg.JPEGImageWriter,Method=checkSOFBands,Line=-1
  3. File=null,Class=com.sun.imageio.plugins.jpeg.JPEGImageWriter,Method=writeOnThread,Line=-1
  4. File=null,Class=com.sun.imageio.plugins.jpeg.JPEGImageWriter,Method=write,Line=-1

Also, I know that the BufferedImage is a TYPE_BYTE_BINARY, and the raster has 1 band (I printed this in a debug message above). So the Exception message would make me think that I need to define in the app0JFIF metadata that we are exporting 1 band. I don't know how to define this, though. Does anyone have any experience with this? This metadata is difficult to work with, or is it just me?

Thanks in advance.

英文:

Using Java ImageIO, is it possible to export a jpeg image that has a bit-depth of 8? How would I do this? Even when exporting a BufferedImage of TYPE_BYTE_BINARY, which is a grayscale image, the result is a JPEG with bit-depth of 24.

This is what I have so far.

  1. public void testJpegBitDepth() throws Exception{
  2. Path pIn = Paths.get(&quot;testing/jpg/box1.jpg&quot;), pOut;
  3. BufferedImage bi;
  4. //*******************************************
  5. //Write 8 bit jpg
  6. //Init ImageWriter
  7. Iterator&lt;ImageWriter&gt; it = ImageIO.getImageWritersByFormatName(&quot;jpg&quot;);
  8. ImageWriter writer = null;
  9. while(it.hasNext()) {
  10. try {
  11. writer = it.next();
  12. //Read input
  13. bi = ImageIO.read(pIn.toFile());
  14. if(bi == null)
  15. throw new Exception(&quot;Failed to read input file: &quot; + pIn);
  16. //Convert to gray
  17. bi = AWTImaging.convertToGray(bi);
  18. log.debug(&quot;Num bands from the image raster: &quot; + bi.getRaster().getNumBands());
  19. pOut = test.outputDir.resolve(&quot;jpegBitDepth-8-&quot;
  20. + pIn.getFileName().toString() + &quot;.jpg&quot;);
  21. //Init ImageTypeSpecifier
  22. ImageTypeSpecifier imageType = ImageTypeSpecifier.createGrayscale(
  23. 8, //8 bits per pixel
  24. DataBuffer.TYPE_BYTE, //stored in a byte
  25. false); //unsigned
  26. //Init WriteParam
  27. ImageWriteParam param = writer.getDefaultWriteParam();
  28. param.setDestinationType(imageType);
  29. //Not sure if this is required or not, but the same Exception occurs either way
  30. //param.setSourceBands(new int[] {0});
  31. //Init meta
  32. IIOMetadata meta = writer.getDefaultImageMetadata(imageType, param);
  33. String metadataFormat = &quot;javax_imageio_jpeg_image_1.0&quot;;
  34. IIOMetadataNode root = new IIOMetadataNode(metadataFormat);
  35. IIOMetadataNode jpegVariety = new IIOMetadataNode(&quot;JPEGvariety&quot;);
  36. IIOMetadataNode markerSequence = new IIOMetadataNode(&quot;markerSequence&quot;);
  37. //I think we want app0JFIF metadata here, as it can specify a grayscale image https://docs.oracle.com/javase/10/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html
  38. IIOMetadataNode app0JFIF = new IIOMetadataNode(&quot;app0JFIF&quot;);
  39. root.appendChild(jpegVariety);
  40. root.appendChild(markerSequence);
  41. jpegVariety.appendChild(app0JFIF);
  42. meta.mergeTree(metadataFormat, root);
  43. //Export jpg
  44. Files.deleteIfExists(pOut);
  45. ImageOutputStream ios = ImageIO.createImageOutputStream(pOut.toFile());
  46. writer.setOutput(ios);
  47. writer.write(meta, new IIOImage(bi, null, meta), param);
  48. log.debug(&quot;Succeded writing jpeg with writer: &quot; + writer.getClass().toString());
  49. break;
  50. }catch(Exception e) {
  51. log.error(&quot;Failed writing jpeg with writer: &quot; + (writer != null ? writer.getClass().toString():&quot;null&quot;));
  52. log.error(&quot;Ex: &quot; + e);
  53. }
  54. }
  55. }

I'm getting an Exception thrown from JpegImageWriter, here is the relevant stack trace:

  1. Ex: javax.imageio.IIOException: Metadata components != number of destination bands
  2. File=null,Class=com.sun.imageio.plugins.jpeg.JPEGImageWriter,Method=checkSOFBands,Line=-1
  3. File=null,Class=com.sun.imageio.plugins.jpeg.JPEGImageWriter,Method=writeOnThread,Line=-1
  4. File=null,Class=com.sun.imageio.plugins.jpeg.JPEGImageWriter,Method=write,Line=-1

Also I know that the Buffered Image is a TYPE_BYTE_BINARY, and the raster has 1 band (I printed this in a debug message above). So the Exception message would make me think that I need to define in the app0JFIF metadata that we are exporting 1 band. I don't know how to define this though, does anyone have any experience with this? This metadata is difficult to work with, or is it just me?

Thanks in advance.

答案1

得分: 0

你关于只需要一个通道是正确的。以下是我的操作方法:

  1. if (bi.getSampleModel().getNumBands() != 1) {
  2. ColorModel colorModel = new ComponentColorModel(
  3. ColorSpace.getInstance(ColorSpace.CS_GRAY), false, false,
  4. Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
  5. BufferedImage oneBandedImage = new BufferedImage(colorModel,
  6. colorModel.createCompatibleWritableRaster(
  7. bi.getWidth(), bi.getHeight()),
  8. false, new Properties());
  9. Graphics g = oneBandedImage.createGraphics();
  10. g.drawImage(bi, 0, 0, null);
  11. g.dispose();
  12. bi = oneBandedImage;
  13. }

经过这样的处理,我不需要直接获取一个ImageWriter,也不需要设置任何元数据;只需要使用ImageIO.write(bi, "JPEG", file)就足够了。

我在结果上运行了/usr/bin/file,得到了以下结果:

  1. JPEG image dataJFIF standard 1.02aspect ratiodensity 1x1segment length 16baselineprecision 8315x180components 1

我认为components 1部分表示它只有一个通道。

英文:

You are correct about needing one band. Here’s how I did it:

  1. if (bi.getSampleModel().getNumBands() != 1) {
  2. ColorModel colorModel = new ComponentColorModel(
  3. ColorSpace.getInstance(ColorSpace.CS_GRAY), false, false,
  4. Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
  5. BufferedImage oneBandedImage = new BufferedImage(colorModel,
  6. colorModel.createCompatibleWritableRaster(
  7. bi.getWidth(), bi.getHeight()),
  8. false, new Properties());
  9. Graphics g = oneBandedImage.createGraphics();
  10. g.drawImage(bi, 0, 0, null);
  11. g.dispose();
  12. bi = oneBandedImage;
  13. }

After doing that, I didn’t need to directly obtain an ImageWriter and I didn’t need to set any metadata; ImageIO.write(bi, &quot;JPEG&quot;, file) was sufficient.

I ran /usr/bin/file on the result, and got this:

  1. JPEG image data, JFIF standard 1.02, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 315x180, components 1

I assume the components 1 part means that it has only one channel.

huangapple
  • 本文由 发表于 2020年4月11日 09:40:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/61151066.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定