# jni und opencv



## Vorby (21. Sep 2006)

Guten Tag ihr lieben

um erstmal mein porblem zu schildern, vorweg was ich vor habe.
ich habe ein Java Programm erstellt, womit ich ein JPG file einlesen kann - diese File wird dann gespeichert in RGB-werten (Arrays) [Es wir auch angezeigt (JFrame)]
Weiterhin möchte ich das Bild (Arrays) weiter bearbeiten.
Dafür eignet sich openCV recht gut. Die nahliegendste Möglichkeit OpenCV und Java zusammenzubringen ist jni?!
Am anfang habe ich nur mit c++ das bild Heller/Dunkler gemacht. Einfach Array werte plus oder minus einen Betrag.

In OpenCV ist diese sache schon impl. -> Bsp: cvConvertScale( InImg, OutImg, 1, S1_Value);

So nun mal zu meinem Problem:
Dieses ist mein Bild in Java:


```
public class ColorImage {
    public int Width = 0;
    public int Height = 0;
    public int ColorType = 0;			// 0 - RGB, 1 - YCrCb, 2 - HSV
    
    public float C1[];					// R; Y; H
    public float C2[];					// G; Cr; S
    public float C3[];					// B; Cb; V
    public float Mask[];

    /** Creates a new instance of ColorImage */
    public ColorImage() { }    
    public ColorImage(int w, int h) {
        C1 = new float[w * h];
        C2 = new float[w * h];
        C3 = new float[w * h];
        Mask = new float[w * h];
        Width = w;
        Height = h;
        ColorType = 0;
        
    }
}
```

