Kotlin with OpenCV 4.10 on macOS terminal

#1

Hi!
I’m new to Kotlin (and also OpenCV) and can’t get a simple library test to work (either on macOS or linux).

The test code is:

package org.mytest
import org.opencv.core.Core
import org.opencv.core.CvType
import org.opencv.core.Mat


fun main(args: Array<String>) {
    System.loadLibrary(Core.NATIVE_LIBRARY_NAME)

    val mat = Mat.eye(5, 5, CvType.CV_8UC1)
    println("mat = ${mat.dump()}")
}

After use:

kotlinc test-opencv.kt -classpath /usr/local/opt/opencv/share/java/opencv4/opencv-410.jar -include-runtime -d test-opencv.jar

I get the jar file.

But then, when I try:

java -Djava.library.path=/usr/local/opt/opencv/share/java/opencv4 -cp /usr/local/opt/opencv/share/java/opencv4/opencv-410.jar:. -jar test-opencv.jar

I got:

Exception in thread "main" java.lang.NoClassDefFoundError: org/opencv/core/Core
	at org.mytest.Test_opencvKt.main(test-opencv.kt:9)
Caused by: java.lang.ClassNotFoundException: org.opencv.core.Core
	at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 1 more

Any ideas?

#2

Use Gradle or an IDE to load the classpath. If you need a self contained jar with all dependencies use the shadow plugin for Gradle.
Otherwise if you just need a an easy script include the OpenCV Maven dependencies with annotations and let the compiler do the job:

@file:DependsOn("org.openpnp:opencv:3.4.2-1")

import org.opencv.core.Core
import org.opencv.core.CvType
import org.opencv.core.Mat

System.loadLibrary(Core.NATIVE_LIBRARY_NAME)

val mat = Mat.eye(5, 5, CvType.CV_8UC1)
println("mat = ${mat.dump()}")

Save it into something like MyScript.kts and then launch:

kotlinc -script MyScript.kts

This Groovy Grapes-like feature for kotlin scripting is darn cool!

You could even add the shebang and give it execution privileges so you can run it easily:

#!/path/to/kotlinc -script 
// remember to change the path to the correct location of kotlinc!!

@file:DependsOn("org.openpnp:opencv:3.4.2-1")

import org.opencv.core.Core
import org.opencv.core.CvType
import org.opencv.core.Mat

System.loadLibrary(Core.NATIVE_LIBRARY_NAME)

val mat = Mat.eye(5, 5, CvType.CV_8UC1)
println("mat = ${mat.dump()}")

Then you can just call ./MyScript.kts and it will work. But still, with an IDE is way more easier lol.
There’s the link to install Kotlin using HomeBrew.

#3

@lamba92, thanks for your help! :slightly_smiling_face:

I’m trying to stay as simple (less tools) as possible, working only at command line with vi and a few files.

Look this other sample (pure Java):

$ cat ShowVersions.java 

import org.opencv.core.Core;

public class ShowVersions {

  static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
  }

  public static void main(String[] args) {

System.out.println("Hello OpenCV " + Core.VERSION + "!");

System.out.println("Hello to you too, Java " + System.getProperty("java.version") + "!");
  }

}

$ javac -cp /usr/share/java/opencv4/opencv-410.jar ShowVersions.java

$ ll
total 8,0K
-rw-r--r-- 1 peracchi peracchi 1,2K mai 19 17:09 ShowVersions.class
-rw-r--r-- 1 peracchi peracchi  333 mai 19 17:07 ShowVersions.java

$ java -Djava.library.path=/usr/lib -cp /usr/share/java/opencv4/opencv-410.jar:. ShowVersions

Hello OpenCV 4.1.0!
Hello to you too, Java 12.0.1!

Trying the same thing in “pure Kotlin”:

$ cat ShowVersions.kt 

import org.opencv.core.Core

fun main(args: Array<String>) {

  System.loadLibrary(Core.NATIVE_LIBRARY_NAME)

  println("Hello OpenCV " + Core.VERSION + "!")

  println("Hello to you too, Java " + System.getProperty("java.version") + "!")

}

