We are using Tess4J/Tesseract to perform OCR on a webapp. On Windows everything works fine but when deployed on a Linux machine(CentOS 6.8) the program crashes and automatically kill the Apache tomcat server.
We are read more than one file(different file) simultaneously.if we run the OCR it running approximately 1 minutes after it through fatal error. Can you please suggest how to resolve?
A fatal error has been detected by the Java Runtime Environment:
SIGSEGV (0xb) at pc=0x00007f7d5934ff90, pid=17649,
tid=140176377489152
JRE version: Java(TM) SE Runtime Environment (8.0_60-b27) (build 1.8.0_60-b27)
Java VM: Java HotSpot(TM) 64-Bit Server VM (25.60-b23 mixed mode linux-amd64 compressed oops)
Problematic frame:
C [libtesseract.so.3.0.2+0x22cf90] tesseract::HistogramRect(unsigned char const*, int, int, int, int, int, int, int*)+0x70
Failed to write core dump. Core dumps have been disabled. To enable core dumping, try ulimit -c unlimited before starting Java again
I fixed it by resizing the image to a fixed size (you could do a percentage resize I guess) in javacv before passing it to tess4j.
Example of my resize method.
public static IplImage resize(IplImage img_source){
IplImage resized = IplImage.create(600, 480, img_source.depth(), img_source.nChannels());
cvResize(img_source,resized);
return resized;
}
Then I do my tesseract extraction below:
public static String extract(BufferedImage bi, Rectangle r) throws CvHandler, IOException, TesseractException{
ITesseract tess = new Tesseract();
String tessPath = getTess();
tess.setPageSegMode(1);
tess.setLanguage("eng");
tess.setDatapath(tessPath);
tess.setOcrEngineMode(TessOcrEngineMode.OEM_DEFAULT);
tess.setTessVariable("load_system_dawg", "false");
tess.setTessVariable("load_freq_dawg", "false");
tess.setTessVariable("tessedit_create_hocr", "0");
tess.setTessVariable("tessedit_char_whitelist","ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
String result = "";
if (!r.getBounds().isEmpty()){
try{
result = tess.doOCR(bi, r);
}catch(TesseractException e){
throw new CvHandler(e.getMessage());
}
}else result = tess.doOCR(bi);
return result;
}
Helper method for converting IplImage to BufferedImage Source:
public static BufferedImage convertIplToBuffered(IplImage img){
OpenCVFrameConverter.ToIplImage grabberConverter = new OpenCVFrameConverter.ToIplImage();
Java2DFrameConverter paintConverter = new Java2DFrameConverter();
Frame frame = grabberConverter.convert(img);
BufferedImage img_result = paintConverter.getBufferedImage(frame,1);
return img_result;
}
Related
I am writing a Java application that executes USB I/O on Windows 10 with Java 10 JDK. My i/o library is the libusb api. The app loads a JNI library .dll compiled/built in Visual Studio 2017 with both Microsoft Visual 2013 C++ and Microsoft Visual C++ 2015-2019 installed. This application generates the following crash warning when ran inside a Netbeans 11.1 project.
EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x000002e1bbcd122e, pid=444, tid=3244
JRE version: Java(TM) SE Runtime Environment (10.0+46) (build 10+46)
Java VM: Java HotSpot(TM) 64-Bit Server VM (10+46, mixed mode, tiered, compressed oops, g1 gc, windows-amd64)
Problematic frame:
C [VCRUNTIME140D.dll+0x122e]
The problem function is my USB read function within the C code in the VisualStudio-built .dll.
JNI.c
JNIEXPORT jint JNICALL Java_SWMAPI_IoUSB_read
(JNIEnv * env, jobject thisObj, jbyteArray jArr, jint size, jlong javaHandle){
//Convert jLong to device handle pointer
libusb_device_handle * handle = (libusb_device_handle * )javaHandle;
//jbyteArray jArr: byte [] buffer. Must be overwritten with USB read data from device with
// corresponding handle javaHandle.
//jlong javaHandle: long passed from java representing the device to I/O.
//jint size: jArr length (e.g. buffer.length), used for debugging.
int status; //return code value
int bytes_written;
int bytes_read;
MESSAGE Message; //C struct containing 9 bytes of message header and (n) bytes of payload.
int length = (*env)->GetArrayLength(env, jArr);
jbyte * bufferIn = (*env)->GetPrimitiveArrayCritical(env, jArr, NULL); //get memory from jArr
//copy data in bufferIn to a Message struct.
memcpy((unsigned char *)&Message, (unsigned char *)bufferIn, length);
//write the Message out to device
bytes_written = libusb_control_transfer(handle, CTRL_OUT, MEM_RQ, 0, 0, (unsigned char *)&Message, length, 0);
if(bytes_written == length){
//read data in from Device to &Message
bytes_read = libusb_control_transfer(handle, CTRL_IN, MEM_RQ, 0, 0, (unsigned char
*)&Message, sizeof(MESSAGE), 30000);
status = (bytes_written < 0) ? bytes_read : // I/O Error
(bytes_written < length) ? USB_STATUS_SHORT_WRITE : // Check for short write
LIBUSB_SUCCESS;
if(Message.payload.length >= 0){ //If read data > 0
printf("Attempting memcpy\n");
fflush(stdout);
// The error appears on the line below
memcpy((unsigned char *)bufferIn,(unsigned char *)&Message, MAX_HEADER_LENGTH +
Message.payload.length);
printf("Success memcpy\n");
fflush(stdout);
}
}
(*env)->ReleasePrimitiveArrayCritical(env, jArr, bufferIn, 0);
return status;
}
This function has been working for my project up until today. Its frequency is approx 1 out of 3 runs. Can anyone please advise me on how I can move forward to solving this issue?
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x5f0c25fe, pid=14780, tid=11168
#
# JRE version: Java(TM) SE Runtime Environment (7.0_80-b15) (build 1.7.0_80-b15)
# Java VM: Java HotSpot(TM) Client VM (24.80-b11 mixed mode, sharing windows-x86 )
# Problematic frame:
# C [ZBRGraphics.dll+0x25fe]
I keep getting this error when using the Zebra printer DLL in Java program.
public class Tester {
public static void main(String[] args) {
ZBRGraphics zGraphics = ZBRGraphics.INSTANCE;
String text = "Print this";
byte[] textB = text.getBytes(StandardCharsets.UTF_8);
String font= "Arial";
byte[] fontB = text.getBytes(StandardCharsets.UTF_8);
System.out.println(zGraphics.ZBRGDIDrawText(0, 0, textB, fontB, 12, 1, 0x0FF0000, 0));
}
}
public interface ZBRGraphics extends Library {
ZBRGraphics INSTANCE = (ZBRGraphics) Native.loadLibrary("ZBRGraphics", ZBRGraphics.class);
int ZBRGDIDrawText(int x, int y, byte[] text, byte[] font, int fontSize, int fontStyle, int color, int err);
}
I have the DLL in C:\Windows\System32 and in my 32 bit Java .
I'm using a 64 bit machine as my laptop for development.
If my google-fu skills are any good, you appear to be interfacing with the Zebra printer's API. According to the "ZXP1 & ZXP3 Software Developers Reference Manual" (found here), the Java mapping of the function is incorrect.
This is the actual C function prototype:
int ZBRGDIDrawText(
int x,
int y,
char *text,
char *font,
int fontSize,
int fontStyle,
int color,
int *err
)
As you can see, err is not an int, but a pointer to one. Also, since text and font are strings, you can just use a String as the Java type. Additionally, the API docs say that the return value is an int with either 1 for success or 0 for failure, meaning that you can use a boolean for ease of use.
The following Java mapping should be correct:
boolean ZBRGDIDrawText(
int x,
int y,
String text,
String font,
int fontSize,
int fontStyle,
int color,
IntByReference err
);
and you might use it like so:
IntByReference returnCode = new IntByReference(0);
boolean success = zGraphics.ZBRGDIDrawText(
0,
0,
"Print this",
"Arial",
12,
1,
0x0FF0000,
returnCode
);
if (success) {
System.out.println("success");
} else {
System.out.println("ZBRGDIDrawText failed with code " + returnCode.getValue());
}
I have in my Java class a string variable:
public class myclass {
protected final String file;
myclass(String f) {
file = f;
}
public native void processFiles();
public static void main(String[] args) {
myclass mc = new myclass(args[0]);
mc.processFiles();
}
}
In C++, I have:
JNIEXPORT void JNICALL Java_myclass_processFiles(JNIEnv *env, jobject obj) {
jclass baseClass = env->GetObjectClass(obj);
jfieldID fid = env->GetFieldID(baseClass, "file", "Ljava/lang/String;");
jchar filename = env->GetCharField(baseClass, fid);
jstring fileFieldString = env->NewString(&filename, sizeof(filename));
const char *nativeFileString = env->GetStringUTFChars(fileFieldString, NULL);
printf("JNI: File path: %s\n", nativeFileString);
env->ReleaseStringUTFChars(fileFieldString, nativeFileString);
}
My output is:
JNI: File path: ??2
What am I doing wrong that isn't converting the Java string to the char* string properly? I am providing the path ~/Desktop/myfile as the sole argument, so there is a value in args[0]. My thought was that sizeof(filename) wasn't right, but there's no other option from what I can tell.
I did try this: JNI. How to get jstring from jobject and convert it to char* but when I typecast the result from GetObjectField() to jstring, I get an error:
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGSEGV (0xb) at pc=0x00000001043111e8, pid=6191, tid=3591
#
# JRE version: Java(TM) SE Runtime Environment (8.0_45-b14) (build 1.8.0_45-b14)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.45-b02 mixed mode bsd-amd64 compressed oops)
# Problematic frame:
# V [libjvm.dylib+0x3111e8] jni_GetStringUTFChars+0x66
#
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
Also, this is just JNI and Java 8 on OSX, nothing related to Android.
Thank you.
Update:
I was able to get a friend to look at it, and got it working with:
jfieldID fid = env->GetFieldID(baseClass, "file", "Ljava/lang/String;");
jstring jstr = (jstring) env->GetObjectField(thiz, fid);
const char *nativeFileString = env->GetStringUTFChars(jstr, NULL);
printf("JNI: File path: %s\n", nativeFileString);
You are doing jchar filename = env->GetCharField(baseClass, fid);
But fid is a field of type Ljava/lang/String;, not a char. So you should get that String using env->GetObjectField() and then follow what that link says.
You can also debug this better by adding env->ExceptionDescribe() after every line to see if an Exception is being thrown after your calls to env (that would be just to debug, in real production code you should be checking for exceptions after every env call and do something if something goes wrong).
By the way, maybe your code is just an example, but if that's your real code, it would be a lot easier to declare the native method as static and just pass the string as a parameter.
After upgrading to the latest JDK, we've got (on some machines) a strange OutOfMemoryException.
Consider this simple application:
public class Test
{
public static void main (String[] args) {
try {
java.text.SimpleDateFormat dateFormatter = new java.text.SimpleDateFormat("E dd/MM/yyyy HH:mm");
System.out.println("formatted date: " + dateFormatter.format(new java.util.Date()));
} catch (Exception x) {
System.err.println(x);
System.exit(1);
}
}
}
Running this small program will result in this exception (even when running it with -Xmx2048M -Xms2048):
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Currency.readLongArray(Currency.java:657)
at java.util.Currency.access$100(Currency.java:76)
at java.util.Currency$1.run(Currency.java:211)
at java.security.AccessController.doPrivileged(Native Method)
at java.util.Currency.<clinit>(Currency.java:192)
at java.text.DecimalFormatSymbols.initialize(DecimalFormatSymbols.java:566)
at java.text.DecimalFormatSymbols.<init>(DecimalFormatSymbols.java:94)
at java.text.DecimalFormatSymbols.getInstance(DecimalFormatSymbols.java:157)
at java.text.NumberFormat.getInstance(NumberFormat.java:767)
at java.text.NumberFormat.getIntegerInstance(NumberFormat.java:439)
at java.text.SimpleDateFormat.initialize(SimpleDateFormat.java:664)
at java.text.SimpleDateFormat.<init>(SimpleDateFormat.java:585)
at java.text.SimpleDateFormat.<init>(SimpleDateFormat.java:560)
at Test.main(Test.java:5)
Could someone please explain this to me?
The java version we are using is:
java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)
When we are using a prior version, we have no problem:
java -version
java version "1.7.0_25"
Java(TM) SE Runtime Environment (build 1.7.0_25-b16)
Java HotSpot(TM) 64-Bit Server VM (build 23.25-b01, mixed mode)
java Test
formatted date: ma 21/10/2013 10:19
Update: Before everyone is mentioning increasing the heap size... I already tried it:
java -Xmx2048M -Xms2048M Test
Runtime.getRuntime().freeMemory(): 2048120480
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Currency.readLongArray(Currency.java:657)
at java.util.Currency.access$100(Currency.java:76)
at java.util.Currency$1.run(Currency.java:211)
at java.security.AccessController.doPrivileged(Native Method)
at java.util.Currency.<clinit>(Currency.java:192)
at java.text.DecimalFormatSymbols.initialize(DecimalFormatSymbols.java:566)
at java.text.DecimalFormatSymbols.<init>(DecimalFormatSymbols.java:94)
at java.text.DecimalFormatSymbols.getInstance(DecimalFormatSymbols.java:157)
at java.text.NumberFormat.getInstance(NumberFormat.java:767)
at java.text.NumberFormat.getIntegerInstance(NumberFormat.java:439)
at java.text.SimpleDateFormat.initialize(SimpleDateFormat.java:664)
at java.text.SimpleDateFormat.<init>(SimpleDateFormat.java:585)
at java.text.SimpleDateFormat.<init>(SimpleDateFormat.java:560)
at Test.main(Test.java:8)
I dug into the code of java.util.Currency. It reads in a resource file located in the Java JRE, in the static Class initializer block.
String homeDir = System.getProperty("java.home");
try {
String dataFile = homeDir + File.separator +
"lib" + File.separator + "currency.data";
DataInputStream dis = new DataInputStream(
new BufferedInputStream(
new FileInputStream(dataFile)));
if (dis.readInt() != MAGIC_NUMBER) {
throw new InternalError("Currency data is possibly corrupted");
}
formatVersion = dis.readInt();
if (formatVersion != VALID_FORMAT_VERSION) {
throw new InternalError("Currency data format is incorrect");
}
dataVersion = dis.readInt();
mainTable = readIntArray(dis, A_TO_Z * A_TO_Z);
int scCount = dis.readInt();
scCutOverTimes = readLongArray(dis, scCount);
As you can see, it reads in JRE/lib/currency.data. The fourth integer contains the scCount. This integer will be too high. I guess that that file is corrupt. Try replacing it.
My Processing code is below.
import hypermedia.video.*;
import processing.video.*;
import java.awt.Rectangle;
OpenCV opencv;
int width = 320;
int height = 240;
void setup() {
size( 320, 240 ); //set window size
opencv = new OpenCV( this ); //setup openCV
opencv.capture( width, height ); // open video stream
opencv.cascade( OpenCV.CASCADE_FRONTALFACE_ALT );
}
void draw(){
opencv.read();
image(opencv.image(), 0, 0);
Rectangle[] faces = opencv.detect( 1.2, 2, OpenCV.HAAR_DO_CANNY_PRUNING, 40, 40 );
noFill();
stroke(255,0,0);
for( int i=0; i<faces.length; i++ ) {
rect( faces[i].x, faces[i].y, faces[i].width, faces[i].height );
}
}
This code works for few seconds then an exception occurs.
#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x6d961b22, pid=232, tid=4008
#
# JRE version: 6.0_33-b03
# Java VM: Java HotSpot(TM) Client VM (20.8-b03 mixed mode windows-x86 )
# Problematic frame:
# V [jvm.dll+0xa1b22]
#
# An error report file with more information is saved as:
# C:\Documents and Settings\Administrator\Desktop\processing-2.0b7\hs_err_pid232.log
#
# If you would like to submit a bug report, please visit:
# http://java.sun.com/webapps/bugreport/crash.jsp
#
i think it is a memory allocation issue with opencv_core.CvMemStorage. this is a class that's not exposed in your library. i am having the same problem. I'm using javacv (directly, not javacvpro) because i want to run multiple haar cascades (you can load only one with javacvpro or the older hypermedia.video.*). if i run ALL of them on EACH frame, i'm good. if i run a different detector on each frame (and then cycle thru the detectors again), i get this error after a few cycles.
the snippet below FAILS, but it is what i want to do (process each subsequent frame with a different detector):
// Using JavaCV directly, not JavaCVPro!
import com.googlecode.javacpp.Loader;
import com.googlecode.javacv.*;
import com.googlecode.javacv.*;
import com.googlecode.javacv.cpp.*;
String cascades[] = {
"haarcascade_eye.xml", // 0
"haarcascade_eye_tree_eyeglasses.xml", // 1
"haarcascade_frontalface_alt.xml" // 2
}
int detectors[] = {1,3};
// haar detectors to use
String cascPath = "C:/opencv/data/haarcascades/";
// preload multiple classifiers. can do this with javacvpro, not with javacv or hypermedia.video.*
void haarSetup() {
for (int i = 0; i < detectors.length; i++) {
String classifierFile = cascPath+cascades[detectors[i]];
classifier[i] =
new opencv_objdetect.CvHaarClassifierCascade(opencv_core.cvLoad(classifierFile));
}
storage = opencv_core.CvMemStorage.create();
opencv_core.cvClearMemStorage(storage); // is this needed? couldn't hurt, right?
}
// contains list of preloaded haar cascades. code not included here...
opencv_core.CvSeq features[] = new opencv_core.CvSeq[detectors.length];
int whichHaar = 0;
// run one cascade per frame, then cycle through them.
void processHaars(PImage piz) {
// convert to IplImage...
BufferedImage imgBuf = (BufferedImage) piz.getNative();
opencv_core.IplImage iplImgOut=opencv_core.IplImage.createFrom(imgBuf);
// do one haar cascade per invocation.
int ii = whichHaar;
features[ii] = opencv_objdetect.cvHaarDetectObjects(iplImgOut, classifier[ii], storage, 1.1, 3, opencv_objdetect.CV_HAAR_DO_CANNY_PRUNING);
whichHaar++;
if (whichHaar >= detectors.length){
whichHaar = 0;
// is THIS causing the problem??
opencv_core.cvClearMemStorage(storage);
}
}
this snippet WORKS forever, but i don't want what it does (run all detectors on a single frame):
void processHaars(PImage piz) {
// convert to IplImage...
BufferedImage imgBuf = (BufferedImage) piz.getNative();
opencv_core.IplImage iplImgOut=opencv_core.IplImage.createFrom(imgBuf);
for (ii=0; ii<detectors.length; ii++)
faces[ii] = opencv_objdetect.cvHaarDetectObjects(iplImgOut, classifier[ii], storage, 1.1, 3, opencv_objdetect.CV_HAAR_DO_CANNY_PRUNING);
opencv_core.cvClearMemStorage(storage);
}
If i find a full solution, I'll post it.