B4J Question Image quality

focus330

Member
Licensed User
Longtime User
Every test I made, writing an image , gives me always an output quality of 96x96 ppi.
Also reading many threads I don't understand how to write an image with a resolution of 200x200 ppi.
Somebody can help me ?
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
You can use this code to set the dpi of the saved image:
B4X:
Sub SaveImage(img As Image, out As OutputStream)
   Dim jo As JavaObject = Me
   Dim dpi As Float = 96
   jo.RunMethod("saveImage", Array(out,img,  dpi))
   out.Close
End Sub





#if Java

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;

import javafx.embed.swing.SwingFXUtils;
import javafx.scene.image.Image;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOInvalidTreeException;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.ImageOutputStream;
   public static void saveImage(OutputStream output, Image image, float dpi) throws IOException {

     final String formatName = "png";

     for (Iterator<ImageWriter> iw = ImageIO.getImageWritersByFormatName(formatName); iw.hasNext();) {
       ImageWriter writer = iw.next();
       ImageWriteParam writeParam = writer.getDefaultWriteParam();
       ImageTypeSpecifier typeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
       IIOMetadata metadata = writer.getDefaultImageMetadata(typeSpecifier, writeParam);
       if (metadata.isReadOnly() || !metadata.isStandardMetadataFormatSupported()) {
         continue;
       }

       setDPI(metadata, dpi);

       final ImageOutputStream stream = ImageIO.createImageOutputStream(output);
       writer.setOutput(stream);
       writer.write(metadata, new IIOImage(SwingFXUtils.fromFXImage(image, null), null, metadata), writeParam);
       stream.close();
       break;
     }
   }

   private static void setDPI(IIOMetadata metadata, float dpi) throws IIOInvalidTreeException {

     // for PMG, it's dots per millimeter
     double dotsPerMilli = 1.0 * dpi / 10 / 2.54;

     IIOMetadataNode horiz = new IIOMetadataNode("HorizontalPixelSize");
     horiz.setAttribute("value", Double.toString(dotsPerMilli));

     IIOMetadataNode vert = new IIOMetadataNode("VerticalPixelSize");
     vert.setAttribute("value", Double.toString(dotsPerMilli));

     IIOMetadataNode dim = new IIOMetadataNode("Dimension");
     dim.appendChild(horiz);
     dim.appendChild(vert);

     IIOMetadataNode root = new IIOMetadataNode("javax_imageio_1.0");
     root.appendChild(dim);

     metadata.mergeTree("javax_imageio_1.0", root);
   }

#End If

Based on this answer: https://stackoverflow.com/questions/321736/how-to-set-dpi-information-in-an-image/4833697#4833697
 
Upvote 0

focus330

Member
Licensed User
Longtime User
Erel.
Exactly what I want. It's working perfectly.
It's also a useful code for me to understand how to work.
Thanks very much.
 
Upvote 0

focus330

Member
Licensed User
Longtime User
The saved image results of 188 KB but if I load with a program like Digital Image Pro and save with another name it results of 84 KB while DPI and size not changed. Why ?
 
Last edited:
Upvote 0

MaFu

Well-Known Member
Licensed User
Longtime User
DPI is only a value stored in the image file, changing this value doesn't change anything on the image. Therefore it has nothing to do with the size of the image file. Maybe Digital Image Pro saves the image with other parameters.
 
Upvote 0

focus330

Member
Licensed User
Longtime User
I see that the difference between the images is on deep. The first, originated using Erel's example code is 32. After rewrite is 24. Height, width and dpi are equal.
To obtain an image reduced have I set to 24 ? If yes how to di this ?
 
Upvote 0

focus330

Member
Licensed User
Longtime User
DPi OK but deep no .Probably my bad English. Then is better attach four photos to explain.

1-Foto.jpg is a photo with correct parameters for me (created externaly with a photo prg.) used as model .
2-Foto_1bis.jpg is the same loaded in an Image node with InizializeSample and registred with the enhance code
3-FotoFromCanvas is registred with the enhance code from Canvas with the same height and width of the model
4-FotoFromCanvas2 is registred like the previous but with different height s width

A Foto.jpg and Foto:1bis.jpg have a deep of 24 - FotoFromCanvas and FotoCanvas2 have 32
B Size (Kb) are very different.
C Opening with a foto prg, the 1 2 and 4 images have the same inches while FotoFromCanvas is incorrect