$ kotlinc ShowVersions.kt -classpath /usr/share/java/opencv4/opencv-410.jar -include-runtime -d ShowVersions.jar

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.intellij.openapi.util.JDOMUtil$2 to constructor com.sun.xml.internal.stream.XMLInputFactoryImpl()
WARNING: Please consider reporting this to the maintainers of com.intellij.openapi.util.JDOMUtil$2
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

$ ll
total 1,3M
-rw-r--r-- 1 peracchi peracchi 1,3M mai 19 17:18 ShowVersions.jar
-rw-r--r-- 1 peracchi peracchi  242 mai 19 17:16 ShowVersions.kt

$ java -Djava.library.path=/usr/lib -cp /usr/share/java/opencv4/opencv-410.jar:. -jar ShowVersions.jar

Exception in thread "main" java.lang.NoClassDefFoundError: org/opencv/core/Core
        at ShowVersionsKt.main(ShowVersions.kt:5)
Caused by: java.lang.ClassNotFoundException: org.opencv.core.Core
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
        ... 1 more

I thought that I could do the same thing done in Java with Kotlin (without need a IDE, Gradle, Maven or another tool).

Some additional infos on the linux computer I’m trying this code:

$ uname -a
Linux nitro-5 5.1.1-2-MANJARO #1 SMP PREEMPT Sat May 11 12:12:50 UTC 2019 x86_64 GNU/Linux

$ java -version
java version "12.0.1" 2019-04-16
Java(TM) SE Runtime Environment (build 12.0.1+12)
Java HotSpot(TM) 64-Bit Server VM (build 12.0.1+12, mixed mode, sharing)

$ javac -version
javac 12.0.1

$ kotlin -version
Kotlin version 1.3.31-release-197 (JRE 12.0.1+12)

$ kotlinc -version
info: kotlinc-jvm 1.3.31 (JRE 12.0.1+12)
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.intellij.openapi.util.JDOMUtil$2 to constructor com.sun.xml.internal.stream.XMLInputFactoryImpl()
WARNING: Please consider reporting this to the maintainers of com.intellij.openapi.util.JDOMUtil$2
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
#4

Many things changed in Sun libs in Java 9+. Try using the JDK 8.

Still, I suppose you already installed Kotlin in your machine, you should try running it as a script like:

#!/usr/bin/env kotlinc -script

@file:DependsOn("org.openpnp:opencv:3.4.2-1")

import org.opencv.core.Core

System.loadLibrary(Core.NATIVE_LIBRARY_NAME)
println("Hello OpenCV " + Core.VERSION + "!")
println("Hello to you too, Java " + System.getProperty("java.version") + "!")

Then:

chmod +x ShowVersion.kts
./ShowVersion.kts

All the possible misconfigured classpath problems would be handled by the Kotlin compiler so if it does not work is probably the JDK version.

I had problems with the Sun XML libs in the past. When using JDK 9+ I’ve always looked up the Maven package containing the class and imported it manually in the project/script using Maven/Gradle/ecc.

Do not trust Sun libraries!

If the same error happens again add this line to the script:

@file:DependsOn("com.sun.xml.stream:sjsxp:1.0.2")
#5

The reason I moved from Java 8 to Java 12 was that in Manjaro OpenCV 4.1.0 was “prepared” to a more newer version of Java. Take a look:

$ sudo archlinux-java status
Available Java environments:
  java-12-jdk
  java-8-jdk (default)

$ java -version
java version "1.8.0_212"
Java(TM) SE Runtime Environment (build 1.8.0_212-b10)
Java HotSpot(TM) 64-Bit Server VM (build 25.212-b10, mixed mode)

$ javac -version
javac 1.8.0_212

$ javac -cp /usr/share/java/opencv4/opencv-410.jar ShowVersions.java
ShowVersions.java:1: error: cannot access Core
import org.opencv.core.Core;
                      ^
  bad class file: /usr/share/java/opencv4/opencv-410.jar(org/opencv/core/Core.class)
    class file has wrong version 55.0, should be 52.0
    Please remove or make sure it appears in the correct subdirectory of the classpath.

But if I change to Java 12…

$ sudo archlinux-java set java-12-jdk
 
$ sudo archlinux-java status
Available Java environments:
  java-12-jdk (default)
  java-8-jdk
 
$ javac -cp /usr/share/java/opencv4/opencv-410.jar ShowVersions.java
 