der Nativer aufruf lautet:
JNIEXPORT void JNICALL Java_NewJFrame_CThresholdSegmentationInv
(JNIEnv* env, jobject thisObj, jobject Image, jfloat h){

Mein Objekt ist einmal das Bild (in C1 C2 C3 = RGB) und der float-wert (floar für änderung der Helligkeit)

...es ist kein Thema die C1 C2 C3 etc. Werte Auszulesen

in der CPP file steht folgendes:

```
// Klasse bestimmen
	jclass image = env->GetObjectClass(Image);

	// get Field-IDs
	jfieldID C1_fid = env->GetFieldID(image, "C1", "[F"); // "[" bedeutet ein Feld / "F" ist ein Float
	jfieldID C2_fid = env->GetFieldID(image, "C2", "[F");
	jfieldID C3_fid = env->GetFieldID(image, "C3", "[F");
	
	//RGB C1 C2 C3
	// get C1 from Image

	jfloatArray c1 = (jfloatArray)(env->GetObjectField(Image, C1_fid));		// cast jintArray from jObject !!!!
	jfloat* C1 = env->GetFloatArrayElements(c1, &isCopyC1);								// get the array (copy)

	// get C2 from Image
	jfloatArray c2 = (jfloatArray)(env->GetObjectField(Image, C2_fid));		// cast jintArray from jObject !!!!
	jfloat* C2 = env->GetFloatArrayElements(c2, &isCopyC2);								// get the array (copy)

	// get C3 from Image
	jfloatArray c3 = (jfloatArray)(env->GetObjectField(Image, C3_fid));		// cast jintArray from jObject !!!!
	jfloat* C3 = env->GetFloatArrayElements(c3, &isCopyC3);								// get the array (copy)

	// get Width from Image
	jint Width = env->GetIntField(Image, env->GetFieldID(image, "Width", "I"));
	// get Height from Image
	jint Height = env->GetIntField(Image, env->GetFieldID(image, "Height", "I"));
```

mein Problem ist nun!
cvConvertScale von OpenCV erwartet den Typ - *IplImage

Bsp.:
IplImage *Image;
dann kann man nämlich Image->height; Image->width; aufrufen
also ein Zeiger auf Höhe / Breite und auch dem Array mit den Information über das Bidl (Pixel)

ich dagegen kann nur in meiner CPP habe nur Höhe / Breite direkt in in einem Int wert (bzw jint)

Wie kann ich den gleich Typ (evtl aus Java) direkt "übergeben". Oder kann ich mir den in C Basteln?
so das ich z.b. den aufruf cvConvertScale( (*IplImage)InImg,(*IplImage)OutImg, 1, S1_Value); casten kann.

Ich habe schon versucht, einfach in dem CPP-File: 
IplImage *Image;
und dann Image->height=(int)Height; //aber hier schmiert mir dann schon Java ab nach aufruf der dll

kann ich keine neuen Objekte erzugen? so als Übergangslösung?

Ich hoffe es ist versändlich was ich hier will 
Ansonsten gern noch mal nachfragen.

Bedanken möchte ich mich jetzt schon ganz gerne, bei allen die sich mit dem Thema auseinandersetzen.


----------



## Anmeldeboykottierer (21. Sep 2006)

Hi,
ich glaube Java Objekte direkt unter C verwenden zu wollen ist eine ganz schlechte Lösung. 
Natürlich kannst du jetzt sagen, dass du C++ verwendest, aber JNI ist eindeutig zu C kompatibel und damit haben wir wohl den kleinsten gemeinsamen Nenner.

Das Problem ist, dass du hier zwei völlig verschiedene Welten vereinen möchtest. C kennt gar keine Objekte und C++ sicherlich andere als Java.
Deshalb solltest du lieber den Umweg über die primitiven Datentypen gehen. Direkte Zugriffe bringen immer verschiedene Gefahren mit sich. Das siehst du schon allein an der Arraybenutzung unter C mit JNI. Da du hier schon die Byteorder beachten müsstest und C mit einem direkten Zeiger auf eine Adresse arbeitet, wird immer der Umweg über die lokalen Kopien verwendet, die dann auch in ein Objekt zurück geschrieben werden. Allein das fehlen der Länge eines solchen C Arrays spricht ja auch für sich.

Ja, auch die Objekte sehen entsprechend unterschiedlich aus. Java Objekte sind und bleiben natürlich immer Nachfahren von Object und diese Klasse hat bestimmte Eigenschaften, die ein C++ Objekt so nicht kennt (und vererbt). Dazu kommt noch, dass Java auf einer virtuellen Maschine läuft, auf dieser kann alles anders aussehen, als in der nativen C++ Welt (hier unterscheiden sich ja auch viele Details von Plattform zu Plattform). 
Deshalb würde ich dir empfehlen, dass du hier mit nativen C/C++ Strukturen oder Objekten arbeitest. Diese kannst du dann über setter einfach in ein Java Objekt übertragen. Ist nicht gerade der perfomanteste Weg, aber ich fürchte der einzigste der sicher funktioniert (vielleicht ja auch nur ein fall-back falls du nicht weiterkommst oder eine Basis?!).
Wobei ich auch hier sagen muss, dass die eigentlichen Flaschenhälse ja auch eher in der Bearbeitung der Bilder liegt, was die OpenCV Bibliothek natürlich schön reduziert. Das kopieren von Speicher ist nun wirklich eine schnelle Sache.

Also ich hab mal eine Tiff-Bibliothek in Delphi geschrieben, die konnte 16 Bit Graustufen Tiffs ohne Komprimierung, die so ca. 120-150 MByte hatten in < 200 ms einlesen. Die Vorgänger Bibliothek (ohne API Funktionen zum direkten kopieren der Pixeldaten) brauchte hier etwas über eine Minute.

Wenn du also per JNI ein Array übergibst, dürfte Java schon zu einem sehr OS nahen Befehl greifen und das geht echt flink! Deswegen solltest du es wirklich mit der Sicherheit der primitiven Datentypen versuchen und dann mal schauen, ob die Performance wirklich leidet (der overhead für viele Daten ist sicherlich kaum größer als für ein einzelnes Datum auf das per JNI zugegriffen wird).

Gruß Der Anmeldeboykottierer


----------



## Vorby (22. Sep 2006)

ersteinmal sehr herzlich danke für deine Antwort.


Prinzipell hab ich schon verstanden was du mir sagen möchtest.
(muss es aber sicher noch einmal durchlesen, früh am morgen ;-))

Also ich möchte mit dem Array arbeiten (auch mit höhe, breite und ein paar int werten, was ich brauch zum Arbeiten mit Bildern)
und da es in OpenCV schon fertig Funktionen gibt, wollt ich diese nutzen.

Ich schreib mal so als pseudokode, da ich noch keinen fertigen ansatz habe, wie ich mir das vorstelle!
Also die Höhe habe ich z.b. ja schon aus dem Array:

```
jint Height = env->GetIntField(Image, env->GetFieldID(image, "Height", "I"));
```

Nun hab ich mir ein IplImage angelegt: und Höhe und Breite übergeben

```
IplImage *InImage=new IplImage;
InImage->height=Height;
```
 etc.

scheint soweit ok zu sein (werde das noch genauer prüfen müssen)

nun gibt es die Funktion cvConvertScale - diese erwarte ein "InImage" und ein "OutImage"
OutImage, kann se haben - dacht ich mir -> 
	
	
	
	





```
IplImage *OutImage=new IplImage;
```

man (muss==sollte?) diese OutImage ínitialisieren (in OpenCV macht man es halt)


```
OutImage = cvCreateImage(cvSize(InImage->width, InImage->height), IPL_DEPTH_8U, 1);
```

so und da liegt nun der Hase im Pfeffer !
Die Initialisierung scheint probleme zu machen.
C-Compiler kompiliert alles (selbstverständlich mit angaben der OpenCV Libs / includs)
Java bricht aber einfach ab - mit

# An unexpected error has been detected by HotSpot Virtual Machine:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x650c1438, pid=648, tid=592
#
# Java VM: Java HotSpot(TM) Client VM (1.5.0_08-b03 mixed mode, sharing)
# Problematic frame:
# C  [libNative1.so+0x1438]

Zuweisen mit der "hand" funktioniert - also OutImage->ID=0; OutImage->height=300; etc

Ich vermute auf irgnedwas mit speicherbereiche :?: 
(Diese Java-Felhermeldung kamm nämlich auch als ich "ausversehen" IplImage *InImg = 0; geschrieben habe)

Also wie du schon sagtest Anmeldeboykottierer,
ich will Arrays kopieren (sollte doch reichen den Pointer zu "kopieren")
dann mit der OpenCV funktion arbeiten (mein Output ist ja denn in mein OutImage)
und dann mein "OutImage-Array" wieder ab nach Java.

Meine IDE ist NetBeans 5.0


ich sag schonmal danke an Anmeldeboykottierer 

so long...


----------



## Anmeldeboykottierer (22. Sep 2006)

Ok, nur um noch mal sicher zu gehen (stimmt schon mit dem frühen morgen ;-))
Du legst dein Out und In Image als C++ Klassen an, sieht ja soweit richtig aus. Auch die Art, wie du die Höhe übergibst ist so, wie ich es meinte.  Was die Breite angeht, die setzt du aber auch? Falls ja ist das ganz analoge klar, falls du das vergessen hast, kann es natürlich Probleme geben.

Also ich würde sagen der einfachste Weg ist es, dass du erst ein C++ Programm schreibst, in dem alles funktioniert, du aber erstmal Höhe, Breite und das Array "per Hand" initialisierst. Dieses kannst du dann einfach linken, ausführen, sicher gehen, dass hier kein Fehler auftritt. Dann baust du für JNI einfach nur einen Wrapper, der die Höhe und Breite sowie die Daten des Arrays aus deiner JMV ausliest und an diese (getestete) Bibliothek weiter reicht. Wenn es dann noch Probleme gibt, würde es mich fast wundern, aber man kann sie dann leichter finden. 
So bleibt das Problem, dass ich nur sagen kann, dass sich dein Ansatz gut anhört (für mich) und ich das so meinte wie du es nochmal aufgeschrieben hast.

Gruß Der Anmeldeboykottierer


----------



## Vorby (23. Sep 2006)

Das ist mal eine Idee  :!: 

ich schreibe einfach erstmal das OpenCV Programm. Habe ich zwar schon, aber halt mit Ausgabefenster, was ich hier nicht brauche. Aber das kann ich ja einfach mal rauschmeissen und mir die "Ausgaben" in Zahlen anzeigen lassen.
(Also auslesen des Array etc.)

Bist also der Meinung, wenn mein C-Programm Funktioniert, kann ich es Anloag verwenden mit dem Java-Programm.
(natürlich mit jni)
Klingt gut, nur das ich festgestellt habe das es "unterschiede" geben muss.

Bsp:
In meinem Funktionsfähigen OpenCV (C++) Programm, nutze ich zum Beispiel

```
IplImage *InImage=0;
```
diese eine Zeile "funktioniert" schon nicht mehr mit Java, beendet sich dann mit einem Fehler.
Musst ich es so umgeschrieben:

```
IplImage inImage = new IplImage;
```


Naja, aber könnt ja auch an wa anderes liegen.  ???:L 

Ich werde mal das Programm in C++ Fehlerfrei schreiben.


 :toll: danke für die tollen Antworten!


schönes Wochenende
Gruss Vorby


----------



## Guest (23. Sep 2006)

Vorby hat gesagt.:
			
		

> Bist also der Meinung, wenn mein C-Programm Funktioniert, kann ich es Anloag verwenden mit dem Java-Programm.
> (natürlich mit jni)
> Klingt gut, nur das ich festgestellt habe das es "unterschiede" geben muss.
> 
> ...



Nein, da hast du mich noch nicht 100% richtig verstanden. 
Ich meine du solltest in mehreren Schritten vorgehen, du erstellst dir zuerst mal eine Datei A.c (ist jetzt nur irgendein Name!).
A enthält dabei einfach nur die Logik um mit der OpenCV Bibliothek zu arbeiten. Wenn ich dich richtig verstanden habe, dann besteht die IplImage Struktur nur aus einem Array mit den Pixelinformationen und ein wenig MetaInfos (Größe des Bildes). A erstellt ein IplImage und weißt ihm ein Array und die Größe usw. zu, diese Daten bekommt A (bzw. eine Methode in A) aber von "außen" übergeben.
A ist die einzigste Datei, die direkt mit der OpenCV zu tun hat. Nur hier taucht also z.B. IplImage auf.

Weiter geht es mit B. B ist andere Datei. B kann gar nicht direkt auf die OpenCV Bibliothek zugreifen. In B werden irgendwie die Bilddaten besort. Hier könntest du also einfach ein statisches Array haben (per Hand belegt) oder auch eine Datei laden, oder oder. 
Hier gibt es nur die Möglichkeit auf diese Daten zuzugreifen. Du kannst alle Daten holen, die benötigt werden (Höhe, Breite, Array mit Pixelinfos, ...). Natürlich kannst du die auch wieder reinschreiben, ist also nur ein Container für diese Daten.

Dann hast du noch einen Adapter C. Dieser bringt A und B zusammen. Die beiden Wissen garnichts voneinander. C nimmt nun die Daten von B und ruft eine Methode von A mit diesen Daten auf. Das ergebnis schreibt C dann wieder in B zurück. A weiß nie wo die Daten herkommen und B weiß gar nichts, speichert nur Daten.

Wenn das mit einem B klappt, dass in C++ geschrieben ist, dann kannst du hier leicht mit JNI ansetzen. Dazu ersetzt du B und C durch eine JNI Variante. Diese bekommt direkt (du legst ja eine native Methode im Java Code an) die Höhe, Breite, das Array, ... Diese Daten werden zum Aufruf einer Methode in A verwendet und das Ergebnis wird zurück an dein Javaprogramm gegeben. Da du hier nie den Code von A änderst (wo der ganze Aufruf der OpenCV liegt) dürfte es eigentlich keine Probleme geben.

Ja, dir auch ein schönes WE


----------



## Vorby (26. Sep 2006)

Hallo.

Ich habe mir gedacht, ich mach einfach mal was ganz ganz einfaches :wink: 

sehr kurzes JavaProgramm (was nix macht ausser mein native aufruft ohne Parameter)


```
public class test {
public static native void CTest();    
    public test() {      
    }
    public static void main(String[] args) {
    System.load(System.getProperty("user.dir")+System.getProperty("file.separator")+"libNative1.so");
    CTest();
   }
```

und dann in C mein ganz ganz einfach IplImage (reine Initialisierung)
Was an sich allein in C++ funktioniert!

```
#include "cv.h"
#include "highgui.h"
#include <stdio.h>
#include "test.h"

JNIEXPORT void JNICALL Java_test_CTest
  (JNIEnv *, jclass){
IplImage *InImg =0; 
InImg = cvCreateImage(cvSize(300,300), IPL_DEPTH_8U, 3);
return;
}
```
Um mal zu Zeigen wie IplImage aussieht...
aus der doku von ObenCV

```
typedefstruct_IplImage{
						IPL.H
int nSize/*sizeofiplImagestruct*/
int ID/*imageheaderversion*/
int nChannels;
int alphaChannel;
int depth;/*pixeldepthinbits*/
char colorModel[4];
char channelSeq[4];
int dataOrder;
int origin;
int align;/*4-or8-bytealign*/
int width;
int height;
struct _IplROI *roi;/*pointertoROIifany*/
struct _IplImage *maskROI;/*pointertomaskROIifany*/
void *imageId;/*useoftheapplication*/
struct _IplTileInfo *tileInfo;/*containsinformationontiling*/
int imageSize;/*usefulsizeinbytes*/
char *imageData;/*pointertoalignedimage*/
int widthStep;/*sizeofalignedlineinbytes*/
int BorderMode[4];/*thetop,bottom,left,andrightbordermode*/
int BorderConst[4];/*constantsforthetop,bottom,left,andrightborder*/
char *imageDataOrigin;/*ptrtofull,nonalignedimage*/
} IplImage;
```

so und diese miniprogramm was ja nichts macht, ausser ein IplImage initiealisiert, läuft auch nicht.
cvCreateImage arbeitet wie folgt. cvSize (per hand eingegeben 300 x 300 ) = 90000 * 3 (3 = Farbtiefe) = 270000
und dies wäre dann imageSize. Und so werden dann alle Werte "belegt".

aber das da schon ein Fehler kommt, ist nun wirklich seltsam.  :? 

Sorry das ich jetzt nicht das mit A - B - C (s.o) ausprobiert habe. Aber ich dacht mir mal, ich fang noch mal von vorne an.

Jetzt wäre ein Geistesblitz toll.

Ich hoffe jetzt auf dich Anmeldeboykottierer, da du hier anscheind der einzige im Forum bist, der diese Thema versteht.

Danke!

Gruss


----------



## Anmeldeboykottierer (26. Sep 2006)

Hi mal wieder,
ich bin mir nicht ganz sicher, aber was lädt man denn mit load? War das nicht etwas JVM Spezifisches? 

Also ich weiß nicht genau (bin zu faul selbst nachzuschauen), ob du auch System.load verwenden kannst, aber native Bibliotheken werden eigentlich mit System.LoadLibrary geladen. Versuch es mal damit, vielleicht ist dann schon ein Fehler weg.

Ansonsten schau ich mir den Beitrag auch noch mal in Ruhe an.
Gruß


----------



## Guest (26. Sep 2006)

Anmeldeboykottierer hat gesagt.:
			
		

> ... ob du auch System.load verwenden kannst, aber native Bibliotheken werden eigentlich mit System.LoadLibrary geladen. Versuch es mal damit, vielleicht ist dann schon ein Fehler weg.
> Gruß



Hab ich auch versucht System.loadLibrary("libNative1"); (habe die *.so in *.dll umbenannt, sollt ja gehen)
Fehler ist aber der selbe.

```
An unexpected error has been detected by HotSpot Virtual Machine:
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00005148, pid=272, tid=1436
# Java VM: Java HotSpot(TM) Client VM (1.5.0_08-b03 mixed mode, sharing)
```
Da ja in der IplImage struktur so ein IPL.H steht bin ich auf der suche nach dieser (ist bei OpenCV nicht dabei)
Jetzt habe ich noch "Intel Performance Library Suite" - da ist sie dabei.

Nun bin ich am lesen ob und wie man die benutzen sollte / könnte.


----------



## Vorby (26. Sep 2006)

Also ich würde sagen ich hab's.

Nur das es blöd ist  :bahnhof: 

Also wieder am Beispiel "miniprogramm": (obwohl es nicht an den Programmen liegt sonder an dem g++)

Ich kompiliere folgender massen


> c++ -c -IC:\Programme\Java\jdk1.5.0_08 -IC:\Programme\Java\jdk1.5.0_08\include\win32
> -IC:\Programme\Java\jdk1.5.0_08\include -IC:\Programme\OpenCV\cv\include -IC:\Programme\OpenCV\cvaux\include -IC:\Programme\OpenCV\cxcore\include -IC:\Programme\Intel\plsuite\include -IC:\Programme\OpenCV\otherlibs\highgui GUI1.cpp


hier sind schon die ganzen intel sachen mit drin (wusst ja nicht woher der Fehler war)

und dann habe ich die DLL bzw SO datei gebastelt:


> dllwrap --driver-name=c++ --output-def MyImage.def --add-stdcall-alias --dllname=libNative1.dll -s GUI1.o C:\Programme\OpenCV\lib\*.lib


Dies alles lief problemlos mit C - deswegen wollt ich es ja auch mit OpenCV nehmen.

die Lösung (sage es ungern) ist "Visual C++ 2005 Express Edition" -> Mircosoft
Habe nur die Command Promt


> cl -IC:\Programme\OpenCV\cv\include -IC:\Programme\OpenCV\cvaux\include -IC:\Programme\OpenCV\cxcore\include -IC:\Programme\OpenCV\otherlibs\highgui C:\Programme\OpenCV\lib\*.lib -IC:\Programme\Java\jdk1.5.0_08\include -I C:\Programme\Java\jdk1.5.0_08\include\win32 -MD -LD GUI1.cpp -libNative1.dll


so und nun scheint es zu gehen - obwohl ich das jetzt
1. nicht verstehe
2. Mircosoft nicht wollte ;-)

aber ich denk mal, ich lebe erstmal damit, und schau ob es wirklich funktioniert.

Wollt es nur mal verkünden - brauchst also erstmal nix weiter lesen ...

werde darauf noch näher eingehen (aber evtl. ist der Fehler nun lokalisiert )

so long...[/quote]


----------



## Anmeldeboykottierer (26. Sep 2006)

Hm, irgendwie scheint es da wirklich Probleme mit G++ zu geben, jmd. anderes (nicht hier) hat auch gerade Probleme damit, dass VC und der BCC alles richtig machen, aber MinGW einen Fehler produziert (JNI unter C).

Schön jedenfalls, wenn es erstmal läuft!

Gruß Der Anmeldeboykottierer


----------



## Vorby (6. Okt 2006)

Hallo

Hat gar nix mehr mit dem Thema zu tun...

Kann jemand mal ein häkchen setzen - ich find das nirgends
(liegt wohl daran, das ich nur "gast" bin :wink: )


----------