My gol is to obtain by code a FotoFromCanvas identical to Foto.jpg from a Canvas or another graphic node.
 

Attachments

  • test.zip
    483.3 KB · Views: 260
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
These are the details of FotoFromCanvas:

SS-2017-07-25_09.15.28.png


What have you set the dpi to?
 
Upvote 0

focus330

Member
Licensed User
Longtime User
Into code I used
Dim dpi As Float = 300 and the result is correct but size (KB) is biggest and metrics change . I had to resize canvas to a different size ( but it depends also of screen density I suppose) and so I obtain FotoFromCanvas2. KB of booth are significatly big. It's important because I have to obtain am jpg image less 35KB with 3.3cm x 4cm. with a dpi at least of 200 to be compatible with ICAO international rules (Passport and other documents). I used 300 only because the model has this dpi but testing with 200 the result is the same.
 
Last edited:
Upvote 0

focus330

Member
Licensed User
Longtime User
Jpeg format is another rule. The lib ( from B4j community) I used to trasform a png to a jpg give me a 96x96 dpi image. I'll try again to find a code to be used for jpg images.
I'll control if ' com.sun.imageio.plugins.jpeg.JPEGImageWriter ' can help me.
 
Upvote 0

focus330

Member
Licensed User
Longtime User
Erel, with this code I saved a wonderful 0 KB image !

B4X:
        CanvasMain1.SetSize(390,472)
       
        Dim MyIm As Image
        MyIm = CanvasMain1.Snapshot
       
        Dim Out As OutputStream  = File.OpenOutput(DirOut, SUF & "_FOTO_" & edtName.Text &".jpg",False)
  
        Enhance3.SaveImage3(MyIm, Out, "300", "01")
        Out.Close

And this:

B4X:
'Static code module
Sub Process_Globals
    Private fx As JFX
End Sub

Public Sub SaveImage3(img As Image, out As OutputStream, dpi As String, ppi As String)
    Dim jo As JavaObject = Me
    jo.RunMethod("saveImage3", Array(out,img, dpi, ppi))
    out.Close
End Sub


#if Java

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;

import javafx.embed.swing.SwingFXUtils;
import javafx.scene.image.Image;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOInvalidTreeException;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.ImageOutputStream;

   public static void saveImage3(OutputStream output, Image image, String dpi, String ppi) throws IOException {

     final String formatName = "jpeg";
      
     for (Iterator<ImageWriter> iw = ImageIO.getImageWritersByFormatName(formatName); iw.hasNext();) {
       ImageWriter writer = iw.next();
       ImageWriteParam writeParam = writer.getDefaultWriteParam();
       ImageTypeSpecifier typeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
       IIOMetadata metadata = writer.getDefaultImageMetadata(typeSpecifier, writeParam);
       if (metadata.isReadOnly() || !metadata.isStandardMetadataFormatSupported()) {
         continue;
       }

       setDPI3(metadata, dpi, ppi);
      
       final ImageOutputStream stream = ImageIO.createImageOutputStream(output);
       writer.setOutput(stream);
       writer.write(metadata, new IIOImage(SwingFXUtils.fromFXImage(image, null), null, metadata), writeParam);
       stream.close();
       break;

     }
   }

private static void setDPI3(IIOMetadata metadata, String dpi, String ppi) throws IIOInvalidTreeException {
    String metadataFormat = "javax_imageio_jpeg_image_1.0";
    IIOMetadataNode root = new IIOMetadataNode(metadataFormat);
    IIOMetadataNode jpegVariety = new IIOMetadataNode("JPEGvariety");
    IIOMetadataNode markerSequence = new IIOMetadataNode("markerSequence");

    IIOMetadataNode app0JFIF = new IIOMetadataNode("app0JFIF");
    app0JFIF.setAttribute("majorVersion", "1");
    app0JFIF.setAttribute("minorVersion", "2");
    app0JFIF.setAttribute("thumbWidth", "0");
    app0JFIF.setAttribute("thumbHeight", "0");
    app0JFIF.setAttribute("resUnits", ppi);
    app0JFIF.setAttribute("Xdensity", dpi);
    app0JFIF.setAttribute("Ydensity", dpi);

    root.appendChild(jpegVariety);
    root.appendChild(markerSequence);
    jpegVariety.appendChild(app0JFIF);

   metadata.mergeTree(metadataFormat, root);
}

#End If
 
Upvote 0
Top