$ ls -lh
total 8,0K
-rw-r--r-- 1 lperacchi lperacchi 1,2K mai 20 08:24 ShowVersions.class
-rw-r--r-- 1 lperacchi lperacchi  333 mai 20 08:11 ShowVersions.java
$

As you can see, everything goes fine with Java 12 and OpenCV 4.1.0 in Manjaro.

With the code below:

#!/usr/bin/kotlinc -script

@file:DependsOn("org.openpnp:opencv:3.4.2-1")

import org.opencv.core.Core

System.loadLibrary(Core.NATIVE_LIBRARY_NAME)
println("Hello OpenCV " + Core.VERSION + "!")
println("Hello to you too, Java " + System.getProperty("java.version") + "!")

The output is (with Java 12):

$ ./ShowVersion.kts 
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.intellij.openapi.util.JDOMUtil$2 to constructor com.sun.xml.internal.stream.XMLInputFactoryImpl()
WARNING: Please consider reporting this to the maintainers of com.intellij.openapi.util.JDOMUtil$2
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
ShowVersion.kts:3:7: error: unresolved reference: DependsOn
@file:DependsOn("org.openpnp:opencv:3.4.2-1")
      ^
ShowVersion.kts:5:12: error: unresolved reference: opencv
import org.opencv.core.Core
           ^
ShowVersion.kts:7:20: error: unresolved reference: Core
System.loadLibrary(Core.NATIVE_LIBRARY_NAME)
                   ^
ShowVersion.kts:8:27: error: unresolved reference: Core
println("Hello OpenCV " + Core.VERSION + "!")

And this other one (with Java 8):

$ ./ShowVersion.kts 
ShowVersion.kts:3:7: error: unresolved reference: DependsOn
@file:DependsOn("org.openpnp:opencv:3.4.2-1")
      ^
ShowVersion.kts:5:12: error: unresolved reference: opencv
import org.opencv.core.Core
           ^
ShowVersion.kts:7:20: error: unresolved reference: Core
System.loadLibrary(Core.NATIVE_LIBRARY_NAME)
                   ^
ShowVersion.kts:8:27: error: unresolved reference: Core
println("Hello OpenCV " + Core.VERSION + "!")
#6

I think you original problem is the Sun XML library, try adding it manually to your classpath using this jar.

For the script, I misread the manual! To make it work you need the kotlin-main-kts.jar in your classpath:

#!/usr/bin/env kotlinc -cp <path/to/kotlin-main-kts.jar> -script

Notice the /usr/bin/env. On macOS env correctly splits the arguments for the bash. It is not always true when using other executables to handle the shebang (I think you did not encounter this problem, but you never know!).

I do realize that this jar stuff adds complexity to the script and it’s may vanify the effort of simplifying stuff. My bad!

#7

Unfortunately doesn’t work either on macOS either in Manjaro Linux.

I got “error: invalid argument”. I also checked “kotlinc -help” and syntax appear to be ok.

Can you make this last simple example run on your computer?

I can’t understand why the simple example runs smoothly on Java and not on Kotlin (should not be compatible?).

#8

Seemes related to https://youtrack.jetbrains.com/issue/KT-30387
I’ll try with JDK11

EDIT:
Using JDK11 the XML problem goes away. The script problems still persists tho. Tomorrow I will try to make it work in a normal Kotlin JVM project

#9

Turns out you were not loading properly the library!
When you call System.loadLibrary(Core.NATIVE_LIBRARY_NAME) you are just asking the JDK to look for a library called something like opencv_java342 in the environment path. Of course the .so/.dll/.dylib is not in the path but inside the jar! It needs to be extracted and loaded using an absolute path!

There is a class inside OpenCV library thar does exactly that!

import nu.pattern.OpenCV
import org.opencv.core.Core

fun main() {
    OpenCV.loadLocally()
    println("Hello OpenCV ${Core.VERSION}!")
    println("Hello to you too, Java ${System.getProperty("java.version")}!")
}

Output:

Hello OpenCV 3.4.2!
Hello to you too, Java 12!

It works using JDK 12, 11 and 8!

For the script, I think something Is just not working with the classpath. I’ll investigate later :slight_smile: