B4J Question [SOLVED] How to create JPEG output stream

Discussion in 'B4J Questions' started by JackKirk, Sep 12, 2017.

  1. JackKirk

    JackKirk Well-Known Member Licensed User

    I need to be able to create a JPEG output stream in B4J so I can save images arriving via FTP in AWS S3 buckets as JPEGs (PNG doesn't cut it).

    In B4A I was doing this without problems with code of form:

    ....WriteToStream(wrk_out, jpg_quality, "JPEG")

    B4J apparently doesn't support JPEG (although B4A does - another of life's little mysteries).

    I have been playing around with code I found here:

    http://www.java2s.com/Code/Java/2D-...fileTheJPEGqualitycanbespecifiedinpercent.htm

    So far I have:
    Code:
    Private Sub WriteToStreamJPEG(In_image As Image, JPEG_quality As Int) As OutputStream
       
        
    Private wrk_out As OutputStream
       
        wrk_out.InitializeToBytesArray(
    0)
       
        nativeMe.RunMethod(
    "saveImageAsJPEG"Array(In_image, wrk_out, JPEG_quality))
    '    nativeMe.RunMethod("saveImageAsJPEG", Array(Array As Image (In_image), Array As OutputStream (wrk_out), Array As Int (JPEG_quality)))
       
        
    Return wrk_out
       
    End Sub

    #If Java

    //See http://www.java2s.com/Code/Java/2D-Graphics-GUI/WritesanimagetoanoutputstreamasaJPEGfileTheJPEGqualitycanbespecifiedinpercent.htm

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

        import javax.imageio.IIOImage;
        import javax.imageio.ImageIO;
        import javax.imageio.ImageReader;
        import javax.imageio.ImageWriteParam;
        import javax.imageio.ImageWriter;
        import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
        import javax.imageio.stream.ImageInputStream;
        import javax.imageio.stream.ImageOutputStream;

        /**
        * Writes an image to an output stream as a JPEG file. The JPEG quality can
        * be specified in percent.
        *
        * @param image
        *            image to be written
        * @param stream
        *            target stream
        * @param qualityPercent
        *            JPEG quality in percent
        *
        * @throws IOException
        *             if an I/O error occured
        * @throws IllegalArgumentException
        *             if qualityPercent not between 0 and 100
        */
        public static void saveImageAsJPEG(BufferedImage image,
                OutputStream stream, int qualityPercent) throws IOException {
            if ((qualityPercent < 0) || (qualityPercent > 100)) {
                throw new IllegalArgumentException("Quality out of bounds!");
            }
            float quality = qualityPercent / 100f;
            ImageWriter writer = null;
            Iterator iter = ImageIO.getImageWritersByFormatName("jpg");
            if (iter.hasNext()) {
                writer = (ImageWriter) iter.next();
            }
            ImageOutputStream ios = ImageIO.createImageOutputStream(stream);
            writer.setOutput(ios);
            ImageWriteParam iwparam = new JPEGImageWriteParam(Locale.getDefault());
            iwparam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            iwparam.setCompressionQuality(quality);
            writer.write(null, new IIOImage(image, null, null), iwparam);
            ios.flush();
            writer.dispose();
            ios.close();
        }

    #End If
    Which dies with:

    Extensive googling would suggest my problem is trying to pass a B4J Image to a java BufferedImage.

    I can find no way around this and I really need to get it resolved - any and all help appreciated...
     
  2. Erel

    Erel Administrator Staff Member Licensed User

  3. JackKirk

    JackKirk Well-Known Member Licensed User

    Erel,

    Yes I saw that thread when I was researching the problem.

    There is no way in that thread, that I can see, to adjust the jpeg quality and hence reduce file size - which is the whole reason for wanting to use it.

    It seems to me that I just need a mechanism to convert B4J Image to java BufferedImage - any thoughts.

    BTW I have seen the BufferedImage stuff in the jCoreExtras library but can not see how or if it is relevant or usable.

    Again all help appreciated...
     
    Last edited: Sep 12, 2017
  4. OliverA

    OliverA Well-Known Member Licensed User

    If you are receiving the image via ftp, why not just treat it as a binary file and then upload it to AWS that way. No need to treat the file as an image.
     
  5. JackKirk

    JackKirk Well-Known Member Licensed User

    I have to do some processing after receiving the files via FTP - generate thumbnails, do facial recognition, define watermark locations amongst others...

    I control the JPG quality of the FTP stuff at the cameras and want the ability to have different (lower) JPG qualities of the final AWS S3 files - reason being that facial recognition is better with higher quality but higher quality final images means unnecessarily slower downloading to ultimate customer.

    I really need some help with the original problem....
     
  6. OliverA

    OliverA Well-Known Member Licensed User

    Curiosity: How are you achieving this? Within B4J? Using external tools?
     
  7. OliverA

    OliverA Well-Known Member Licensed User

    If external tools are allowed, you could look into combining jshell and GraphicMagick, especially for re-sampling/resizing.
     
  8. Daestrum

    Daestrum Well-Known Member Licensed User

    I think, if you are passing an image to your java code you need to use
    Code:
    import javafx.embed.swing.SwingFXUtils;
    import javafx.scene.image.Image;
    Change the method definition so it requires an Image
    Code:
    public static void saveImageAsJPEG(Image image,
                            
    OutputStream stream, int qualityPercent) throws IOException {
    Then call immediately on entry to create the buffered image (bufImg)
    Code:
    BufferedImage bufImg = SwingFXUtils.fromFXImage(image,null);
    Then you can pass that to your writer
    Code:
    ...
    writer.write(
    null, new IIOImage(bufImg, nullnull), iwparam);
    ...
    **not tested but pretty sure that will solve it as it compiles with no errors.
     
    JackKirk likes this.
  9. JackKirk

    JackKirk Well-Known Member Licensed User

    Oliver,

    Firstly thank you for your interest in my problem.

    As regards the facial recognition I fear I should have omitted reference to it - this is a major project and I am obliged to be quiet. I will say that as of today it would appear we have every major hurdle crossed and it is working beautifully - I will elaborate in the "my creations" forums when we go public in a couple of months.
     
  10. JackKirk

    JackKirk Well-Known Member Licensed User

    Daestrum,

    I can not thank you enough for your response, although it was not the whole answer it set me on the path to the final solution which I outline below:

    TO GENERATE A JPEG OUTPUT STREAM WITH CONTROL OVER JPEG QUALITY YOU CAN USE THIS CODE

    Code:
    Private Sub WriteToStreamJPEG(In_image As Image, JPEG_quality As Int) As OutputStream

        
    Private wrk_out As OutputStream

        wrk_out.InitializeToBytesArray(
    0)

        joMe.RunMethod(
    "saveImageAsJPEG"Array(In_image, wrk_out, JPEG_quality))

        
    Return wrk_out

    End Sub

    #If Java

    //See http://www.java2s.com/Code/Java/2D-Graphics-GUI/WritesanimagetoanoutputstreamasaJPEGfileTheJPEGqualitycanbespecifiedinpercent.htm
    //and https://stackoverflow.com/questions/19548363/image-saved-in-javafx-as-jpg-is-pink-toned
    //and https://www.b4x.com/android/forum/threads/how-to-create-jpeg-output-stream.83830/#post-531118

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

        import javafx.scene.image.Image;
        import javafx.embed.swing.SwingFXUtils;
        import java.awt.Graphics2D;

        import javax.imageio.IIOImage;
        import javax.imageio.ImageIO;
        import javax.imageio.ImageWriteParam;
        import javax.imageio.ImageWriter;
        import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
        import javax.imageio.stream.ImageOutputStream;

        /**
        * Writes an image to an output stream as a JPEG file. The JPEG quality can
        * be specified in percent.
        *
        * @param image
        *            image to be written
        * @param stream
        *            target stream
        * @param qualityPercent
        *            JPEG quality in percent
        *
        * @throws IOException
        *             if an I/O error occured
        * @throws IllegalArgumentException
        *             if qualityPercent not between 0 and 100
        */
        public static void saveImageAsJPEG(Image img,
                OutputStream stream, int qualityPercent) throws IOException {
            if ((qualityPercent < 0) || (qualityPercent > 100)) {
                throw new IllegalArgumentException("Quality out of bounds!");
            }

            // Get buffered image
            BufferedImage buffimg = SwingFXUtils.fromFXImage(img, null);

            // Remove alpha-channel from buffered image
            BufferedImage imageRGB = new BufferedImage(buffimg.getWidth(), buffimg.getHeight(), BufferedImage.OPAQUE);
            Graphics2D graphics = imageRGB.createGraphics();
            graphics.drawImage(buffimg, 0, 0, null);
            graphics.dispose();
        
            float quality = qualityPercent / 100f;
            ImageWriter writer = null;
            Iterator iter = ImageIO.getImageWritersByFormatName("jpg");
            if (iter.hasNext()) {
                writer = (ImageWriter) iter.next();
            }
            ImageOutputStream ios = ImageIO.createImageOutputStream(stream);
            writer.setOutput(ios);
            ImageWriteParam iwparam = new JPEGImageWriteParam(Locale.getDefault());
            iwparam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            iwparam.setCompressionQuality(quality);
            writer.write(null, new IIOImage(imageRGB, null, null), iwparam);
            ios.flush();
            writer.dispose();
            ios.close();
        }

    #End If
    I found after adding Daestrum's contribution that it all worked fine - except when you supplied it with certain images that had been resized.

    Sometimes I would get a result which was washed with a reddish tinge - this may be a symptom of Erel's comment "all kinds of color issues with the JPEG encoder" in this post:

    https://www.b4x.com/android/forum/threads/save-canvas-in-jpg.42839/#post-259532

    A day and a lot of googling, groping and frustration I finally found:

    https://stackoverflow.com/questions/19548363/image-saved-in-javafx-as-jpg-is-pink-toned

    which provided a fairly straight forward solution (removing the alpha channel) - I inserted that and now all is rosy (well actually all is not rosy :p:p:p)

    Thanks again...
     
    Last edited: Sep 13, 2017
    inakigarm likes this.
  11. OliverA

    OliverA Well-Known Member Licensed User

    It looks like you already found your solution (via inline Java code) and my previous question was more of a fishing expedition to see if external tools would or would not fit into your project. Since you are already using S3 and depending how much resources your project is going to expend in resizing various JPEGs, I just wanted to point out another method, using the Lambda and API services provided by Amazon/AWS. This may not fit your project, but could be a way to off-load some processing away from your resources to the seemingly endless (depending on wallet, of course) resources of AWS.

    Resize Images on the Fly with Amazon S3, AWS Lambda, and Amazon API Gateway
     
    JackKirk likes this.
  12. JackKirk

    JackKirk Well-Known Member Licensed User

    Oliver,

    At the moment we are staying with AWS EC2 Windows instances - mainly because of a long comfortable exposure to Wintel.

    We are aware of Lambda and have it marked for future exploration but right now the heat is on to get up and running so we are sticking with what we know best.

    Thanks for the link - that looks like it could be very useful - I will digest it this evening.

    Have you actually written Lambda stuff with B4J?
     
  13. OliverA

    OliverA Well-Known Member Licensed User

    Sadly, no. The only AWS exposure I have so far is in testing Beanstalk (using Amazon's OS image) and RDS with B4J Server/ABMaterial. I've been looking into S3 in the past and happen to run into the whole picture scaling information during that time and so when you mentioned S3, JPEGs and picture scaling, I thought this (Lambda/S3/API) info may be relevant/interesting to you.
     
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice