B4J Library Java Image Processing with JH LABS

I have compiled the JH LABS java code into a library (jImageFilters.jar) that can be used in B4J. The attached project uses inline Java code to apply some of the available image filters by making use of the jImageFilters.jar. I have added 7 of the available filters in classes within the B4J project.

Posting the following:
1. B4J project demonstrating the filters that I have added
2. B4J library files - JH LABS code that was compiled into a jar (copy it to your B4J additional library folder
3. The JH LABS Java code as you will need it to reference public getters and setters that should be added to the inline Java code if you want to add some of the other filters that JH LABS provides for (there are 167 odd filters that you can make use of). See the classes that I have added and cross reference them to the JH LABS java code for those classes that I have added in the B4J project via inline Java code.

I will continue to add to this project as and when time permits (....and perhaps convert this into a fully fledged B4J library via a wrapper).

Will gladly assist anyone that needs help should you want to add any of the other available filters.

1.jpg


Sample code for the B4J main module (but also see the code in the B4J classes within the project - incorporating inline Java code):

B4X:
#Region  Project Attributes
    #MainFormWidth: 1000
    #MainFormHeight: 800
    #AdditionalJar: sqlite-jdbc-3.7.2

#End Region

Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Private ImageView1 As ImageView
    Private ImageView2 As ImageView
    Private ImageView3 As ImageView
    Private ImageView4 As ImageView   
    Private ImageView5 As ImageView       
    Private ImageView6 As ImageView
    Private ImageView7 As ImageView   
    Private ImageView8 As ImageView
    Private ImageView9 As ImageView
    Private ImageView10 As ImageView
    Private ImageView11 As ImageView
    Private ImageView12 As ImageView

    Private b As Image

    Private bf As boxBlurFilter
    Private ef As embossFilter
    Private crystal As crystallizeFilter
    Private tf As twirlFilter
    Private gsf As grayscaleFilter
    Private invf As invertFilter
    Private edgef1 As edgeFilter
    Private edgef2 As edgeFilter
    Private edgef3 As edgeFilter
    Private edgef4 As edgeFilter

End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.SetFormStyle("UNIFIED")
    MainForm.RootPane.LoadLayout("main") 'Load the layout file.
    MainForm.Show
    MainForm.BackColor = fx.Colors.ARGB(100,0,0,255)
'    MainForm.Initialize("", -1, -1)
    MainForm.WindowWidth = 1500
    MainForm.WindowLeft = 0
    MainForm.WindowTop = 0
    MainForm.WindowHeight = 1000


    b.Initialize(File.DirAssets,"Stitch1.jpg")
    ImageView1.SetImage(b)
    Log(ImageView1)
    Log(b)

    'Apply a Box Blur Filter
    bf.Initialize
    bf.HRadius = 0.0
    bf.VRadius = 5.0
    bf.Iterations = 15
    ImageView2.SetImage(bf.applyBoxBlurFilter(b))

    'Apply an Emboss Filter
    ef.Initialize
    ef.BumpHeight = 1.0
'    ef.Azimuth = 3.0
'    ef.Elevation = 30.0
    ef.Emboss = True
    ImageView3.SetImage(ef.applyEmbossFilter(b))

    'Apply Crystallize Filter
    crystal.Initialize
    crystal.Scale = 60
    crystal.EdgeThickness = 0.4
    crystal.FadeEdges = False
    crystal.EdgeColor = 0x44ff0000
    ImageView4.SetImage(crystal.applyCrystallizeFilter(b))   

    'Apply Twirl Filter
    tf.Initialize
    tf.Angle = 2.876
    tf.Radius = 500
    ImageView5.SetImage(tf.applyTwirlFilter(b))       

    'Apply Grayscale Filter
    gsf.Initialize
    ImageView6.SetImage(gsf.applyGrayscaleFilter(b))   

    'Apply Invert Filter
    invf.Initialize
    ImageView7.SetImage(invf.applyInvertFilter(b))   

    'Apply Edge Filter - PREWITT
    edgef1.Initialize
    edgef1.HEdgeMatrix = edgef1.PREWITT
    edgef1.VEdgeMatrix = edgef1.PREWITT
    ImageView8.SetImage(edgef1.applyEdgeFilter(b))       

    'Apply Edge Filter - SOBEL
    edgef2.Initialize
    edgef2.HEdgeMatrix = edgef2.SOBEL
    edgef2.VEdgeMatrix = edgef2.SOBEL
    ImageView9.SetImage(edgef2.applyEdgeFilter(b))   

    'Apply Edge Filter - ROBERTS
    edgef3.Initialize
    edgef3.HEdgeMatrix = edgef3.ROBERTS
    edgef3.VEdgeMatrix = edgef3.ROBERTS
    ImageView10.SetImage(edgef3.applyEdgeFilter(b))

    'Apply Edge Filter - FREI_CHEN
    edgef4.Initialize
    edgef4.HEdgeMatrix = edgef4.FREI_CHEN
    edgef4.VEdgeMatrix = edgef4.FREI_CHEN
    ImageView11.SetImage(edgef4.applyEdgeFilter(b))       

    'Apply Sobel Edge Filter, then Grayscale Filter, then Invert Filter
    ImageView12.SetImage(invf.applyInvertFilter(gsf.applyGrayscaleFilter(edgef2.applyEdgeFilter(b))))   

End Sub

Example of code in Class Module for the Edge Filter:

B4X:
'Class module
Sub Class_Globals
    Private fx As JFX
    Private nativeMe As JavaObject
 
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize
 
    nativeMe = Me

End Sub

public Sub applyEdgeFilter(img As Image) As Image
    Return nativeMe.RunMethod("applyEdgeFilter", Array(img))
End Sub

public Sub ROBERTS() As String
    Return "ROBERTS"
End Sub

public Sub PREWITT() As String
    Return "PREWITT"
End Sub

public Sub SOBEL() As String
    Return "SOBEL"
End Sub

public Sub FREI_CHEN() As String
    Return "FREI_CHEN"
End Sub

public Sub setVEdgeMatrix(vEdgeMatrix As String)
    nativeMe.RunMethod("setVEdgeMatrix", Array(vEdgeMatrix))
End Sub

public Sub setHEdgeMatrix(hEdgeMatrix As String)
    nativeMe.RunMethod("setHEdgeMatrix", Array(hEdgeMatrix))
End Sub

#If Java

import java.awt.image.BufferedImage;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import java.awt.Graphics2D;
import javafx.embed.swing.SwingFXUtils;
import com.jhlabs.image.EdgeFilter;
import com.jhlabs.image.ImageMath;
import java.awt.image.*;
import java.awt.*;
import java.awt.geom.*;

private String edgeFilterHtype = "SOBEL_H";
private String edgeFilterVtype = "SOBEL_V";

private float[] vEdgeMatrix = SOBEL_V;
private float[] hEdgeMatrix = SOBEL_H;
private static float R2 = (float)Math.sqrt(2);

public final static float[] ROBERTS_V = {
    0,  0, -1,
    0,  1,  0,
    0,  0,  0,
};
public final static float[] ROBERTS_H = {
    -1,  0,  0,
    0,  1,  0,
    0,  0,  0,
};
public final static float[] PREWITT_V = {
    -1,  0,  1,
    -1,  0,  1,
    -1,  0,  1,
};
public final static float[] PREWITT_H = {
    -1, -1, -1,
    0,  0,  0,
    1,  1,  1,
};
public final static float[] SOBEL_V = {
    -1,  0,  1,
    -2,  0,  2,
    -1,  0,  1,
};
public static float[] SOBEL_H = {
    -1, -2, -1,
    0,  0,  0,
    1,  2,  1,
};
public final static float[] FREI_CHEN_V = {
    -1,  0,  1,
    -R2,  0,  R2,
    -1,  0,  1,
};
public static float[] FREI_CHEN_H = {
    -1, -R2, -1,
    0,  0,  0,
    1,  R2,  1,
};


public Image applyEdgeFilter(Image img) {

    BufferedImage bmp = SwingFXUtils.fromFXImage(img, null); 
    BufferedImage srcImage = bmp;
    EdgeFilter ef = new EdgeFilter();
 
    ef.setVEdgeMatrix(vEdgeMatrix);
    ef.setHEdgeMatrix(hEdgeMatrix);
    srcImage = ef.filter(srcImage, srcImage);

    img = SwingFXUtils.toFXImage(srcImage, null);
    return img;
}


public void setVEdgeMatrix(String verticalEdgeMatrix) {
    if (verticalEdgeMatrix.equals("ROBERTS"))
        this.vEdgeMatrix = ROBERTS_V;
    if (verticalEdgeMatrix.equals("PREWITT"))
        this.vEdgeMatrix = PREWITT_V; 
    if (verticalEdgeMatrix.equals("SOBEL"))
        this.vEdgeMatrix = SOBEL_V;     
    if (verticalEdgeMatrix.equals("FREI_CHEN"))
        this.vEdgeMatrix = FREI_CHEN_V;                     
}

public void setHEdgeMatrix(String horizontalEdgeMatrix) {
    if (horizontalEdgeMatrix.equals("ROBERTS"))
        this.hEdgeMatrix = ROBERTS_H;
    if (horizontalEdgeMatrix.equals("PREWITT"))
        this.hEdgeMatrix = PREWITT_H; 
    if (horizontalEdgeMatrix.equals("SOBEL"))
        this.hEdgeMatrix = SOBEL_H;     
    if (horizontalEdgeMatrix.equals("FREI_CHEN"))
        this.hEdgeMatrix = FREI_CHEN_H;         
}


#End If
 

Attachments

  • jImageFiltersLibFiles.zip
    277 KB · Views: 645
  • b4jImageProcessing.zip
    279.6 KB · Views: 610
  • TheJavaCode.zip
    298.9 KB · Views: 613
Last edited:

Swissmade

Well-Known Member
Licensed User
Longtime User
Nice Library,

I like to use Gamma Filter but can not see how to do this:(

Thanks for help me out here.
 

Johan Schoeman

Expert
Licensed User
Longtime User
Nice Library,

I like to use Gamma Filter but can not see how to do this:(

Thanks for help me out here.
This adds the Gamma Filter - see the most right top and bottom images. The B4J library is unchanged (use the library files in post #1). Posting the updated B4J project.

gamma.png


Code in the B4J class (class gammaFilter)
B4X:
'Class module
Sub Class_Globals
    Private fx As JFX
    Private nativeMe As JavaObject
   
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize
   
    nativeMe = Me

End Sub

public Sub applyGammaFilter_1(img As Image) As Image
    Return nativeMe.RunMethod("applyGammaFilter_1", Array(img))
End Sub

public Sub applyGammaFilter_2(img As Image) As Image
    Return nativeMe.RunMethod("applyGammaFilter_2", Array(img))
End Sub


'Set the gamma levels.
'r = the gamma level For the red channel
'g = the gamma level For the blue channel
'b = the gamma level For the green channel
public Sub setGamma_1(r As Float, g As Float, b As Float)
    nativeMe.RunMethod("setGamma_1", Array(r,g,b))
End Sub


public Sub setGamma_2(rgb As Float)
    nativeMe.RunMethod("setGamma_2", Array(rgb))
End Sub

#If Java

import java.awt.image.BufferedImage;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import java.awt.Graphics2D;
import javafx.embed.swing.SwingFXUtils;
import com.jhlabs.image.GammaFilter;
import com.jhlabs.image.ImageMath;
import java.awt.image.*;
import java.awt.*;
import java.awt.geom.*;

private float rGamma, gGamma, bGamma, gamma = 0.0f;


    public Image applyGammaFilter_1(Image img) {

      BufferedImage bmp = SwingFXUtils.fromFXImage(img, null);   
      BufferedImage srcImage = bmp;
      GammaFilter gf = new GammaFilter();
      gf.setGamma(rGamma, gGamma, bGamma);
   
      srcImage = gf.filter(srcImage, srcImage);

      img = SwingFXUtils.toFXImage(srcImage, null);
      return img;
    }
   
    public Image applyGammaFilter_2(Image img) {

      BufferedImage bmp = SwingFXUtils.fromFXImage(img, null);   
      BufferedImage srcImage = bmp;
      GammaFilter gf = new GammaFilter();
      gf.setGamma(gamma);
   
      srcImage = gf.filter(srcImage, srcImage);

      img = SwingFXUtils.toFXImage(srcImage, null);
      return img;
    }   

    /**
     * Set the gamma levels.
     * @param rGamma the gamma level for the red channel
     * @param gGamma the gamma level for the blue channel
     * @param bGamma the gamma level for the green channel
     * @see #getGamma
     */
    public void setGamma_1(float rGamma, float gGamma, float bGamma) {
        this.rGamma = rGamma;
        this.gGamma = gGamma;
        this.bGamma = bGamma;
    }

    /**
     * Set the gamma level.
     * @param gamma the gamma level for all RGB channels
     * @see #getGamma
     */
    public void setGamma_2(float gamma) {
        this.gamma = gamma;
        BA.Log("gamma = " + gamma);
    }
   

#End If

Code in the Main activity(note there are two methods to set the Gamma Filter with:
B4X:
    'Apply Gamma Filter by specifying seperate values for each of R, G, and B channels
    gf1.Initialize
    gf1.setGamma_1(0.1, 0.5, 0.9)
    ImageView13.SetImage(gf1.applyGammaFilter_1(b))               
   
    'Apply Gamma Filter by specifying a single value that will be applied to the R, G, and B channels
    gf1.Initialize
    gf1.Gamma_2 = 0.5
    ImageView14.SetImage(gf1.applyGammaFilter_2(b))
 

Attachments

  • b4jImageProcessing.zip
    280.8 KB · Views: 513

Johan Schoeman

Expert
Licensed User
Longtime User
For those of you that want to add some additional filters and not sure how to do it, here is a "detail" explanation of how to for eg add the Weave Filter:

1. Download the Java Code that I have posted in post #1.
2. Down the B4J project in post #3
3. Add a new class in the B4J project called weaveFilter (Project -> Add New Module -> Class Module)
4. Copy the entire code of class gammaFilter to class weaveFilter
5. Class weaveFilter should now look like this:
B4X:
'Class module
Sub Class_Globals
    Private fx As JFX
    Private nativeMe As JavaObject

End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize

    nativeMe = Me

End Sub

public Sub applyGammaFilter_1(img As Image) As Image
    Return nativeMe.RunMethod("applyGammaFilter_1", Array(img))
End Sub

public Sub applyGammaFilter_2(img As Image) As Image
    Return nativeMe.RunMethod("applyGammaFilter_2", Array(img))
End Sub


'Set the gamma levels.
'r = the gamma level For the red channel
'g = the gamma level For the blue channel
'b = the gamma level For the green channel
public Sub setGamma_1(r As Float, g As Float, b As Float)
    nativeMe.RunMethod("setGamma_1", Array(r,g,b))
End Sub


public Sub setGamma_2(rgb As Float)
    nativeMe.RunMethod("setGamma_2", Array(rgb))
End Sub

#If Java

import java.awt.image.BufferedImage;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import java.awt.Graphics2D;
import javafx.embed.swing.SwingFXUtils;
import com.jhlabs.image.GammaFilter;
import com.jhlabs.image.ImageMath;
import java.awt.image.*;
import java.awt.*;
import java.awt.geom.*;

private float rGamma, gGamma, bGamma, gamma = 0.0f;


    public Image applyGammaFilter_1(Image img) {

      BufferedImage bmp = SwingFXUtils.fromFXImage(img, null);
      BufferedImage srcImage = bmp;
      GammaFilter gf = new GammaFilter();
      gf.setGamma(rGamma, gGamma, bGamma);

      srcImage = gf.filter(srcImage, srcImage);

      img = SwingFXUtils.toFXImage(srcImage, null);
      return img;
    }

    public Image applyGammaFilter_2(Image img) {

      BufferedImage bmp = SwingFXUtils.fromFXImage(img, null);
      BufferedImage srcImage = bmp;
      GammaFilter gf = new GammaFilter();
      gf.setGamma(gamma);

      srcImage = gf.filter(srcImage, srcImage);

      img = SwingFXUtils.toFXImage(srcImage, null);
      return img;
    }

    /**
     * Set the gamma levels.
     * @param rGamma the gamma level for the red channel
     * @param gGamma the gamma level for the blue channel
     * @param bGamma the gamma level for the green channel
     * @see #getGamma
     */
    public void setGamma_1(float rGamma, float gGamma, float bGamma) {
        this.rGamma = rGamma;
        this.gGamma = gGamma;
        this.bGamma = bGamma;
    }

    /**
     * Set the gamma level.
     * @param gamma the gamma level for all RGB channels
     * @see #getGamma
     */
    public void setGamma_2(float gamma) {
        this.gamma = gamma;
    }

#End If
6. Now you need open the Java code and browse to folder src\com\jhlabs\image. In this folder is a java class called WeaveFilter.java. Open it with something such as Notepad or Notepad++.
7. The code inside this java class looks as follows:
B4X:
/*
Copyright 2006 Jerry Huxtable

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package com.jhlabs.image;

import java.awt.*;
import java.awt.image.*;

public class WeaveFilter extends PointFilter {

    private float xWidth = 16;
    private float yWidth = 16;
    private float xGap = 6;
    private float yGap = 6;
    private int rows = 4;
    private int cols = 4;
    private int rgbX = 0xffff8080;
    private int rgbY = 0xff8080ff;
    private boolean useImageColors = true;
    private boolean roundThreads = false;
    private boolean shadeCrossings = true;

    public int[][] matrix = {
        { 0, 1, 0, 1 },
        { 1, 0, 1, 0 },
        { 0, 1, 0, 1 },
        { 1, 0, 1, 0 },
    };

    public WeaveFilter() {
    }

    public void setXGap(float xGap) {
        this.xGap = xGap;
    }

    public void setXWidth(float xWidth) {
        this.xWidth = xWidth;
    }

    public float getXWidth() {
        return xWidth;
    }

    public void setYWidth(float yWidth) {
        this.yWidth = yWidth;
    }

    public float getYWidth() {
        return yWidth;
    }

    public float getXGap() {
        return xGap;
    }

    public void setYGap(float yGap) {
        this.yGap = yGap;
    }

    public float getYGap() {
        return yGap;
    }

    public void setCrossings(int[][] matrix) {
        this.matrix = matrix;
    }

    public int[][] getCrossings() {
        return matrix;
    }

    public void setUseImageColors(boolean useImageColors) {
        this.useImageColors = useImageColors;
    }

    public boolean getUseImageColors() {
        return useImageColors;
    }

    public void setRoundThreads(boolean roundThreads) {
        this.roundThreads = roundThreads;
    }

    public boolean getRoundThreads() {
        return roundThreads;
    }

    public void setShadeCrossings(boolean shadeCrossings) {
        this.shadeCrossings = shadeCrossings;
    }

    public boolean getShadeCrossings() {
        return shadeCrossings;
    }

    public int filterRGB(int x, int y, int rgb) {
        x += xWidth+xGap/2;
        y += yWidth+yGap/2;
        float nx = ImageMath.mod(x, xWidth+xGap);
        float ny = ImageMath.mod(y, yWidth+yGap);
        int ix = (int)(x / (xWidth+xGap));
        int iy = (int)(y / (yWidth+yGap));
        boolean inX = nx < xWidth;
        boolean inY = ny < yWidth;
        float dX, dY;
        float cX, cY;
        int lrgbX, lrgbY;

        if (roundThreads) {
            dX = Math.abs(xWidth/2-nx) / xWidth / 2;
            dY = Math.abs(yWidth/2-ny) / yWidth / 2;
        } else {
            dX = dY = 0;
        }

        if (shadeCrossings) {
            cX = ImageMath.smoothStep(xWidth/2, xWidth/2+xGap, Math.abs(xWidth/2-nx));
            cY = ImageMath.smoothStep(yWidth/2, yWidth/2+yGap, Math.abs(yWidth/2-ny));
        } else {
            cX = cY = 0;
        }

        if (useImageColors) {
            lrgbX = lrgbY = rgb;
        } else {
            lrgbX = rgbX;
            lrgbY = rgbY;
        }
        int v;
        int ixc = ix % cols;
        int iyr = iy % rows;
        int m = matrix[iyr][ixc];
        if (inX) {
            if (inY) {
                v = m == 1 ? lrgbX : lrgbY;
                v = ImageMath.mixColors(2 * (m == 1 ? dX : dY), v, 0xff000000);
            } else {
                if (shadeCrossings) {
                    if (m != matrix[(iy+1) % rows][ixc]) {
                        if (m == 0)
                            cY = 1-cY;
                        cY *= 0.5f;
                        lrgbX = ImageMath.mixColors(cY, lrgbX, 0xff000000);
                    } else if (m == 0)
                        lrgbX = ImageMath.mixColors(0.5f, lrgbX, 0xff000000);
                }
                v = ImageMath.mixColors(2 * dX, lrgbX, 0xff000000);
            }
        } else if (inY) {
            if (shadeCrossings) {
                if (m != matrix[iyr][(ix+1) % cols]) {
                    if (m == 1)
                        cX = 1-cX;
                    cX *= 0.5f;
                    lrgbY = ImageMath.mixColors(cX, lrgbY, 0xff000000);
                } else if (m == 1)
                    lrgbY = ImageMath.mixColors(0.5f, lrgbY, 0xff000000);
            }
            v = ImageMath.mixColors(2 * dY, lrgbY, 0xff000000);
        } else
            v = 0x00000000;
        return v;
    }

    public String toString() {
        return "Texture/Weave...";
    }

}
8. In this class we are interested in the private variables that have been declared as well as some of the public methods:
Private variables:
B4X:
private float xWidth = 16;
private float yWidth = 16;
private float xGap = 6;
private float yGap = 6;
private int rows = 4;
private int cols = 4;
private int rgbX = 0xffff8080;
private int rgbY = 0xff8080ff;
private boolean useImageColors = true;
private boolean roundThreads = false;
private boolean shadeCrossings = true;

Public methods (getters and setters):
B4X:
    public void setXGap(float xGap) {  //WILL USE
        this.xGap = xGap;
    }

    public void setXWidth(float xWidth) { //WILL USE
        this.xWidth = xWidth;
    }

    public float getXWidth() {
        return xWidth;
    }

    public void setYWidth(float yWidth) {  //WILL USE
        this.yWidth = yWidth;
    }

    public float getYWidth() {
        return yWidth;
    }

    public float getXGap() {
        return xGap;
    }

    public void setYGap(float yGap) {  //WILL USE
        this.yGap = yGap;
    }

    public float getYGap() {
        return yGap;
    }

    public void setCrossings(int[][] matrix) {
        this.matrix = matrix;
    }

    public int[][] getCrossings() {
        return matrix;
    }

    public void setUseImageColors(boolean useImageColors) {  //WILL USE
        this.useImageColors = useImageColors;
    }

    public boolean getUseImageColors() {
        return useImageColors;
    }

    public void setRoundThreads(boolean roundThreads) {  //WILL USE
        this.roundThreads = roundThreads;
    }

    public boolean getRoundThreads() {
        return roundThreads;
    }

    public void setShadeCrossings(boolean shadeCrossings) { //WILL USE
        this.shadeCrossings = shadeCrossings;
    }

    public boolean getShadeCrossings() {
        return shadeCrossings;
    }

9. We will not be using all the public methods. Only the 7 that have been marked as //WILL USE.

10. Let's first start by cleaning up our new B4J class (weaveFilter) that at present contains the code of B4J class gammaFilter after the copy that we have done in point 4 above.

11. Remove/delete everything in red from B4J class weaveFilter:

'Class module
Sub Class_Globals
Private fx As JFX
Private nativeMe As JavaObject​
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize
nativeMe = Me​
End Sub

public Sub applyGammaFilter_1(img As Image) As Image
Return nativeMe.RunMethod("applyGammaFilter_1", Array(img))
End Sub

public Sub applyGammaFilter_2(img As Image) As Image

Return nativeMe.RunMethod("applyGammaFilter_2", Array(img))
End Sub

'Set the gamma levels.
'r = the gamma level For the red channel
'g = the gamma level For the blue channel
'b = the gamma level For the green channel
public Sub setGamma_1(r As Float, g As Float, b As Float)

nativeMe.RunMethod("setGamma_1", Array(r,g,b))
End Sub

public Sub setGamma_2(rgb As Float)

nativeMe.RunMethod("setGamma_2", Array(rgb))
End Sub

#If Java

import java.awt.image.BufferedImage;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import java.awt.Graphics2D;
import javafx.embed.swing.SwingFXUtils;
import com.jhlabs.image.GammaFilter;
import com.jhlabs.image.ImageMath;
import java.awt.image.*;
import java.awt.*;
import java.awt.geom.*;

private float rGamma, gGamma, bGamma, gamma = 0.0f;

public Image applyGammaFilter_1(Image img) {
BufferedImage bmp = SwingFXUtils.fromFXImage(img, null);
BufferedImage srcImage = bmp;
GammaFilter gf = new GammaFilter();
gf.setGamma(rGamma, gGamma, bGamma);
srcImage = gf.filter(srcImage, srcImage);
img = SwingFXUtils.toFXImage(srcImage, null);
return img;​
}

public Image applyGammaFilter_2(Image img) {
BufferedImage bmp = SwingFXUtils.fromFXImage(img, null);
BufferedImage srcImage = bmp;
GammaFilter gf = new GammaFilter();
gf.setGamma(gamma);
srcImage = gf.filter(srcImage, srcImage);
img = SwingFXUtils.toFXImage(srcImage, null);
return img;
}

/**
* Set the gamma levels.
* @param rGamma the gamma level for the red channel
* @param gGamma the gamma level for the blue channel
* @param bGamma the gamma level for the green channel
* @see #getGamma
*/
public void setGamma_1(float rGamma, float gGamma, float bGamma) {

this.rGamma = rGamma;
this.gGamma = gGamma;
this.bGamma = bGamma;
}

/**
* Set the gamma level.
* @param gamma the gamma level for all RGB channels
* @see #getGamma
*/
public void setGamma_2(float gamma) {

this.gamma = gamma;
}

#End If

12. The class in the Java code that we are working with is called WeaveFilter. We need to import this class into our inline Java code. Replace
import com.jhlabs.image.GammaFilter;
with
import com.jhlabs.image.WeaveFilter;
in the inline Java code of B4J class weaveFilter

13. Next we change method applyGammaFilter_1 in our inline Java code from:

public Image applyGammaFilter_1(Image img) {
BufferedImage bmp = SwingFXUtils.fromFXImage(img, null);
BufferedImage srcImage = bmp;
GammaFilter gf = new GammaFilter();
gf.setGamma(rGamma, gGamma, bGamma);
srcImage = gf.filter(srcImage, srcImage);
img = SwingFXUtils.toFXImage(srcImage, null);
return img;​
}

to

public Image applyWeaveFilter(Image img) {
BufferedImage bmp = SwingFXUtils.fromFXImage(img, null);
BufferedImage srcImage = bmp;
WeaveFilter wv = new WeaveFilter();
srcImage = wv.filter(srcImage, srcImage);
img = SwingFXUtils.toFXImage(srcImage, null);
return img;​
}

15. We have now eliminated all the "noise" that was carried over from B4J class gammaFilter when we copied it to B4J class weaveFilter.
16. Now we need to go add our setter to the inline Java code so that we can set the properties of the weave filter from our B4J project's main activity.

....continuing in post #6....
 
Last edited:

Johan Schoeman

Expert
Licensed User
Longtime User
17. Our B4J weaveFilter class should at this stage look as follows:

B4X:
'Class module
Sub Class_Globals
    Private fx As JFX
    Private nativeMe As JavaObject


End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize

    nativeMe = Me

End Sub


#If Java

import java.awt.image.BufferedImage;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import java.awt.Graphics2D;
import javafx.embed.swing.SwingFXUtils;
import com.jhlabs.image.WeaveFilter;
import com.jhlabs.image.ImageMath;
import java.awt.image.*;
import java.awt.*;
import java.awt.geom.*;


    public Image applyWeaveFilter(Image img) {

      BufferedImage bmp = SwingFXUtils.fromFXImage(img, null);
      BufferedImage srcImage = bmp;
      WeaveFilter wv = new WeaveFilter();

      srcImage = wv.filter(srcImage, srcImage);

      img = SwingFXUtils.toFXImage(srcImage, null);
      return img;
    }



#End If

18. Now we need to add the public methods from point (9) in the previous post to our inline Java code. We also need to add the private variables from the Java class (WeaveFilter.java) to our inline Java code. The inline Java code in our B4J class weaveFilter should look as follows once we have added the public methods (note that I have commented out those public methods that we are not going to access from the B4J project's main activity with //:
B4X:
#If Java

import java.awt.image.BufferedImage;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import java.awt.Graphics2D;
import javafx.embed.swing.SwingFXUtils;
import com.jhlabs.image.WeaveFilter;
import com.jhlabs.image.ImageMath;
import java.awt.image.*;
import java.awt.*;
import java.awt.geom.*;


private float xWidth = 16;
private float yWidth = 16;
private float xGap = 6;
private float yGap = 6;
private int rows = 4;
private int cols = 4;
private int rgbX = 0xffff8080;
private int rgbY = 0xff8080ff;
private boolean useImageColors = true;
private boolean roundThreads = true;
private boolean shadeCrossings = true;


    public Image applyWeaveFilter(Image img) {

      BufferedImage bmp = SwingFXUtils.fromFXImage(img, null);
      BufferedImage srcImage = bmp;
      WeaveFilter wv = new WeaveFilter();
  
      wv.setXGap(xGap);
      wv.setYGap(yGap);
      wv.setXWidth(xWidth);
      wv.setYWidth(yWidth);
      wv.setUseImageColors(useImageColors);
      wv.setRoundThreads(roundThreads);
      wv.setShadeCrossings(shadeCrossings);

      srcImage = wv.filter(srcImage, srcImage);

      img = SwingFXUtils.toFXImage(srcImage, null);
      return img;
    }



    public void setXGap(float xGap) {
        this.xGap = xGap;
    }

    public void setXWidth(float xWidth) {
        this.xWidth = xWidth;
    }

//    public float getXWidth() {
//        return xWidth;
//    }

    public void setYWidth(float yWidth) {
        this.yWidth = yWidth;
    }

//    public float getYWidth() {
//        return yWidth;
//    }

//    public float getXGap() {
//        return xGap;
//    }

    public void setYGap(float yGap) {
        this.yGap = yGap;
    }

//    public float getYGap() {
//        return yGap;
//    }

//    public void setCrossings(int[][] matrix) {
//        this.matrix = matrix;
//    }

//    public int[][] getCrossings() {
//        return matrix;
//    }

    public void setUseImageColors(boolean useImageColors) {
        this.useImageColors = useImageColors;
    }

//    public boolean getUseImageColors() {
//        return useImageColors;
//    }

    public void setRoundThreads(boolean roundThreads) {
        this.roundThreads = roundThreads;
    }

//    public boolean getRoundThreads() {
//        return roundThreads;
//    }

    public void setShadeCrossings(boolean shadeCrossings) {
        this.shadeCrossings = shadeCrossings;
    }

//    public boolean getShadeCrossings() {
//        return shadeCrossings;
//    }


#End If

19. Now we need to add some B4J subs to our B4J class (weaveFilter) that we can access from our project's main activity. These B4J subs will pass the necessary values to the public methods in our inline Java code. There should be a B4J sub in B4J class weaveFilter for each of the public methods in the inline Java code that we want to make use of.
20. For the method....
B4X:
    public void setXGap(float xGap) {

        this.xGap = xGap;

    }
....in our inline Java code we add a B4J sub to out B4J class...
B4X:
public Sub setXGap(xgap As Float)
    nativeMe.RunMethod("setXGap", Array(xgap))
End Sub

21. Note that the type of parameter of the java method and that of the B4J sub match one another - in this case type float
22. setXGap should now be visible from our main activity
23. nativeMe.RunMethod("setXGap", Array(xgap)) is the call from the B4J sub in our B4J class to the public method setXGap in the inline Java code.
24. We do this for all the public methods that we want to access from our projects main activity i.e add a B4J sub that in turn will access the appropriate public method in the inline Java code.
25. Our B4J class (weaveFilter) should look as follows once we have added all the B4J subs to it:
B4X:
'Class module
Sub Class_Globals
    Private fx As JFX
    Private nativeMe As JavaObject


End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize

    nativeMe = Me

End Sub

public Sub applyWeaveFilter(img As Image) As Image
    Return nativeMe.RunMethod("applyWeaveFilter", Array(img))
End Sub


public Sub setXGap(xgap As Float)
    nativeMe.RunMethod("setXGap", Array(xgap))
End Sub

public Sub setXWidth(xwidth As Float)
    nativeMe.RunMethod("setXWidth", Array(xwidth))
End Sub

public Sub setYWidth(ywidth As Float)
    nativeMe.RunMethod("setYWidth", Array(ywidth))
End Sub

public Sub setYGap(ygap As Float)
    nativeMe.RunMethod("setYGap", Array(ygap))
End Sub

public Sub setUseImageColors(useimagecolors As Boolean)
    nativeMe.RunMethod("setUseImageColors", Array(useimagecolors))
End Sub

public Sub setRoundThreads(roundthreads As Boolean)
    nativeMe.RunMethod("setRoundThreads", Array(roundthreads))
End Sub

public Sub setShadeCrossings(shadecrossings As Boolean)
    nativeMe.RunMethod("setShadeCrossings", Array(shadecrossings))
End Sub



#If Java

import java.awt.image.BufferedImage;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import java.awt.Graphics2D;
import javafx.embed.swing.SwingFXUtils;
import com.jhlabs.image.WeaveFilter;
import com.jhlabs.image.ImageMath;
import java.awt.image.*;
import java.awt.*;
import java.awt.geom.*;


private float xWidth = 16;
private float yWidth = 16;
private float xGap = 6;
private float yGap = 6;
private int rows = 4;
private int cols = 4;
private int rgbX = 0xffff8080;
private int rgbY = 0xff8080ff;
private boolean useImageColors = true;
private boolean roundThreads = true;
private boolean shadeCrossings = true;


    public Image applyWeaveFilter(Image img) {

      BufferedImage bmp = SwingFXUtils.fromFXImage(img, null);
      BufferedImage srcImage = bmp;
      WeaveFilter wv = new WeaveFilter();

      srcImage = wv.filter(srcImage, srcImage);

      img = SwingFXUtils.toFXImage(srcImage, null);
      return img;
    }



    public void setXGap(float xGap) {
        this.xGap = xGap;
    }

    public void setXWidth(float xWidth) {
        this.xWidth = xWidth;
    }

//    public float getXWidth() {
//        return xWidth;
//    }

    public void setYWidth(float yWidth) {
        this.yWidth = yWidth;
    }

//    public float getYWidth() {
//        return yWidth;
//    }

//    public float getXGap() {
//        return xGap;
//    }

    public void setYGap(float yGap) {
        this.yGap = yGap;
    }

//    public float getYGap() {
//        return yGap;
//    }

//    public void setCrossings(int[][] matrix) {
//        this.matrix = matrix;
//    }

//    public int[][] getCrossings() {
//        return matrix;
//    }

    public void setUseImageColors(boolean useImageColors) {
        this.useImageColors = useImageColors;
    }

//    public boolean getUseImageColors() {
//        return useImageColors;
//    }

    public void setRoundThreads(boolean roundThreads) {
        this.roundThreads = roundThreads;
    }

//    public boolean getRoundThreads() {
//        return roundThreads;
//    }

    public void setShadeCrossings(boolean shadeCrossings) {
        this.shadeCrossings = shadeCrossings;
    }

//    public boolean getShadeCrossings() {
//        return shadeCrossings;
//    }
 
#End If

26. Next we need to add some inline Java code to method applyWeaveFilter of our inline Java code. We add the following to give our setter effect when applyWeaveFilter is called from our main activity:
B4X:
    public Image applyWeaveFilter(Image img) {

      BufferedImage bmp = SwingFXUtils.fromFXImage(img, null);  
      BufferedImage srcImage = bmp;
      WeaveFilter wv = new WeaveFilter();
    
      wv.setXGap(xGap);
      wv.setYGap(yGap);
      wv.setXWidth(xWidth);
      wv.setYWidth(yWidth);
      wv.setUseImageColors(useImageColors);
      wv.setRoundThreads(roundThreads);
      wv.setShadeCrossings(shadeCrossings);
  
      srcImage = wv.filter(srcImage, srcImage);

      img = SwingFXUtils.toFXImage(srcImage, null);
      return img;
    }

27. Note that the calls that we have now added matches the corresponding methods in the Java code of class WeaveFilter.java
28. We can clean up our inline java code by removing all the methods that we have commented out in point 25 above (i.e everything commented out with //). One done our inline Java code will look as follows:
B4X:
#If Java

import java.awt.image.BufferedImage;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import java.awt.Graphics2D;
import javafx.embed.swing.SwingFXUtils;
import com.jhlabs.image.WeaveFilter;
import com.jhlabs.image.ImageMath;
import java.awt.image.*;
import java.awt.*;
import java.awt.geom.*;


private float xWidth = 16;
private float yWidth = 16;
private float xGap = 6;
private float yGap = 6;
private int rows = 4;
private int cols = 4;
private int rgbX = 0xffff8080;
private int rgbY = 0xff8080ff;
private boolean useImageColors = true;
private boolean roundThreads = true;
private boolean shadeCrossings = true;


    public Image applyWeaveFilter(Image img) {

      BufferedImage bmp = SwingFXUtils.fromFXImage(img, null);  
      BufferedImage srcImage = bmp;
      WeaveFilter wv = new WeaveFilter();
    
      wv.setXGap(xGap);
      wv.setYGap(yGap);
      wv.setXWidth(xWidth);
      wv.setYWidth(yWidth);
      wv.setUseImageColors(useImageColors);
      wv.setRoundThreads(roundThreads);
      wv.setShadeCrossings(shadeCrossings);
  
      srcImage = wv.filter(srcImage, srcImage);

      img = SwingFXUtils.toFXImage(srcImage, null);
      return img;
    }
  
    public void setXGap(float xGap) {
        this.xGap = xGap;
    }

    public void setXWidth(float xWidth) {
        this.xWidth = xWidth;
    }

    public void setYWidth(float yWidth) {
        this.yWidth = yWidth;
    }

    public void setYGap(float yGap) {
        this.yGap = yGap;
    }

    public void setUseImageColors(boolean useImageColors) {
        this.useImageColors = useImageColors;
    }

    public void setRoundThreads(boolean roundThreads) {
        this.roundThreads = roundThreads;
    }

    public void setShadeCrossings(boolean shadeCrossings) {
        this.shadeCrossings = shadeCrossings;
    }
  

#End If
29. Our entire B4J class (weaveFilter) should now look as follows:
B4X:
'Class module
Sub Class_Globals
    Private fx As JFX
    Private nativeMe As JavaObject
  
  
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize
  
    nativeMe = Me

End Sub

public Sub applyWeaveFilter(img As Image) As Image
    Return nativeMe.RunMethod("applyWeaveFilter", Array(img))
End Sub


public Sub setXGap(xgap As Float)
    nativeMe.RunMethod("setXGap", Array(xgap))
End Sub

public Sub setXWidth(xwidth As Float)
    nativeMe.RunMethod("setXWidth", Array(xwidth))
End Sub

public Sub setYWidth(ywidth As Float)
    nativeMe.RunMethod("setYWidth", Array(ywidth))
End Sub

public Sub setYGap(ygap As Float)
    nativeMe.RunMethod("setYGap", Array(ygap))
End Sub

public Sub setUseImageColors(useimagecolors As Boolean)
    nativeMe.RunMethod("setUseImageColors", Array(useimagecolors))
End Sub

public Sub setRoundThreads(roundthreads As Boolean)
    nativeMe.RunMethod("setRoundThreads", Array(roundthreads))
End Sub

public Sub setShadeCrossings(shadecrossings As Boolean)
    nativeMe.RunMethod("setShadeCrossings", Array(shadecrossings))
End Sub



#If Java

import java.awt.image.BufferedImage;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import java.awt.Graphics2D;
import javafx.embed.swing.SwingFXUtils;
import com.jhlabs.image.WeaveFilter;
import com.jhlabs.image.ImageMath;
import java.awt.image.*;
import java.awt.*;
import java.awt.geom.*;


private float xWidth = 16;
private float yWidth = 16;
private float xGap = 6;
private float yGap = 6;
private int rows = 4;
private int cols = 4;
private int rgbX = 0xffff8080;
private int rgbY = 0xff8080ff;
private boolean useImageColors = true;
private boolean roundThreads = true;
private boolean shadeCrossings = true;


    public Image applyWeaveFilter(Image img) {

      BufferedImage bmp = SwingFXUtils.fromFXImage(img, null);  
      BufferedImage srcImage = bmp;
      WeaveFilter wv = new WeaveFilter();
    
      wv.setXGap(xGap);
      wv.setYGap(yGap);
      wv.setXWidth(xWidth);
      wv.setYWidth(yWidth);
      wv.setUseImageColors(useImageColors);
      wv.setRoundThreads(roundThreads);
      wv.setShadeCrossings(shadeCrossings);
  
      srcImage = wv.filter(srcImage, srcImage);

      img = SwingFXUtils.toFXImage(srcImage, null);
      return img;
    }
  
    public void setXGap(float xGap) {
        this.xGap = xGap;
    }

    public void setXWidth(float xWidth) {
        this.xWidth = xWidth;
    }

    public void setYWidth(float yWidth) {
        this.yWidth = yWidth;
    }

    public void setYGap(float yGap) {
        this.yGap = yGap;
    }

    public void setUseImageColors(boolean useImageColors) {
        this.useImageColors = useImageColors;
    }

    public void setRoundThreads(boolean roundThreads) {
        this.roundThreads = roundThreads;
    }

    public void setShadeCrossings(boolean shadeCrossings) {
        this.shadeCrossings = shadeCrossings;
    }
  

#End If

....continuing in post #7....
 
Last edited:

Johan Schoeman

Expert
Licensed User
Longtime User
30. Our B4J project class weaveFilter is now completed. Now we need to add a Imageview and Label to our project in Designer.
31. In my project I have added ImageView17 and Label17. I have set the text of Label17 in Designer and added:
Private ImageView17 As ImageView
Private wf1 As weaveFilter
to the B4J project's main activity.
32. Now we need to add some B4J code to our main activity to use our B4J class weaveFilter. So, in the main activity I added:
B4X:
    'Weave Filter
    wf1.Initialize
    ImageView17.SetImage(wf1.applyWeaveFilter(b))

33. I have not added any code to make use of our setter in the inline Java code. It will use the default values that was declared with the private variables in our inline Java code i.e:
B4X:
private float xWidth = 16;
private float yWidth = 16;
private float xGap = 6;
private float yGap = 6;
private int rows = 4;
private int cols = 4;
private int rgbX = 0xffff8080;
private int rgbY = 0xff8080ff;
private boolean useImageColors = true;
private boolean roundThreads = true;
private boolean shadeCrossings = true;

34. If we run our project as is (i.e without making use of any of the setters in our inline Java code) we should get the below pic:

weave.jpg


35. To now use any of the setters that we have created we should use the following code in our project's main activity (pass the correct type for each of them):
B4X:
wf1.RoundThreads = .....
wf1.ShadeCrossings = .....
wf1.UseImageColors = .....
wf1.XWidth = .....
wf1.YGap = .....
wf1.YWidth = .....
wf1.XGap = .....  
ImageView17.SetImage(wf1.applyWeaveFilter(b))

That should change the appearance of the pic depending on the values passed from the code in your main activity.

The remainder of the filters in the project can be added to the B4J project in a similar manner.

Enjoy!
 
Last edited:

Johan Schoeman

Expert
Licensed User
Longtime User
I saw this very nice posting a few days ago:


So, have come across this posting on the web that uses JH Labs and seeing that I have already done a lot of work on it (see earlier in this thread including the library files in post #1 of this thread) I though I would see what JH Labs can do as far as Pencil Sketches are concerned. Attached the B4J project.

One of my employees picked up the kitten outside our work premises and brought it to me - it was 14 Feb 2023 and I took it home for my wife as a Valentine's Day Gift. He was a probably about 3 to 4 weeks old at that time. He is called Nunus but also reacts to the following names:

1. Vetgat - fat bum
2. Buffel - Buffalo
3. Nunu Bug - what my wife calls him
4. Baksteen - Brick (because he as as big and heavy as a brick)
5. Gesiggie - Cutie Face

....as long as what there is food to eat he is a happy CAT and you can call him whatever you like as far as what he is concerned.

Nunu.png
 

Attachments

  • b4jPencilSketch.zip
    319.5 KB · Views: 6
Last edited:
Top