B4J Code Snippet OpenCV Image to JPEG byte array with resize/quality options

This is my third "Image to JPEG byte array with resize/quality options" snippet. The various versions are:
  1. Inline Java version, B4J only: https://www.b4x.com/android/forum/t...byte-array-with-resize-quality-options.91746/
  2. XUI version, working across B4A, B4i, and B4J: https://www.b4x.com/android/forum/t...byte-array-with-resize-quality-options.91774/
  3. A new OpenCV version that is heavily based on the first version (published here). The initial resize is unchanged, but the code to convert the resulting BufferedImage to a JPG image and then convert it to a byte array is now using OpenCV classes/methods.
This version, unlike the previous ones, has some external prerequisites.
  • For Windows, the prerequisites are the opencv*.jar and the opencv*.dll files. The .jar file needs to be placed in the Additional Library folder of B4J. The opencv*.dll file needs to be located somewhere accessible to the application since it needs to be loaded at runtime. They can be found in the .exe installer from OpenCV's site (https://opencv.org/releases/). After downloading the .exe file, you can open it via a zip manager or just unpack it via windows (after renaming the .exe to .zip). The opencv*.jar file can be found in opencv\build\java and the appropriate opencv*.dll can be found in \opencv\build\java\x64 for 64bit Windows and \opencv\build\java\x86 for 32bit Windows.
    • Note: In my case, I downloaded OpenCV Release 4.6.0. The .exe file was opencv-4.6.0-vc14_vc15.exe, the .jar file was opencv-460.jar
    • One way to handle the .dll files could be to rename them (for example the 64bit opencv_java460.dll to opencv_java460_64.dll and the 32bit opencv_java460.dll to opencv_java460_32.dll) and place them in the Files folder of your project. Then use the following snippet to load the appropriate library for the Windows system your application is running on before using the resize snippet
B4X:
Sub LoadOpenCVDLL
    If GetSystemProperty("os.name", "unknown").ToLowerCase.Contains("win") Then
        If Not(File.Exists(File.DirApp, "opencv_java460.dll")) Then
            Dim bitness As String = GetSystemProperty("sun.arch.data.model", "unknown")
            File.Copy(File.DirAssets, $"opencv_java460_${bitness}.dll"$, File.DirApp, "opencv_java460.dll")
        End If
        Dim joSystem As JavaObject
        joSystem.InitializeStatic("java.lang.System")
        joSystem.RunMethod("load", Array(File.Combine(File.DirApp, "opencv_java460.dll")))
    End If
End Sub
Disclaimer: This may not be the proper way to get OpenCV properly working for Java under Windows. This happens to work for me, but maybe a full install of OpenCV is required.
  • Android: May be do-able. Android's Bitmap class has a compress method though that may provide part of the solution without requiring OpenCV. User input welcome.
  • Any other OS: User input welcome

Why create this version? It's been pointed out by @Magma that the first two versions create temp files (https://www.b4x.com/android/forum/threads/image-writetostream-using-disk-how-to-avoid-it.143772/) under Windows. @Magma also wondered if there is a way to do the same without temp files and his research brought up OpenCV (he thought of more and @kimstudio also proposed some solutions, I just picked OpenCV). This version (based on OpenCV) may be the answer to the temp file creation and it seems to be a little under 2 times faster than the first solution and a bit over twice faster than the second solution listed above (via a simplistic testing of running each method 1000 times and noting the time).

B4X:
' Converts image to JPEG a byte array of the resulting JPEG. Ability to resize and adjust JPEG quality.
' Negative width and height values = %, such that -50 = 50% and -200 = 200%
' Positive width and height values = pixel dimensions
' If one value (either width or height) are 0, then the other value is proportionally
'  calculated from the first.
' If both width and height are 0 or -100, no resizing takes place
' If quality = -1, use Java's default quality
Sub OCVImageToJPEGByteArray(aImage As Image, width As Int, height As Int, quality As Int) As Byte()
    Dim jo As JavaObject = Me
    Return jo.RunMethod("ocvImageToJPEGByteArray", Array As Object (aImage, width, height, quality))
End Sub
B4X:
#if Java
import java.awt.AlphaComposite;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.geom.AffineTransform;
import java.awt.Graphics2D;

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

import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.core.MatOfInt;
import org.opencv.imgcodecs.Imgcodecs;

// Image resizing
// https://stackoverflow.com/a/4205711
// https://stackoverflow.com/questions/21540378/convert-javafx-image-to-bufferedimage
// https://stackoverflow.com/a/13605411
// https://www.mkyong.com/java/how-to-convert-bufferedimage-to-byte-in-java/

// OpenCV links for creating JPG at given quality
// Convert BufferedImage (Java) to byte array in put to Mat (OpenCV)
// https://stackoverflow.com/a/15095653
// Convert Mat to byte array
// https://stackoverflow.com/a/45960893


// Converts image to JPEG a byte array of the resulting JPEG. Ability to resize and adjust JPEG quality.
// Negative width and height values = %, such that -50 = 50% and -200 = 200%
// Positive width and height values = pixel dimensions
// If one value (either width or height) are 0, then the other value is proportionally
//  calculated from the first.
// If both width and height are 0 or -100, no resizing takes place
// If quality = -1, use Java's default quality
public static byte[] ocvImageToJPEGByteArray(Image aImage, int width, int height, int qualityPercent) {

   if ((qualityPercent < -1) || (qualityPercent > 100)) {
      throw new IllegalArgumentException("Quality out of bounds!");
    }
   
   //float quality = qualityPercent / 100f;
   //OpenCV just needs an int
   int quality = qualityPercent;
 
   double oldWidth = aImage.getWidth();
   double oldHeight = aImage.getHeight();
   if (oldWidth == 0 || oldHeight == 0) {
       throw new IllegalArgumentException("Source image with 0 width and/or height!");
   }
 
   boolean resize = true;
   if ((width == 0 && height == 0) || (width == -100 && height == -100)) resize = false;

   BufferedImage destImage;
   if (resize) {
       double newWidth = (double) width;
       double newHeight = (double) height;
       // Calculate new dimensions
       if (newWidth < 0) newWidth = -1 * oldWidth * newWidth / 100;
       if (newHeight < 0) newHeight = -1 * oldHeight * newHeight / 100;
       if (newWidth == 0) newWidth = oldWidth * newHeight / oldHeight;
       if (newHeight == 0) newHeight = oldHeight * newWidth / oldWidth;
       // Convert JavaFX image to BufferedImage and transform according to new dimensions
       destImage = new BufferedImage((int) newWidth, (int) newHeight, BufferedImage.TYPE_3BYTE_BGR);
       BufferedImage srcImage = SwingFXUtils.fromFXImage(aImage, null);
       Graphics2D g = destImage.createGraphics();
       AffineTransform at = AffineTransform.getScaleInstance(newWidth/oldWidth, newHeight/oldHeight);
       g.drawRenderedImage(srcImage, at);
       g.dispose();
   } else {
       //destImage = SwingFXUtils.fromFXImage(aImage, null);
       //Need to see if we alread have the correct BufferedImage type, if not, convert
       if (srcImage.getType() != BufferedImage.TYPE_3BYTE_BGR) {
          //http://stackoverflow.com/questions/21740729/converting-bufferedimage-to-mat-opencv-in-java
           destImage = new BufferedImage(srcImage.getWidth(), srcImage.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
           g = destImage.createGraphics();
           g.setComposite(AlphaComposite.Src);
           g.drawImage(srcImage, 0, 0, null);
           g.dispose();
       } else {
          destImage = srcImage;
       }
   }
 
   //Create OpenCV Mat, retrieve data from BufferedImage and assign to Mat
   Mat mRGB = new Mat(destImage.getHeight(), destImage.getWidth(), CvType.CV_8UC3);
   byte[] pixels = ((DataBufferByte) destImage.getRaster().getDataBuffer()).getData();
   mRGB.put(0, 0, pixels);
  
  
   //Convert mat to JPG byte array of given quality
   MatOfByte mob=new MatOfByte();                        //Destination Mat byte array
   int[] intParams = new int[2];                        //Create parameters for imencode
   intParams[0] = Imgcodecs.IMWRITE_JPEG_QUALITY;
   intParams[1] = (int) quality;
   MatOfInt params = new MatOfInt(intParams);
   Imgcodecs.imencode(".jpg", mRGB, mob, params);       
  
   return mob.toArray();                                //Convert Mat byte array to regular byte array
}

#End If
1st Edit: Cleaned up the Java code. Removed unnecessary imports, IOException declaration, and unneeded byte array.
2nd Edit: Thanks to the prodding from @kimstudio, I found a mistake in the original code. The incorrect BufferedImage was created/used when no resizing was done.
 
Last edited:

kimstudio

Active Member
Licensed User
Longtime User
Very helpful for learning how to call OpenCV by this way. ?
@OliverA I noticed the resizing is still conducted by java, maybe we can also use OpenCV's resize since we already used the whole dll.
 
Last edited:

OliverA

Expert
Licensed User
Longtime User
@OliverA I noticed the resizing is still conducted by java, maybe we can also use OpenCV's resize since we already used the whole dll.
Yes, but it does not make it any faster to any degree that would matter (in my opinion). I'll post two modified versions below and you can test them for yourself.
 

OliverA

Expert
Licensed User
Longtime User
@OliverA I noticed the resizing is still conducted by java, maybe we can also use OpenCV's resize since we already used the whole dll.
This version uses as much OpenCV as it can and it is the slowest version of them all.
B4X:
' Converts image to JPEG a byte array of the resulting JPEG. Ability to resize and adjust JPEG quality.
' Negative width and height values = %, such that -50 = 50% and -200 = 200%
' Positive width and height values = pixel dimensions
' If one value (either width or height) are 0, then the other value is proportionally
'  calculated from the first.
' If both width and height are 0 or -100, no resizing takes place
' If quality = -1, use Java's default quality
Sub OCVImageToJPEGByteArray2(aImage As Image, width As Int, height As Int, quality As Int) As Byte()
    Dim jo As JavaObject = Me
    Return jo.RunMethod("ocvImageToJPEGByteArray2", Array As Object (aImage, width, height, quality))
End Sub

B4X:
#if Java
import java.awt.image.BufferedImage;

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

import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.core.MatOfInt;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

public static byte[] ocvImageToJPEGByteArray2(Image aImage, int width, int height, int qualityPercent) {

   if ((qualityPercent < -1) || (qualityPercent > 100)) {
      throw new IllegalArgumentException("Quality out of bounds!");
    }
    
   //float quality = qualityPercent / 100f;
   //OpenCV just needs an int
   int quality = qualityPercent;

   //OpenCV uses int's
   int oldWidth = (int) aImage.getWidth();
   int oldHeight = (int) aImage.getHeight();
   if (oldWidth == 0 || oldHeight == 0) {
       throw new IllegalArgumentException("Source image with 0 width and/or height!");
   }
 
   boolean resize = true;
   if ((width == 0 && height == 0) || (width == -100 && height == -100)) resize = false;

   BufferedImage srcImage = SwingFXUtils.fromFXImage(aImage, null);
   Mat srcMat = bufferedImage2Mat(srcImage);
   Mat destMat;
   
   if (resize) {
      int newWidth = width;
      int newHeight = height;
      // Calculate new dimensions
      if (newWidth < 0) newWidth = -1 * oldWidth * newWidth / 100;
      if (newHeight < 0) newHeight = -1 * oldHeight * newHeight / 100;
      if (newWidth == 0) newWidth = oldWidth * newHeight / oldHeight; 
      if (newHeight == 0) newHeight = oldHeight * newWidth / oldWidth;

      int interpolation;
      if ((newHeight * newWidth) > (oldHeight * oldWidth)) {
         //upscaling
         interpolation = Imgproc.INTER_CUBIC;
      } else {
         //downscaling
         interpolation = Imgproc.INTER_AREA;
      }
      destMat = new Mat(newHeight, newWidth, srcMat.type());
      Imgproc.resize(srcMat, destMat, destMat.size(), 0, 0, interpolation);
   } else {
      destMat = srcMat;
   }
   
   //Convert mat to JPG byte array of given quality
   MatOfByte mob=new MatOfByte();                        //Destination Mat byte array
   int[] intParams = new int[2];                        //Create parameters for imencode
   intParams[0] = Imgcodecs.IMWRITE_JPEG_QUALITY;
   intParams[1] = (int) quality;
   MatOfInt params = new MatOfInt(intParams);
   Imgcodecs.imencode(".jpg", destMat, mob, params);        
   
   return mob.toArray();                                //Convert Mat byte array to regular byte array
}

//https://stackoverflow.com/a/34772385
public static Mat bufferedImage2Mat(BufferedImage in)
{
      Mat out;
      byte[] data;
      int r, g, b;
      int height = in.getHeight();
      int width = in.getWidth();
      if(in.getType() == BufferedImage.TYPE_INT_RGB || in.getType() == BufferedImage.TYPE_INT_ARGB)
      {
          out = new Mat(height, width, CvType.CV_8UC3);
          data = new byte[height * width * (int)out.elemSize()];
          int[] dataBuff = in.getRGB(0, 0, width, height, null, 0, width);
          for(int i = 0; i < dataBuff.length; i++)
          {
              data[i*3 + 2] = (byte) ((dataBuff[i] >> 16) & 0xFF);
              data[i*3 + 1] = (byte) ((dataBuff[i] >> 8) & 0xFF);
              data[i*3] = (byte) ((dataBuff[i] >> 0) & 0xFF);
          }
      }
      else
      {
          out = new Mat(height, width, CvType.CV_8UC1);
          data = new byte[height * width * (int)out.elemSize()];
          int[] dataBuff = in.getRGB(0, 0, width, height, null, 0, width);
          for(int i = 0; i < dataBuff.length; i++)
          {
            r = (byte) ((dataBuff[i] >> 16) & 0xFF);
            g = (byte) ((dataBuff[i] >> 8) & 0xFF);
            b = (byte) ((dataBuff[i] >> 0) & 0xFF);
            data[i] = (byte)((0.21 * r) + (0.71 * g) + (0.07 * b)); //luminosity
          }
       }
       out.put(0, 0, data);
       return out;
 }
#End If
 

OliverA

Expert
Licensed User
Longtime User
@OliverA I noticed the resizing is still conducted by java, maybe we can also use OpenCV's resize since we already used the whole dll.
This version uses Java to produce the proper BufferedImage (TYPE_3BYTE_BGR) and OpenCV to resize and convert to JPG with given quality. I cannot discern any perfomance benefits from this version (it seems to be on par with the original in the first post).

B4X:
' Converts image to JPEG a byte array of the resulting JPEG. Ability to resize and adjust JPEG quality.
' Negative width and height values = %, such that -50 = 50% and -200 = 200%
' Positive width and height values = pixel dimensions
' If one value (either width or height) are 0, then the other value is proportionally
'  calculated from the first.
' If both width and height are 0 or -100, no resizing takes place
' If quality = -1, use Java's default quality
Sub OCVImageToJPEGByteArray3(aImage As Image, width As Int, height As Int, quality As Int) As Byte()
    Dim jo As JavaObject = Me
    Return jo.RunMethod("ocvImageToJPEGByteArray3", Array As Object (aImage, width, height, quality))
End Sub
B4X:
#if Java
import java.awt.AlphaComposite;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.Graphics2D;

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

import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.core.MatOfInt;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

// Converts image to JPEG a byte array of the resulting JPEG. Ability to resize and adjust JPEG quality.
// Negative width and height values = %, such that -50 = 50% and -200 = 200%
// Positive width and height values = pixel dimensions
// If one value (either width or height) are 0, then the other value is proportionally
//  calculated from the first.
// If both width and height are 0 or -100, no resizing takes place
// If quality = -1, use Java's default quality
public static byte[] ocvImageToJPEGByteArray3(Image aImage, int width, int height, int qualityPercent) {

   if ((qualityPercent < -1) || (qualityPercent > 100)) {
      throw new IllegalArgumentException("Quality out of bounds!");
    }
    
   //float quality = qualityPercent / 100f;
   //OpenCV just needs an int
   int quality = qualityPercent;
 
   //OpenCV uses int's
   int oldWidth = (int) aImage.getWidth();
   int oldHeight = (int) aImage.getHeight();
   if (oldWidth == 0 || oldHeight == 0) {
       throw new IllegalArgumentException("Source image with 0 width and/or height!");
   }
 
   boolean resize = true;
   if ((width == 0 && height == 0) || (width == -100 && height == -100)) resize = false;

   BufferedImage srcImage = SwingFXUtils.fromFXImage(aImage, null);
   BufferedImage destImage;
   Graphics2D g;
   //Need to see if we alread have the correct BufferedImage type, if not, convert
   if (srcImage.getType() != BufferedImage.TYPE_3BYTE_BGR) {
      //http://stackoverflow.com/questions/21740729/converting-bufferedimage-to-mat-opencv-in-java
       destImage = new BufferedImage(srcImage.getWidth(), srcImage.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
       g = destImage.createGraphics();
       g.setComposite(AlphaComposite.Src);
       g.drawImage(srcImage, 0, 0, null);
       g.dispose();
   } else {
      destImage = srcImage;
   }
   
   //Create OpenCV Mat, retrieve data from BufferedImage and assign to Mat
   Mat srcMat = new Mat(destImage.getHeight(), destImage.getWidth(), CvType.CV_8UC3);
   byte[] pixels = ((DataBufferByte) destImage.getRaster().getDataBuffer()).getData();
   srcMat.put(0, 0, pixels);
   Mat destMat;
   
   if (resize) {
      int newWidth = width;
      int newHeight = height;
      // Calculate new dimensions
      if (newWidth < 0) newWidth = -1 * oldWidth * newWidth / 100;
      if (newHeight < 0) newHeight = -1 * oldHeight * newHeight / 100;
      if (newWidth == 0) newWidth = oldWidth * newHeight / oldHeight; 
      if (newHeight == 0) newHeight = oldHeight * newWidth / oldWidth;

      int interpolation;
      if ((newHeight * newWidth) > (oldHeight * oldWidth)) {
         //upscaling
         interpolation = Imgproc.INTER_CUBIC;
      } else {
         //downscaling
         interpolation = Imgproc.INTER_AREA;
      }
      destMat = new Mat(newHeight, newWidth, srcMat.type());
      Imgproc.resize(srcMat, destMat, destMat.size(), 0, 0, interpolation);
   } else {
      destMat = srcMat;
   }
   
   //Convert mat to JPG byte array of given quality
   MatOfByte mob=new MatOfByte();                        //Destination Mat byte array
   int[] intParams = new int[2];                        //Create parameters for imencode
   intParams[0] = Imgcodecs.IMWRITE_JPEG_QUALITY;
   intParams[1] = (int) quality;
   MatOfInt params = new MatOfInt(intParams);
   Imgcodecs.imencode(".jpg", destMat, mob, params);        
   
   return mob.toArray();                                //Convert Mat byte array to regular byte array
}

#End If
 

Magma

Expert
Licensed User
Longtime User
Hi there...

I am trying to test your class... but i am taking this:

B4J Version: 9.80
Parsing code. (0.02s)
Java Version: 11
Building folders structure. (0.00s)
Compiling code. (0.02s)

ObfuscatorMap.txt file created in Objects folder.
Compiling layouts code. (0.00s)
Organizing libraries. (0.00s)
Compiling generated Java code. Error
src\b4j\example\main.java:878: error: cannot find symbol
if (srcImage.getType() != BufferedImage.TYPE_3BYTE_BGR) {
^
symbol: variable srcImage
location: class main
Note: src\b4j\example\main.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
1 error

javac 11.0.1

I thought that i must.. add that:
#AdditionalJar: opencv-460.jar

Before that copied at files assets and rename the dll as opencv_java460_64.dll...
Also calling LoadOpenCVDLL first and then...

calling it as:

B4X:
    Dim bbi2() As Byte
    bbi2=OCVImageToJPEGByteArray(bbi, 0,0,75)     'XUIImageToJPEGByteArray2(bbi, 0,0,75)

    Log("NEW:" & bbi2.Length)

but getting the error as i said.... seems that something miss...
 

OliverA

Expert
Licensed User
Longtime User
I am trying to test your class... but i am taking this:
That's a compile-time error and should have nothing to do with
I thought that i must.. add that:
and
Before that copied at files assets and rename the dll as opencv_java460_64.dll...
Also calling LoadOpenCVDLL first and then...

calling it as:
Try compiling the attached do-nothing demo. I get
B4J Version: 9.80
Parsing code. (0.00s)
Java Version: 11
Building folders structure. (0.00s)
Compiling code. (0.00s)
Compiling layouts code. (0.00s)
Organizing libraries. (0.00s)
Compiling generated Java code. (1.00s)
Building jar file. (0.34s)
Jar file created: C:\B4X\Forum\TestOCVimgOption3Ui\Objects\TestOCVimgOption3Ui.jar
Running application. (0.00s)
Completed successfully.
Note 1: This is on Windows 11 Home, 22H2 edition using the JDK11 from the B4X.com B4J page.
Note 2: The demo project is not intended to run, since it will fail. The project's purpose is to check the compiling of the project, not actual usage.
 

Attachments

  • TestOCVimgOption3Ui.zip
    3.6 KB · Views: 198

Magma

Expert
Licensed User
Longtime User
@OliverA Yes this work 100% but... at first post... you had

this... not working:
B4X:
#if Java
import java.awt.AlphaComposite;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.geom.AffineTransform;
import java.awt.Graphics2D;

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

import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.core.MatOfInt;
import org.opencv.imgcodecs.Imgcodecs;

// Image resizing
// https://stackoverflow.com/a/4205711
// https://stackoverflow.com/questions/21540378/convert-javafx-image-to-bufferedimage
// https://stackoverflow.com/a/13605411
// https://www.mkyong.com/java/how-to-convert-bufferedimage-to-byte-in-java/

// OpenCV links for creating JPG at given quality
// Convert BufferedImage (Java) to byte array in put to Mat (OpenCV)
// https://stackoverflow.com/a/15095653
// Convert Mat to byte array
// https://stackoverflow.com/a/45960893


// Converts image to JPEG a byte array of the resulting JPEG. Ability to resize and adjust JPEG quality.
// Negative width and height values = %, such that -50 = 50% and -200 = 200%
// Positive width and height values = pixel dimensions
// If one value (either width or height) are 0, then the other value is proportionally
//  calculated from the first.
// If both width and height are 0 or -100, no resizing takes place
// If quality = -1, use Java's default quality
public static byte[] ocvImageToJPEGByteArray(Image aImage, int width, int height, int qualityPercent) {

   if ((qualityPercent < -1) || (qualityPercent > 100)) {
      throw new IllegalArgumentException("Quality out of bounds!");
    }
   
   //float quality = qualityPercent / 100f;
   //OpenCV just needs an int
   int quality = qualityPercent;
 
   double oldWidth = aImage.getWidth();
   double oldHeight = aImage.getHeight();
   if (oldWidth == 0 || oldHeight == 0) {
       throw new IllegalArgumentException("Source image with 0 width and/or height!");
   }
 
   boolean resize = true;
   if ((width == 0 && height == 0) || (width == -100 && height == -100)) resize = false;

   BufferedImage destImage;
   if (resize) {
       double newWidth = (double) width;
       double newHeight = (double) height;
       // Calculate new dimensions
       if (newWidth < 0) newWidth = -1 * oldWidth * newWidth / 100;
       if (newHeight < 0) newHeight = -1 * oldHeight * newHeight / 100;
       if (newWidth == 0) newWidth = oldWidth * newHeight / oldHeight;
       if (newHeight == 0) newHeight = oldHeight * newWidth / oldWidth;
       // Convert JavaFX image to BufferedImage and transform according to new dimensions
       destImage = new BufferedImage((int) newWidth, (int) newHeight, BufferedImage.TYPE_3BYTE_BGR);
       BufferedImage srcImage = SwingFXUtils.fromFXImage(aImage, null);
       Graphics2D g = destImage.createGraphics();
       AffineTransform at = AffineTransform.getScaleInstance(newWidth/oldWidth, newHeight/oldHeight);
       g.drawRenderedImage(srcImage, at);
       g.dispose();
   } else {
       //destImage = SwingFXUtils.fromFXImage(aImage, null);
       //Need to see if we alread have the correct BufferedImage type, if not, convert
       if (srcImage.getType() != BufferedImage.TYPE_3BYTE_BGR) {
          //http://stackoverflow.com/questions/21740729/converting-bufferedimage-to-mat-opencv-in-java
           destImage = new BufferedImage(srcImage.getWidth(), srcImage.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
           g = destImage.createGraphics();
           g.setComposite(AlphaComposite.Src);
           g.drawImage(srcImage, 0, 0, null);
           g.dispose();
       } else {
          destImage = srcImage;
       }
   }
 
   //Create OpenCV Mat, retrieve data from BufferedImage and assign to Mat
   Mat mRGB = new Mat(destImage.getHeight(), destImage.getWidth(), CvType.CV_8UC3);
   byte[] pixels = ((DataBufferByte) destImage.getRaster().getDataBuffer()).getData();
   mRGB.put(0, 0, pixels);
  
  
   //Convert mat to JPG byte array of given quality
   MatOfByte mob=new MatOfByte();                        //Destination Mat byte array
   int[] intParams = new int[2];                        //Create parameters for imencode
   intParams[0] = Imgcodecs.IMWRITE_JPEG_QUALITY;
   intParams[1] = (int) quality;
   MatOfInt params = new MatOfInt(intParams);
   Imgcodecs.imencode(".jpg", mRGB, mob, params);       
  
   return mob.toArray();                                //Convert Mat byte array to regular byte array
}

#End If



#if Java

import java.awt.image.BufferedImage;
import java.awt.geom.AffineTransform;
import java.awt.Graphics2D;

import java.io.ByteArrayOutputStream;
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 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;


// Image resizing
// https://stackoverflow.com/a/4205711
// https://stackoverflow.com/questions/21540378/convert-javafx-image-to-bufferedimage
// https://stackoverflow.com/a/13605411
// https://www.mkyong.com/java/how-to-convert-bufferedimage-to-byte-in-java/
// JPEG quality
// https://www.b4x.com/android/forum/threads/solved-how-to-create-jpeg-output-stream.83830/#post-531280
// http://www.java2s.com/Code/Java/2D-Graphics-GUI/WritesanimagetoanoutputstreamasaJPEGfileTheJPEGqualitycanbespecifiedinpercent.htm


// Converts image to JPEG a byte array of the resulting JPEG. Ability to resize and adjust JPEG quality.
// Negative width and height values = %, such that -50 = 50% and -200 = 200%
// Positive width and height values = pixel dimensions
// If one value (either width or height) are 0, then the other value is proportionally
//  calculated from the first.
// If both width and height are 0 or -100, no resizing takes place
// If quality = -1, use Java's default quality
public static byte[] imageToJPEGByteArray(Image aImage, int width, int height, int qualityPercent) throws IOException {
 byte[] imageBytes = new byte[0];
 

   float quality = 75 / 100f;

   BufferedImage destImage;
   destImage = SwingFXUtils.fromFXImage(aImage, null);
 
   // Output JPEG byte array
   ByteArrayOutputStream baos = new ByteArrayOutputStream();
 
   if(qualityPercent != -1) {
       // Start to create JPEG with quality option
       ImageWriter writer = null;
       Iterator iter = ImageIO.getImageWritersByFormatName("gif");
       if (iter.hasNext()) {
         writer = (ImageWriter) iter.next();
       }
       ImageOutputStream ios = ImageIO.createImageOutputStream(baos);
       writer.setOutput(ios); 
       ImageWriteParam iwparam = new JPEGImageWriteParam(Locale.getDefault());
       iwparam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
       iwparam.setCompressionQuality(quality);
       writer.write(null, new IIOImage(destImage, null, null), iwparam);
       ios.flush();
       writer.dispose();
       ios.close();
       // Done creating JPEG with quality option
   } else {
       // This one line below created a JPEG file without quality option
       ImageIO.write(destImage, "gif", baos);
   }
 
   baos.flush();
   imageBytes = baos.toByteArray();
   baos.close();
 
   // Done
   return imageBytes; 
}

#End If

but using the latest code... you upload - all working like you said :)
 
Top